Skip to content

Show the Policy spellings that type-check - #334

Merged
derek73 merged 1 commit into
masterfrom
docs/type-clean-policy-idioms
Aug 6, 2026
Merged

Show the Policy spellings that type-check#334
derek73 merged 1 commit into
masterfrom
docs/type-clean-policy-idioms

Conversation

@derek73

@derek73 derek73 commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

nameparser ships py.typed, and every Policy configuration example in the docs needed a # type: ignore[arg-type]. This changes the docs to teach the spellings that check clean.

Five spellings failed, not the two originally noticed:

documented why it failed
Policy(script_orders={}) dicttuple
Policy(segment_scripts=()) tuplefrozenset
Policy(nickname_delimiters={("[", "]")}) setfrozenset
Policy(maiden_delimiters={("(", ")")}) setfrozenset
Policy(script_orders=dict(DEFAULT_SCRIPT_ORDERS) | {...}) dicttuple

The bare set literal for a delimiter field is the one most likely to bite in practice — it is the spelling anyone reaches for first, and it appeared in concepts.rst as the introductory example.

What is not changed, and why

The annotations. A dataclass field annotation types both the __init__ parameter and the attribute, and Policy normalizes in __post_init__ (its validators take object), so those two types genuinely differ. Python cannot express that with one annotation.

The annotation stays the storage type, because reading the attribute is the commoner operation and a union there would cascade to every reader — the same reasoning that already kept empty_attribute_default narrow. This is a real expressiveness gap, not sloppiness, and it applies to every normalizing field.

Union spellings were already fine and are untouched: frozenset | set is frozenset, so DEFAULT_NICKNAME_DELIMITERS | {("{", "}")} always checked clean.

The one spelling that needed a contract first

The script_orders override becomes:

Policy(script_orders=(*DEFAULT_SCRIPT_ORDERS, (Script.HAN, GIVEN_FIRST)))

which depends on a later entry replacing an earlier one. That behavior was emergent — _validated_script_orders builds a dict before sorting, and the comment there justifies it for hashability, not for override semantics. Prose telling readers to append is prose that depends on it, so tests/v2/test_policy.py now pins the override, the survival of the other entries, one-entry-per-script, and equality with the mapping spelling it replaces.

Verification

  • Every spelling the docs now show, extracted verbatim into one file: mypy clean.
  • uv run sphinx-build -b doctest docs — exit 0.
  • uv run pytest -q — 3060 passed (was 3059; one new test).
  • uv run mypy, uv run ruff check — clean.

Note for review

The .. note:: in customize.rst previously told readers the readable spellings need a # type: ignore. It now explains the storage-vs-constructor split instead, and says the wider spellings still parse identically for anyone not running a type checker.

I did not add a release-log entry: no behavior changed. Arguably the newly-contracted last-wins rule is a promise the library now makes and could warrant a line — happy to add one if you read it that way.

🤖 Generated with Claude Code

The package ships py.typed, and every Policy configuration example in
the guide needed a # type: ignore[arg-type]. Five spellings failed, not
the two first noticed: script_orders={}, segment_scripts=(),
nickname_delimiters={...}, maiden_delimiters={...} and the
dict(DEFAULT_SCRIPT_ORDERS) | {...} override. A bare SET literal for a
delimiter field is the one most likely to bite, being the spelling
anyone reaches for first.

The cause is not sloppiness and is not fixed here: a dataclass field
annotation types both the __init__ parameter and the attribute, and
Policy normalizes in __post_init__, so those two types genuinely
differ. The annotation stays the storage type, because reading the
attribute is the commoner operation and a union there would cascade to
every reader -- the same reasoning that already kept
empty_attribute_default narrow.

What changes is which spelling the docs teach. Each one has a
type-clean equivalent that means exactly the same thing, verified
under mypy. Union spellings are untouched: frozenset | set is already
frozenset, so DEFAULT_NICKNAME_DELIMITERS | {...} was always clean.

The script_orders override becomes (*DEFAULT_SCRIPT_ORDERS, (Script.HAN,
GIVEN_FIRST)), which depends on a later entry replacing an earlier one.
That was emergent from _validated_script_orders building a dict before
sorting rather than a stated contract, so prose telling readers to
append needed it pinned: a test now asserts the override, the survival
of the other entries, one entry per script, and equality with the
mapping spelling it replaces.

The note in customize.rst said the opposite of what is true now; it
explains the storage-vs-constructor split instead.
@derek73 derek73 added this to the v2.1 milestone Aug 6, 2026
@derek73 derek73 added the docs Documentation fixes and updates label Aug 6, 2026
@codecov

codecov Bot commented Aug 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.48%. Comparing base (57f3b5d) to head (afde560).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #334   +/-   ##
=======================================
  Coverage   98.48%   98.48%           
=======================================
  Files          41       41           
  Lines        2832     2832           
=======================================
  Hits         2789     2789           
  Misses         43       43           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@derek73
derek73 merged commit 65a95e2 into master Aug 6, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation fixes and updates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant