-
Notifications
You must be signed in to change notification settings - Fork 23
/
_utils.py
138 lines (110 loc) · 4.8 KB
/
_utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
from __future__ import annotations
import logging
import os
import sys
import json
import subprocess
from pathlib import Path
from typing import Any
from . import __pyright_version__, node
from .utils import env_to_bool, get_latest_version, get_cache_dir
from . import _mureq as mureq
ROOT_CACHE_DIR = get_cache_dir() / 'pyright-python'
DEFAULT_PACKAGE_JSON: dict[str, Any] = {
'name': 'pyright-binaries',
'version': '1.0.0',
'private': True,
'description': 'Cache directory created by Pyright Python to store downloads of the NPM package',
'main': 'node_modules/pyright/index.js',
'author': 'RobertCraigie',
'license': 'Apache-2.0',
}
log: logging.Logger = logging.getLogger(__name__)
def install_pyright(args: tuple[object, ...], *, quiet: bool | None) -> Path:
"""Internal helper function to install the Pyright npm package to a cache.
This returns the path to the installed package.
This accepts a single argument which corresponds to the arguments given to the CLI / langserver
which are used to determine whether or not certain warnings / logs will be printed.
"""
version = _get_configured_pyright_version()
if version == 'latest':
version = node.latest('pyright')
else:
if _should_warn_version(version, args=args, quiet=quiet):
print(
f'WARNING: there is a new pyright version available (v{version} -> v{get_latest_version()}).\n'
+ 'Please install the new version or set PYRIGHT_PYTHON_FORCE_VERSION to `latest`\n'
)
cache_dir = ROOT_CACHE_DIR / version
cache_dir.mkdir(exist_ok=True, parents=True)
pkg_dir = cache_dir / 'node_modules' / 'pyright'
package_json = cache_dir / 'package.json'
current_version = node.get_pkg_version(pkg_dir / 'package.json')
if current_version is None or current_version != version:
# We need to create a dummy `package.json` file so that `npm` doesn't try
# and search for it elsewhere.
#
# If it finds a different `package.json` file then the `pyright` package
# will be installed there instead of our cache directory.
if not package_json.exists():
package_json.write_text(json.dumps(DEFAULT_PACKAGE_JSON, indent=2))
silent = '--outputjson' in args
node.run(
'npm',
'install',
f'pyright@{version}',
cwd=str(cache_dir),
check=True,
stdout=subprocess.PIPE if silent else sys.stdout,
stderr=subprocess.PIPE if silent else sys.stderr,
)
return pkg_dir
def _get_configured_pyright_version() -> str:
force_version = os.environ.get('PYRIGHT_PYTHON_FORCE_VERSION')
if force_version:
return force_version
pylance_version = os.environ.get('PYRIGHT_PYTHON_PYLANCE_VERSION')
if pylance_version:
return _get_pylance_pyright_version(pylance_version)
return __pyright_version__
def _get_pylance_pyright_version(pylance_version: str) -> str:
url = f'https://raw.githubusercontent.com/microsoft/pylance-release/main/releases/{pylance_version}.json'
try:
response = mureq.get(url, timeout=1)
response.raise_for_status()
data = response.json()
log.debug(f'Pylance release data: {data}')
version = data["pyrightVersion"]
log.debug(f'Pylance {pylance_version} uses pyright version {version}')
return version
except Exception as exc:
log.debug(
f"Failed to download release metadata for Pylance {pylance_version} from {url}: {type(exc)} - {exc}"
)
raise
def _should_warn_version(
version: str,
*,
args: tuple[object, ...],
quiet: bool | None,
) -> bool:
if quiet:
# This flag is set by the language server as the output must always be machine parseable
return False
if '--outputjson' in args:
# If this flag is set then the output must be machine parseable
return False
if env_to_bool('PYRIGHT_PYTHON_IGNORE_WARNINGS', default=False):
return False
# Don't warn about the pyright version if a Pylance version is specified, since the latest
# Pylance release may not include the latest pyright release yet.
if os.environ.get('PYRIGHT_PYTHON_PYLANCE_VERSION'):
return False
# NOTE: there is an edge case here where a new pyright version has been released
# but we haven't made a new pyright-python release yet and the user has set
# PYRIGHT_PYTHON_FORCE_VERSION to the new pyright version.
# This should rarely happen as we make new releases very frequently after
# pyright does. Also in order to correctly compare versions we would need an additional
# dependency. As such this is an acceptable bug.
latest = get_latest_version()
return latest is not None and latest != version