Skip to content

Commit e5f25a5

Browse files
committed
fix regression about v12 vs v12.0.1
1 parent a23a644 commit e5f25a5

File tree

5 files changed

+53
-49
lines changed

5 files changed

+53
-49
lines changed

clang_tools/install.py

+18-21
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414
from typing import Optional, cast
1515

1616
from . import release_tag, install_os, RESET_COLOR, suffix, YELLOW
17-
from .util import download_file, verify_sha512, get_sha_checksum, parse_version
17+
from .util import download_file, verify_sha512, get_sha_checksum, Version
1818

1919

2020
#: This pattern is designed to match only the major version number.
2121
RE_PARSE_VERSION = re.compile(rb"version\s([\d\.]+)", re.MULTILINE)
2222

2323

24-
def is_installed(tool_name: str, version: int) -> Optional[Path]:
24+
def is_installed(tool_name: str, version: Version) -> Optional[Path]:
2525
"""Detect if the specified tool is installed.
2626
2727
:param tool_name: The name of the specified tool.
@@ -30,7 +30,9 @@ def is_installed(tool_name: str, version: int) -> Optional[Path]:
3030
:returns: The path to the detected tool (if found), otherwise `None`.
3131
"""
3232
exe_name = (
33-
f"{tool_name}" + (f"-{version}" if install_os != "windows" else "") + suffix
33+
f"{tool_name}"
34+
+ (f"-{version.info[0]}" if install_os != "windows" else "")
35+
+ suffix
3436
)
3537
try:
3638
result = subprocess.run(
@@ -56,16 +58,16 @@ def is_installed(tool_name: str, version: int) -> Optional[Path]:
5658
path = Path(exe_path).resolve()
5759
print("at", str(path))
5860
ver_tuple = ver_match.split(".")
59-
if ver_tuple is None or ver_tuple[0] != str(version):
61+
if ver_tuple is None or ver_tuple[0] != str(version.info[0]):
6062
return None # version is unknown or not the desired major release
6163
return path
6264

6365

64-
def clang_tools_binary_url(tool: str, version: int, tag: str = release_tag) -> str:
66+
def clang_tools_binary_url(tool: str, version: str, tag: str = release_tag) -> str:
6567
"""Assemble the URL to the binary.
6668
6769
:param tool: The name of the tool to download.
68-
:param version: The major version of the tool to download.
70+
:param version: The version of the tool to download.
6971
:param tag: The release tag used in the base URL.
7072
7173
:returns: The URL used to download the specified tool.
@@ -79,12 +81,12 @@ def clang_tools_binary_url(tool: str, version: int, tag: str = release_tag) -> s
7981

8082

8183
def install_tool(
82-
tool_name: str, version: int, directory: str, no_progress_bar: bool
84+
tool_name: str, version: str, directory: str, no_progress_bar: bool
8385
) -> bool:
8486
"""An abstract function that can install either clang-tidy or clang-format.
8587
8688
:param tool_name: The name of the clang-tool to install.
87-
:param version: The major version of the tools to install.
89+
:param version: The version of the tools to install.
8890
:param directory: The installation directory.
8991
:param no_progress_bar: A flag used to disable the downloads' progress bar.
9092
@@ -154,7 +156,7 @@ def move_and_chmod_bin(old_bin_name: str, new_bin_name: str, install_dir: str) -
154156

155157
def create_sym_link(
156158
tool_name: str,
157-
version: int,
159+
version: str,
158160
install_dir: str,
159161
overwrite: bool = False,
160162
target: Optional[Path] = None,
@@ -163,7 +165,7 @@ def create_sym_link(
163165
doesn't have the version number appended.
164166
165167
:param tool_name: The name of the clang-tool to symlink.
166-
:param version: The major version of the clang-tool to symlink.
168+
:param version: The version of the clang-tool to symlink.
167169
:param install_dir: The installation directory to create the symlink in.
168170
:param overwrite: A flag to indicate if an existing symlink should be overwritten.
169171
:param target: The target executable's path and name for which to create a symlink
@@ -212,11 +214,11 @@ def create_sym_link(
212214
return False
213215

214216

215-
def uninstall_tool(tool_name: str, version: int, directory: str):
217+
def uninstall_tool(tool_name: str, version: str, directory: str):
216218
"""Remove a specified tool of a given version.
217219
218220
:param tool_name: The name of the clang tool to uninstall.
219-
:param version: The major version of the clang-tools to remove.
221+
:param version: The version of the clang-tools to remove.
220222
:param directory: The directory from which to remove the
221223
installed clang-tools.
222224
"""
@@ -241,17 +243,12 @@ def uninstall_clang_tools(version: str, directory: str):
241243
"""
242244
install_dir = install_dir_name(directory)
243245
print(f"Uninstalling version {version} from {str(install_dir)}")
244-
version_tuple = parse_version(version)
245246
for tool in ("clang-format", "clang-tidy"):
246-
uninstall_tool(tool, version_tuple[0], install_dir)
247+
uninstall_tool(tool, version, install_dir)
247248

248249

249250
def install_clang_tools(
250-
version: int,
251-
tools: str,
252-
directory: str,
253-
overwrite: bool,
254-
no_progress_bar: bool,
251+
version: Version, tools: str, directory: str, overwrite: bool, no_progress_bar: bool
255252
) -> None:
256253
"""Wraps functions used to individually install tools.
257254
@@ -272,7 +269,7 @@ def install_clang_tools(
272269
native_bin = is_installed(tool_name, version)
273270
if native_bin is None: # (not already installed)
274271
# `install_tool()` guarantees that the binary exists now
275-
install_tool(tool_name, version, install_dir, no_progress_bar)
272+
install_tool(tool_name, version.string, install_dir, no_progress_bar)
276273
create_sym_link( # pragma: no cover
277-
tool_name, version, install_dir, overwrite, native_bin
274+
tool_name, version.string, install_dir, overwrite, native_bin
278275
)

clang_tools/main.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from .install import install_clang_tools, uninstall_clang_tools
1111
from . import RESET_COLOR, YELLOW
12-
from .util import parse_version
12+
from .util import Version
1313

1414

1515
def get_parser() -> argparse.ArgumentParser:
@@ -70,10 +70,10 @@ def main():
7070
if args.uninstall:
7171
uninstall_clang_tools(args.uninstall, args.directory)
7272
elif args.install:
73-
version = parse_version(args.install)
74-
if version != (0, 0, 0):
73+
version = Version(args.install)
74+
if version.info != (0, 0, 0):
7575
install_clang_tools(
76-
version[0],
76+
version,
7777
args.tool,
7878
args.directory,
7979
args.overwrite,

clang_tools/util.py

+16-10
Original file line numberDiff line numberDiff line change
@@ -101,20 +101,26 @@ def verify_sha512(checksum: str, exe: bytes) -> bool:
101101
return checksum == hashlib.sha512(exe).hexdigest()
102102

103103

104-
def parse_version(version: str) -> Tuple[int, int, int]:
104+
class Version:
105105
"""Parse the given version string into a semantic specification.
106106
107107
:param version: The version specification as a string.
108108
109109
:returns: A tuple of ints that describes the major, minor, and patch versions.
110110
If the version is a path, then the tuple is just 3 zeros.
111111
"""
112-
version_tuple = version.split(".")
113-
if len(version_tuple) < 3:
114-
# append minor and patch version numbers if not specified
115-
version_tuple += ["0"] * (3 - len(version_tuple))
116-
try:
117-
return tuple([int(x) for x in version_tuple]) # type: ignore[return-value]
118-
except ValueError:
119-
assert Path(version).exists(), "specified version is not a semantic or a path"
120-
return (0, 0, 0)
112+
113+
def __init__(self, user_input: str):
114+
self.string = user_input
115+
version_tuple = user_input.split(".")
116+
self.info: Tuple[int, int, int]
117+
if len(version_tuple) < 3:
118+
# append minor and patch version numbers if not specified
119+
version_tuple += ["0"] * (3 - len(version_tuple))
120+
try:
121+
self.info = tuple([int(x) for x in version_tuple]) # type: ignore[assignment]
122+
except ValueError:
123+
assert Path(
124+
user_input
125+
).exists(), "specified version is not a semantic or a path"
126+
self.info = (0, 0, 0)

tests/test_install.py

+11-10
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
is_installed,
1313
uninstall_clang_tools,
1414
)
15+
from clang_tools.util import Version
1516

1617

17-
@pytest.mark.parametrize("version", list(range(7, 17)))
18+
@pytest.mark.parametrize("version", [str(v) for v in range(7, 17)] + ["12.0.1"])
1819
@pytest.mark.parametrize(
1920
"tool_name",
2021
["clang-format", "clang-tidy", "clang-query", "clang-apply-replacements"],
2122
)
22-
def test_clang_tools_binary_url(tool_name: str, version: int):
23+
def test_clang_tools_binary_url(tool_name: str, version: str):
2324
"""Test `clang_tools_binary_url()`"""
2425
url = clang_tools_binary_url(tool_name, version)
2526
assert f"{tool_name}-{version}_{install_os}-amd64" in url
@@ -35,7 +36,7 @@ def test_dir_name(monkeypatch: pytest.MonkeyPatch, directory: str):
3536

3637
def test_create_symlink(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
3738
"""Test creation of symlink."""
38-
tool_name, version = ("clang-tool", 1)
39+
tool_name, version = ("clang-tool", "1")
3940
monkeypatch.chdir(str(tmp_path))
4041
# use a test tar file and rename it to "clang-tool-1" (+ OS suffix)
4142
test_target = tmp_path / f"{tool_name}-{version}{suffix}"
@@ -54,8 +55,8 @@ def test_create_symlink(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
5455
assert not create_sym_link(tool_name, version, str(tmp_path), True)
5556

5657

57-
@pytest.mark.parametrize("version", [12])
58-
def test_install_tools(monkeypatch: pytest.MonkeyPatch, tmp_path: Path, version: int):
58+
@pytest.mark.parametrize("version", ["12"])
59+
def test_install_tools(monkeypatch: pytest.MonkeyPatch, tmp_path: Path, version: str):
5960
"""Test install tools to a temp directory."""
6061
monkeypatch.chdir(tmp_path)
6162
tool_name = "clang-format"
@@ -64,16 +65,16 @@ def test_install_tools(monkeypatch: pytest.MonkeyPatch, tmp_path: Path, version:
6465
# invoking again should return False
6566
assert not install_tool(tool_name, version, str(tmp_path), False)
6667
# uninstall the tool deliberately
67-
uninstall_clang_tools(f"{version}.0.0", str(tmp_path))
68+
uninstall_clang_tools(version, str(tmp_path))
6869
assert f"{tool_name}-{version}{suffix}" not in [
6970
fd.name for fd in tmp_path.iterdir()
7071
]
7172

7273

73-
@pytest.mark.parametrize("version", [0])
74-
def test_is_installed(version: int):
74+
@pytest.mark.parametrize("version", ["0"])
75+
def test_is_installed(version: str):
7576
"""Test if installed version matches specified ``version``"""
76-
tool_path = is_installed("clang-format", version=version)
77+
tool_path = is_installed("clang-format", version=Version(version))
7778
assert tool_path is None
7879

7980

@@ -84,7 +85,7 @@ def test_path_warning(capsys: pytest.CaptureFixture):
8485
2. indicates a failure to download a tool
8586
"""
8687
try:
87-
install_clang_tools(0, "x", ".", False, False)
88+
install_clang_tools(Version("0"), "x", ".", False, False)
8889
except OSError as exc:
8990
if install_dir_name(".") not in os.environ.get("PATH", ""): # pragma: no cover
9091
# this warning does not happen in an activated venv

tests/test_util.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
check_install_os,
99
download_file,
1010
get_sha_checksum,
11-
parse_version,
11+
Version,
1212
)
1313
from clang_tools import release_tag
1414

@@ -25,7 +25,7 @@ def test_check_install_os():
2525
def test_download_file(monkeypatch: pytest.MonkeyPatch, tmp_path: Path, tag: str):
2626
"""Test that deliberately fails to download a file."""
2727
monkeypatch.chdir(str(tmp_path))
28-
url = clang_tools_binary_url("clang-format", 12, tag=tag)
28+
url = clang_tools_binary_url("clang-format", "12", tag=tag)
2929
file_name = download_file(url, "file.tar.gz", True)
3030
assert file_name is not None
3131

@@ -37,11 +37,11 @@ def test_get_sha(monkeypatch: pytest.MonkeyPatch):
3737
expected = Path(f"clang-format-12_{install_os}-amd64.sha512sum").read_text(
3838
encoding="utf-8"
3939
)
40-
url = clang_tools_binary_url("clang-format", 12, tag=release_tag)
40+
url = clang_tools_binary_url("clang-format", "12", tag=release_tag)
4141
assert get_sha_checksum(url) == expected
4242

4343

4444
def test_version_path():
4545
"""Tests version parsing when given specification is a path."""
4646
version = str(Path(__file__).parent)
47-
assert parse_version(version) == (0, 0, 0)
47+
assert Version(version).info == (0, 0, 0)

0 commit comments

Comments
 (0)