Skip to content

Stop installing test-only packages with the executorch package - #24143

Open
shoumikhin wants to merge 2 commits into
pytorch:mainfrom
shoumikhin:trim-test-deps
Open

shoumikhin wants to merge 2 commits into
pytorch:mainfrom
shoumikhin:trim-test-deps

Conversation

@shoumikhin

@shoumikhin shoumikhin commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

pip install executorch installs four packages that are only there for ExecuTorch's own tests: expecttest, hypothesis, kgb and parameterized. hypothesis also brings sortedcontainers. So every user installs five packages they never need.

This moves the four from the package's runtime dependencies (_base_dependencies() in setup.py) to requirements-dev.txt. The developer setup and every CI job that runs tests already install that file, so tests still get them.

Nothing a user imports needs them. Three shipped files do, but only tests or a code generator use them: backends/cadence/aot/typing_stubs.py, examples/models/llama/source_transformation/test_attention_sink.py, and exir/dialects/edge/spec/gen.py.

Effect, resolving the declared dependencies for Python 3.12 (without torch): 55 packages before, 50 after on macOS arm64 and Linux x86_64. The same five go away on Linux aarch64 and Windows.

Kept on purpose: coremltools and scikit-learn (the Core ML backend needs both), and hydra-core, omegaconf, pandas, tabulate and pytorch-tokenizers, which shipped modules import.

The first commit fixes the wheel's clean-install check. It hid undeclared packages so that importlib.util.find_spec raised instead of returning None. torch._dynamo probes for hypothesis with find_spec when it loads, so the check failed once hypothesis was no longer declared, although a real install works.

Test plan

On macOS arm64, in a fresh environment with only the trimmed dependencies, torch 2.14.0 and a nightly executorch installed with --no-deps:

  • Tried to import all ~3,000 modules in the package. Only the three files above failed for lack of a removed package. The rest imported, or stopped on unrelated optional packages such as vendor SDKs.
  • Exported a small model with XNNPACK and with Core ML, ran both, and matched eager PyTorch.
  • The clean-install check fails before the first commit and passes after it, and still fails when a checked module imports an undeclared package.

Tests that use the moved packages pass with requirements-dev.txt installed.

@shoumikhin shoumikhin added the release notes: build Changes related to build, including dependency upgrades, build flags, optimizations, etc. label Oct 25, 2026
Copilot AI balanced review requested due to automatic review settings October 25, 2026 20:37
@pytorch-bot

pytorch-bot Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/24143

Note: Links to docs will display an error until the docs builds have been completed.

✅ No Failures

As of commit c1db4b7 with merge base 4db778e (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 25, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

Copilot AI balanced review requested due to automatic review settings October 25, 2026 21:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@shoumikhin

Copy link
Copy Markdown
Contributor Author

@claude review

@claude

claude Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Claude finished @shoumikhin's task in 3m 26s —— View job


PR Review: #24143

I found no blocking issues; the only things to look at are one minor behaviour change in the clean-install probe and a comment wording nit.

Detailed review

Summary

The PR moves expecttest, hypothesis, kgb and parameterized out of _base_dependencies() in setup.py and into requirements-dev.txt. It also changes the wheel clean-install probe to hide undeclared packages through sys.modules[name] = None instead of a meta-path finder that raised.

Moving the packages doesn't break any install path that runs tests. install_executorch.py always calls install_requirements(), even with --minimal, and that installs requirements-dev.txt. The wheel pre_build_script.sh, the ROCm, Windows and Cadence scripts all install it too. The jobs that install only the wheel (test_wheel_package_qnn.sh and the wheel smoke tests) don't run tests. Two minor issues are below.

Code Quality

  • .ci/scripts/wheel/test_clean_install.py:236-237: the probe now overwrites modules that are already imported. Low confidence; this was found by reading the code, not by running it.
    • sys.modules[name] = None replaces every blocked name, including modules a .pth hook imported when the interpreter started. On Python 3.10/3.11 that includes _distutils_hack from setuptools, which torch doesn't declare. The old _Blocker never touched entries already in sys.modules, so this is a new difference.
    • It is probably harmless for _distutils_hack, because its finder keeps its own references. Any other preloaded package that is imported again later by name from the executorch import chain would now fail the check, even though it is present on a real install.
    • Fix: skip names that are already in sys.modules, for example for name in blocked - sys.modules.keys():. Or, if the difference is intended, say so in the comment.
  • requirements-dev.txt:23: the new comment overstates "tests only". exir/dialects/edge/spec/gen.py is a code generator, not a test. It needs expecttest indirectly, through torch.testing._internal.common_methods_invocations. "Used by tests and dev tooling" would be accurate. Because the comment is a standalone line above gguf, it also reads as if it covers gguf; an inline comment or a blank line after the group would fix that.

Recommendation

Approve

Neither issue blocks the change.

Copilot AI balanced review requested due to automatic review settings October 26, 2026 05:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

The wheel check in .ci/scripts/wheel/test_clean_install.py hides every
package the wheel does not declare, then imports the main modules. It
hid them with an import finder that raised an error from find_spec.

A missing package does not do that. For a missing package,
importlib.util.find_spec returns None, and only an actual import
fails. torch._dynamo relies on this: when it loads, it calls find_spec
on a list of optional packages, hypothesis among them. So once a
package on that list is not declared by the wheel, the check fails
even though a real install works.

Mark hidden packages with None in sys.modules instead. Python then
raises ModuleNotFoundError on import and returns None from find_spec,
the same as for a missing package. The error message still says the
package is not declared by the wheel.
`pip install executorch` installed four packages that are only there
for the test suite: expecttest, hypothesis, kgb and parameterized
(plus sortedcontainers, which hypothesis brings). Nothing a user
imports needs them. The only shipped modules that need them are used
by tests or by the edge dialect YAML generator.

Move them from the package's runtime dependencies to
requirements-dev.txt. The developer setup (install_requirements.py)
and every CI job that runs tests already install that file, so tests
still get them.
@shoumikhin

Copy link
Copy Markdown
Contributor Author

Thanks for the review.

  • requirements-dev.txt: fixed. The comment now says the packages are needed by tests and development tools, and it sits after gguf, so it covers only the four moved packages.
  • Modules that are already loaded: hiding them too is intended. Every blocked name belongs to a package that a clean install of the wheel does not have. If the executorch import chain imported one of them again by name, it would fail for a real user too, so the check should report it. The Linux CPU wheel builds on this pull request, x86-64 and aarch64, passed the check with the new probe.

@shoumikhin

Copy link
Copy Markdown
Contributor Author

@claude review

@claude

claude Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Claude finished @shoumikhin's task in 3m 49s —— View job


PR Review: #24143

I found no blocking issues. The dependency move is safe for every install and CI path the reviewers traced, and only two low-severity notes about the clean-install probe remain.

Detailed review

Summary

The PR moves expecttest, hypothesis, kgb and parameterized from _base_dependencies() in setup.py to requirements-dev.txt. It also changes the wheel clean-install probe to hide undeclared packages with sys.modules[name] = None. There are no blocking problems, and the two minor notes below are both in the probe.

Code Quality

  • .ci/scripts/wheel/test_clean_install.py:236-237: a None entry makes name in sys.modules true. Low confidence; this comes from reading the code, not from running it.
    • On a real clean install that check is false. blocked includes everything the CI environment adds on top of the declared dependencies, such as pytest, hypothesis and expecttest.
    • Code reached from an executorch import that does if "pytest" in sys.modules:, or sys.modules.get(x) followed by an attribute access, would take the wrong branch or raise AttributeError: 'NoneType' .... The check would then fail on a correct wheel, and the error would not carry the "not declared" message.
    • Shipped executorch code does this only in tests. Nobody checked torch, because torch was not available in this checkout.
    • The old meta-path finder did not have this side effect. The wheel jobs on this PR pass, so this is not a problem today.
    • Suggestion: add a short note to the probe comment, so a future NoneType failure is not mistaken for a wheel bug.
  • .ci/scripts/wheel/test_clean_install.py:240-248: the "not declared by the executorch wheel" note now appears only if the ModuleNotFoundError reaches the top level.
    • If a module catches the error and raises something else, for example an optional-dependency shim raising ImportError or RuntimeError with its own message, the probe still fails. The note is lost, though.
    • The traceback then shows only CPython's import of x halted; None in sys.modules or 'x' is not a package, which reads like a probe internal rather than a missing dependency.
    • The old finder attached the note to the innermost error. This affects diagnostics only, not the check's outcome.
    • Suggestion: walk __cause__/__context__ to find a blocked ModuleNotFoundError, or accept the gap.

Recommendation

Approve

Neither note blocks the change.

@shoumikhin

Copy link
Copy Markdown
Contributor Author

Thanks. Both notes are worth knowing, and neither changes a result today.

  • name in sys.modules is true for a hidden package, unlike a missing one. I looked for such checks on the import paths of torch, numpy, sympy, pandas and mpmath. The only one is in pandas, for IPython, and it runs when a pandas option is changed, not at import. If such a check ever runs during import, it will most likely fail loudly, with a NoneType or import error that points at it. I left the probe comment as it is.
  • If a module catches the error and raises its own, the check still fails, and the traceback usually still shows the original ModuleNotFoundError in the chain. I kept the probe simple instead of walking the exception chain.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. release notes: build Changes related to build, including dependency upgrades, build flags, optimizations, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants