chore: faster and stricter mypy (#2968)
* chore: faster mypy This makes mypy around 26% faster from a cold cache. Signed-off-by: Henry Schreiner <henryfs@princeton.edu> * chore: enable more mypy error codes Turn on possibly-undefined, exhaustive-match, and other strict flags, and fix what they report. Two real bugs: pyodide never set `repaired_wheel` when it reused a compatible wheel, so the test step failed with a NameError; the OCI shell read loop spun forever if the shell exited. Assisted-by: ClaudeCode:claude-opus-5 * test: carry default OCI runtime into podman vfs test config Podman ignores its usual config files when CONTAINERS_CONF is set, so it falls back to the first runtime found on PATH. On current GitHub Ubuntu runner images that is crun 1.14.1, which cannot parse the OCI spec v1.2.x config that podman 5.8.4 generates ("crun: unknown version specified"). Query the default runtime path and set it in the generated config. Assisted-by: ClaudeCode:claude-fable-5 * fix: annotate vfs config dict so mypy passes on Linux mypy on macOS marks the rest of the function unreachable after the darwin pytest.skip, so the strict index error only appears on Linux. Assisted-by: ClaudeCode:claude-fable-5 * chore: minor cleanup of variable name Signed-off-by: Henry Schreiner <henryfs@princeton.edu> * test: cover container shell exiting during call Assisted-by: ClaudeCode:claude-opus-5 --------- Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
This commit is contained in:
@@ -318,6 +318,8 @@ for value in schema["properties"].values():
|
||||
case {"type": "string_table_array"}:
|
||||
del value["type"]
|
||||
value["oneOf"] = string_table_array
|
||||
case _:
|
||||
pass
|
||||
|
||||
overrides = yaml.safe_load(
|
||||
"""
|
||||
|
||||
@@ -76,9 +76,9 @@ class WindowsVersions:
|
||||
response.raise_for_status()
|
||||
api_info = response.json()
|
||||
|
||||
for resource in api_info["resources"]:
|
||||
if resource["@type"] == "PackageBaseAddress/3.0.0":
|
||||
endpoint = resource["@id"]
|
||||
endpoint = next(
|
||||
r["@id"] for r in api_info["resources"] if r["@type"] == "PackageBaseAddress/3.0.0"
|
||||
)
|
||||
|
||||
ARCH_DICT = {"32": "win32", "64": "win_amd64", "ARM64": "win_arm64"}
|
||||
PACKAGE_DICT = {"32": "pythonx86", "64": "python", "ARM64": "pythonarm64"}
|
||||
|
||||
@@ -546,9 +546,14 @@ class OCIContainer:
|
||||
else:
|
||||
output_io = sys.stdout.buffer
|
||||
|
||||
while True:
|
||||
return_code = None
|
||||
while return_code is None:
|
||||
line = self.bash_stdout.readline()
|
||||
|
||||
if not line:
|
||||
msg = "Failed to read the return code, the container shell has exited"
|
||||
raise RuntimeError(msg)
|
||||
|
||||
if line.endswith(bytes(end_of_message, encoding="utf8") + b"\n"):
|
||||
# fmt: off
|
||||
footer_offset = (
|
||||
@@ -563,7 +568,6 @@ class OCIContainer:
|
||||
# add the last line to output, without the footer
|
||||
output_io.write(line[0:footer_offset])
|
||||
output_io.flush()
|
||||
break
|
||||
else:
|
||||
output_io.write(line)
|
||||
output_io.flush()
|
||||
|
||||
@@ -540,7 +540,7 @@ def build(options: Options, tmp_path: Path) -> None:
|
||||
f"that is compatible with {config.identifier}. "
|
||||
"Skipping build step..."
|
||||
)
|
||||
test_wheel = compatible_wheel
|
||||
repaired_wheel = compatible_wheel
|
||||
else:
|
||||
if build_options.before_build:
|
||||
log.step("Running before_build...")
|
||||
@@ -632,8 +632,6 @@ def build(options: Options, tmp_path: Path) -> None:
|
||||
|
||||
run_audit(tmp_dir=tmp_path, build_options=build_options, wheel=repaired_wheel)
|
||||
|
||||
test_wheel = repaired_wheel
|
||||
|
||||
if build_options.test_command and build_options.test_selector(config.identifier):
|
||||
if not config.is_simulator:
|
||||
log.step("Skipping tests on non-simulator SDK")
|
||||
@@ -693,7 +691,7 @@ def build(options: Options, tmp_path: Path) -> None:
|
||||
platform_tag,
|
||||
"--target",
|
||||
testbed_path / "iOSTestbed" / "app_packages",
|
||||
f"{test_wheel}{build_options.test_extras}",
|
||||
f"{repaired_wheel}{build_options.test_extras}",
|
||||
*build_options.test_requires,
|
||||
env=test_env,
|
||||
)
|
||||
|
||||
@@ -473,7 +473,7 @@ def build(options: Options, tmp_path: Path) -> None:
|
||||
print(
|
||||
f"\nFound previously built wheel {compatible_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
|
||||
)
|
||||
built_wheel = compatible_wheel
|
||||
repaired_wheel = compatible_wheel
|
||||
else:
|
||||
if build_options.before_build:
|
||||
log.step("Running before_build...")
|
||||
|
||||
@@ -60,6 +60,8 @@ class Analyzer(ast.NodeVisitor):
|
||||
case ast.keyword(arg="python_requires", value=ast.Constant(value=str() as version)):
|
||||
if unnested or name_main_unnested:
|
||||
self.requires_python = version
|
||||
case _:
|
||||
pass
|
||||
|
||||
|
||||
def setup_py_python_requires(content: str) -> str | None:
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ def define_env(env: Any) -> None: # noqa: ANN401
|
||||
"Hook function for mkdocs-macros"
|
||||
|
||||
@env.macro # type: ignore[untyped-decorator]
|
||||
def subprocess_run(*args: str) -> str:
|
||||
def subprocess_run(*args: str) -> str: # type: ignore[misc]
|
||||
"Run a subprocess and return the stdout"
|
||||
env = os.environ.copy()
|
||||
scripts = sysconfig.get_path("scripts")
|
||||
|
||||
+22
-1
@@ -116,10 +116,27 @@ files = [
|
||||
"bin/*.py",
|
||||
"noxfile.py",
|
||||
]
|
||||
disallow_any_decorated = true
|
||||
disallow_any_unimported = true
|
||||
disallow_untyped_globals = true
|
||||
disallow_redefinition = true
|
||||
warn_unused_configs = true
|
||||
strict = true
|
||||
enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
|
||||
enable_error_code = [
|
||||
"deprecated",
|
||||
"exhaustive-match",
|
||||
"ignore-without-code",
|
||||
"mutable-override",
|
||||
"possibly-undefined",
|
||||
"redundant-expr",
|
||||
"redundant-self",
|
||||
"truthy-bool",
|
||||
"truthy-iterable",
|
||||
"unimported-reveal",
|
||||
"unused-awaitable",
|
||||
]
|
||||
warn_unreachable = false
|
||||
native_parser = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = [
|
||||
@@ -130,6 +147,10 @@ module = [
|
||||
]
|
||||
ignore_missing_imports = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = ["cibuildwheel.bashlex_eval"]
|
||||
disable_error_code = ["no-any-unimported"]
|
||||
|
||||
|
||||
[tool.pylint]
|
||||
py-version = "3.11"
|
||||
|
||||
@@ -29,6 +29,7 @@ from cibuildwheel.oci_container import (
|
||||
TYPE_CHECKING = False
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Iterator
|
||||
from typing import Any
|
||||
|
||||
# Test utilities
|
||||
|
||||
@@ -84,6 +85,18 @@ def test_no_lf(container_engine: OCIContainerEngineConfig) -> None:
|
||||
assert container.call(["printf", "hello"], capture_output=True) == "hello"
|
||||
|
||||
|
||||
def test_abnormal_exit(container_engine: OCIContainerEngineConfig) -> None:
|
||||
container = OCIContainer(
|
||||
engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
|
||||
)
|
||||
with container:
|
||||
# kill the shell without a newline, so the write below still goes
|
||||
# through the same buffer and the call fails on read, not on write
|
||||
container.bash_stdin.write(b"exit")
|
||||
with pytest.raises(RuntimeError):
|
||||
container.call(["echo", "hello"])
|
||||
|
||||
|
||||
def test_debug_info(container_engine: OCIContainerEngineConfig) -> None:
|
||||
container = OCIContainer(
|
||||
engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
|
||||
@@ -139,6 +152,7 @@ def test_container_removed(container_engine: OCIContainerEngineConfig) -> None:
|
||||
) as container:
|
||||
assert container.name is not None
|
||||
container_name = container.name
|
||||
docker_containers_listing = ""
|
||||
for _ in range(timeout):
|
||||
docker_containers_listing = subprocess.run(
|
||||
f"{container.engine.name} container ls",
|
||||
@@ -330,7 +344,7 @@ def test_podman_vfs(
|
||||
# This requires that we write configuration files and point to them
|
||||
# with environment variables before we run podman
|
||||
# https://github.com/containers/common/blob/main/docs/containers.conf.5.md
|
||||
vfs_containers_conf_data = {
|
||||
vfs_containers_conf_data: dict[str, dict[str, Any]] = {
|
||||
"containers": {
|
||||
"default_capabilities": [
|
||||
"CHOWN",
|
||||
@@ -348,6 +362,20 @@ def test_podman_vfs(
|
||||
},
|
||||
"engine": {"cgroup_manager": "cgroupfs", "events_logger": "file"},
|
||||
}
|
||||
|
||||
# Setting CONTAINERS_CONF makes podman ignore its usual config files, so
|
||||
# carry over the default OCI runtime; the fallback found on PATH can be
|
||||
# too old for the OCI spec version podman generates (e.g. Ubuntu 24.04's
|
||||
# crun 1.14.1 with podman 5.x).
|
||||
oci_runtime = subprocess.run(
|
||||
["podman", "info", "--format", "{{.Host.OCIRuntime.Path}}"],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
).stdout.strip()
|
||||
runtime_name = Path(oci_runtime).name
|
||||
vfs_containers_conf_data["engine"]["runtime"] = runtime_name
|
||||
vfs_containers_conf_data["engine"]["runtimes"] = {runtime_name: [oci_runtime]}
|
||||
# https://github.com/containers/storage/blob/main/docs/containers-storage.conf.5.md
|
||||
storage_root = vfs_path / ".local/share/containers/vfs-storage"
|
||||
run_root = vfs_path / ".local/share/containers/vfs-runroot"
|
||||
|
||||
Reference in New Issue
Block a user