Skip to content

Adopt src-layout and rename the package to lowercase#14

Merged
maltemindedal merged 1 commit into
mainfrom
refactor/src-layout-and-package-rename
Jul 23, 2026
Merged

Adopt src-layout and rename the package to lowercase#14
maltemindedal merged 1 commit into
mainfrom
refactor/src-layout-and-package-rename

Conversation

@maltemindedal

Copy link
Copy Markdown
Owner

Summary

Audited the repo layout against PEP 8 and PyPA packaging guidance. Seven findings; two were real defects, not style.

Verified defects

1. The console script and python -m diverged. [project.scripts] pointed at PyFetch.cli:main, bypassing __main__.run(). Measured before the fix:

Entry point Ctrl+C behaviour
pyfetch GET ... raw traceback, exit 0xC000013A
python -m PyFetch Operation cancelled by user, exit 0

Two entry points, two behaviours. The entry point now targets pyfetch.__main__:run; both paths are identical.

2. No PEP 561 marker. The package has been mypy --strict clean for a while, but without py.typed every downstream type checker silently ignored all of its annotations. Added, plus a CI step asserting it actually ships in the wheel.

Convention fixes

3. The package directory was CapWords. PEP 8: "Python packages should also have short, all-lowercase names." PyFetch/ was the sole outlier — the repo directory, the GitHub remote, the console script, and the built wheel (pyfetch-1.1.0-py3-none-any.whl, normalized per PEP 503/625) were all lowercase already.

4. Flat layout rather than src/. Without src-layout, import PyFetch from the repo root resolves to the source tree rather than the installed package, so tests can pass against code that was never installed.

5–7. Added CHANGELOG.md, CONTRIBUTING.md, .editorconfig. Pinned argparse prog="pyfetch" — the console script previously rendered its usage line as the interpreter path.

Layout

src/pyfetch/        The package (import name: `pyfetch`)
  __init__.py       Public API; resolves HTTPClient lazily (PEP 562)
  __main__.py       `python -m pyfetch` and the `pyfetch` console script
  cli.py            Argument parsing and response rendering
  http_client.py    HTTP client, session pooling, retry policy
  exceptions.py     Exception hierarchy
  py.typed          PEP 561 marker
tests/              One module per source module: test_<module>.py

Breaking change

The import package is renamed. Bumped to 2.0.0; no compatibility shim, per the decision to keep the tree clean:

from PyFetch import HTTPClient  # 1.x
from pyfetch import HTTPClient  # 2.0+

Only the import path changes — the distribution name, console script, and wheel name are unaffected because they already normalized to lowercase.

Also fixed

mypy now excludes build/ and dist/. Without it, running a build and then mypy . fails with Duplicate module named "pyfetch" — which I hit during this work.

Testing

  • ruff, ruff format --check, mypy strict all clean.
  • 38 tests pass (added a guard that the usage line stays usage: pyfetch).
  • 10-case end-to-end CLI smoke run passes.
  • Wheel builds as pyfetch-2.0.0-py3-none-any.whl and contains pyfetch/py.typed; CI now enforces this.
  • Ctrl+C parity verified on both entry points.
  • Benchmarks unchanged — 0.60 ms/request local, 82 ms CLI start-up. This is a pure restructure.

🤖 Generated with Claude Code

Audited the repo layout against PEP 8 and PyPA packaging guidance. Two of the
findings were real defects rather than style:

- The console script and `python -m` diverged. [project.scripts] pointed at
  `cli:main`, bypassing `__main__.run()`, so Ctrl+C during `pyfetch GET ...`
  produced a raw traceback and exit 0xC000013A while `python -m PyFetch` printed
  "Operation cancelled by user" and exited 0. The entry point now points at
  `pyfetch.__main__:run` and both paths behave identically.
- No PEP 561 marker. The package has been mypy --strict clean for a while, but
  without `py.typed` every downstream type checker silently ignored all of its
  annotations. Added, plus a CI step asserting it ships in the wheel.

Conventions:

- BREAKING: the import package is now `pyfetch`, not `PyFetch` (PEP 8: "Python
  packages should also have short, all-lowercase names"). It was the sole
  outlier -- the repo directory, the GitHub remote, the console script and the
  built wheel all normalise to lowercase already. No compatibility shim; bumped
  to 2.0.0 and documented the one-line migration.
- Moved the package under `src/` per the PyPA src-layout recommendation, so
  tests exercise the installed distribution rather than the working directory.
- Pinned argparse `prog="pyfetch"`; the console script previously rendered its
  usage line as the interpreter path.
- mypy excludes build/ and dist/, which otherwise collide with src/ as a
  duplicate `pyfetch` module after a build.

Added CHANGELOG.md, CONTRIBUTING.md (documenting the layout, the naming rules,
and the retry-testing pitfall) and .editorconfig.

Verified: ruff, ruff format, mypy strict clean; 38 tests pass; 10-case CLI smoke
run passes; wheel builds as pyfetch-2.0.0-py3-none-any.whl containing py.typed;
benchmarks unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@maltemindedal
maltemindedal merged commit 1037c9c into main Jul 23, 2026
8 of 9 checks passed
@maltemindedal
maltemindedal deleted the refactor/src-layout-and-package-rename branch July 23, 2026 14:04
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.

1 participant