Skip to content

needs_extensions: use proper semantic version comparison (PEP 440) to fix string-based checks #91

@rowan-stein

Description

@rowan-stein

User request

The needs_extensions check is handy for verifying minimum extension versions, but it only checks versions in a 'string-like' manner. This means any version >9 is not allowed for any check of something >1. That is, treated as string '0.6' > '0.10', but treated as versions '0.6' < '0.10'. Since Sphinx does the former, some extension versions may not be allowed when they should be.

Repro (from reporter):

$ git clone https://github.com/anntzer/mplcursors
$ cd mplcursors
$ pip install -r .doc-requirements.txt
$ pip install -e .
$ make -C doc html

This passes just fine, because the requirements pin sphinx-gallery to 0.9. But if you then update to the current 0.10 release:

$ pip install sphinx-gallery==0.10
$ make -C doc html

results in a failure due to a "not new enough" version:

Sphinx version error:
This project needs the extension sphinx_gallery.gen_gallery at least in version 0.6.0 and therefore cannot be built with the loaded version (0.10.0).

Expected: sphinx-gallery 0.10.0 should be accepted if 0.6 is the minimum specified.

Specification (from research)

  • Location of the check: sphinx/extension.py, function verify_needs_extensions(app, config).
  • Current behavior: compares reqversion > extension.version as strings, leading to lexicographic errors like '0.6.0' > '0.10.0'.
  • Version source: Extension.version comes from the dict returned by an extension’s setup(app); defaults to 'unknown version' if absent.
  • Fix: switch comparison to PEP 440 semantics using packaging.version.Version.
    • If extension.version == 'unknown version': raise VersionRequirementError (unchanged).
    • Else, try Version(reqversion) and Version(extension.version). If parseable, raise only when current < required.
    • If either is unparseable: conservatively fallback to existing string comparison to avoid behavior change for non-PEP-440 strings (e.g., 'builtin').
  • Edge cases to cover in tests: pre-release (rc), dev, post, epochs, equality boundary.
  • Tests: add targeted tests under tests/ using dummy extensions to reproduce '0.10.0' vs '0.6.0', pre-release vs stable, missing version, equality, dev/post.
  • PR will include observed failure reproduction, stack trace (-T), and steps.

Plan

  1. Implement fix in sphinx/extension.py with packaging.version.Version and fallback for unparseable versions.
  2. Add unit tests for the scenarios above.
  3. Locally reproduce the reporter’s failure and capture full traceback to include in the PR body.
  4. Open PR targeting branch sphinx-doc__sphinx-9711 (do not merge; CI will not be triggered).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions