Complete Python 2 removal and Python 3-only tooling#161
Merged
Conversation
With Python 2 support dropped (python_requires >=3.10), the 2/3 compatibility layer is dead code. This removes: - util.py: the sys.version_info fork and the u/text_type/binary_type/ text_types shims (now just str/bytes builtins); dead NullHandler AttributeError fallback - parser.py: import sys and two version_info branches; folds the Py2 __unicode__ protocol into __str__; replaces u()/text_types/binary_type with str/(str, bytes)/bytes - config/__init__.py: the collections.abc Set ImportError fallback (the collections.Set path no longer exists in 3.10+); type() == binary_type -> isinstance(s, bytes); fixes two Py2-style doctests - all modules: from __future__ import unicode_literals and utf-8 coding cookies (both no-ops in Py3) - tests.py: 27 u(...) calls -> str(...) Bytes-input decoding and the encoding parameter are intentionally kept as real Py3 functionality. All 342 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
distutils was removed from the stdlib in 3.12, so the legacy build path is deprecated. Align the test workflow with the publish workflow: - CI: replace `python setup.py sdist` with `python -m build --sdist` (and install `build`), matching the publish workflow - setup.py: drop the dead `from distutils.core import setup` fallback; setuptools is guaranteed for any modern build Verified `python -m build --sdist` + `twine check` succeed in a clean venv. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
setup.cfg contained only `[bdist_wheel] universal = 1`, which built a py2.py3-none-any wheel advertising Python 2 compatibility that no longer exists. With the flag gone, bdist_wheel tags the wheel py3-none-any by default under Python 3 — matching python_requires >=3.10. Verified: `python -m build --wheel` now produces nameparser-1.1.3-py3-none-any.whl (was py2.py3-none-any). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Python 2 spelled the iterator protocol method next(); Python 3 uses __next__(). HumanName and SetManager each carried a `def next(self): return self.__next__()` forwarding alias to work under both. Nothing in Python 3 calls .next(), so the aliases are dead. The __next__() methods are kept (HumanName's drives iteration via " ".join(self)). SetManager's __next__ is separately broken/dead but left untouched here to keep this commit scoped to the Py2 shims. All 342 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Python 3.14 was released October 2025 and is now the latest stable. Add it to the CI test matrix and the PyPI classifiers. The test suite passes on 3.14 (342 tests, OK with skipped=2, expected failures=10). python_requires remains >=3.10 — it is a floor, so newer versions are already permitted; only the matrix (testing) and classifiers (advertising) need the bump. 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.
Follow-up cleanup after #160 dropped Python 2 support (
python_requires = '>=3.10'). Removes the now-dead 2/3 compatibility layer, aligns the build tooling and wheel metadata with Python 3-only, and adds Python 3.14 to the supported set.The code itself needed nothing for 3.11/3.12: it compiles clean under
SyntaxWarning-as-error (regexes use raw strings), imports clean underDeprecationWarning-as-error, uses no removedunittestaliases, and the CI matrix / classifiers /.readthedocs.yamlalready covered 3.10–3.13. The gaps were in tooling and stale metadata.Commits
sys.version_infofork andu()/text_type/binary_type/text_typesshims inutil.py(now plainstr/bytes); folds the Py2__unicode__protocol into__str__; dropsfrom __future__ import unicode_literalsand utf-8 coding cookies everywhere; removes thecollections.SetImportError fallback (that path no longer exists in 3.10+);type() == binary_type→isinstance(s, bytes); fixes two Py2-style doctests; rewrites 27u(...)calls in tests tostr(...).python setup.py sdistis deprecated. CI now usespython -m build --sdist(matching the publish workflow); dropped the deadfrom distutils.core import setupfallback insetup.py.setup.cfgcontained only[bdist_wheel] universal = 1, which built apy2.py3-none-anywheel falsely advertising Python 2. Removed; wheels now tagpy3-none-any.next()iterator aliases —HumanNameandSetManagereach carried adef next(self): return self.__next__()Py2 shim. Removed;__next__is kept.python_requiresstays>=3.10(a floor).Intentionally preserved
Bytes-input decoding and the
encodingparameter — these are real Python 3 functionality, not 2/3 shims.Verification
python tests.py: 342 tests pass (skipped=2, expected failures=10), unchanged from baseline — confirmed on 3.14.python -m build --sdist+twine check: pass in a clean venv.python -m build --wheel: producesnameparser-1.1.3-py3-none-any.whl(waspy2.py3-none-any).Known follow-up (out of scope)
SetManager.__next__innameparser/config/__init__.pyis dead and broken independent of Python 2 (indexes aset, reads an uninitializedself.count). Left untouched here to keep scope to Py2 shims; worth a separate bug-fix.🤖 Generated with Claude Code