Skip to content

Commit 5952330

Browse files
Erotemicjoerick
andauthored
tests: fix and enable doctests (#2546)
* fix doctest * Update cibuildwheel/oci_container.py Co-authored-by: Joe Rickerby <[email protected]> * Update cibuildwheel/oci_container.py Co-authored-by: Joe Rickerby <[email protected]> * Update cibuildwheel/oci_container.py Co-authored-by: Joe Rickerby <[email protected]> * Add xdoctest to CI * Add doctests to run tests * Replace test map with new native method * Forgot key for arm64 * Allow doctests to be filtered on circleci --------- Co-authored-by: Joe Rickerby <[email protected]>
1 parent 8db3538 commit 5952330

File tree

7 files changed

+58
-18
lines changed

7 files changed

+58
-18
lines changed

bin/run_tests.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,23 @@
3232
# move cwd to the project root
3333
os.chdir(Path(__file__).resolve().parents[1])
3434

35+
# doc tests
36+
doc_test_args = [sys.executable, "-m", "pytest", "cibuildwheel"]
37+
38+
print(
39+
"\n\n================================== DOC TESTS ==================================",
40+
flush=True,
41+
)
42+
result = subprocess.run(doc_test_args, check=False)
43+
if result.returncode not in (0, 5):
44+
# Allow case where no doctests are collected (returncode 5) because
45+
# circleci sets an explicit "-k" filter that disables doctests. There
46+
# isn't a pattern that will only select doctests. This can be removed
47+
# and have check=True if the circleci PYTEST_ADDOPTS is removed.
48+
raise subprocess.CalledProcessError(
49+
result.returncode, result.args, output=result.stdout, stderr=result.stderr
50+
)
51+
3552
# unit tests
3653
unit_test_args = [sys.executable, "-m", "pytest", "unit_test"]
3754

cibuildwheel/oci_container.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,28 @@ class OCIPlatform(Enum):
3636
RISCV64 = "linux/riscv64"
3737
S390X = "linux/s390x"
3838

39+
@classmethod
40+
def native(cls) -> "OCIPlatform":
41+
"""Return the current OCI platform, or raise ValueError if unknown."""
42+
arch = platform.machine().lower()
43+
mapping = {
44+
"i386": cls.i386,
45+
"i686": cls.i386,
46+
"x86_64": cls.AMD64,
47+
"amd64": cls.AMD64,
48+
"armv7l": cls.ARMV7,
49+
"aarch64": cls.ARM64,
50+
"arm64": cls.ARM64,
51+
"ppc64le": cls.PPC64LE,
52+
"riscv64": cls.RISCV64,
53+
"s390x": cls.S390X,
54+
}
55+
try:
56+
return mapping[arch]
57+
except KeyError as ex:
58+
msg = f"Unsupported platform architecture: {arch}"
59+
raise OSError(msg) from ex
60+
3961

4062
@dataclasses.dataclass(frozen=True)
4163
class OCIContainerEngineConfig:
@@ -152,11 +174,19 @@ class OCIContainer:
152174
back to cibuildwheel.
153175
154176
Example:
177+
>>> # xdoctest: +REQUIRES(LINUX)
155178
>>> from cibuildwheel.oci_container import * # NOQA
156179
>>> from cibuildwheel.options import _get_pinned_container_images
180+
>>> import pytest
181+
>>> try:
182+
... oci_platform = OCIPlatform.native()
183+
... except OSError as ex:
184+
... pytest.skip(str(ex))
185+
>>> if oci_platform != OCIPlatform.AMD64:
186+
... pytest.skip('only runs on amd64')
157187
>>> image = _get_pinned_container_images()['x86_64']['manylinux2014']
158188
>>> # Test the default container
159-
>>> with OCIContainer(image=image) as self:
189+
>>> with OCIContainer(image=image, oci_platform=oci_platform) as self:
160190
... self.call(["echo", "hello world"])
161191
... self.call(["cat", "/proc/1/cgroup"])
162192
... print(self.get_environment())

cibuildwheel/options.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,7 @@ class OptionsReader:
391391
by the platform.
392392
393393
Example:
394+
>>> # xdoctest: +SKIP
394395
>>> options_reader = OptionsReader(config_file, platform='macos')
395396
>>> options_reader.get('cool-color')
396397

cibuildwheel/util/helpers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ def format_safe(template: str, **kwargs: str | os.PathLike[str]) -> str:
2020
>>> format_safe('{a} {b[4]:3f}', a='123')
2121
'123 {b[4]:3f}'
2222
23-
To avoid variable expansion, precede with a single backslash e.g.
24-
>>> format_safe('\\{a} {b}', a='123')
23+
To avoid variable expansion, precede with a single hash e.g.
24+
>>> format_safe('#{a} {b}', a='123')
2525
'{a} {b}'
2626
"""
2727

noxfile.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ def tests(session: nox.Session) -> None:
5858
session.run("pytest", *session.posargs)
5959
else:
6060
unit_test_args = ["--run-docker"] if sys.platform.startswith("linux") else []
61+
session.run("pytest", "cibuildwheel") # run doctests
6162
session.run("pytest", "unit_test", *unit_test_args)
6263
session.run("pytest", "test", "-x", "--durations", "0", "--timeout=2400", "test")
6364

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ test = [
9393
"setuptools",
9494
"tomli_w",
9595
"validate-pyproject",
96+
"xdoctest",
9697
]
9798
dev = [
9899
{include-group = "bin"},
@@ -103,7 +104,7 @@ dev = [
103104

104105
[tool.pytest.ini_options]
105106
minversion = "6.0"
106-
addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"]
107+
addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config", "--xdoctest", "--ignore-glob=*venv*"]
107108
junit_family = "xunit2"
108109
xfail_strict = true
109110
filterwarnings = ["error"]

unit_test/oci_container_test.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import json
22
import os
3-
import platform
43
import random
54
import shutil
65
import subprocess
@@ -29,16 +28,7 @@
2928
# for these tests we use manylinux2014 images, because they're available on
3029
# multi architectures and include python3.8
3130
DEFAULT_IMAGE = "quay.io/pypa/manylinux2014:2025.03.08-1"
32-
pm = platform.machine()
33-
DEFAULT_OCI_PLATFORM = {
34-
"AMD64": OCIPlatform.AMD64,
35-
"x86_64": OCIPlatform.AMD64,
36-
"ppc64le": OCIPlatform.PPC64LE,
37-
"s390x": OCIPlatform.S390X,
38-
"aarch64": OCIPlatform.ARM64,
39-
"arm64": OCIPlatform.ARM64,
40-
"ARM64": OCIPlatform.ARM64,
41-
}[pm]
31+
DEFAULT_OCI_PLATFORM = OCIPlatform.native()
4232

4333
PODMAN = OCIContainerEngineConfig(name="podman")
4434

@@ -494,7 +484,7 @@ def test_parse_engine_config(config, name, create_args, capsys):
494484
)
495485

496486

497-
@pytest.mark.skipif(pm != "x86_64", reason="Only runs on x86_64")
487+
@pytest.mark.skipif(DEFAULT_OCI_PLATFORM != OCIPlatform.AMD64, reason="Only runs on x86_64")
498488
def test_enforce_32_bit(container_engine):
499489
with OCIContainer(
500490
engine=container_engine, image=DEFAULT_IMAGE, oci_platform=OCIPlatform.i386
@@ -551,7 +541,7 @@ def test_local_image(
551541
) -> None:
552542
if (
553543
detect_ci_provider() == CIProvider.travis_ci
554-
and pm != "x86_64"
544+
and DEFAULT_OCI_PLATFORM != OCIPlatform.AMD64
555545
and platform != DEFAULT_OCI_PLATFORM
556546
):
557547
pytest.skip("Skipping test because docker on this platform does not support QEMU")
@@ -581,7 +571,7 @@ def test_local_image(
581571
def test_multiarch_image(container_engine, platform):
582572
if (
583573
detect_ci_provider() == CIProvider.travis_ci
584-
and pm != "x86_64"
574+
and DEFAULT_OCI_PLATFORM != OCIPlatform.AMD64
585575
and platform != DEFAULT_OCI_PLATFORM
586576
):
587577
pytest.skip("Skipping test because docker on this platform does not support QEMU")

0 commit comments

Comments
 (0)