-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcheck-dependencies.py
More file actions
156 lines (132 loc) · 4.48 KB
/
check-dependencies.py
File metadata and controls
156 lines (132 loc) · 4.48 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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
from __future__ import annotations
import argparse
import contextlib
import pathlib
import platform
import subprocess
import sys
import urllib.request
from tempfile import NamedTemporaryFile
from typing import NamedTuple
from typing import Optional
class ProgramArguments(NamedTuple):
junit_xml: Optional[str]
no_pytest_install: str
pytest_executable: Optional[pathlib.Path]
def _custom_pytest_executable(custom_pytest_executable: str) -> pathlib.Path:
pytest = pathlib.Path(custom_pytest_executable)
if not pytest.exists():
raise argparse.ArgumentTypeError(f"{pytest} does not exist")
elif not pytest.is_file():
raise argparse.ArgumentTypeError(f"{pytest} is not a file")
elif not pytest.parent.exists():
raise argparse.ArgumentTypeError(f"{pytest} parent directory does not exist")
return pathlib.Path(pytest)
def _setup_arguments() -> ProgramArguments:
parser = argparse.ArgumentParser(
description=(
"Check if Poetry based Python project dependencies "
"declared in pyproject.toml are up to date"
),
)
parser.add_argument(
"--junit-xml",
dest="junit_xml",
help="save check results as JUnit XML to specified path",
)
parser.add_argument(
"--no-pytest-install",
dest="no_pytest_install",
action="store_true",
help="prohibit installing pytest if it is not present on the system",
)
parser.add_argument(
"--pytest",
dest="pytest_executable",
type=_custom_pytest_executable,
help="specify custom pytest executable",
)
args = parser.parse_args()
return ProgramArguments(
junit_xml=args.junit_xml,
no_pytest_install=args.no_pytest_install,
pytest_executable=args.pytest_executable,
)
def _check_python_version():
if sys.version_info.major >= 3 and sys.version_info.minor >= 6:
return
raise IOError(
f"Python 3.6+ is required, currently using {platform.python_version()}"
)
def _ensure_pytest(
custom_pytest_executable: Optional[pathlib.Path],
allow_install: bool,
):
if custom_pytest_executable:
print(f"Using custom pytest executable at {custom_pytest_executable}")
return
python = pathlib.Path(sys.executable)
try:
subprocess.run(
(python, "-m", "pytest", "--version"),
capture_output=True,
check=True,
)
except subprocess.CalledProcessError as cpe:
if not allow_install:
raise IOError(
"pytest is not installed and installing it is prohibited"
) from cpe
else:
return
print(f"> {python.name} -m pip install pytest")
try:
subprocess.run((python, "-m", "pip", "install", "pytest"), check=True)
except subprocess.CalledProcessError as cpe:
raise IOError("Failed to install pytest") from cpe
@contextlib.contextmanager
def _test_script(ref="main") -> pathlib.Path:
repo_slug = "NevercodeHQ/poetry-dependencies-checker"
tests_script_url = f"https://raw.githubusercontent.com/{repo_slug}/{ref}/test_poetry_dependencies.py"
with urllib.request.urlopen(tests_script_url) as response:
tests_script: bytes = response.read()
with NamedTemporaryFile(
prefix="test-poetry-dependencies-",
suffix=".py",
dir=pathlib.Path("."),
) as tf:
tf.write(tests_script)
tf.flush()
yield pathlib.Path(tf.name)
def _run_tests(
pytest_executable: Optional[pathlib.Path],
test_file: pathlib.Path,
junit_xml: Optional[pathlib.Path],
):
if pytest_executable:
test_command = [pytest_executable, test_file, "--noconftest"]
else:
test_command = [sys.executable, "-m", "pytest", test_file, "--noconftest"]
if junit_xml:
test_command.extend(["--junitxml", junit_xml])
cp = subprocess.run(test_command)
return cp.returncode
def main():
try:
_check_python_version()
program_arguments = _setup_arguments()
_ensure_pytest(
program_arguments.pytest_executable,
not program_arguments.no_pytest_install,
)
with _test_script() as tests_path:
return _run_tests(
program_arguments.pytest_executable,
tests_path,
junit_xml=program_arguments.junit_xml,
)
except IOError as ioe:
print(f"Error: {ioe}")
return 1
if __name__ == "__main__":
sys.exit(main())