forked from microsoft/vscode-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstalled_check.py
More file actions
129 lines (104 loc) · 3.66 KB
/
installed_check.py
File metadata and controls
129 lines (104 loc) · 3.66 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
# 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
from importlib_metadata import metadata
from packaging.requirements import Requirement
DEFAULT_SEVERITY = 3
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:
return req
elif req.marker.evaluate():
return req
except:
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:
diagnostics.append(
{
"line": n,
"character": 0,
"endLine": n,
"endCharacter": len(req.name),
"package": req.name,
"code": "not-installed",
"severity": DEFAULT_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:
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:
diagnostics.append(
{
"line": n,
"character": start,
"endLine": n,
"endCharacter": end,
"package": req.name,
"code": "not-installed",
"severity": DEFAULT_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()