Skip to content

[MINOR] Fix Python 3.12+ compatibility (drop distutils, six, pkg_resources)#52

Open
Tchekda wants to merge 1 commit intomasterfrom
py312-compat-drop-distutils-six
Open

[MINOR] Fix Python 3.12+ compatibility (drop distutils, six, pkg_resources)#52
Tchekda wants to merge 1 commit intomasterfrom
py312-compat-drop-distutils-six

Conversation

@Tchekda
Copy link
Copy Markdown

@Tchekda Tchekda commented May 5, 2026

Summary

The pinned invoke==0.22.0 (2017) and direct use of removed/deprecated stdlib APIs prevent invoke-release from running on Python 3.12+ (and partially on 3.11). This PR makes the tool work on modern Python while staying backwards-compatible to Python 3.6+.

What breaks today

API Python version Breakage
distutils.version.LooseVersion removed in 3.12 ImportError at module load
pkg_resources.parse_version deprecated runtime DeprecationWarning, removal pending
wheel.archive.make_wheelfile_inner removed in wheel 0.32 ImportError at module load
invoke==0.22.0 (vendored six.moves) broken on 3.12+ ModuleNotFoundError: invoke.vendor.six.moves
imp.load_module (used by old invoke) removed in 3.12 crash on first invoke <task>

Changes

setup.py:

  • invoke~=0.22.0 -> invoke>=1.0,<3.0
  • Drop six~=1.11.0
  • Add packaging>=20.0

python/invoke_release/tasks.py:

  • distutils.version.LooseVersion -> packaging.version.Version (using .release instead of .version)
  • pkg_resources.parse_version -> packaging.version.parse
  • from wheel import archive (top-level) -> lazy try/except inside the wheel() task. If running on wheel<0.32 the task still works; on modern wheel it raises a clear ReleaseFailure pointing to python -m build --wheel.
  • six.text_type -> str; six.moves.input -> input; six.moves.reload_module -> importlib.reload; six.moves.urllib -> stdlib urllib.request.
  • subprocess.check_output(...) for git remote get-url now uses universal_newlines=True (3.0+ alias for text=) so we get a str directly instead of relying on six.text_type to coerce bytes.

Compatibility

  • Python: 3.6+ (uses importlib.reload which is 3.4+, packaging available everywhere, universal_newlines=True works on all py3).
  • invoke: pin widened to >=1.0,<3.0 so pip picks the latest version compatible with the host Python (1.x on older Py, 2.x on Py 3.6+).
  • wheel: floor unchanged at >=0.31.1. Modern wheel works for everything except the invoke wheel task — which has been broken anyway since wheel 0.32.

Validated on

Python 3.14.4, fresh venv, editable install, invoke version against an actual ebutils checkout:

Python 3.14.4 (main, Apr 14 2026, 14:46:33) [Clang 22.1.3 ]
Invoke 2.2.1
Invoke Release 4.6.0
Eventbrite Common Utilities Library 4.1.0

invoke --help release and invoke --list both render correctly.

Note on PR #51

PR #51 (Aug 2025) covers some of the same ground — it drops six calls and Py 2 boilerplate. However it leaves the actual Py 3.12+ breakage in place: distutils, pkg_resources, wheel.archive, and uses a bare reload() call that does not exist as a builtin in Python 3 (it would NameError at runtime in the rollback-release task). This PR is complementary; rebasing onto #51 if it lands is a small mechanical job.

Test plan

  • pip install -e . succeeds on Python 3.6, 3.9, 3.12, 3.14
  • invoke version runs in a project with tasks.py + version.py + CHANGELOG.txt
  • invoke release --no-stash produces the expected commit shape (manually verified by reading the patched code paths against ebutils' history; not yet end-to-end tested against a real release)
  • invoke wheel raises a clear error message on modern wheel; still works if wheel<0.32 is pinned

The pinned invoke==0.22.0 (2017) and direct uses of removed/deprecated
stdlib APIs prevent invoke-release from running on modern Python:

- distutils removed in Python 3.12 -> use packaging.version.Version
- pkg_resources.parse_version deprecated -> use packaging.version.parse
- wheel.archive removed in wheel 0.32 -> lazy import in `wheel` task only
- six no longer needed for py3-only -> use stdlib (input, str, importlib.reload, urllib.request)
- Old invoke pin breaks on py3.12+ (uses removed `imp`) -> widen pin

Backwards-compatible to Python 3.6+ and to invoke 1.x where the env still
supports it (pip picks the latest compatible invoke per environment).
The `wheel` task gracefully reports a clear error if `wheel.archive` is
not available rather than crashing at import time.

Note: complementary to #51, which drops Py2 boilerplate but leaves the
actual Py 3.12+ breakage in place.
@Tchekda Tchekda marked this pull request as ready for review May 5, 2026 08:54
@Tchekda Tchekda requested a review from brey-eb May 5, 2026 08:55
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.

1 participant