Skip to content

Rewrite CLAUDE.md to guide modernization work#10

Merged
rwilson4 merged 8 commits into
masterfrom
claude/review-unit-parser-HMpUf
May 2, 2026
Merged

Rewrite CLAUDE.md to guide modernization work#10
rwilson4 merged 8 commits into
masterfrom
claude/review-unit-parser-HMpUf

Conversation

@rwilson4
Copy link
Copy Markdown
Owner

@rwilson4 rwilson4 commented May 2, 2026

Captures the current architecture plus a target-state table for the
Python 3.11 / uv / ruff / mypy migration, flags known data-file quirks
(degF offset, year=365, teaspoon precision), the CLI's silent
filler-word handling, and the test-coverage gaps a new contributor
should plan around.

https://claude.ai/code/session_016wVPKqjdaTUrdkCGt5qRTp

claude added 8 commits May 2, 2026 15:03
Captures the current architecture plus a target-state table for the
Python 3.11 / uv / ruff / mypy migration, flags known data-file quirks
(degF offset, year=365, teaspoon precision), the CLI's silent
filler-word handling, and the test-coverage gaps a new contributor
should plan around.

https://claude.ai/code/session_016wVPKqjdaTUrdkCGt5qRTp
- pyproject.toml: requires-python = ">=3.11", dev dep group, ruff +
  mypy config (single-quote format style, mypy --strict)
- Replace Travis CI with GitHub Actions workflow running lint, format
  check, mypy, and pytest against Python 3.11 / 3.12 / 3.13
- Delete defunct .travis.yml, empty requirements.txt, and MANIFEST.in
  (package data is already declared in pyproject.toml)
- Remove vestigial # pyre-strict / # pyre-unsafe headers
- Add __all__ to unit_parser/__init__.py
- Apply ruff fixes: import sorting, drop unused mode arg in open(),
  zip(strict=True), unused `up = ...` assignments in tests, line-length
  fix in divide(), annotate get_cwd helper for mypy
- Generate uv.lock
- Refresh modernization-roadmap section in CLAUDE.md to reflect what's
  done vs. still TODO (tests directory layout, README class rename)

https://claude.ai/code/session_016wVPKqjdaTUrdkCGt5qRTp
- Class examples use UnitParser (CapWords) instead of the legacy
  lowercase unit_parser
- Replace the dead Travis CI badge with a GitHub Actions badge
- Drop the codecov badge (CI doesn't upload coverage to codecov)
- Reword the constructor blurb to match the renamed class

https://claude.ai/code/session_016wVPKqjdaTUrdkCGt5qRTp
UnitParser.convert previously accepted *args: str | float, which gave
callers no static-typing help and silently swallowed the wrong arity at
runtime. Replace it with two @overload signatures plus a fixed-arity
implementation:

    convert("5 feet", "meters")       # 2-arg
    convert(5, "feet", "meters")      # 3-arg

Calling with 4+ positional arguments now raises TypeError (a true
programmer error) instead of the old custom ValueError.

The CLI used to silently accept any filler word between the source and
destination units -- `convert 5 feet banana meters` parsed as
`5 feet -> meters` with no warning. It now requires either no filler
word or the literal "to", and rejects anything else via parser.error()
(usage message + exit 2).

Tests:
- update test_invalid_num_args to expect TypeError (with type-ignore
  since the call is now a static type error)
- type-ignore the intentional bad-arg-type call in test_invalid_convert_args
- add test_command_line_convert_invalid_filler asserting SystemExit

https://claude.ai/code/session_016wVPKqjdaTUrdkCGt5qRTp
The setuptools entry point wraps main() with sys.exit(main()). When
main() returned the float result, sys.exit(60.0) wrote '60.0' to stderr
and exited with status 1 -- so every successful conversion printed the
answer twice (stdout + stderr) and looked like a failure to any caller
checking $?.

Make main() return None and rewrite the two CLI tests to use capsys to
assert against captured stdout instead of the (now-absent) return
value. Successful runs are now silent on stderr and exit 0; argparse
errors continue to exit 2 via parser.error.

https://claude.ai/code/session_016wVPKqjdaTUrdkCGt5qRTp
_UnitSpec was declared frozen=True but held a list[int], so callers
could mutate the cached signature in place and silently corrupt the
unit table. Switch the field to tuple[int, ...] so the dataclass is
deeply immutable.

The cache-hit branch of _signature_and_quantity_for_unit can now
return the stored spec directly instead of building a defensive copy,
since both fields are immutable.

Per-call accumulators inside _signature_and_quantity_for_unit stay as
list[int] for the in-place arithmetic, then package the result as a
tuple at the return.

Update CLAUDE.md to reflect the change.

https://claude.ai/code/session_016wVPKqjdaTUrdkCGt5qRTp
Adds 18 tests covering the gaps we'd been calling out:

- cubed keyword: volume conversion (m^3 -> liters), signature exponent
  is 3 (guards the buffer-doubling logic), and use in the denominator
  (kg/m^3)
- valid custom unit file (new fixture test_files/valid_custom.txt
  exercising signature form, derived units, and a compound-expression
  definition; also confirms _sig_len adopts the file's dimension count
  rather than the default 6)
- _parse_physical_quantity happy path: simple, decimal, no-space
  ("5feet"), and compound-units inputs
- round-trip A->B->A across length / mass / volume / velocity / force,
  parametrized
- case-sensitive lookup ("Feet" is not a valid alias for "feet")
- two-arg form requires a string (covers the ValueError branch in
  convert that mypy already prevents statically)

Coverage rises from previously-not-measured to 99% (47 tests). The two
uncovered lines are the `if __name__ == '__main__'` guard and a
defense-in-depth ValueError in _parse_unit_file that the outer regex
already prevents from firing.

CLAUDE.md updated to swap the "gaps" section for a "coverage" section
documenting the conventions for adding new tests.

https://claude.ai/code/session_016wVPKqjdaTUrdkCGt5qRTp
Layout:
- unit_parser/test_units.py        -> tests/test_units.py
- unit_parser/test_files/*.txt     -> tests/test_files/*.txt
- empty tests/__init__.py so mypy treats it as a package and the
  `tests.*` override actually matches

pyproject.toml:
- [tool.pytest.ini_options] testpaths = ["tests"]
- [tool.coverage.run] source = ["unit_parser"] so coverage is scoped
  to the library, not the test files
- [tool.coverage.report] fail_under = 95, show_missing = true
- [tool.setuptools.packages.find] include = ["unit_parser*"] so the
  built wheel does NOT pull in tests/ (verified via `uv build`)
- mypy files = ["unit_parser", "tests"]; override switched to
  module = "tests.*"

tests/test_units.py:
- absolute imports (from unit_parser.convert / .units) now that the
  module lives outside the package

CLAUDE.md:
- Refresh Commands section to use `uv run pytest`/`uv sync`
- Replace the now-fixed CLI implementation note with a description of
  the actual behavior
- Mark the Tests roadmap row done

Coverage stays at 99.12% (47 tests); the 95% floor in pyproject.toml
will fail CI if a future change drops below it.

https://claude.ai/code/session_016wVPKqjdaTUrdkCGt5qRTp
@rwilson4 rwilson4 merged commit 974ed4a into master May 2, 2026
3 checks passed
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