Skip to content

Commit 212bd7f

Browse files
committed
queue: pytype 2024.10.11 - CI green on PR #2168; skills: gotchas 512-514
CI green on PR #2168: the cp312 riscv64 wheel builds from the upstream checkout with cibuildwheel and the wheel's own unit suite passes (1746 passed, 22 skipped), with the publish job dry-running cleanly. Three reusable findings from the port: 512 - pytest's --ignore/--ignore-glob are silently inert under --pyargs <pkg>; --deselect works and its nodeids are relative to the package directory, not the rootdir pytest prints. 513 - a wheel can carry the test modules but not the helper package they import; stage the helpers with test-sources and copy them into the installed package from test-command. 514 - a tool that models a target Python version caps the matrix by itself, invisibly to a build-and-import check.
1 parent 5dd6b20 commit 212bd7f

5 files changed

Lines changed: 88 additions & 2 deletions

File tree

‎.queue.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4052,9 +4052,9 @@ packages:
40524052
version: 2024.10.11
40534053
home: https://google.github.io/pytype
40544054
repo: https://github.com/google/pytype
4055-
status: ci-running
4055+
status: in-review
40564056
pr: https://github.com/riseproject-dev/python-wheels/pull/2168
4057-
notes: '6 Linux wheels upstream (abi: cp310,cp311,cp312); no riscv64 on PyPI or pypi.riseproject.dev.'
4057+
notes: 'PR #2168 open (branch pytype), CI green. Build-from-checkout with cibuildwheel, mirroring the manylinux_wheels job of upstream build.yml (which is just `pip wheel . --no-deps` + `auditwheel repair` inside the manylinux image); setup.py builds one pybind11 extension, pytype.typegraph.cfg, from six .cc files at cxx_std=20, no arch-specific code anywhere and no CMake/ninja involvement in the wheel build (ninja is only a runtime dep and has riscv64 wheels on PyPI). No patches needed. Matrix is cp312 only: pytype refuses to run under an interpreter past 3.12 (validate_version: "Python versions > 3.12 are not yet supported"), which is why upstream ships cp310-cp312; the extension itself compiles and imports fine on cp313, so this only shows up when the tool actually runs (new gotcha 514). Dependency closure resolves binary-only for riscv64 at cp312 with our registry in play - libcst 1.8.6 and msgspec 0.21.1 come from pypi.riseproject.dev, the rest from PyPI. First CI run failed in the test phase only: pip picked libcst 1.9.0, whose riscv64 form is an sdist needing Rust, over our registry''s 1.8.6 wheel; fixed with CIBW_TEST_ENVIRONMENT PIP_ONLY_BINARY=:all: (gotchas 12/67). Testing runs the wheel''s own 96 *_test.py modules via `pytest --pyargs pytype -n auto` plus a one-file `pytype` CLI run; the pytype.tests helpers those modules import are excluded from the wheel, so three of them are staged with CIBW_TEST_SOURCES and copied into the installed package (new gotcha 513), and nine nodeids are deselected - source-tree-only tests (main_test.py, test_error_doc, two typeshed ones), a root-only one (test_die), two serialize_ast tests that fail identically on a pristine x86 checkout of the same tag, and the two staged helpers themselves. --ignore-glob turned out to be a no-op under --pyargs, which cost the first CI cycle''s triage (new gotcha 512). Rehearsed end to end on x86_64 before pushing (same 1746 passed / 22 skipped as CI). riscv64 CI: wheel built and auditwheel-repaired to manylinux_2_39_riscv64 in ~7 min, 1746 passed / 22 skipped in 8 min, publish job dry-ran cleanly. Left as a draft for the maintainer; not merged, not published.'
40584058
- pkg: pytorch-tokenizers
40594059
version: 1.4.1
40604060
home: https://pytorch.org/executorch/

‎skills/python-project-porting/references/gotchas-index.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,9 @@ The porting gotchas (500 of them) live in [`references/gotchas/`](gotchas/), spl
409409
matrix: `false` means one `py3-none-<platform>` wheel and a single cibuildwheel job,
410410
bindings mean a real per-interpreter matrix; the queue note's `abi: 0` cannot tell them
411411
apart.
412+
- **514** — A tool that models a *target* Python version caps the matrix itself — the extension
413+
compiles and imports on every interpreter, so run the tool's CLI, not `import`, to find the
414+
ceiling (the pytype case).
412415

413416
### Rust, maturin & PyO3 — [`gotchas/rust-maturin-and-pyo3.md`](gotchas/rust-maturin-and-pyo3.md)
414417

@@ -846,6 +849,9 @@ The porting gotchas (500 of them) live in [`references/gotchas/`](gotchas/), spl
846849
- **501** — A separate test job checks the upstream tree out again and needs the same
847850
`git apply` the build job has; the tell is a failure returning byte-for-byte after you
848851
fixed it (the austin-dist case).
852+
- **513** — The wheel ships the tests but not the `<pkg>.tests` helpers they import: stage the
853+
helper modules with `test-sources` and copy them into the installed package from
854+
`test-command` (the pytype case).
849855

850856
### Testing: pytest config, servers & test selection — [`gotchas/pytest-config-servers-and-selection.md`](gotchas/pytest-config-servers-and-selection.md)
851857

@@ -883,6 +889,9 @@ The porting gotchas (500 of them) live in [`references/gotchas/`](gotchas/), spl
883889
- **489** — A hundreds-of-MB upstream test-data tree can be left out of the checkout with
884890
non-cone sparse-checkout (which also switches `actions/checkout` to a `blob:none` clone),
885891
and the suite selected as the complement of the modules that grep for the data constant.
892+
- **512** — `--ignore`/`--ignore-glob` are silently inert under `pytest --pyargs <pkg>`; cut
893+
tests with `--deselect`, whose nodeids are relative to the package directory rather than the
894+
rootdir pytest prints, and confirm the count with `--co -q`.
886895

887896
### Test failures, flakes & arch-specific bugs — [`gotchas/test-failures-and-flakes.md`](gotchas/test-failures-and-flakes.md)
888897

‎skills/python-project-porting/references/gotchas/cibuildwheel-matrix-and-abi3.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ To pull up one entry: `grep -n '^N\. ' references/gotchas/cibuildwheel-matrix-an
5454
newer-Python-only API fails our CI while upstream's own CI stays green.
5555
- **487** — `[tool.cmeel] has-sitelib` decides whether a cmeel port needs an interpreter
5656
matrix at all.
57+
- **514** — A tool that models a *target* Python version caps the matrix itself; its extension
58+
compiles on every interpreter, so only running the tool shows it.
5759

5860
---
5961

@@ -1033,3 +1035,17 @@ To pull up one entry: `grep -n '^N\. ' references/gotchas/cibuildwheel-matrix-an
10331035
- `CIBW_REPAIR_WHEEL_COMMAND: ""` still applies to both: cmeel links its libraries with
10341036
an `$ORIGIN` RUNPATH into the shared cmeel prefix, and auditwheel would only re-tag
10351037
them, which is why upstream's own release workflow disables repair.
1038+
1039+
1040+
514. **A tool that models a *target* Python version caps the matrix by itself — its extension
1041+
still compiles everywhere, so only running the tool shows it (the pytype case).** pytype
1042+
2024.10.11 builds and installs cleanly on cp313: its pybind11 typegraph extension uses no
1043+
version-specific C API, and `pytype --version` prints. The first real analysis then raises
1044+
`UsageError: Python versions > 3.12 are not yet supported` from `validate_version` in
1045+
`pytype/utils.py`, because the target version defaults to the host's and the opcode tables
1046+
stop at 3.12. Upstream's cp310-cp312-only wheel set is the tell, and the matrix has to
1047+
follow it (`python: ["cp312"]` here) even though cp313/cp314 wheels would build and pass
1048+
an import check. Exercise the tool's front door — the CLI on a two-line file, not
1049+
`import <pkg>` — on the newest interpreter of the default matrix before committing to it.
1050+
Applies to anything that consumes the bytecode or AST details of the interpreter it runs
1051+
on: type checkers, bytecode rewriters, coverage/debug tooling.

‎skills/python-project-porting/references/gotchas/pytest-config-servers-and-selection.md‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ To pull up one entry: `grep -n '^N\. ' references/gotchas/pytest-config-servers-
3939
the whole package invents failures; run each file as its own absltest script.
4040
- **489** — An upstream test-data tree of hundreds of MB can be left out of the checkout
4141
entirely, and the suite selected as the complement of the modules that read it.
42+
- **512** — `--ignore`/`--ignore-glob` do nothing under `pytest --pyargs <pkg>`; cut tests with
43+
`--deselect`, whose nodeids are relative to the package dir, not the printed rootdir.
4244

4345
---
4446

@@ -760,3 +762,27 @@ To pull up one entry: `grep -n '^N\. ' references/gotchas/pytest-config-servers-
760762
- Rehearse the complement off-target first (gotcha 52): the two modules that needed the
761763
tree failed identically on x86_64, which is how the split was found without spending a
762764
riscv64 cycle on it.
765+
766+
767+
512. **`--ignore`/`--ignore-glob` do nothing under `pytest --pyargs <pkg>` — cut tests with
768+
`--deselect`, and count what it removed (the pytype case).** When the test command runs an
769+
installed package's own tests with `--pyargs <pkg>`, the usual "don't collect this file"
770+
knobs are silently inert: pytest applies `--ignore`/`--ignore-glob` while recursing into
771+
*directory* arguments, and a `--pyargs` argument is resolved straight to the imported
772+
package's path instead. It fails quietly — the collection count is identical with and
773+
without the flag (`--co -q` printed `1820 tests collected` for
774+
`--ignore-glob='*/pytype/main_test.py'`, `'*main_test.py'` and
775+
`'*site-packages/pytype/main_test.py'` alike) — and you find out when the tests you thought
776+
you had removed fail in CI.
777+
- **`--deselect` does work, and takes a plain file path as well as a
778+
`file.py::Class::test` nodeid**, so dropping a whole module is one flag.
779+
- **Its paths are relative to the collected package's own directory, not to the `rootdir`
780+
pytest prints.** Running `--pyargs pytype` from a `test-sources` staging dir, pytest
781+
reports `rootdir: <staging dir>` while the nodeids are `main_test.py` and
782+
`pytd/serialize_ast_test.py::SerializeAstTest::test_unrestorable_child` — relative to
783+
`site-packages/pytype`. An absolute `--deselect /…/site-packages/pytype/main_test.py`
784+
matches nothing, and site-packages sits at a different absolute path in the container
785+
anyway, so the relative form is also the portable one.
786+
- **Verify with `--co -q` before pushing.** It prints `1768/1820 tests collected (52
787+
deselected)`; a typo'd nodeid is ignored without a warning, so that count is the only
788+
proof the deselection did anything.

‎skills/python-project-porting/references/gotchas/testing-and-shadowing.md‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ To pull up one entry: `grep -n '^N\. ' references/gotchas/testing-and-shadowing.
3131
portion and does not shadow the wheel.
3232
- **501** — A separate test job checks the upstream tree out again, so it needs the same
3333
`git apply` the build job has, or the failure you just fixed comes back unchanged.
34+
- **513** — The wheel ships the tests but not the test *helpers* they import — copy the helper
35+
package into the installed package from `test-command`.
3436

3537
---
3638

@@ -559,3 +561,36 @@ To pull up one entry: `grep -n '^N\. ' references/gotchas/testing-and-shadowing.
559561
log. Check which job the failing step is in before re-reading the patch.
560562
- Keep the two steps identical (checkout with `path: python-wheels`, then the same glob)
561563
so the next person sees one pattern rather than two.
564+
565+
566+
513. **The wheel ships the tests but not the test *helpers* they import — copy the helper
567+
package into the installed package from `test-command` (the pytype case).** pytype's
568+
`setup.cfg` excludes `pytype.tests` from `packages.find`, yet 36 of the 96 `*_test.py`
569+
modules that *are* in the wheel open with `from pytype.tests import test_base`, so
570+
`pytest --pyargs pytype` dies at collection with `ModuleNotFoundError: No module named
571+
'pytype.tests'`. This is the mirror image of gotcha 148: there the suite sits inside the
572+
importable package and shadows it, here the suite is inside the *wheel* and its helpers
573+
are missing. Staging them in the test cwd cannot fix it — they have to be importable as a
574+
subpackage of the *installed* package:
575+
```yaml
576+
CIBW_TEST_SOURCES: pytype/tests/__init__.py pytype/tests/test_base.py pytype/tests/test_utils.py
577+
CIBW_TEST_COMMAND: >-
578+
cp -r pytype/tests "$(python -c 'import pytype, os; print(os.path.dirname(pytype.__file__))')"
579+
&& python -m pytest --pyargs pytype ...
580+
```
581+
- **Stage the helper modules only, never the whole suite directory.** Copying upstream's
582+
full `<pkg>/tests/` in makes `--pyargs` collect the entire integration suite as well
583+
(142 files for pytype, hours on a riscv64 runner) on top of the unit tests you wanted.
584+
- **The helpers are themselves named `test_*.py`, so pytest collects them as tests**
585+
(`tests/test_utils.py::test_data_file` then errors on a missing fixture); deselect them
586+
(gotcha 512) rather than renaming, since the imports name them.
587+
- **Diff against a source checkout before blaming the wheel or the arch.** Of the failures
588+
left under `--pyargs`, `test_error_doc` and two typeshed ones read `docs/errors.md` and
589+
`typeshed/tests/`, which are in neither the wheel nor the sdist, while two
590+
`serialize_ast` tests fail identically in a pristine x86 `git clone --branch <tag>`
591+
under plain `unittest`. Cloning the tag and dropping the built `.so` into it separates
592+
"not in the wheel", "broken upstream" and "riscv64" in five minutes.
593+
- **A test that asserts an operation *fails* for lack of privilege only passes as a
594+
non-root user.** `tool_utils_test.TestMakeDirsOrDie.test_die` expects
595+
`makedirs_or_die('/nonexistent/path')` to raise `SystemExit`; the build container runs
596+
as root, where it simply creates the directory.

0 commit comments

Comments
 (0)