Merge pull request #1258 from henryiii/henryiii/chore/dev

chore: nicer tracebacks and dev improvements
This commit is contained in:
Joe Rickerby
2022-09-25 09:34:05 +01:00
committed by GitHub
19 changed files with 95 additions and 58 deletions
+33 -15
View File
@@ -1,4 +1,17 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""
Check known projects for usage of requires-python.
Usage:
./bin/inspect_all_known_projects.py --online=$GITHUB_TOKEN
This will cache the results to all_known_setup.yaml; you can reprint
the results without the `--online` setting.
"""
from __future__ import annotations from __future__ import annotations
import ast import ast
@@ -7,7 +20,7 @@ from typing import Iterator
import click import click
import yaml import yaml
from ghapi.core import GhApi, HTTP404NotFoundError from github import Github, GithubException
from rich import print from rich import print
from cibuildwheel.projectfiles import Analyzer from cibuildwheel.projectfiles import Analyzer
@@ -47,33 +60,38 @@ def check_repo(name: str, contents: str) -> str:
class MaybeRemote: class MaybeRemote:
def __init__(self, cached_file: Path | str, *, online: bool) -> None: github: Github | None
self.online = online contents: dict[str, dict[str, str | None]]
if self.online:
self.contents: dict[str, dict[str, str | None]] = { def __init__(self, cached_file: Path | str, *, online: str | None) -> None:
if online is not None:
self.github = Github(online)
self.contents = {
"setup.py": {}, "setup.py": {},
"setup.cfg": {}, "setup.cfg": {},
"pyproject.toml": {}, "pyproject.toml": {},
} }
else: else:
self.github = None
with open(cached_file) as f: with open(cached_file) as f:
self.contents = yaml.safe_load(f) self.contents = yaml.safe_load(f)
def get(self, repo: str, filename: str) -> str | None: def get(self, repo: str, filename: str) -> str | None:
if self.online: if self.github:
try: try:
self.contents[filename][repo] = ( gh_file = self.github.get_repo(repo).get_contents(filename)
GhApi(*repo.split("/")).get_content(filename).decode() except GithubException:
)
except HTTP404NotFoundError:
self.contents[filename][repo] = None self.contents[filename][repo] = None
else:
assert not isinstance(gh_file, list)
self.contents[filename][repo] = gh_file.decoded_content.decode(encoding="utf-8")
return self.contents[filename][repo] return self.contents[filename][repo]
elif repo in self.contents[filename]: elif repo in self.contents[filename]:
return self.contents[filename][repo] return self.contents[filename][repo]
else: else:
raise RuntimeError( msg = f"Trying to access {repo}:{filename} and not in cache, rebuild cache"
f"Trying to access {repo}:{filename} and not in cache, rebuild cache" raise RuntimeError(msg)
)
def save(self, filename: Path | str) -> None: def save(self, filename: Path | str) -> None:
with open(filename, "w") as f: with open(filename, "w") as f:
@@ -87,8 +105,8 @@ class MaybeRemote:
@click.command() @click.command()
@click.option("--online", is_flag=True, help="Remember to set GITHUB_TOKEN") @click.option("--online", help="Set to $GITHUB_TOKEN")
def main(online: bool) -> None: def main(online: str | None) -> None:
with open(DIR / "../docs/data/projects.yml") as f: with open(DIR / "../docs/data/projects.yml") as f:
known = yaml.safe_load(f) known = yaml.safe_load(f)
+4 -2
View File
@@ -81,7 +81,8 @@ def ci_service_for_config_file(config_file):
if service.name == service_name: if service.name == service_name:
return service return service
raise ValueError(f"unknown ci service for config file {config_file}") msg = f"unknown ci service for config file {config_file}"
raise ValueError(msg)
@click.command() @click.command()
@@ -100,7 +101,8 @@ def run_example_ci_configs(config_files=None):
for config_file in config_files: for config_file in config_files:
service = ci_service_for_config_file(config_file) service = ci_service_for_config_file(config_file)
if service.name in configs_by_service: if service.name in configs_by_service:
raise Exception("You cannot specify more than one config per CI service") msg = "You cannot specify more than one config per CI service"
raise Exception(msg)
configs_by_service[service.name] = config_file configs_by_service[service.name] = config_file
if git_repo_has_changes(): if git_repo_has_changes():
+6 -3
View File
@@ -141,7 +141,8 @@ class PyPyVersions:
releases = [r for r in releases if self.get_arch_file(r)] releases = [r for r in releases if self.get_arch_file(r)]
if not releases: if not releases:
raise RuntimeError(f"PyPy Win {self.arch} not found for {spec}! {self.releases}") msg = f"PyPy Win {self.arch} not found for {spec}! {self.releases}"
raise RuntimeError(msg)
version_arch = "win32" if self.arch == "32" else "win_amd64" version_arch = "win32" if self.arch == "32" else "win_amd64"
@@ -159,13 +160,15 @@ class PyPyVersions:
def update_version_macos(self, spec: Specifier) -> ConfigMacOS: def update_version_macos(self, spec: Specifier) -> ConfigMacOS:
if self.arch != "64": if self.arch != "64":
raise RuntimeError("Other archs not supported yet on macOS") msg = "Other archs not supported yet on macOS"
raise RuntimeError(msg)
releases = [r for r in self.releases if spec.contains(r["python_version"])] releases = [r for r in self.releases if spec.contains(r["python_version"])]
releases = sorted(releases, key=lambda r: r["pypy_version"]) # type: ignore[no-any-return] releases = sorted(releases, key=lambda r: r["pypy_version"]) # type: ignore[no-any-return]
if not releases: if not releases:
raise RuntimeError(f"PyPy macOS {self.arch} not found for {spec}!") msg = f"PyPy macOS {self.arch} not found for {spec}!"
raise RuntimeError(msg)
release = releases[-1] release = releases[-1]
version = release["python_version"] version = release["python_version"]
+2 -1
View File
@@ -149,7 +149,8 @@ def main() -> None:
try: try:
(project_dir,) = temp_dir.iterdir() (project_dir,) = temp_dir.iterdir()
except ValueError: except ValueError:
raise SystemExit("invalid sdist: didn't contain a single dir") from None msg = "invalid sdist: didn't contain a single dir"
raise SystemExit(msg) from None
# This is now the new package dir # This is now the new package dir
args.package_dir = project_dir.resolve() args.package_dir = project_dir.resolve()
+10 -8
View File
@@ -32,7 +32,8 @@ def evaluate(
command_node = bashlex.parsesingle(value) command_node = bashlex.parsesingle(value)
if len(command_node.parts) != 1: if len(command_node.parts) != 1:
raise ValueError(f'"{value}" has too many parts') msg = f"{value!r} has too many parts"
raise ValueError(msg)
value_word_node = command_node.parts[0] value_word_node = command_node.parts[0]
@@ -54,7 +55,8 @@ def evaluate_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
elif node.kind == "parameter": elif node.kind == "parameter":
return evaluate_parameter_node(node, context=context) return evaluate_parameter_node(node, context=context)
else: else:
raise ValueError(f'Unsupported bash construct: "{node.kind}"') msg = f"Unsupported bash construct: {node.kind!r}"
raise ValueError(msg)
def evaluate_word_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str: def evaluate_word_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
@@ -65,10 +67,8 @@ def evaluate_word_node(node: bashlex.ast.node, context: NodeExecutionContext) ->
part_value = evaluate_node(part, context=context) part_value = evaluate_node(part, context=context)
if part_string not in value: if part_string not in value:
raise RuntimeError( msg = f"bash parse failed. part {part_string!r} not found in {value!r}. Word was {node.word!r}. Full input was {context.input!r}"
f'bash parse failed. part "{part_string}" not found in "{value}". ' raise RuntimeError(msg)
f'Word was "{node.word}". Full input was "{context.input}"'
)
value = value.replace(part_string, part_value, 1) value = value.replace(part_string, part_value, 1)
@@ -95,9 +95,11 @@ def evaluate_nodes_as_compound_command(
result += evaluate_command_node(node, context=context) result += evaluate_command_node(node, context=context)
elif node.kind == "operator": elif node.kind == "operator":
if node.op != ";": if node.op != ";":
raise ValueError(f'Unsupported bash operator: "{node.op}"') msg = f"Unsupported bash operator: {node.op!r}"
raise ValueError(msg)
else: else:
raise ValueError(f'Unsupported bash node in compound command: "{node.kind}"') msg = f"Unsupported bash node in compound command: {node.kind!r}"
raise ValueError(msg)
return result return result
+4 -7
View File
@@ -21,10 +21,8 @@ class cached_property(Generic[_T]):
if self.attrname is None: if self.attrname is None:
self.attrname = name self.attrname = name
elif name != self.attrname: elif name != self.attrname:
raise TypeError( msg = f"Cannot assign the same cached_property to two different names ({self.attrname!r} and {name!r})."
"Cannot assign the same cached_property to two different names " raise TypeError(msg)
f"({self.attrname!r} and {name!r})."
)
@overload @overload
def __get__(self, instance: None, owner: type[Any] | None = ...) -> cached_property[_T]: def __get__(self, instance: None, owner: type[Any] | None = ...) -> cached_property[_T]:
@@ -38,9 +36,8 @@ class cached_property(Generic[_T]):
if instance is None: if instance is None:
return self return self
if self.attrname is None: if self.attrname is None:
raise TypeError( msg = "Cannot use cached_property instance without calling __set_name__ on it."
"Cannot use cached_property instance without calling __set_name__ on it." raise TypeError(msg)
)
try: try:
cache = instance.__dict__ cache = instance.__dict__
except AttributeError: # not all objects have __dict__ (e.g. class defines slots) except AttributeError: # not all objects have __dict__ (e.g. class defines slots)
+2 -1
View File
@@ -367,7 +367,8 @@ def build(options: Options, tmp_path: Path) -> None: # pylint: disable=unused-a
cwd = Path.cwd() cwd = Path.cwd()
abs_package_dir = options.globals.package_dir.resolve() abs_package_dir = options.globals.package_dir.resolve()
if cwd != abs_package_dir and cwd not in abs_package_dir.parents: if cwd != abs_package_dir and cwd not in abs_package_dir.parents:
raise Exception("package_dir must be inside the working directory") msg = "package_dir must be inside the working directory"
raise Exception(msg)
container_project_path = PurePosixPath("/project") container_project_path = PurePosixPath("/project")
container_package_dir = container_project_path / abs_package_dir.relative_to(cwd) container_package_dir = container_project_path / abs_package_dir.relative_to(cwd)
+4 -2
View File
@@ -197,14 +197,16 @@ def build_description_from_identifier(identifier: str) -> str:
elif python_interpreter == "pp": elif python_interpreter == "pp":
build_description += "PyPy" build_description += "PyPy"
else: else:
raise Exception("unknown python") msg = f"unknown python {python_interpreter!r}"
raise Exception(msg)
build_description += f" {python_version[0]}.{python_version[1:]} " build_description += f" {python_version[0]}.{python_version[1:]} "
try: try:
build_description += PLATFORM_IDENTIFIER_DESCRIPTIONS[platform_identifier] build_description += PLATFORM_IDENTIFIER_DESCRIPTIONS[platform_identifier]
except KeyError as e: except KeyError as e:
raise Exception("unknown platform") from e msg = f"unknown platform {platform_identifier!r}"
raise Exception(msg) from e
return build_description return build_description
+4 -2
View File
@@ -146,7 +146,8 @@ def setup_python(
elif implementation_id.startswith("pp"): elif implementation_id.startswith("pp"):
base_python = install_pypy(tmp, python_configuration.url) base_python = install_pypy(tmp, python_configuration.url)
else: else:
raise ValueError("Unknown Python implementation") msg = "Unknown Python implementation"
raise ValueError(msg)
assert base_python.exists() assert base_python.exists()
log.step("Setting up build environment...") log.step("Setting up build environment...")
@@ -466,7 +467,8 @@ def build(options: Options, tmp_path: Path) -> None:
) )
) )
else: else:
raise RuntimeError("unreachable") msg = "unreachable"
raise RuntimeError(msg)
# skip this test # skip this test
continue continue
+2 -1
View File
@@ -58,7 +58,8 @@ class OCIContainer:
engine: ContainerEngine = "docker", engine: ContainerEngine = "docker",
): ):
if not image: if not image:
raise ValueError("Must have a non-empty image to run.") msg = "Must have a non-empty image to run."
raise ValueError(msg)
self.image = image self.image = image
self.simulate_32_bit = simulate_32_bit self.simulate_32_bit = simulate_32_bit
+10 -5
View File
@@ -138,7 +138,8 @@ def _dig_first(*pairs: tuple[Mapping[str, Setting], str], ignore_empty: bool = F
_dig_first((dict1, "key1"), (dict2, "key2"), ...) _dig_first((dict1, "key1"), (dict2, "key2"), ...)
""" """
if not pairs: if not pairs:
raise ValueError("pairs cannot be empty") msg = "pairs cannot be empty"
raise ValueError(msg)
for dict_like, key in pairs: for dict_like, key in pairs:
if key in dict_like: if key in dict_like:
@@ -208,13 +209,15 @@ class OptionsReader:
if config_overrides is not None: if config_overrides is not None:
if not isinstance(config_overrides, list): if not isinstance(config_overrides, list):
raise ConfigOptionError("'tool.cibuildwheel.overrides' must be a list") msg = "'tool.cibuildwheel.overrides' must be a list"
raise ConfigOptionError(msg)
for config_override in config_overrides: for config_override in config_overrides:
select = config_override.pop("select", None) select = config_override.pop("select", None)
if not select: if not select:
raise ConfigOptionError("'select' must be set in an override") msg = "'select' must be set in an override"
raise ConfigOptionError(msg)
if isinstance(select, list): if isinstance(select, list):
select = " ".join(select) select = " ".join(select)
@@ -328,14 +331,16 @@ class OptionsReader:
if isinstance(result, dict): if isinstance(result, dict):
if table is None: if table is None:
raise ConfigOptionError(f"{name!r} does not accept a table") msg = f"{name!r} does not accept a table"
raise ConfigOptionError(msg)
return table["sep"].join( return table["sep"].join(
item for k, v in result.items() for item in _inner_fmt(k, v, table) item for k, v in result.items() for item in _inner_fmt(k, v, table)
) )
if isinstance(result, list): if isinstance(result, list):
if sep is None: if sep is None:
raise ConfigOptionError(f"{name!r} does not accept a list") msg = f"{name!r} does not accept a list"
raise ConfigOptionError(msg)
return sep.join(result) return sep.join(result)
if isinstance(result, int): if isinstance(result, int):
+2 -1
View File
@@ -137,7 +137,8 @@ def setup_python(
assert python_configuration.url is not None assert python_configuration.url is not None
base_python = install_pypy(tmp, python_configuration.arch, python_configuration.url) base_python = install_pypy(tmp, python_configuration.arch, python_configuration.url)
else: else:
raise ValueError("Unknown Python implementation") msg = "Unknown Python implementation"
raise ValueError(msg)
assert base_python.exists() assert base_python.exists()
log.step("Setting up build environment...") log.step("Setting up build environment...")
-1
View File
@@ -68,7 +68,6 @@ module = [
"bashlex", "bashlex",
"bashlex.*", "bashlex.*",
"importlib_resources", "importlib_resources",
"ghapi.*",
] ]
ignore_missing_imports = true ignore_missing_imports = true
-3
View File
@@ -20,7 +20,6 @@ extras = {
], ],
"bin": [ "bin": [
"click", "click",
"ghapi",
"pip-tools", "pip-tools",
"pygithub", "pygithub",
"pyyaml", "pyyaml",
@@ -46,6 +45,4 @@ extras["dev"] = [
*extras["bin"], *extras["bin"],
] ]
extras["all"] = sum(extras.values(), [])
setup(extras_require=extras) setup(extras_require=extras)
+2 -1
View File
@@ -108,7 +108,8 @@ def test_pinned_versions(tmp_path, python_version, build_frontend_env):
w for w in utils.expected_wheels("spam", "0.1.0") if "-cp39" in w or "-pp39" in w w for w in utils.expected_wheels("spam", "0.1.0") if "-cp39" in w or "-pp39" in w
] ]
else: else:
raise ValueError("unhandled python version") msg = "unhandled python version"
raise ValueError(msg)
assert set(actual_wheels) == set(expected_wheels) assert set(actual_wheels) == set(expected_wheels)
+4 -2
View File
@@ -23,7 +23,8 @@ elif sys.platform.startswith("darwin"):
elif sys.platform in ["win32", "cygwin"]: elif sys.platform in ["win32", "cygwin"]:
platform = "windows" platform = "windows"
else: else:
raise Exception("Unsupported platform") msg = f"Unsupported platform {sys.platform!r}"
raise Exception(msg)
def cibuildwheel_get_build_identifiers(project_path, env=None, *, prerelease_pythons=False): def cibuildwheel_get_build_identifiers(project_path, env=None, *, prerelease_pythons=False):
@@ -210,7 +211,8 @@ def expected_wheels(
) )
else: else:
raise Exception("unsupported platform") msg = f"Unsupported platform {platform!r}"
raise Exception(msg)
for platform_tag in platform_tags: for platform_tag in platform_tags:
wheels.append(f"{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl") wheels.append(f"{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl")
+2 -1
View File
@@ -31,7 +31,8 @@ def mock_protection(monkeypatch):
""" """
def fail_on_call(*args, **kwargs): def fail_on_call(*args, **kwargs):
raise RuntimeError("This should never be called") msg = "This should never be called"
raise RuntimeError(msg)
def ignore_call(*args, **kwargs): def ignore_call(*args, **kwargs):
pass pass
+2 -1
View File
@@ -126,7 +126,8 @@ def get_default_repair_command(platform):
elif platform == "windows": elif platform == "windows":
return "" return ""
else: else:
raise ValueError("Unknown platform", platform) msg = f"Unknown platform: {platform!r}"
raise ValueError(msg)
@pytest.mark.parametrize("repair_command", [None, "repair", "repair -w {dest_dir} {wheel}"]) @pytest.mark.parametrize("repair_command", [None, "repair", "repair -w {dest_dir} {wheel}"])
+2 -1
View File
@@ -19,7 +19,8 @@ ALL_IDS = {"cp36", "cp37", "cp38", "cp39", "cp310", "cp311", "pp37", "pp38", "pp
@pytest.fixture @pytest.fixture
def mock_build_container(monkeypatch): def mock_build_container(monkeypatch):
def fail_on_call(*args, **kwargs): def fail_on_call(*args, **kwargs):
raise RuntimeError("This should never be called") msg = "This should never be called"
raise RuntimeError(msg)
def ignore_call(*args, **kwargs): def ignore_call(*args, **kwargs):
pass pass