Reorganize tests into a pytest package (and fix bugs it exposed)#165
Merged
Conversation
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…me for pytest collection
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When empty_attribute_default=None, initials() interpolated the literal string 'None' for missing name parts (e.g. 'J. None D.' for 'John Doe'). Render empty parts as '' so str.format produces clean output, and fall back to empty_attribute_default only for a fully-empty result, matching the other attribute accessors (e.g. first/title). Exposed by running the test suite with empty_attribute_default=None under isolated state. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Several tests mutate scalar config on the global CONSTANTS singleton (capitalize_name, force_mixed_case_capitalization, string_format, etc.) without restoring it. The original suite only survived because unittest runs methods alphabetically, so a sibling test happened to reset the value; pytest runs in definition order, leaking state across files. The autouse fixture now snapshots and restores these scalars around every test, making tests order-independent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Revert an injected conditional pytest.xfail() in test_initials_simple_name back to the verbatim original (the underlying initials() bug is now fixed). Also remove test_custom_regex_constant, a test that was fabricated during migration and added to both tests.py and test_constants.py; it is not part of the original suite. tests.py restored to its original 344 methods. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The autouse fixture only snapshotted scalar config attrs, so tests that mutate a global collection (e.g. CONSTANTS.titles) relied on manual, non-exception-safe cleanup. If such a test failed before its cleanup ran, the mutation leaked into later tests — reintroducing the order-dependence the fixture was meant to eliminate. Snapshot and restore the collection managers (SetManager / TupleManager / RegexTupleManager) too, via a small type-aware clone (deepcopy can't round-trip RegexTupleManager's compiled patterns), and reset the lazy _pst cache so it recomputes from the restored collections. The manual cleanup in test_can_change_global_constants is now redundant and removed. Verified order-independence across randomized seeds and via an injected test that mutates a global collection then fails before cleanup: the following test still sees pristine config. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add explicit regression tests for the bug this reorg fixed: with
empty_attribute_default=None, initials() used to interpolate the literal
"None" for empty name parts ("John Doe" -> "J. None D.") and for a fully
-empty name ("None None None"). The tests assert empty parts render as ''
and a fully-empty result falls back to empty_attribute_default, matching
the first/last accessors.
Both fail against master's parser.py and pass on this branch, so they
pin the fix rather than relying on the parametrized fixture happening to
exercise a pre-existing test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
encoding=sys.stdout.encoding was never consumed (input comes from argv as str, and encoding only affects bytes input), and sys.stdout.encoding can be None under redirection — misleading dead config. Also switch the Initials line from string concatenation to a comma so it prints harmlessly when initials() returns empty_attribute_default (which may be None) instead of raising TypeError. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The CONTRIBUTING.md `python -m nameparser` example showed capitalized field labels (Title:/First:/…) and omitted the nickname field; the actual repr uses lowercase labels and includes nickname. Update it to match. Also set xfail_strict in pytest config so an xfail test that starts passing fails the suite instead of silently becoming an xpass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ials fix The usage.rst initials doctests had unterminated strings, wrong expected output (Python list repr, leaked global initials_format), and a missing blank line after a doctest directive. Fix them to pass under the doctest builder and reset CONSTANTS.initials_format so they don't leak state. Add an Unreleased release_log entry for the initials() None fix, the python -m nameparser CLI, and the pytest test reorg. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The implementation plan was a working artifact for this PR and isn't part of the published docs or package; drop it before merge. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Splits the single 97KB
tests.py(344 tests in one file,unittest) into atests/pytest package — one file per concern — and runs everything under pytest. Along the way, the migration exposed and fixed two real defects.Test reorganization
tests/test_*.pyfiles (one per concern);tests/base.pyholds a plainHumanNameTestBasewith them()assert helper + thinassert*shims, so test bodies moved over verbatim.tests/conftest.pyreproduces the old dual run (every test underempty_attribute_default = ''andNone) as a clean parametrized autouse fixture — reported counts are doubled (692 results from 346 methods).@unittest.expectedFailure→@pytest.mark.xfail(10 methods),@unittest.skipUnless(dill,…)→@pytest.mark.skipif,self.assertRaises→pytest.raises.ConstantsCustomization→ConstantsCustomizationTestsso it matchespython_classes = ["*Tests","*TestCase"].tests.pyintonameparser/__main__.py:python -m nameparser "Some Name".AGENTS.md,CONTRIBUTING.md, GitHub Actions,MANIFEST.in) updated to pytest.Bug fixes the migration surfaced
initials()rendered the literal"None"for empty name parts whenempty_attribute_default=None(e.g."J. None D."). Now empty parts render as''and a fully-empty result falls back toempty_attribute_default, consistent with thefirst/titleaccessors. (Behavior change forempty_attribute_default=Noneusers.) Pinned by dedicated regression tests intests/test_initials.py(they fail against the oldparser.py).CONSTANTSwithout restoring it; the old suite only passed becauseunittestruns methods alphabetically. pytest runs in definition order, which exposed it. The fixture now snapshots/restores both the scalar and collection config per test (including the globalSetManager/TupleManagerconstants and the lazy_pstcache), so even a test that mutates a global collection and fails before its own cleanup can't leak — tests are order-independent.Review follow-ups
initials()/Nonefix.nameparser/__main__.py: dropped the inertencoding=sys.stdout.encodingargument and switched theInitials:line to comma-style printing (noTypeErrorwheninitials()returnsNone).xfail_strictso anxfailthat starts passing fails the suite instead of silently becoming anxpass.python -m nameparserrepr example inCONTRIBUTING.md(lowercase field labels +nickname).Test Plan
pytest→ 672 passed, 20 xfailed, zero failuresruff checkclean;mypycleansphinx-buildand sdist packaging unaffectedpython -m nameparser "Dr. Juan Q. Xavier de la Vega III"parses correctly🤖 Generated with Claude Code