From 6a2ce11ace1d045b5f0a588cc4d0f2e6168059ca Mon Sep 17 00:00:00 2001 From: Charles Swartz Date: Mon, 16 Dec 2024 10:57:24 -0500 Subject: [PATCH] feat: bundle pyright inside wheel See original https://github.com/RobertCraigie/pyright-python/pull/300 --- .github/workflows/release.yml | 6 +++- .github/workflows/test.yml | 27 ++++++++++++-- dev-requirements.txt | 1 + pyproject.toml | 3 +- scripts/download_pyright.py | 68 +++++++++++++++++++++++++++++++++++ setup.py | 2 +- src/pyright/_utils.py | 6 ++++ tests/test_main.py | 8 ++++- tests/utils.py | 14 ++++++++ 9 files changed, 129 insertions(+), 6 deletions(-) create mode 100644 scripts/download_pyright.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dee8619..25f9118 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,12 +17,16 @@ jobs: - name: Set up Python 3.9 uses: actions/setup-python@v2 with: - python-version: 3.9 + python-version: "3.12" - name: Install dependencies run: | python -m pip install -r dev-requirements.txt + - name: Download pyright dist + run: | + python scripts/download_pyright.py + - name: Get version run: | echo "NEW_VERSION=$(python .github/scripts/get_version.py --compare)" >> $GITHUB_ENV diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 959d096..4d4c092 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,7 +25,7 @@ jobs: matrix: os: [ubuntu-latest, macos-12, windows-latest] node: [14, 16] - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.12'] steps: - uses: actions/checkout@v3 @@ -45,6 +45,10 @@ jobs: python -m pip install --upgrade pip pip install -r dev-requirements.txt + - name: Download pyright dist + run: | + python scripts/download_pyright.py + - uses: actions/cache@v2 with: path: ~/.cache/pip @@ -119,6 +123,15 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e . + + - name: Download pyright dist + run: | + python scripts/download_pyright.py + - name: Docker Build uses: docker/build-push-action@v3 # https://github.com/docker/build-push-action/#inputs @@ -136,6 +149,16 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v3 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e . + + - name: Download pyright dist + run: | + python scripts/download_pyright.py + - name: Docker Build # Use --% to allow double hyphen # Caching not currently working since we don't use buildx yet, windows @@ -173,4 +196,4 @@ jobs: - name: Decide whether the needed jobs succeeded or failed uses: re-actors/alls-green@release/v1 with: - jobs: ${{ toJSON(needs) }} + jobs: ${{ toJSON(needs) }} \ No newline at end of file diff --git a/dev-requirements.txt b/dev-requirements.txt index f5d842d..b57c791 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -5,5 +5,6 @@ ruff==0.6.6 pytest==7.4.0 coverage==5.3.1 pytest-subprocess==1.5.0 +setuptools -e .[all] diff --git a/pyproject.toml b/pyproject.toml index e195cfa..b4a2479 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,8 @@ include = [ "tests", ] exclude = [ - "src/pyright/_mureq.py" + "src/pyright/_mureq.py", + "src/pyright/dist", ] pythonVersion = "3.9" diff --git a/scripts/download_pyright.py b/scripts/download_pyright.py new file mode 100644 index 0000000..6e71dba --- /dev/null +++ b/scripts/download_pyright.py @@ -0,0 +1,68 @@ +import io +import sys +import json +import shutil +import tarfile +from pathlib import Path + +import pyright +from pyright import _mureq, __pyright_version__ + +DIST_DIR = Path(pyright.__file__).parent / 'dist' + + +def _should_download() -> bool: + if '--force' in sys.argv: + return True + + pkg_path = DIST_DIR / 'package.json' + if not pkg_path.exists(): + return True + + pkg_json = json.loads(pkg_path.read_text()) + if pkg_json['version'] == __pyright_version__: + print( + f'skipping download as the current pyright version ({__pyright_version__}) is already downloaded. use --force to override' + ) + return False + + return True + + +def download_tarball(*, version: str) -> None: + if not _should_download(): + return + + if DIST_DIR.exists(): + shutil.rmtree(DIST_DIR) + + rsp = _mureq.get(f'https://registry.npmjs.org/pyright/{version}') + rsp.raise_for_status() + + info = rsp.json() + tar_url = info['dist']['tarball'] + print(f'downloading tar from {tar_url}') + + rsp = _mureq.get(tar_url) + rsp.raise_for_status() + + with tarfile.open(fileobj=io.BytesIO(rsp.body)) as tar: + members = tar.getmembers() + + # npm tarballs will always output one `package/` directory which is + # not necessary for our case, so we strip out the `package/` prefix + for member in members: + if member.path.startswith('package/'): + member.path = member.path.replace('package/', '', 1) + else: + raise RuntimeError(f'expected tar member path to start with `package/` but got {member.path}') + + tar.extractall(path=DIST_DIR, members=members) + + +def main() -> None: + download_tarball(version=__pyright_version__) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/setup.py b/setup.py index 6d0278f..8db5069 100755 --- a/setup.py +++ b/setup.py @@ -46,7 +46,7 @@ include=['pyright', 'pyright.*'], ), package_dir={'': 'src'}, - package_data={'': ['py.typed']}, + package_data={'': ['py.typed', 'dist/**']}, python_requires='>=3.7', include_package_data=True, zip_safe=False, diff --git a/src/pyright/_utils.py b/src/pyright/_utils.py index e43b4b7..9d2c45c 100644 --- a/src/pyright/_utils.py +++ b/src/pyright/_utils.py @@ -43,6 +43,12 @@ def install_pyright(args: tuple[object, ...], *, quiet: bool | None) -> Path: + 'Please install the new version or set PYRIGHT_PYTHON_FORCE_VERSION to `latest`\n' ) + if version == __pyright_version__: + bundled_path = Path(__file__).parent.joinpath('dist') + if bundled_path.exists(): + log.debug('using bundled pyright at %s', bundled_path) + return bundled_path + cache_dir = ROOT_CACHE_DIR / version cache_dir.mkdir(exist_ok=True, parents=True) diff --git a/tests/test_main.py b/tests/test_main.py index b214df1..5f21bb8 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -13,8 +13,9 @@ import pyright from pyright import __pyright_version__ -from tests.utils import assert_matches +from tests.utils import assert_matches, is_relative_to from pyright.utils import maybe_decode +from pyright._utils import install_pyright if TYPE_CHECKING: from _pytest.monkeypatch import MonkeyPatch @@ -238,3 +239,8 @@ def test_package_json_in_parent_dir(tmp_path: Path, monkeypatch: MonkeyPatch) -> check=True, ) assert proc.returncode == 0 + + +def test_install_pyright_uses_bundled_by_default() -> None: + install_path = install_pyright(tuple(), quiet=None) + assert is_relative_to(install_path, Path(pyright.__file__).parent) \ No newline at end of file diff --git a/tests/utils.py b/tests/utils.py index a173e74..fab38fb 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,6 +1,11 @@ from __future__ import annotations import re +from os import PathLike +from pathlib import Path +from typing_extensions import TypeAlias + +StrPath: TypeAlias = 'str | PathLike[str]' def assert_matches(pattern: re.Pattern[str], contents: str) -> re.Match[str]: @@ -9,3 +14,12 @@ def assert_matches(pattern: re.Pattern[str], contents: str) -> re.Match[str]: raise ValueError(f'Pattern, {pattern}, did not match input: {contents}') return match + + +def is_relative_to(path: StrPath, to: StrPath) -> bool: + """Backport of Path.is_relative_to for Python < 3.9""" + try: + Path(path).relative_to(to) + return True + except ValueError: + return False \ No newline at end of file