forked from DonJayamanne/pythonVSCode
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathinstalled_check.py
More file actions
132 lines (107 loc) · 3.84 KB
/
installed_check.py
File metadata and controls
132 lines (107 loc) · 3.84 KB
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
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import argparse
import json
import os
import pathlib
import sys
from typing import Dict, List, Optional, Sequence, Tuple, Union
LIB_ROOT = pathlib.Path(__file__).parent / "lib" / "python"
sys.path.insert(0, os.fspath(LIB_ROOT))
import tomli # noqa: E402
from importlib_metadata import metadata # noqa: E402
from packaging.requirements import Requirement # noqa: E402
DEFAULT_SEVERITY = "3" # 'Hint'
try:
SEVERITY = int(os.getenv("VSCODE_MISSING_PGK_SEVERITY", DEFAULT_SEVERITY))
except ValueError:
SEVERITY = int(DEFAULT_SEVERITY)
def parse_args(argv: Optional[Sequence[str]] = None):
if argv is None:
argv = sys.argv[1:]
parser = argparse.ArgumentParser(
description="Check for installed packages against requirements"
)
parser.add_argument("FILEPATH", type=str, help="Path to requirements.[txt, in]")
return parser.parse_args(argv)
def parse_requirements(line: str) -> Optional[Requirement]:
try:
req = Requirement(line.strip("\\"))
if req.marker is None or req.marker.evaluate():
return req
except Exception:
pass
return None
def process_requirements(req_file: pathlib.Path) -> List[Dict[str, Union[str, int]]]:
diagnostics = []
for n, line in enumerate(req_file.read_text(encoding="utf-8").splitlines()):
if line.startswith(("#", "-", " ")) or line == "":
continue
req = parse_requirements(line)
if req:
try:
# Check if package is installed
metadata(req.name)
except Exception:
diagnostics.append(
{
"line": n,
"character": 0,
"endLine": n,
"endCharacter": len(req.name),
"package": req.name,
"code": "not-installed",
"severity": SEVERITY,
}
)
return diagnostics
def get_pos(lines: List[str], text: str) -> Tuple[int, int, int, int]:
for n, line in enumerate(lines):
index = line.find(text)
if index >= 0:
return n, index, n, index + len(text)
return (0, 0, 0, 0)
def process_pyproject(req_file: pathlib.Path) -> List[Dict[str, Union[str, int]]]:
diagnostics = []
try:
raw_text = req_file.read_text(encoding="utf-8")
pyproject = tomli.loads(raw_text)
except Exception:
return diagnostics
lines = raw_text.splitlines()
reqs = pyproject.get("project", {}).get("dependencies", [])
for raw_req in reqs:
req = parse_requirements(raw_req)
n, start, _, end = get_pos(lines, raw_req)
if req:
try:
# Check if package is installed
metadata(req.name)
except Exception:
diagnostics.append(
{
"line": n,
"character": start,
"endLine": n,
"endCharacter": end,
"package": req.name,
"code": "not-installed",
"severity": SEVERITY,
}
)
return diagnostics
def get_diagnostics(req_file: pathlib.Path) -> List[Dict[str, Union[str, int]]]:
diagnostics = []
if not req_file.exists():
return diagnostics
if req_file.name == "pyproject.toml":
diagnostics = process_pyproject(req_file)
else:
diagnostics = process_requirements(req_file)
return diagnostics
def main():
args = parse_args()
diagnostics = get_diagnostics(pathlib.Path(args.FILEPATH))
print(json.dumps(diagnostics, ensure_ascii=False))
if __name__ == "__main__":
main()