Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PyInstaller build spec + JSON RPC interface for client #321

Merged
merged 6 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ MANIFEST
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
!build.spec

# Installer logs
pip-log.txt
Expand Down
5 changes: 5 additions & 0 deletions kai/constants.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import pathlib
import sys

"""
This file exists because we need to define some constants - specifically file
Expand All @@ -9,6 +10,10 @@

PATH_KAI = os.path.dirname(os.path.abspath(__file__))

# pyinstaller sets sys attributes to help determine when program runs in bin
if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
PATH_KAI = sys._MEIPASS

PATH_GIT_ROOT = os.path.join(PATH_KAI, "..")

PATH_DATA = os.path.join(PATH_KAI, "data")
Expand Down
13 changes: 9 additions & 4 deletions kai/kai_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ def setup_console_handler(logger, log_level: str = "INFO"):


def setup_file_handler(
logger, log_file_name: str, log_dir: str, log_level: str = "DEBUG"
logger,
log_file_name: str,
log_dir: str,
log_level: str = "DEBUG",
silent: bool = False,
):
# Ensure any needed log directories exist
log_dir = process_log_dir_replacements(log_dir)
Expand All @@ -46,9 +50,10 @@ def setup_file_handler(
file_handler.setLevel(log_level)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
print(
f"File logging for '{logger.name}' is set to level '{log_level}' writing to file: '{log_file_path}'"
)
if not silent:
print(
f"File logging for '{logger.name}' is set to level '{log_level}' writing to file: '{log_file_path}'"
)


def initLogging(console_log_level, file_log_level, log_dir, log_file="kai_server.log"):
Expand Down
4 changes: 4 additions & 0 deletions playpen/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build/**
node_modules/**
package.json
package-lock.json
83 changes: 83 additions & 0 deletions playpen/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,86 @@
# Playpen

Playpen is intended to be a location for exploring and sharing concepts. The material created under this directory may be broken and contain approaches that end up not being useful, or the material here may help to rapidly try out a concept that ends up being incorporated into Kai.

## PyInstaller and JSON-RPC Client

The goals of this effort are:

- Figure out how we can enable communication between the Kai Client and different IDE extensions (possibly running in restricted environments) in a uniform way.
- Figure out a way to package the client into an independent binary that can be run on different platforms.

As of writing this, here's the progress we made on the goals above:

- We have a JSON-RPC interface in front of the Client CLI. The JSON-RPC interface can be found in [./client/rpc.py](./client/rpc.py). It exposes `get_incident_solutions_for_file` function that generates a fix for one file. There are two example clients (Python and Javascript) we have written that talk with the interface over I/O streams.
- We have a `build.spec` file that builds the JSON-RPC client into a binary using PyInstaller.

### Building JSON-RPC interface into a binary

Before you can build the binary, you need to activate Kai virtual environment. Once venv is activated, you need to install Kai module in the env. To install Kai module, navigate to the Kai project root and run:

```sh
pip install -e .
```

Now we install pyinstaller in current venv:

```sh
pip install pyinstaller
```

Next, we run pyinstaller to generate a binary:

```sh
pyinstaller build.spec
```

Once successful, a binary will be generated at `./dist/cli`.

### Testing JSON-RPC binary

Now that we have built our JSON-RPC interface into a binary, we will test it using a Python and a JS client that communicates. Both of these clients use a hardcoded path `./dist/cli` to run the JSON-RPC server. Make sure you have built the binary before moving forward.

#### Testing with Python client

To run the Python JSON-RPC client, install a dependency:

```sh
pip install pylspclient
```

To run the client:

```sh
python rpc-client.py <KAI_TOML_CONFIG> <APP_NAME> <ANALYSIS_OUTPUT_PATH> <INPUT_FILE_PATH>
```

See [arguments](#client-arguments) for help on arguments above.

#### Testing with JS client

To run the Javascript client, install a dependency:

```sh
npm install vscode-jsonrpc
```

To run the client:

```sh
node rpc-client.js <KAI_TOML_CONFIG> <APP_NAME> <ANALYSIS_OUTPUT_PATH> <INPUT_FILE_PATH>
```

##### Client arguments

Both the Python and JS clients take exactly the same arguments in order:

- <KAI_TOML_CONFIG>: Absolute path to the Kai config you want to use to generate fix
- <APP_NAME>: The name of the application you're analyzing
- <ANALYSIS_OUTPUT_PATH>: Absolute path to an analysis report containing incidents
- <INPUT_FILE_PATH>: Absolute path to the input file for which you want to generate incidents

When successful, both clients will print the updated file followed by the following message:

```sh
Received response successfully!
```
56 changes: 56 additions & 0 deletions playpen/build.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# -*- mode: python ; coding: utf-8 -*-

# This is a PyInstaller build spec to build Kai Client into a binary
# To run this spec, activate Kai venv and run `pyinstaller ./build.spec`

import sys
import os
from PyInstaller.building.datastruct import Tree
from PyInstaller.building.build_main import Analysis
from PyInstaller.building.api import PYZ, EXE, COLLECT
from PyInstaller.utils.hooks import collect_data_files

data_dirs = [
('../kai/data/templates', 'data/templates'),
]

script_path = 'client/rpc.py'

a = Analysis(
[script_path],
pathex=[os.path.dirname(script_path)],
binaries=[],
datas=data_dirs,
hiddenimports=["_ssl"],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
# cipher=None,
noarchive=False,
)

pyz = PYZ(a.pure, a.zipped_data)

exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name="cli",
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)

Loading
Loading