chore: pattern matching (#2434)

* chore: use match for stringify_setting

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* chore: more match statements

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
This commit is contained in:
Henry Schreiner
2025-07-08 08:59:20 -04:00
committed by GitHub
co-authored by Joe Rickerby
parent 46ef6044e2
commit 834ea40510
7 changed files with 283 additions and 277 deletions
+6 -3
View File
@@ -95,7 +95,8 @@ def _check_engine_version(engine: OCIContainerEngineConfig) -> None:
try: try:
version_string = call(engine.name, "version", "-f", "{{json .}}", capture_stdout=True) version_string = call(engine.name, "version", "-f", "{{json .}}", capture_stdout=True)
version_info = json.loads(version_string.strip()) version_info = json.loads(version_string.strip())
if engine.name == "docker": match engine.name:
case "docker":
client_api_version = FlexibleVersion(version_info["Client"]["ApiVersion"]) client_api_version = FlexibleVersion(version_info["Client"]["ApiVersion"])
server_api_version = FlexibleVersion(version_info["Server"]["ApiVersion"]) server_api_version = FlexibleVersion(version_info["Server"]["ApiVersion"])
# --platform support was introduced in 1.32 as experimental, 1.41 removed the experimental flag # --platform support was introduced in 1.32 as experimental, 1.41 removed the experimental flag
@@ -110,7 +111,7 @@ def _check_engine_version(engine: OCIContainerEngineConfig) -> None:
The API version found by cibuildwheel is {version}. The API version found by cibuildwheel is {version}.
""" """
) )
elif engine.name == "podman": case "podman":
# podman uses the same version string for "Version" & "ApiVersion" # podman uses the same version string for "Version" & "ApiVersion"
client_version = FlexibleVersion(version_info["Client"]["Version"]) client_version = FlexibleVersion(version_info["Client"]["Version"])
if "Server" in version_info: if "Server" in version_info:
@@ -128,10 +129,12 @@ def _check_engine_version(engine: OCIContainerEngineConfig) -> None:
The version found by cibuildwheel is {version}. The version found by cibuildwheel is {version}.
""" """
) )
else: case _:
assert_never(engine.name) assert_never(engine.name)
if version < minimum_version: if version < minimum_version:
raise OCIEngineTooOldError(error_msg) from None raise OCIEngineTooOldError(error_msg) from None
except (subprocess.CalledProcessError, KeyError, ValueError) as e: except (subprocess.CalledProcessError, KeyError, ValueError) as e:
msg = f"Build failed because {engine.name} is too old or is not working properly." msg = f"Build failed because {engine.name} is too old or is not working properly."
raise OCIEngineTooOldError(msg) from e raise OCIEngineTooOldError(msg) from e
+18 -12
View File
@@ -343,11 +343,12 @@ def _apply_inherit_rule(
msg = f"Don't know how to merge {before!r} and {after!r} with {rule}" msg = f"Don't know how to merge {before!r} and {after!r} with {rule}"
raise OptionsReaderError(msg) raise OptionsReaderError(msg)
if rule == InheritRule.APPEND: match rule:
case InheritRule.APPEND:
return option_format.merge_values(before, after) return option_format.merge_values(before, after)
if rule == InheritRule.PREPEND: case InheritRule.PREPEND:
return option_format.merge_values(after, before) return option_format.merge_values(after, before)
case _:
assert_never(rule) assert_never(rule)
@@ -355,27 +356,32 @@ def _stringify_setting(
setting: SettingValue, setting: SettingValue,
option_format: OptionFormat | None, option_format: OptionFormat | None,
) -> str: ) -> str:
if isinstance(setting, Mapping): match setting:
case {}:
assert isinstance(setting, Mapping) # MyPy 1.15 doesn't narrow this for us
try: try:
if option_format is None: if option_format is None:
raise OptionFormat.NotSupported raise OptionFormat.NotSupported
return option_format.format_table(setting) return option_format.format_table(setting)
except OptionFormat.NotSupported: except OptionFormat.NotSupported:
msg = f"Error converting {setting!r} to a string: this setting doesn't accept a table" msg = (
f"Error converting {setting!r} to a string: this setting doesn't accept a table"
)
raise OptionsReaderError(msg) from None raise OptionsReaderError(msg) from None
case bool() | int():
if not isinstance(setting, str) and isinstance(setting, Sequence): return str(setting)
case [*_]:
try: try:
if option_format is None: if option_format is None:
raise OptionFormat.NotSupported raise OptionFormat.NotSupported
return option_format.format_list(setting) return option_format.format_list(setting)
except OptionFormat.NotSupported: except OptionFormat.NotSupported:
msg = f"Error converting {setting!r} to a string: this setting doesn't accept a list" msg = (
f"Error converting {setting!r} to a string: this setting doesn't accept a list"
)
raise OptionsReaderError(msg) from None raise OptionsReaderError(msg) from None
case _:
if isinstance(setting, bool | int): assert isinstance(setting, str) # MyPy 1.15 doesn't narrow this for us
return str(setting)
return setting return setting
+8 -6
View File
@@ -387,10 +387,11 @@ def setup_python(
env.setdefault("IPHONEOS_DEPLOYMENT_TARGET", "13.0") env.setdefault("IPHONEOS_DEPLOYMENT_TARGET", "13.0")
log.step("Installing build tools...") log.step("Installing build tools...")
if build_frontend == "pip": match build_frontend:
case "pip":
# No additional build tools required # No additional build tools required
pass pass
elif build_frontend == "build": case "build":
call( call(
"pip", "pip",
"install", "install",
@@ -399,7 +400,7 @@ def setup_python(
*constraint_flags(dependency_constraint), *constraint_flags(dependency_constraint),
env=env, env=env,
) )
else: case _:
assert_never(build_frontend) assert_never(build_frontend)
return target_install_path, env return target_install_path, env
@@ -494,7 +495,8 @@ def build(options: Options, tmp_path: Path) -> None:
if constraints_path: if constraints_path:
combine_constraints(build_env, constraints_path, None) combine_constraints(build_env, constraints_path, None)
if build_frontend.name == "pip": match build_frontend.name:
case "pip":
# Path.resolve() is needed. Without it pip wheel may try to # Path.resolve() is needed. Without it pip wheel may try to
# fetch package from pypi.org. See # fetch package from pypi.org. See
# https://github.com/pypa/cibuildwheel/pull/369 # https://github.com/pypa/cibuildwheel/pull/369
@@ -509,7 +511,7 @@ def build(options: Options, tmp_path: Path) -> None:
*extra_flags, *extra_flags,
env=build_env, env=build_env,
) )
elif build_frontend.name == "build": case "build":
call( call(
"python", "python",
"-m", "-m",
@@ -520,7 +522,7 @@ def build(options: Options, tmp_path: Path) -> None:
*extra_flags, *extra_flags,
env=build_env, env=build_env,
) )
else: case _:
assert_never(build_frontend) assert_never(build_frontend)
test_wheel = built_wheel = next(built_wheel_dir.glob("*.whl")) test_wheel = built_wheel = next(built_wheel_dir.glob("*.whl"))
+4 -3
View File
@@ -275,7 +275,8 @@ def build_in_container(
build_frontend, build_options.build_verbosity, build_options.config_settings build_frontend, build_options.build_verbosity, build_options.config_settings
) )
if build_frontend.name == "pip": match build_frontend.name:
case "pip":
container.call( container.call(
[ [
"python", "python",
@@ -289,7 +290,7 @@ def build_in_container(
], ],
env=env, env=env,
) )
elif build_frontend.name == "build" or build_frontend.name == "build[uv]": case "build" | "build[uv]":
if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags: if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags:
extra_flags += ["--installer=uv"] extra_flags += ["--installer=uv"]
container.call( container.call(
@@ -304,7 +305,7 @@ def build_in_container(
], ],
env=env, env=env,
) )
else: case _:
assert_never(build_frontend) assert_never(build_frontend)
built_wheel = container.glob(built_wheel_dir, "*.whl")[0] built_wheel = container.glob(built_wheel_dir, "*.whl")[0]
+14 -8
View File
@@ -345,7 +345,8 @@ def setup_python(
env.setdefault("SDKROOT", arm64_compatible_sdks[0]) env.setdefault("SDKROOT", arm64_compatible_sdks[0])
log.step("Installing build tools...") log.step("Installing build tools...")
if build_frontend == "pip": match build_frontend:
case "pip":
call( call(
"pip", "pip",
"install", "install",
@@ -354,7 +355,7 @@ def setup_python(
*constraint_flags(dependency_constraint), *constraint_flags(dependency_constraint),
env=env, env=env,
) )
elif build_frontend == "build": case "build":
call( call(
"pip", "pip",
"install", "install",
@@ -364,7 +365,7 @@ def setup_python(
*constraint_flags(dependency_constraint), *constraint_flags(dependency_constraint),
env=env, env=env,
) )
elif build_frontend == "build[uv]": case "build[uv]":
assert uv_path is not None assert uv_path is not None
call( call(
uv_path, uv_path,
@@ -376,7 +377,7 @@ def setup_python(
*constraint_flags(dependency_constraint), *constraint_flags(dependency_constraint),
env=env, env=env,
) )
else: case _:
assert_never(build_frontend) assert_never(build_frontend)
return base_python, env return base_python, env
@@ -467,7 +468,8 @@ def build(options: Options, tmp_path: Path) -> None:
build_env, constraints_path, identifier_tmp_dir if use_uv else None build_env, constraints_path, identifier_tmp_dir if use_uv else None
) )
if build_frontend.name == "pip": match build_frontend.name:
case "pip":
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org # Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
# see https://github.com/pypa/cibuildwheel/pull/369 # see https://github.com/pypa/cibuildwheel/pull/369
call( call(
@@ -481,8 +483,12 @@ def build(options: Options, tmp_path: Path) -> None:
*extra_flags, *extra_flags,
env=build_env, env=build_env,
) )
elif build_frontend.name == "build" or build_frontend.name == "build[uv]": case "build" | "build[uv]":
if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags: if (
use_uv
and "--no-isolation" not in extra_flags
and "-n" not in extra_flags
):
extra_flags.append("--installer=uv") extra_flags.append("--installer=uv")
call( call(
"python", "python",
@@ -494,7 +500,7 @@ def build(options: Options, tmp_path: Path) -> None:
*extra_flags, *extra_flags,
env=build_env, env=build_env,
) )
else: case _:
assert_never(build_frontend) assert_never(build_frontend)
built_wheel = next(built_wheel_dir.glob("*.whl")) built_wheel = next(built_wheel_dir.glob("*.whl"))
+12 -6
View File
@@ -305,7 +305,8 @@ def setup_python(
call("pip", "--version", env=env) call("pip", "--version", env=env)
log.step("Installing build tools...") log.step("Installing build tools...")
if build_frontend == "build": match build_frontend:
case "build":
call( call(
"pip", "pip",
"install", "install",
@@ -314,7 +315,7 @@ def setup_python(
*constraint_flags(dependency_constraint), *constraint_flags(dependency_constraint),
env=env, env=env,
) )
elif build_frontend == "build[uv]": case "build[uv]":
assert uv_path is not None assert uv_path is not None
call( call(
uv_path, uv_path,
@@ -467,7 +468,8 @@ def build(options: Options, tmp_path: Path) -> None:
if constraints_path: if constraints_path:
combine_constraints(build_env, constraints_path, identifier_tmp_dir) combine_constraints(build_env, constraints_path, identifier_tmp_dir)
if build_frontend.name == "pip": match build_frontend.name:
case "pip":
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org # Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
# see https://github.com/pypa/cibuildwheel/pull/369 # see https://github.com/pypa/cibuildwheel/pull/369
call( call(
@@ -481,8 +483,12 @@ def build(options: Options, tmp_path: Path) -> None:
*extra_flags, *extra_flags,
env=build_env, env=build_env,
) )
elif build_frontend.name == "build" or build_frontend.name == "build[uv]": case "build" | "build[uv]":
if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags: if (
use_uv
and "--no-isolation" not in extra_flags
and "-n" not in extra_flags
):
extra_flags.append("--installer=uv") extra_flags.append("--installer=uv")
call( call(
@@ -495,7 +501,7 @@ def build(options: Options, tmp_path: Path) -> None:
*extra_flags, *extra_flags,
env=build_env, env=build_env,
) )
else: case _:
assert_never(build_frontend) assert_never(build_frontend)
built_wheel = next(built_wheel_dir.glob("*.whl")) built_wheel = next(built_wheel_dir.glob("*.whl"))
+9 -27
View File
@@ -14,32 +14,16 @@ def get_parent(node: ast.AST | None, depth: int = 1) -> ast.AST | None:
def is_main(parent: ast.AST | None) -> bool: def is_main(parent: ast.AST | None) -> bool:
if parent is None: match parent:
return False case ast.If(test=ast.Compare(left=left, ops=[ast.Eq()], comparators=[comp])):
values = {left, comp}
# This would be much nicer with 3.10's pattern matching!
if not isinstance(parent, ast.If):
return False
if not isinstance(parent.test, ast.Compare):
return False
try:
(op,) = parent.test.ops
(comp,) = parent.test.comparators
except ValueError:
return False
if not isinstance(op, ast.Eq):
return False
values = {comp, parent.test.left}
mains = {x for x in values if isinstance(x, ast.Constant) and x.value == "__main__"} mains = {x for x in values if isinstance(x, ast.Constant) and x.value == "__main__"}
if len(mains) != 1: if len(mains) != 1:
return False return False
consts = {x for x in values if isinstance(x, ast.Name) and x.id == "__name__"} consts = {x for x in values if isinstance(x, ast.Name) and x.id == "__name__"}
return len(consts) == 1 return len(consts) == 1
case _:
return False
class Analyzer(ast.NodeVisitor): class Analyzer(ast.NodeVisitor):
@@ -65,12 +49,10 @@ class Analyzer(ast.NodeVisitor):
parent is not None and get_parent(parent) is None and is_main(get_parent(node, 3)) parent is not None and get_parent(parent) is None and is_main(get_parent(node, 3))
) )
if ( match node:
node.arg == "python_requires" case ast.keyword(arg="python_requires", value=ast.Constant(value=version)):
and isinstance(node.value, ast.Constant) if unnested or name_main_unnested:
and (unnested or name_main_unnested) self.requires_python = version
):
self.requires_python = node.value.value
def setup_py_python_requires(content: str) -> str | None: def setup_py_python_requires(content: str) -> str | None: