Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: nicer tracebacks and dev improvements #1258

Merged
merged 4 commits into from Sep 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 33 additions & 15 deletions bin/inspect_all_known_projects.py
@@ -1,4 +1,17 @@
#!/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

import ast
Expand All @@ -7,7 +20,7 @@

import click
import yaml
from ghapi.core import GhApi, HTTP404NotFoundError
from github import Github, GithubException
from rich import print

from cibuildwheel.projectfiles import Analyzer
Expand Down Expand Up @@ -47,33 +60,38 @@ def check_repo(name: str, contents: str) -> str:


class MaybeRemote:
def __init__(self, cached_file: Path | str, *, online: bool) -> None:
self.online = online
if self.online:
self.contents: dict[str, dict[str, str | None]] = {
github: Github | None
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.cfg": {},
"pyproject.toml": {},
}
else:
self.github = None
with open(cached_file) as f:
self.contents = yaml.safe_load(f)

def get(self, repo: str, filename: str) -> str | None:
if self.online:
if self.github:
try:
self.contents[filename][repo] = (
GhApi(*repo.split("/")).get_content(filename).decode()
)
except HTTP404NotFoundError:
gh_file = self.github.get_repo(repo).get_contents(filename)
except GithubException:
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]
elif repo in self.contents[filename]:
return self.contents[filename][repo]
else:
raise RuntimeError(
f"Trying to access {repo}:{filename} and not in cache, rebuild cache"
)
msg = f"Trying to access {repo}:{filename} and not in cache, rebuild cache"
raise RuntimeError(msg)

def save(self, filename: Path | str) -> None:
with open(filename, "w") as f:
Expand All @@ -87,8 +105,8 @@ def on_each(self, repos: list[str]) -> Iterator[tuple[str, str, str | None]]:


@click.command()
@click.option("--online", is_flag=True, help="Remember to set GITHUB_TOKEN")
def main(online: bool) -> None:
@click.option("--online", help="Set to $GITHUB_TOKEN")
def main(online: str | None) -> None:
with open(DIR / "../docs/data/projects.yml") as f:
known = yaml.safe_load(f)

Expand Down
6 changes: 4 additions & 2 deletions bin/run_example_ci_configs.py
Expand Up @@ -81,7 +81,8 @@ def ci_service_for_config_file(config_file):
if service.name == service_name:
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()
Expand All @@ -100,7 +101,8 @@ def run_example_ci_configs(config_files=None):
for config_file in config_files:
service = ci_service_for_config_file(config_file)
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

if git_repo_has_changes():
Expand Down
9 changes: 6 additions & 3 deletions bin/update_pythons.py
Expand Up @@ -141,7 +141,8 @@ def update_version_windows(self, spec: Specifier) -> ConfigWinCP:
releases = [r for r in releases if self.get_arch_file(r)]

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"

Expand All @@ -159,13 +160,15 @@ def update_version_windows(self, spec: Specifier) -> ConfigWinCP:

def update_version_macos(self, spec: Specifier) -> ConfigMacOS:
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 = sorted(releases, key=lambda r: r["pypy_version"]) # type: ignore[no-any-return]

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]
version = release["python_version"]
Expand Down
3 changes: 2 additions & 1 deletion cibuildwheel/__main__.py
Expand Up @@ -149,7 +149,8 @@ def main() -> None:
try:
(project_dir,) = temp_dir.iterdir()
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
args.package_dir = project_dir.resolve()
Expand Down
18 changes: 10 additions & 8 deletions cibuildwheel/bashlex_eval.py
Expand Up @@ -32,7 +32,8 @@ def evaluate(
command_node = bashlex.parsesingle(value)

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]

Expand All @@ -54,7 +55,8 @@ def evaluate_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
elif node.kind == "parameter":
return evaluate_parameter_node(node, context=context)
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:
Expand All @@ -65,10 +67,8 @@ def evaluate_word_node(node: bashlex.ast.node, context: NodeExecutionContext) ->
part_value = evaluate_node(part, context=context)

if part_string not in value:
raise RuntimeError(
f'bash parse failed. part "{part_string}" not found in "{value}". '
f'Word was "{node.word}". Full input was "{context.input}"'
)
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}"
raise RuntimeError(msg)

value = value.replace(part_string, part_value, 1)

Expand All @@ -95,9 +95,11 @@ def evaluate_nodes_as_compound_command(
result += evaluate_command_node(node, context=context)
elif node.kind == "operator":
if node.op != ";":
raise ValueError(f'Unsupported bash operator: "{node.op}"')
msg = f"Unsupported bash operator: {node.op!r}"
raise ValueError(msg)
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

Expand Down
11 changes: 4 additions & 7 deletions cibuildwheel/functools_cached_property_38.py
Expand Up @@ -21,10 +21,8 @@ def __set_name__(self, owner: type[Any], name: str) -> None:
if self.attrname is None:
self.attrname = name
elif name != self.attrname:
raise TypeError(
"Cannot assign the same cached_property to two different names "
f"({self.attrname!r} and {name!r})."
)
msg = f"Cannot assign the same cached_property to two different names ({self.attrname!r} and {name!r})."
raise TypeError(msg)

@overload
def __get__(self, instance: None, owner: type[Any] | None = ...) -> cached_property[_T]:
Expand All @@ -38,9 +36,8 @@ def __get__(self, instance: object | None, owner: type[Any] | None = None) -> An
if instance is None:
return self
if self.attrname is None:
raise TypeError(
"Cannot use cached_property instance without calling __set_name__ on it."
)
msg = "Cannot use cached_property instance without calling __set_name__ on it."
raise TypeError(msg)
try:
cache = instance.__dict__
except AttributeError: # not all objects have __dict__ (e.g. class defines slots)
Expand Down
3 changes: 2 additions & 1 deletion cibuildwheel/linux.py
Expand Up @@ -367,7 +367,8 @@ def build(options: Options, tmp_path: Path) -> None: # pylint: disable=unused-a
cwd = Path.cwd()
abs_package_dir = options.globals.package_dir.resolve()
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_package_dir = container_project_path / abs_package_dir.relative_to(cwd)
Expand Down
6 changes: 4 additions & 2 deletions cibuildwheel/logger.py
Expand Up @@ -197,14 +197,16 @@ def build_description_from_identifier(identifier: str) -> str:
elif python_interpreter == "pp":
build_description += "PyPy"
else:
raise Exception("unknown python")
msg = f"unknown python {python_interpreter!r}"
raise Exception(msg)

build_description += f" {python_version[0]}.{python_version[1:]} "

try:
build_description += PLATFORM_IDENTIFIER_DESCRIPTIONS[platform_identifier]
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

Expand Down
6 changes: 4 additions & 2 deletions cibuildwheel/macos.py
Expand Up @@ -146,7 +146,8 @@ def setup_python(
elif implementation_id.startswith("pp"):
base_python = install_pypy(tmp, python_configuration.url)
else:
raise ValueError("Unknown Python implementation")
msg = "Unknown Python implementation"
raise ValueError(msg)
assert base_python.exists()

log.step("Setting up build environment...")
Expand Down Expand Up @@ -466,7 +467,8 @@ def build(options: Options, tmp_path: Path) -> None:
)
)
else:
raise RuntimeError("unreachable")
msg = "unreachable"
raise RuntimeError(msg)

# skip this test
continue
Expand Down
3 changes: 2 additions & 1 deletion cibuildwheel/oci_container.py
Expand Up @@ -58,7 +58,8 @@ def __init__(
engine: ContainerEngine = "docker",
):
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.simulate_32_bit = simulate_32_bit
Expand Down
15 changes: 10 additions & 5 deletions cibuildwheel/options.py
Expand Up @@ -137,7 +137,8 @@ def _dig_first(*pairs: tuple[Mapping[str, Setting], str], ignore_empty: bool = F
_dig_first((dict1, "key1"), (dict2, "key2"), ...)
"""
if not pairs:
raise ValueError("pairs cannot be empty")
msg = "pairs cannot be empty"
raise ValueError(msg)

for dict_like, key in pairs:
if key in dict_like:
Expand Down Expand Up @@ -207,13 +208,15 @@ def __init__(

if config_overrides is not None:
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:
select = config_override.pop("select", None)

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):
select = " ".join(select)
Expand Down Expand Up @@ -327,14 +330,16 @@ def get(

if isinstance(result, dict):
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(
item for k, v in result.items() for item in _inner_fmt(k, v, table["item"])
)

if isinstance(result, list):
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)

if isinstance(result, int):
Expand Down
3 changes: 2 additions & 1 deletion cibuildwheel/windows.py
Expand Up @@ -137,7 +137,8 @@ def setup_python(
assert python_configuration.url is not None
base_python = install_pypy(tmp, python_configuration.arch, python_configuration.url)
else:
raise ValueError("Unknown Python implementation")
msg = "Unknown Python implementation"
raise ValueError(msg)
assert base_python.exists()

log.step("Setting up build environment...")
Expand Down
1 change: 0 additions & 1 deletion pyproject.toml
Expand Up @@ -67,7 +67,6 @@ module = [
"pytest", # ignored in pre-commit to speed up check
"bashlex",
"importlib_resources",
"ghapi.*",
]
ignore_missing_imports = true

Expand Down
3 changes: 0 additions & 3 deletions setup.py
Expand Up @@ -20,7 +20,6 @@
],
"bin": [
"click",
"ghapi",
"pip-tools",
"pygithub",
"pyyaml",
Expand All @@ -46,6 +45,4 @@
*extras["bin"],
]

extras["all"] = sum(extras.values(), [])

setup(extras_require=extras)
3 changes: 2 additions & 1 deletion test/test_dependency_versions.py
Expand Up @@ -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
]
else:
raise ValueError("unhandled python version")
msg = "unhandled python version"
raise ValueError(msg)

assert set(actual_wheels) == set(expected_wheels)

Expand Down
6 changes: 4 additions & 2 deletions test/utils.py
Expand Up @@ -23,7 +23,8 @@
elif sys.platform in ["win32", "cygwin"]:
platform = "windows"
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):
Expand Down Expand Up @@ -210,7 +211,8 @@ def expected_wheels(
)

else:
raise Exception("unsupported platform")
msg = f"Unsupported platform {platform!r}"
raise Exception(msg)

for platform_tag in platform_tags:
wheels.append(f"{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl")
Expand Down
3 changes: 2 additions & 1 deletion unit_test/main_tests/conftest.py
Expand Up @@ -31,7 +31,8 @@ def mock_protection(monkeypatch):
"""

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):
pass
Expand Down