|
| 1 | +import tempfile |
1 | 2 | import re |
2 | 3 | import subprocess |
3 | 4 | from typing import Optional |
| 5 | +from pathlib import Path |
4 | 6 |
|
5 | 7 |
|
6 | 8 | def find_latest_version(pkg_name: str, major: Optional[int] = None) -> str: |
7 | | - output = subprocess.check_output( |
8 | | - [ |
9 | | - "pip", |
10 | | - "install", |
11 | | - "--no-deps", |
12 | | - "--ignore-installed", |
13 | | - "--no-cache-dir", |
14 | | - "--dry-run", |
15 | | - f"{pkg_name}{f'=={major}.*' if major is not None else ''}", |
16 | | - ], |
17 | | - stderr=subprocess.DEVNULL, |
| 9 | + pip_log = _pretend_pip_install(pkg_name=pkg_name, major=major) |
| 10 | + would_install_line = _extract_would_install_line(pip_log=pip_log) |
| 11 | + package_with_version = _extract_package_with_version( |
| 12 | + pip_log_would_install_line=would_install_line, pkg_name=pkg_name, major=major |
18 | 13 | ) |
19 | | - output_lines = output.decode("utf-8").splitlines() |
20 | | - would_install_line = [line for line in output_lines if "Would install" in line][0] |
21 | | - regex_rule = f"{pkg_name.replace('_', '-')}-{major if major is not None else ''}.[^ ]+" |
22 | | - match = re.search(regex_rule, would_install_line.replace("_", "-")) |
23 | | - assert match is not None |
24 | | - return match[0][len(f"{pkg_name}-") :] |
| 14 | + return package_with_version[len(f"{pkg_name}-") :] |
25 | 15 |
|
26 | 16 |
|
27 | 17 | def increase_minor(version: str) -> str: |
28 | 18 | major, minor, patch = version.split(".") |
29 | 19 | assert major.isdigit() and minor.isdigit() |
30 | 20 | return f"{major}.{int(minor) + 1}.0" |
| 21 | + |
| 22 | + |
| 23 | +def _pretend_pip_install(pkg_name: str, major: Optional[int]) -> str: |
| 24 | + with tempfile.NamedTemporaryFile() as log_file: |
| 25 | + result = subprocess.run( |
| 26 | + [ |
| 27 | + "pip", |
| 28 | + "install", |
| 29 | + "--no-deps", |
| 30 | + "--ignore-installed", |
| 31 | + "--dry-run", |
| 32 | + f"--log={log_file.name}", |
| 33 | + f"{pkg_name}{f'=={major}.*' if major is not None else ''}", |
| 34 | + ], |
| 35 | + stdout=subprocess.PIPE, |
| 36 | + stderr=subprocess.PIPE, |
| 37 | + text=True, |
| 38 | + ) |
| 39 | + if result.returncode != 0: |
| 40 | + raise subprocess.SubprocessError( |
| 41 | + f"Something went wrong while using pip to find the version of {pkg_name}{f'(major version {major})' if major else ''}.\n\nSTDOUT: {result.stdout}\n\nSTDERR: {result.stderr}" |
| 42 | + ) |
| 43 | + return Path(log_file.name).read_text() |
| 44 | + |
| 45 | + |
| 46 | +def _extract_would_install_line(pip_log: str) -> str: |
| 47 | + would_install_lines = [line for line in pip_log.splitlines() if "Would install" in line] |
| 48 | + assert ( |
| 49 | + len(would_install_lines) == 1 |
| 50 | + ), f"Expected exactly one line containing 'Would install' in the pip log generated by pip installing the requested package, but found {len(would_install_lines)} lines." |
| 51 | + return would_install_lines[0] |
| 52 | + |
| 53 | + |
| 54 | +def _extract_package_with_version(pip_log_would_install_line: str, pkg_name: str, major: Optional[int]) -> str: |
| 55 | + regex_rule = f"{pkg_name.replace('_', '-')}-{major if major is not None else ''}.[^ ]+" |
| 56 | + match = re.search(regex_rule, pip_log_would_install_line.replace("_", "-")) |
| 57 | + assert ( |
| 58 | + match is not None and match[0] != "" |
| 59 | + ), f"It was not possible to determine the version, because we could not find a match to the regex {regex_rule} in {pip_log_would_install_line.replace('_', '-')}" |
| 60 | + return match[0] |
0 commit comments