Skip to content

Commit

Permalink
PyInstaller build spec + JSON RPC interface for client (#321)
Browse files Browse the repository at this point in the history
* checkpoint

Signed-off-by: Pranav Gaikwad <pgaikwad@redhat.com>

* checkpoint

Signed-off-by: Pranav Gaikwad <pgaikwad@redhat.com>

* add JSON-RPC interface and PyInstaller build spec

Signed-off-by: Pranav Gaikwad <pgaikwad@redhat.com>

checkpoint

Signed-off-by: Pranav Gaikwad <pgaikwad@redhat.com>

* update README

Signed-off-by: Pranav Gaikwad <pgaikwad@redhat.com>

* checkpoint:

Signed-off-by: Pranav Gaikwad <pgaikwad@redhat.com>

* cleanup

Signed-off-by: Pranav Gaikwad <pgaikwad@redhat.com>

---------

Signed-off-by: Pranav Gaikwad <pgaikwad@redhat.com>
  • Loading branch information
pranavgaikwad committed Sep 3, 2024
1 parent 1e0252c commit 8863dd9
Show file tree
Hide file tree
Showing 10 changed files with 597 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,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

0 comments on commit 8863dd9

Please sign in to comment.