feat(xtest): make xtest an installable distribution (DSPX-4793) - #598
Draft
dmihalcik-virtru wants to merge 4 commits into
Draft
dmihalcik-virtru wants to merge 4 commits into
dmihalcik-virtru wants to merge 4 commits into
Conversation
xtest buildable distribution
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Replace the Jira-dump scaffold with the seven-heading template: why the flat layout is the install blocker (bare conftest at the site-packages root under importmode=prepend), the four cwd-relative read sites with verified citations, the pytest11-plugin-vs-packaged-conftest decision and the alternatives rejected, and measured acceptance criteria.
Pure relocation, no content changes: 56 files renamed and nothing else, so
the diff is reviewable as a move. The follow-up commit makes it build.
xtest/*.py -> xtest/src/xtest/
xtest/conftest.py -> xtest/src/xtest/plugin.py
xtest/test_*.py -> xtest/src/xtest/tests/
xtest/{fixtures,perf}/ -> xtest/src/xtest/{fixtures,perf}/
xtest/manifest.schema.json -> xtest/src/xtest/data/
xtest/extra-keys.json -> xtest/src/xtest/data/
xtest/golden/ -> xtest/src/xtest/data/golden/
This commit alone does not run: imports, pyproject and the data-file reads
are fixed in the next one.
xtest is consumed by four downstream repos and could not be installed by any of them: no [build-system] table, a flat layout whose bare conftest.py lands at the site-packages root and collides with a consumer's own under importmode=prepend, and data files opened by cwd-relative name. B1 - buildable. Add [build-system] with the uv_build backend, matching the otdf-sdk-mgr and otdf-local siblings. Add __init__.py for xtest, xtest.tests and xtest.data, and qualify every intra-suite import. conftest.py becomes plugin.py registered as a pytest11 entry point: an installed conftest is loaded after the command line is parsed, so its pytest_addoption never runs, which would silently drop all ~20 options. '-p no:xtest' opts out. isort known-first-party collapses from eight top-level names to one. The license metadata said BSD-3-Clause while the repo LICENSE is BSD-3-Clause-Clear, a different license that withholds patent rights; corrected to the SPDX expression now that a wheel with that metadata can exist. B2 - no cwd coupling. New xtest.paths.sdk_dir() resolves --sdk-dir > XT_SDK_DIR > ./sdk, decided in pytest_configure and written back to the environment for subprocesses; the value is read on every call and never captured at import. load_otdfctl() moves behind a functools.cache accessor and test_self.py takes the otdfctl fixture instead of constructing the tool at module scope, so an environment with no SDK tree deselects rather than erroring during collection. Markers move from [tool.pytest.ini_options] to config.addinivalue_line in the plugin: the ini table is only read from the session rootdir, which is the consumer's repo once installed. B3 - package data. manifest.schema.json, extra-keys.json and golden/*.tdf resolve through importlib.resources via xtest.data. SCHEMA_FILE degrades from mandatory to override, so the four workflow lines setting it come out. test.env is generated per environment and is deliberately not shipped; setup-cli-tool/action.yaml is a composite Action and cannot live in a wheel, so it stays at the repo root. Also repoints otdf-local's cross-package read of extra-keys.json, which returns [] when the file is missing and would otherwise have started the platform with the wrong key set and no error. Verified: 'uv build' emits a wheel, and from /tmp, 'uv run --no-project --with <wheel> pytest --pyargs xtest.tests --collect-only' collects 335/337 with no ImportPathMismatchError and no unknown-marker warnings; the nine offline modules give 208 passed out of tree. In tree, 'uv sync --locked --no-build' and the existing check.yml invocation are unchanged in behaviour. XT_ENV_FILE is deferred: its only consumers are sdk/*/cli.sh, which belongs to DSPX-4791/DSPX-4792.
dmihalcik-virtru
force-pushed
the
DSPX-4793-xtest-buildable-distribution
branch
from
September 15, 2026 15:18
d0ca5fc to
35963ed
Compare
|
X-Test Failure Report |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.




What
xtest is consumed by four downstream repos and cannot currently be installed by any of them. This makes
xtest/a real Python distribution:uv buildproduces a wheel, andpytest --pyargs xtest.testsworks from a directory that is not the source tree.Steps B1–B3 of DSPX-4793. Design and rationale in
spec/DSPX-4793.md.Why it did not work before
Four things, and fixing any three of them still leaves it uninstallable:
[build-system]—xtest/pyproject.toml:59said so out loud: "This is a test suite, not a distributable package." Nothing touv add.xtest/with no__init__.py. Installed,conftest.pylands atsite-packages/conftest.py; pytest's defaultimportmode=prependderives the module name from the basename, so it and a consumer's ownconftest.pyboth want to be the top-level moduleconftestand the second one collected raisesImportPathMismatchError. Same for every unqualifiedtest_*.py, and for generic names likesizes,assertions,audit_logs.[tool.ruff.lint.isort] known-first-partyenumerating eight top-level names was the tell.tdfs.py:671(f"sdk/{sdk}/dist/{version}/cli.sh"),tdfs.py:947-948,otdfctl.py:55, andconftest.py:692callingload_otdfctl()at module scope, which probes the filesystem while conftest is being imported.manifest.schema.json(via a mandatorySCHEMA_FILE),extra-keys.json,golden/*.tdf.Commits
docs(spec)spec/DSPX-4793.mdrefactor(xtest)feat(xtest)The move is deliberately its own commit so the relocation is readable as a move; review the third commit for actual content. That third commit carries B1's content changes together with B2 and B3 — splitting those apart would have required inventing intermediate states (e.g. a
Path(__file__).parents[2] / "data"that exists for exactly one commit) that nobody reviews on their own merits.Notable decisions
conftest.py→plugin.py, registered as apytest11entry point. Not cosmetic. Aconftest.pyinside an installed package is loaded after the command line is parsed, so itspytest_addoptionnever runs and all ~20 of the suite's options silently disappear. An entry-point plugin loads during startup, before argument parsing.-p no:xtestopts out.Markers moved out of
[tool.pytest.ini_options]intoconfig.addinivalue_linein the plugin. That ini table is only read from the session's rootdir, which is the consumer's repo once installed — out-of-tree runs emitted fourPytestUnknownMarkWarnings before this change.load_otdfctl()is now lazy (functools.cache) andtest_self.pytakes theotdfctlfixture instead of constructing the tool at module scope. An always-loaded plugin must not touch the filesystem at import. With no SDK tree, the suite now logs at DEBUG and deselects rather than erroring during collection.License metadata corrected from
BSD-3-Clauseto the SPDX expressionBSD-3-Clause-Clear, matching the repoLICENSE. Different licence — the Clear variant withholds patent rights. Harmless while nothing was published; wrong the moment a wheel exists.otdf-local's cross-package read ofextra-keys.jsonis repointed. Its failure mode isreturn [], i.e. a platform that starts successfully with the wrong key set and no error.Verification
No
ImportPathMismatchError, noPytestUnknownMarkWarning.In tree, unchanged:
uv sync --locked --no-build;uv run --frozen --no-build pytestover the offline list →208 passed in 5.16s;uv run pytest --collect-only -q→ the same335/337;ruff check→All checks passed!;ruff format --check→49 files already formatted;pyright→0 errors, 0 warnings, 0 informations.New knobs (all with backward-compatible defaults)
Deferred
XT_ENV_FILE, though DSPX-4640 lists it under B2. Its only consumers aresdk/{go,java,js}/cli.shandsdk/go/otdfctl.sh, which belong to DSPX-4791/DSPX-4792. A Python-side accessor nothing reads is dead code, and exporting a resolved value into the subprocess environment would change the env dict that 18 argv/env assertions intest_sdk_commands.pypin.container_type/sdk_type/feature_type— DSPX-4794.sdk/*/cli.sh— DSPX-4791/DSPX-4792.sdk/go/cli.sh:174walks up for apyproject.tomlcontainingname = "xtest"; that line and the location ofsdk/are both unchanged, so it still resolves.Compatibility
Every existing option, env var and marker keeps its name and default, the distribution name and version are unchanged, and an in-tree
uv run pytestfromxtest/behaves as before. The four repos pinningopentdf/tests@mainmust keep working — nothing in the public interface changed, but that is the argument, not the evidence: it needs a run in each consumer. Workflows in this repo are updated for the new layout (check.ymloffline test list,xtest.ymltest-file args and theextra-keys.jsonpath, and the four now-redundantSCHEMA_FILElines removed).Known wart
The wheel is 10.2 MB and 10.0 MiB of it is one golden file,
big-java-4.3.0-e0f8caf.tdf, which onlytest_legacy.pyreads. Splitting the corpus behind an extra was considered and rejected for this slice — it doubles the release surface to fix a problem nobody has reported, and it is reversible.