Skip to content

fix: stop the repository root from shadowing the deepcode entry point - #236

Merged
Zongwei9888 merged 1 commit into
HKUDS:mainfrom
raymondginger2018-sudo:pr/root-init-shadowing
Sep 15, 2026
Merged

Zongwei9888 merged 1 commit into
HKUDS:mainfrom
raymondginger2018-sudo:pr/root-init-shadowing

Conversation

@raymondginger2018-sudo

Copy link
Copy Markdown
Contributor

Problem

The repository root contains a tracked __init__.py. On a case-insensitive filesystem (Windows) that makes the checkout directory itself a package whose name matches the deepcode module:

C:\...\deepcode\__init__.py     # package named "deepcode"
C:\...\deepcode\deepcode.py      # the real entry point (main, _bootstrap_logging)

pytest's default --import-mode=prepend inserts the package root on sys.path, so import deepcode binds to the checkout directory instead of deepcode.py and deepcode.main() does not exist:

AttributeError: module 'deepcode' has no attribute 'main'
AttributeError: module 'deepcode' has no attribute '_bootstrap_logging'

Any checkout literally named deepcode hits this — including a plain git clone on Windows. Upstream CI never sees it because it checks out as DeepCode (capital D), which does not match deepcode on a case-sensitive filesystem.

Reproduction

With the tree checked out at HEAD and the directory named deepcode:

python -m pytest tests/test_cli_logging_bootstrap.py tests/test_version_metadata.py -q
→ 6 failed, 1 passed

Fix

Delete the root __init__.py and mirror that in MANIFEST.in (it previously shipped include __init__.py); a comment records why the root is deliberately not a package. The package metadata lives in setup.py (py_modules = ["deepcode"] plus the deepcode=deepcode:main console script), so nothing depends on the root __init__.py.

After

python -m pytest tests/test_cli_logging_bootstrap.py tests/test_version_metadata.py -q
→ 7 passed
python -c "import deepcode; print(deepcode.__file__)"
→ ...\deepcode.py

check-manifest stays consistent because MANIFEST.in is updated in the same commit.

I did not take the alternative route of re-exporting main/_bootstrap_logging from the root __init__.py: tests/test_cli_logging_bootstrap.py monkeypatches deepcode._bootstrap_logging, and under package binding that patch would no longer reach the module global that main() reads — the test would pass while testing nothing.

`import deepcode` must bind to `deepcode.py` (setup.py declares
`py_modules=["deepcode"]` and the `deepcode=deepcode:main` console script), but the
repository root also carried a tracked `__init__.py`, making the checkout directory a
package of the same name. pytest's default prepend import mode resolves the package
root by walking up while `__init__.py` exists (`tests/__init__.py` -> repo root), and
then inserts that root into `sys.path`; with the repo checked out into a directory
literally named `deepcode` (as on this machine) the earlier `sys.path` entry wins and
`import deepcode` yields the directory package instead of `deepcode.py`:

    AttributeError: module 'deepcode' has no attribute 'main'

That broke 6 tests locally (tests/test_cli_logging_bootstrap.py, 5 cases;
tests/test_version_metadata.py, 1 case) and would break any source checkout on a
case-insensitive filesystem whose directory name matches the module. Upstream CI is
unaffected only by accident: it checks out `DeepCode`, which does not match the
module name on a case-sensitive filesystem.

Delete the root `__init__.py` and the matching `include __init__.py` from
MANIFEST.in (pre-commit's check-manifest keeps the two in sync). Nothing imports the
root package and the sdist no longer ships a file that turns the unpacked checkout
into a shadowing package.

Verified: tests/test_cli_logging_bootstrap.py + tests/test_version_metadata.py ->
7 passed (6 failed before); `python -c "import deepcode"` resolves to deepcode.py.
@Zongwei9888
Zongwei9888 merged commit ec3e4f7 into HKUDS:main Sep 15, 2026
15 checks passed
@Zongwei9888

Copy link
Copy Markdown
Collaborator

Merged into main as ec3e4f7. Thank you @raymondginger2018-sudo — the case-insensitive-checkout shadowing was a real trap, and the reasoning for deleting the root package instead of re-exporting main from it (the monkeypatch in test_cli_logging_bootstrap.py would silently stop reaching the module global) was exactly right.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants