From f7e0a91d5b60f71a587b4a56f23a20a746453a1a Mon Sep 17 00:00:00 2001 From: Donnie Adams Date: Wed, 26 Jun 2024 16:17:12 -0400 Subject: [PATCH] chore: remove packaged binary Signed-off-by: Donnie Adams --- README.md | 22 +----- gptscript/gptscript.py | 9 +-- gptscript/install.py | 170 ---------------------------------------- pyproject.toml | 3 - scripts/package | 135 +------------------------------ tests/test_gptscript.py | 9 --- tox.ini | 1 - 7 files changed, 8 insertions(+), 341 deletions(-) delete mode 100644 gptscript/install.py diff --git a/README.md b/README.md index 6283c43..f4b5724 100644 --- a/README.md +++ b/README.md @@ -15,28 +15,10 @@ pip install gptscript On MacOS, Windows X6 -### SDIST and none-any wheel installations - -When installing from the sdist or the none-any wheel, the binary is not packaged by default. You must run the -install_gptscript command to install the binary. - -```bash -install_gptscript -``` - -The script is added to the same bin directory as the python executable, so it should be in your path. - -Or you can install the gptscript cli from your code by running: - -```python -from gptscript.install import install - -install() -``` - ### Using an existing gptscript cli -If you already have the gptscript cli installed, you can use it by setting the envvar: +If you already have the gptscript cli installed, then, by default, `py-gptscript` will use it if it is on your `PATH`. +Otherwise, you can use it by setting the envvar: ```bash export GPTSCRIPT_BIN="/path/to/gptscript" diff --git a/gptscript/gptscript.py b/gptscript/gptscript.py index 614aed0..75820e7 100644 --- a/gptscript/gptscript.py +++ b/gptscript/gptscript.py @@ -1,9 +1,8 @@ import json -import platform import os +import platform from socket import socket from subprocess import Popen, PIPE -from sys import executable from time import sleep from typing import Any, Callable, Awaitable @@ -145,8 +144,4 @@ def _get_command(): if os.getenv("GPTSCRIPT_BIN") is not None: return os.getenv("GPTSCRIPT_BIN") - bin_path = os.path.join(os.path.dirname(executable), "gptscript") - if platform.system() == "Windows": - bin_path += ".exe" - - return bin_path if os.path.exists(bin_path) else "gptscript" \ No newline at end of file + return "gptscript" + (".exe" if platform.system() == "Windows" else "") diff --git a/gptscript/install.py b/gptscript/install.py deleted file mode 100644 index e20c654..0000000 --- a/gptscript/install.py +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env python3 - -import os -import platform -import shutil -import sys -import tarfile -import zipfile -from pathlib import Path - -import requests -from tqdm import tqdm - -# Define platform-specific variables -platform_name = platform.system().lower() - -machine = platform.machine().lower() -if machine in ["x86_64", "amd64"]: - arch = "amd64" -elif machine in ["aarch64", "arm64"]: - arch = "arm64" -else: - # Handle other architectures or set a default/fallback - arch = "unknown" - print(f"Warning: Unhandled architecture '{machine}'. This may not be supported.") - -gptscript_info = { - "name": "gptscript", - "url": "https://github.com/gptscript-ai/gptscript/releases/download/", - "version": "v0.8.5", -} - -pltfm = {"windows": "windows", "linux": "linux", "darwin": "macOS"}.get( - platform_name, None -) - -if platform_name == "darwin": - arch = "universal" - -suffix = {"windows": "zip", "linux": "tar.gz", "darwin": "tar.gz"}.get( - platform_name, None -) - -if not pltfm or not suffix: - print("Unsupported platform:", platform_name) - sys.exit(1) - -url = f"{gptscript_info['url']}{gptscript_info['version']}/gptscript-{gptscript_info['version']}-{pltfm}-{arch}.{suffix}" - -# Define output directory -output_dir = Path(__file__).resolve().parent / ".." / "scratch" -gptscript_binary_name = "gptscript" + (".exe" if platform_name == "windows" else "") -gptscript_binary_path = output_dir / gptscript_binary_name - -# Define Python bin directory -python_bin_dir = Path(sys.executable).parent - - -def file_exists(file_path): - return file_path.exists() - - -def download_file(url, save_path): - response = requests.get(url, stream=True) - total_size = int(response.headers.get("content-length", 0)) - block_size = 1024 - progress_bar = tqdm(total=total_size, unit="B", unit_scale=True) - - with open(save_path, "wb") as f: - for data in response.iter_content(block_size): - progress_bar.update(len(data)) - f.write(data) - - progress_bar.close() - - -def extract_zip(zip_path, extract_dir): - with zipfile.ZipFile(zip_path, "r") as zip_ref: - zip_ref.extractall(extract_dir) - - -def extract_tar_gz(tar_path, extract_dir): - with tarfile.open(tar_path, "r:gz") as tar_ref: - tar_ref.extractall(extract_dir) - - -def copy_binary_to_python_bin(binary_path, target): - shutil.copy2(binary_path, target) - print(f"Binary copied to {target}") - - -def symlink_versioned_binary_to_bin(versioned_binary_path, python_bin_dir): - symlink_name = "gptscript" + (".exe" if platform_name == "windows" else "") - symlink_path = python_bin_dir / symlink_name - - if symlink_path.is_symlink(): - existing_target = Path(os.readlink(symlink_path)) - if existing_target != versioned_binary_path: - symlink_path.unlink() # Remove the old symlink if it doesn't point to the correct versioned binary - symlink_path.symlink_to(versioned_binary_path) - print(f"Symlink updated to point to {versioned_binary_path}.") - else: - print("Symlink is already up to date.") - else: - # If the path exists but is not a symlink (i.e., a regular file or directory), remove it before creating a symlink - if symlink_path.exists(): - symlink_path.unlink() - symlink_path.symlink_to(versioned_binary_path) - print(f"Symlink created to point to {versioned_binary_path}.") - - -def install(): - versioned_binary_name = f"gptscript-{gptscript_info['version']}" + ( - ".exe" if platform_name == "windows" else "" - ) - versioned_binary_path = python_bin_dir / versioned_binary_name - - if versioned_binary_path.exists(): - print(f"{versioned_binary_name} is already installed.") - else: - if os.environ.get("GPTSCRIPT_SKIP_INSTALL_BINARY") == "true": - print("Skipping binary download") - sys.exit(0) - - # Create output directory if it doesn't exist - output_dir.mkdir(parents=True, exist_ok=True) - - # Download the file - print(f"Downloading {url}...") - download_file( - url, - output_dir - / f"gptscript-{gptscript_info['version']}-{pltfm}-{arch}.{suffix}", - ) - - # Extract the file - print("Extracting...") - if suffix == "zip": - extract_zip( - output_dir - / f"gptscript-{gptscript_info['version']}-{pltfm}-{arch}.{suffix}", - output_dir, - ) - elif suffix == "tar.gz": - extract_tar_gz( - output_dir - / f"gptscript-{gptscript_info['version']}-{pltfm}-{arch}.{suffix}", - output_dir, - ) - - # Find the extracted binary and rename/move it to the versioned name in the python bin directory - extracted_binary_path = next( - output_dir.glob(gptscript_binary_name), None - ) # Adjust the glob pattern if necessary - if extracted_binary_path: - shutil.move(str(extracted_binary_path), str(versioned_binary_path)) - print(f"Copied {extracted_binary_path} to {versioned_binary_path}") - - # Remove the output directory - print("Removing the output directory...") - shutil.rmtree(output_dir) - - # Update the symlink to point to the new version - symlink_versioned_binary_to_bin(versioned_binary_path, python_bin_dir) - - print("Installation or update completed.") - - -if __name__ == "__main__": - install() diff --git a/pyproject.toml b/pyproject.toml index fe4057c..9a840dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,9 +25,6 @@ dependencies = [ "Issues" = "https://github.com/gptscript-ai/py-gptscript/issues" "Repository" = "https://github.com/gptscript-ai/py-gptscript.git" -[project.scripts] -install_gptscript = "gptscript.install:install" - [tool.wheel] [tool.setuptools] diff --git a/scripts/package b/scripts/package index b05f856..0323942 100755 --- a/scripts/package +++ b/scripts/package @@ -1,17 +1,8 @@ #!/usr/bin/env python import os -import sys -import hashlib -import requests -import zipfile -import tarfile -import shutil import subprocess - +import sys from pathlib import Path -from tqdm import tqdm -from wheel.wheelfile import WheelFile -from tempfile import TemporaryDirectory # Define the base information for the download gptscript_info = { @@ -36,7 +27,7 @@ suffixes = { def wheel_platform_tag(platform, arch): - py_arch= { + py_arch = { "universal": "universal2", "arm64": "aarch64", "amd64": "x86_64", @@ -47,68 +38,13 @@ def wheel_platform_tag(platform, arch): py_platform = { "linux": "manylinux2014", - "macOS":"macosx_10_9", - "windows":"win", + "macOS": "macosx_10_9", + "windows": "win", }[platform] return f"{py_platform}_{py_arch}" -def download_file(url, save_path): - response = requests.get(url, stream=True) - total_size = int(response.headers.get("content-length", 0)) - block_size = 1024 - progress_bar = tqdm( - total=total_size, - unit="B", - unit_scale=True, - desc=f"Downloading {url.split('/')[-1]}", - ) - - with open(save_path, "wb") as f: - for data in response.iter_content(block_size): - progress_bar.update(len(data)) - f.write(data) - - progress_bar.close() - - -def extract_archive(archive_path, extract_dir): - if archive_path.suffix == ".zip": - with zipfile.ZipFile(archive_path, "r") as zip_ref: - zip_ref.extractall(extract_dir) - elif archive_path.suffixes[-2:] == [".tar", ".gz"]: - with tarfile.open(archive_path, "r:gz") as tar_ref: - tar_ref.extractall(extract_dir) - - -def setup_directories(base_dir): - for platform_name in platform_names.values(): - for arch in platform_name["archs"]: - os.makedirs(base_dir / platform_name["name"] / arch, exist_ok=True) - - -def stage_gptscript_binaries(base_dir): - setup_directories(base_dir) - - for platform_name in platform_names.values(): - for arch in platform_name["archs"]: - pltfm_dir = platform_name["name"] - file_suffix = suffixes[pltfm_dir] - - artifact_name = f"{gptscript_info['name']}-{gptscript_info['version']}-{pltfm_dir}-{arch}.{file_suffix}" - url = f"{gptscript_info['url']}{gptscript_info['version']}/{artifact_name}" - - download_path = base_dir / pltfm_dir / arch / artifact_name - download_file(url, download_path) - - # Extract the downloaded archive - extract_archive(download_path, base_dir / pltfm_dir / arch) - - # Remove the archive file after extraction - download_path.unlink() - - def build_wheel_for_platform(output_dir): """ Build a wheel for each platform specified in platform_names. @@ -128,75 +64,12 @@ def build_wheel_for_platform(output_dir): del os.environ["PLAT"] -def file_hash_and_size(file_path): - """Compute the SHA256 hash and size of a file.""" - sha256 = hashlib.sha256() - size = 0 - with open(file_path, 'rb') as f: - while True: - data = f.read(8192) - if not data: - break - sha256.update(data) - size += len(data) - return sha256.hexdigest(), f"{size}" - - -def modify_and_repackage_wheel(wheel_path, binary_dir, platform, arch): - with TemporaryDirectory() as temp_dir: - temp_dir_path = Path(temp_dir) - with WheelFile(wheel_path, "r") as original_wheel: - original_wheel.extractall(temp_dir_path) - - # Identify the .dist-info directory - dist_info_dirs = list(temp_dir_path.glob('*.dist-info')) - if len(dist_info_dirs) != 1: - raise RuntimeError("Expected exactly one .dist-info directory") - dist_info_dir = dist_info_dirs[0] - - # Perform necessary modifications here - # Example: Adding a binary file to the .data directory - binary_name = "gptscript" + (".exe" if platform == "windows" else "") - binary_src = binary_dir / platform / arch / binary_name - data_dir = temp_dir_path / dist_info_dir.name.replace('.dist-info', '.data') / 'scripts' - data_dir.mkdir(parents=True, exist_ok=True) - shutil.copy(binary_src, data_dir / binary_name) - - # Update the RECORD file with new files - record_path = dist_info_dir / "RECORD" - with open(record_path, "a") as record_file: - binary_rel_path = data_dir.relative_to(temp_dir_path) / binary_name - hash_digest, size = file_hash_and_size(data_dir / binary_name) - record_file.write(f"{binary_rel_path},{hash_digest},{size}\n") - - platform_tag = "none-" + wheel_platform_tag(platform, arch) - new_wheel_filename = wheel_path.name.replace("none-any", platform_tag) - new_wheel_path = wheel_path.parent / new_wheel_filename - - # Repackage the wheel, overwriting the original - #new_wheel_path = wheel_path # Overwrite the original wheel or specify a new path - with WheelFile(new_wheel_path, "w") as new_wheel: - new_wheel.write_files(temp_dir_path) - - print(f"Wheel modified and saved to: {new_wheel_path}") - - def main(): base_dir = Path(__file__).resolve().parent - bin_dir = base_dir.parent / "bin" dist_dir = base_dir.parent / "dist" - stage_gptscript_binaries(bin_dir) build_wheel_for_platform(dist_dir) - # Modify and repackage wheels - wheel_files = list(dist_dir.glob("*.whl")) - for _, data in platform_names.items(): - for arch in data["archs"]: - # Find the wheel file (assuming there's only one for simplicity) - if wheel_files: - modify_and_repackage_wheel(wheel_files[0], bin_dir, data["name"], arch) - if __name__ == "__main__": main() diff --git a/tests/test_gptscript.py b/tests/test_gptscript.py index 6e2c062..debfa07 100644 --- a/tests/test_gptscript.py +++ b/tests/test_gptscript.py @@ -1,13 +1,11 @@ import os import platform -import subprocess import pytest from gptscript.confirm import AuthResponse from gptscript.frame import RunEventType, CallFrame, RunFrame, RunState, PromptFrame from gptscript.gptscript import GPTScript -from gptscript.install import install, gptscript_binary_name, python_bin_dir from gptscript.opts import GlobalOptions, Options from gptscript.prompt import PromptResponse from gptscript.run import Run @@ -80,13 +78,6 @@ def tool_list(): ] -def test_install(): - install() - bin_name = str(python_bin_dir / gptscript_binary_name) - process = subprocess.Popen([bin_name, '-v'], stdout=subprocess.PIPE, text=True) - assert process.stdout.read().startswith('gptscript version ') - - @pytest.mark.asyncio async def test_create_another_gptscript(): g = GPTScript() diff --git a/tox.ini b/tox.ini index d7abfd3..28b0a6b 100644 --- a/tox.ini +++ b/tox.ini @@ -11,5 +11,4 @@ passenv = OPENAI_API_KEY GPTSCRIPT_DISABLE_SERVER GPTSCRIPT_CONFIG_FILE commands = - install_gptscript pytest -s tests/