Skip to content

Commit f15e56f

Browse files
feat: bundle pyright inside wheel (#300)
1 parent f5c7731 commit f15e56f

File tree

8 files changed

+126
-4
lines changed

8 files changed

+126
-4
lines changed

.github/workflows/release.yml

+5-1
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@ jobs:
1717
- name: Set up Python 3.9
1818
uses: actions/setup-python@v2
1919
with:
20-
python-version: 3.9
20+
python-version: '3.11'
2121

2222
- name: Install dependencies
2323
run: |
2424
python -m pip install -r dev-requirements.txt
2525
26+
- name: Download pyright dist
27+
run: |
28+
python scripts/download_pyright.py
29+
2630
- name: Get version
2731
run: |
2832
echo "NEW_VERSION=$(python .github/scripts/get_version.py --compare)" >> $GITHUB_ENV

.github/workflows/test.yml

+23
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ jobs:
5050
python -m pip install --upgrade pip
5151
pip install -r dev-requirements.txt
5252
53+
- name: Download pyright dist
54+
run: |
55+
python scripts/download_pyright.py
56+
5357
- uses: actions/cache@v2
5458
with:
5559
path: ~/.cache/pip
@@ -124,6 +128,15 @@ jobs:
124128
- name: Set up Docker Buildx
125129
uses: docker/setup-buildx-action@v2
126130

131+
- name: Install dependencies
132+
run: |
133+
python -m pip install --upgrade pip
134+
pip install -e .
135+
136+
- name: Download pyright dist
137+
run: |
138+
python scripts/download_pyright.py
139+
127140
- name: Docker Build
128141
uses: docker/build-push-action@v3
129142
# https://github.com/docker/build-push-action/#inputs
@@ -141,6 +154,16 @@ jobs:
141154
runs-on: windows-latest
142155
steps:
143156
- uses: actions/checkout@v3
157+
158+
- name: Install dependencies
159+
run: |
160+
python -m pip install --upgrade pip
161+
pip install -e .
162+
163+
- name: Download pyright dist
164+
run: |
165+
python scripts/download_pyright.py
166+
144167
- name: Docker Build
145168
# Use --% to allow double hyphen
146169
# Caching not currently working since we don't use buildx yet, windows

pyproject.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ include = [
4646
"tests",
4747
]
4848
exclude = [
49-
"src/pyright/_mureq.py"
49+
"src/pyright/_mureq.py",
50+
"src/pyright/dist",
5051
]
5152
pythonVersion = "3.9"
5253

scripts/download_pyright.py

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import io
2+
import sys
3+
import json
4+
import shutil
5+
import tarfile
6+
from pathlib import Path
7+
8+
import pyright
9+
from pyright import _mureq, __pyright_version__
10+
11+
DIST_DIR = Path(pyright.__file__).parent / 'dist'
12+
13+
14+
def _should_download() -> bool:
15+
if '--force' in sys.argv:
16+
return True
17+
18+
pkg_path = DIST_DIR / 'package.json'
19+
if not pkg_path.exists():
20+
return True
21+
22+
pkg_json = json.loads(pkg_path.read_text())
23+
if pkg_json['version'] == __pyright_version__:
24+
print(
25+
f'skipping download as the current pyright version ({__pyright_version__}) is already downloaded. use --force to override'
26+
)
27+
return False
28+
29+
return True
30+
31+
32+
def download_tarball(*, version: str) -> None:
33+
if not _should_download():
34+
return
35+
36+
if DIST_DIR.exists():
37+
shutil.rmtree(DIST_DIR)
38+
39+
rsp = _mureq.get(f'https://registry.npmjs.org/pyright/{version}')
40+
rsp.raise_for_status()
41+
42+
info = rsp.json()
43+
tar_url = info['dist']['tarball']
44+
print(f'downloading tar from {tar_url}')
45+
46+
rsp = _mureq.get(tar_url)
47+
rsp.raise_for_status()
48+
49+
with tarfile.open(fileobj=io.BytesIO(rsp.body)) as tar:
50+
members = tar.getmembers()
51+
52+
# npm tarballs will always output one `package/` directory which is
53+
# not necessary for our case, so we strip out the `package/` prefix
54+
for member in members:
55+
if member.path.startswith('package/'):
56+
member.path = member.path.replace('package/', '', 1)
57+
else:
58+
raise RuntimeError(f'expected tar member path to start with `package/` but got {member.path}')
59+
60+
tar.extractall(path=DIST_DIR, members=members)
61+
62+
63+
def main() -> None:
64+
download_tarball(version=__pyright_version__)
65+
66+
67+
if __name__ == '__main__':
68+
main()

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
include=['pyright', 'pyright.*'],
4747
),
4848
package_dir={'': 'src'},
49-
package_data={'': ['py.typed']},
49+
package_data={'': ['py.typed', 'dist/**']},
5050
python_requires='>=3.7',
5151
include_package_data=True,
5252
zip_safe=False,

src/pyright/_utils.py

+6
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ def install_pyright(args: tuple[object, ...], *, quiet: bool | None) -> Path:
4343
+ 'Please install the new version or set PYRIGHT_PYTHON_FORCE_VERSION to `latest`\n'
4444
)
4545

46+
if version == __pyright_version__ and env_to_bool('PYRIGHT_PYTHON_USE_BUNDLED_PYRIGHT', default=True):
47+
bundled_path = Path(__file__).parent.joinpath('dist')
48+
if bundled_path.exists():
49+
log.debug('using bundled pyright at %s', bundled_path)
50+
return bundled_path
51+
4652
cache_dir = ROOT_CACHE_DIR / version
4753
cache_dir.mkdir(exist_ok=True, parents=True)
4854

tests/test_main.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313

1414
import pyright
1515
from pyright import __pyright_version__
16-
from tests.utils import assert_matches
16+
from tests.utils import assert_matches, is_relative_to
1717
from pyright.utils import maybe_decode
18+
from pyright._utils import install_pyright
1819

1920
if TYPE_CHECKING:
2021
from _pytest.monkeypatch import MonkeyPatch
@@ -238,3 +239,8 @@ def test_package_json_in_parent_dir(tmp_path: Path, monkeypatch: MonkeyPatch) ->
238239
check=True,
239240
)
240241
assert proc.returncode == 0
242+
243+
244+
def test_install_pyright_uses_bundled_by_default() -> None:
245+
install_path = install_pyright(tuple(), quiet=None)
246+
assert is_relative_to(install_path, Path(pyright.__file__).parent)

tests/utils.py

+14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
from __future__ import annotations
22

33
import re
4+
from os import PathLike
5+
from pathlib import Path
6+
from typing_extensions import TypeAlias
7+
8+
StrPath: TypeAlias = 'str | PathLike[str]'
49

510

611
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]:
914
raise ValueError(f'Pattern, {pattern}, did not match input: {contents}')
1015

1116
return match
17+
18+
19+
def is_relative_to(path: StrPath, to: StrPath) -> bool:
20+
"""Backport of Path.is_relative_to for Python < 3.9"""
21+
try:
22+
Path(path).relative_to(to)
23+
return True
24+
except ValueError:
25+
return False

0 commit comments

Comments
 (0)