Skip to content

fix: replace distutils.version.LooseVersion for Python 3.12+ compatibility#616

Merged
btorresgil merged 1 commit into
PaloAltoNetworks:developfrom
gh-ajinks:fix/distutils-python312
May 14, 2026
Merged

fix: replace distutils.version.LooseVersion for Python 3.12+ compatibility#616
btorresgil merged 1 commit into
PaloAltoNetworks:developfrom
gh-ajinks:fix/distutils-python312

Conversation

@gh-ajinks
Copy link
Copy Markdown

Problem

distutils was deprecated in Python 3.10 and removed entirely in Python 3.12, making the library completely unusable on any modern Python installation:

ModuleNotFoundError: No module named 'distutils'

Fixes #580.

Why not pip install setuptools?

The common workaround is adding setuptools as a runtime dependency, because setuptools ships its own distutils copy. This has two problems:

  1. It's the wrong tool for the job. setuptools is a build-time dependency. Pulling it in at runtime just to recover 40 lines of version-parsing code couples every user's environment to a build tool they may not otherwise need.
  2. It breaks CI/CD silently. Users running clean virtual environments (Docker images, GitHub Actions runners, etc.) hit the ModuleNotFoundError at import time with no clear signal that setuptools is required, since it isn't listed in the package's install dependencies.

Why not the packaging library?

packaging.version.Version is the modern, maintained replacement for distutils.version — but it enforces PEP 440. PAN-OS version strings like 10.1.3-h3, 9.0.0-b5, and 11.0.0-c1 are not PEP 440-compliant (h is not a recognised pre-release identifier), so packaging.version.Version raises InvalidVersion on them. packaging.version.LegacyVersion, which did handle arbitrary strings, was removed in packaging 22.0 (2022).

What this PR does

Inlines a minimal _LooseVersion class using only stdlib re. It replicates the exact parsing behaviour that PanOSVersion depends on:

  • 10.1.3-h3[10, 1, 3, '-', 'h', 3]
  • 9.0.0-b5[9, 0, 0, '-', 'b', 5]
  • 11.0.0[11, 0, 0]

PanOSVersion already overrides every comparison operator (__lt__, __gt__, __eq__, __le__, __ge__, __ne__), so the only things inherited from LooseVersion were the parser and __str__. Both are reproduced exactly. No new dependencies are introduced.

Testing

All 244 existing tests in test_init.py, test_PanOScomp.py, and test_updater.py pass unchanged on Python 3.14.

…ility

distutils was deprecated in Python 3.10 and removed entirely in Python 3.12,
causing an immediate ModuleNotFoundError on any import of panos. Fixes PaloAltoNetworks#580.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@btorresgil btorresgil self-requested a review May 14, 2026 23:46
Copy link
Copy Markdown
Member

@btorresgil btorresgil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gh-ajinks Looks great! Thanks for fixing this!

@btorresgil btorresgil merged commit d85ddf6 into PaloAltoNetworks:develop May 14, 2026
6 of 7 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.

panos use deprecated distutils library, not existing in current python (3.12 and later)

2 participants