Plan 10 — Collapse _impl.py
Superseded by Plan 11 — Use direct subclass. The ABC wrapper pattern introduced here was later found to fix a problem that only occurs at test collection time, not in production. Plan 11 replaces the wrapper with a direct
class SPDXMarkdown(Markdown):and moves the cold-import fix totests/conftest.py.
Problem¶
Two issues with the current two-file architecture:
-
isinstancebreakage —_SPDXMarkdownImpldoes not subclassSPDXMarkdown, soisinstance(formatter, SPDXMarkdown)returnsFalseeven thoughSPDXMarkdown(config)returns that object. This is a latent bug for any code (tests, commitizen internals, consumers) that relies on type checking. -
Unnecessary complexity — The two-file split (
spdx_markdown.py+_impl.py) with lazy__new__resolution was motivated by a circular-import concern. The concern is real (commitizen'schangelog_formats.__init__.pyloads entry points at import time), but the same ordering can be preserved in a single file.
Resolution¶
1. Collapse to single file¶
Replace src/commitizen_spdx_changelog/formatters/spdx_markdown.py with a
single-file implementation. The final form:
SPDXMarkdownwrapper defined first (before any commitizen imports) — preserves the entry-point scanning order; inherits fromabc.ABC_SPDXMarkdownImpl(Markdown)— inherits fromMarkdownonly;SPDXMarkdown.register(_SPDXMarkdownImpl)creates the virtual subclass relationship, fixing theisinstancecheck- No
__new__override needed on_SPDXMarkdownImpl - Same
_strip_frontmatterutility (unchanged logic) - Same method overrides (
get_metadata,get_latest_full_release) - Remove
import sys as _sys(was unused) - Core commitizen types (
Metadata,IncrementalMergeInfo,Markdown) are imported at top level;BaseConfigstays lazy inside__init__(cosmetic — not a workaround) - Staggered imports after class definition are necessary (E402 suppressed via
noqa: E402)
2. Delete _impl.py¶
Remove src/commitizen_spdx_changelog/formatters/_impl.py entirely.
3. py.typed verification¶
Run uv build && unzip -l dist/*.whl | grep py.typed to confirm the PEP 561
marker file lands in the wheel.
4. ruff cleanup¶
Run ruff check src/. The staggered imports after the class definition are
necessary (circular-import avoidance) and produce E402 warnings. Suppress them
with noqa: E402 comments. The original code also had these — they are inherent
to the approach.
5. Smoke test¶
Run cz bump --dry-run --changelog as a real end-to-end smoke test of the code
path the lazy-loading was nominally protecting.
Test compatibility¶
Existing tests (test_spdx_markdown.py) import SPDXMarkdown and
_strip_frontmatter from spdx_markdown — zero changes needed. The formatter
instantiation SPDXMarkdown(config) now works honestly via virtual subclassing,
matching what the tests already expect.
Not in scope¶
Update 03-design.md & 09-doc-generation.md to reflect the new architecture.
Tasks¶
- Replace
spdx_markdown.py— write single-file implementation:SPDXMarkdown(abc.ABC)wrapper defined first,_SPDXMarkdownImpl(Markdown)defined after commitizen imports,SPDXMarkdown.register()for virtual subclassing. - Delete
_impl.py— removesrc/commitizen_spdx_changelog/formatters/_impl.py. - Verify
py.typed—uv build && unzip -l dist/*.whl | grep py.typed. - Run ruff check —
ruff check src/passes cleanly (E402 suppressed). - Run tests —
uv run pytest -vpasses (existing 19/19). - Smoke test with cz —
cz bump --dry-run --changelogsucceeds.
Files touched¶
| Action | File |
|---|---|
| Modify | src/commitizen_spdx_changelog/formatters/spdx_markdown.py |
| Delete | src/commitizen_spdx_changelog/formatters/_impl.py |
Dependencies¶
- Current architecture must be stable (plans 01-09 complete).
commitizenwithMarkdownchangelog format (built-in).- Python 3.13+,
ruff,uv.
Acceptance¶
-
spdx_markdown.pyis a single file, no imports from_impl. -
_SPDXMarkdownImplregistered as virtual subclass ofSPDXMarkdownviaabc.ABC— isinstance checks pass. -
_impl.pyno longer exists. -
py.typedpresent in the built wheel. -
ruff check src/— zero warnings. -
uv run pytest -v— 19/19 passes unchanged. -
cz bump --dry-run --changelogsucceeds.
See also¶
- Plan 01 — Problem and Goal
- Plan 03 — Design (will not be updated)
- Plan 09 — Doc Generation (will not be updated)
- OpenSpec change
collapse-impl-module - GitHub issue #4