Skip to content

fix(tests): put pipelines/utils on sys.path so utils resolves - #258

Open
abhishekKokadwar wants to merge 1 commit into
kubeflow:mainfrom
abhishekKokadwar:fix/tests-utils-namespace-shadow
Open

abhishekKokadwar wants to merge 1 commit into
kubeflow:mainfrom
abhishekKokadwar:fix/tests-utils-namespace-shadow

Conversation

@abhishekKokadwar

@abhishekKokadwar abhishekKokadwar commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

Why

Since #249, pytest on main aborts during collection before running a single test:

ImportError: cannot import name 'clean_content' from 'utils' (unknown location)
ERROR tests/test_code_utils.py
ERROR tests/test_issues_pipeline.py
ERROR tests/test_pipeline_utils.py
!!!!!!!!!!!!!!!!!!! Interrupted: 3 errors during collection !!!!!!!!!!!!!!!!!!!

Exit code 2, reproduced on a clean checkout of dedbc84. Because collection is interrupted, no test in the suite runs, including the ones unrelated to these three files.

Root cause

#249 moved utils.py into pipelines/utils/utils.py. These three modules put only pipelines/ on sys.path, where the new utils/ directory (no __init__.py) shadows the module as a namespace package:

>>> sys.path.insert(0, ".../docs-agent-mcp/pipelines")
>>> import utils
>>> utils.__file__
None
>>> utils.__path__
_NamespacePath(['.../docs-agent-mcp/pipelines/utils'])

So from utils import clean_content finds a directory, not the module.

The pipelines already handle this with _UTILS_DIR = Path(__file__).resolve().parent / "utils" on sys.path. This PR applies the same idea to the three test modules: one extra sys.path.insert, pointing at pipelines/utils.

What this does and does not fix

Being explicit, because CI will still be red after this merges:

dedbc84 this PR
Collection aborts, 3 errors completes
Tests run 0 178
Passed 0 108
Exit code 2 1

The three modules fixed here go from 0 to 69 passing. The remaining 7 failures and 63 errors are pre-existing on dedbc84 (verified by running those files directly on a clean checkout, which gives the same counts) and are outside this change:

Those need someone who knows the intended new interfaces, so I've left them alone. The value of this PR is that the suite runs again and those failures become visible, instead of being hidden behind a collection abort.

Do the recovered tests still check intended behaviour?

A path-only fix should not just make old tests pass. They should still describe what production does, so I checked that.

No assertion is changed. The diff is three sys.path lines plus comments. No expected value, test or skip marker is touched.

The mirrors match what production runs. Per the issues-pipeline.py header, KFP components are self-contained and utils/* holds mirrors of their logic for unit testing. The tests only mean something if those mirrors match the inline copies the components actually execute. I extracted the inline functions from the component source with ast and ran both versions on the same inputs:

Mirror vs. inline component copy Comparisons Differences
resolve_github_token (inline in all 4 pipelines) 8 edge cases (empty, whitespace, env precedence) 0
issues_utils: metadata parsing, resolve_issue_metadata, prefix, content segments 29 (structured, legacy markdown, no labels, string issue number, empty body, garbage input) 0
code_utils: YAML multi-doc, Python AST, chunk_code_file (YAML/Python/JSON/large text) 11 0

The tests load the real modules and catch regressions. I deliberately broke each module and re-ran:

Mutation Result
truncate_for_tei stops truncating 2 failed
Github_Pat fallback removed from resolve_github_token 1 failed
issue metadata prefix drops State: 1 failed
YAML kind lookup in code_utils broken 3 failed

One caveat: 19 of the 26 tests in test_pipeline_utils.py cover clean_content, which has no caller on main (nor before #249). The v4 docs pipeline cleans content in hugo_ingest / canonical_rag_ingest instead. Those 19 tests pass, but they don't exercise production code. I've left them in place, since removing tests is a maintainer call, but they shouldn't be read as coverage of the v4 path.

Verification

  • Before/after numbers above come from the same environment (Python 3.11, requirements-test.txt plus the KFP SDK). Without KFP, as in PR Safety, the KFP-dependent component tests skip, so absolute counts are lower there. The collection abort happens either way.
  • Reproduced from a neutral working directory. Running from the repo root can mask the shadowing, depending on what else is on sys.path.
  • pyflakes clean on all three files. pycodestyle reports only E402, which already appears on main and is allow-listed for exactly these three files in pyproject.toml. I couldn't run ruff itself: a Windows Application Control policy blocks the binary on my machine, so CI is the authoritative lint check.
  • Test-only change: 3 files, +9 lines, no production code touched. DCO signed.

Three test modules abort at collection on main:

    ImportError: cannot import name 'clean_content' from 'utils'
    (unknown location)

`pytest` exits 2, so the whole suite stops before running and both
workflows that invoke it are red.

kubeflow#249 moved `utils.py` into `pipelines/utils/`. These modules put only
`pipelines/` on `sys.path`, where the sibling directory of the same name
now shadows the module as a namespace package — `utils.__file__` is None
and `utils.__path__` is the directory, so no attribute resolves.

The pipelines themselves already handle this with a `_UTILS_DIR` entry
pointing at `pipelines/utils`; this applies the same thing to the tests
that import `utils` directly.

Recovers 72 tests across the four affected modules. The 7 failures and 63
errors that remain are pre-existing on dedbc84 and are not touched here:
`test_mcp_server.py` references `server.client`, removed when kubeflow#249 split
the MCP server, and `test_docs_pipeline.py` exercises the `store_milvus`
component that kubeflow#249 replaced with `utils/milvus_store.py`.

Signed-off-by: Abhishek <abhikokadwar2@gmail.com>
Copilot AI balanced review requested due to automatic review settings September 30, 2026 05:24

Copilot AI left a comment

Copy link
Copy Markdown

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 reached their quota limit.

@google-oss-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign franciscojavierarceo for approval. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants