Skip to content

Fix lxml and distutils compatibility for Python 3.13#388

Open
Aparup-Roy-24f3001874 wants to merge 10 commits into
Cloud-CV:masterfrom
Aparup-Roy-24f3001874:fix-lxml-python313
Open

Fix lxml and distutils compatibility for Python 3.13#388
Aparup-Roy-24f3001874 wants to merge 10 commits into
Cloud-CV:masterfrom
Aparup-Roy-24f3001874:fix-lxml-python313

Conversation

@Aparup-Roy-24f3001874

@Aparup-Roy-24f3001874 Aparup-Roy-24f3001874 commented Jan 2, 2026

Copy link
Copy Markdown

Description

This PR fixes the installation and runtime errors on Python 3.13 (specifically for Windows users) as reported in #383.

Changes:

  • Requirements: Updated lxml version gating in requirements.txt. It now uses lxml>=5.0.0 for Python 3.12+ to ensure pre-compiled wheels are available, avoiding the need for C++ Build Tools.
  • Dependency Fix: Added packaging and setuptools to requirements.txt to provide the distutils functionality that was removed in Python 3.12.
  • Code Fix: Updated evalai/utils/updates.py to use packaging.version.Version instead of the deprecated distutils.version.StrictVersion.

Fixes #383

Summary by CodeRabbit

  • Bug Fixes

    • Improved version handling for release updates to use a more reliable version parser.
    • Updated dependency support so lxml installs correctly across older and newer Python versions.
  • Chores

    • Adjusted package setup and test tooling dependencies for better compatibility with current environments.

@Aparup-Roy-24f3001874

Copy link
Copy Markdown
Author

I've fixed the lxml/distutils compatibility issues for Python 3.13 and ensured the CI build passes by resolving the PEP 8 style requirements. Ready for review!

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR updates version handling and packaging metadata for newer Python releases. It changes the version import source, revises lxml dependency markers, replaces setup path resolution, and adjusts test dependency pins.

Changes

Python compatibility updates

Layer / File(s) Summary
Runtime version import and lxml markers
evalai/utils/updates.py, requirements.txt
Version is imported from packaging.version, and lxml now uses Python-version markers in requirements.txt.
Setup path and test pins
setup.py
setup.py adds get_version_path(*paths) for the version file path and updates the test dependency pins.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

I nibbled a carrot and peered at the sky,
Version checks hopped by as the build rolled by.
lxml found its path with a neat little cue,
And my fluffy paws thumped: “This build will do!”
Hooray for tidy installs, whee! 🐰

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning setup.py also changes test/dev dependencies (coveralls, flake8) that are unrelated to the install-fix scope. Move CI/dev dependency version changes to a separate PR unless they are required for the Python 3.13 install fix.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is specific and matches the main compatibility fix for Python 3.13.
Linked Issues check ✅ Passed The lxml pinning and distutils replacement directly address the Python 3.13 Windows install failure.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@evalai/utils/updates.py`:
- Line 3: Update get_latest_version() in updates.py so it returns a parsed
StrictVersion object instead of the raw release string, using the existing
StrictVersion alias imported from packaging.version. Then update the version
check in evalai/main.py to parse __version__ with StrictVersion before comparing
it to latest_version, so both sides use version objects and not lexicographic
string comparison.

In `@requirements.txt`:
- Around line 6-7: The Python 3.12+ lxml pin is still allowed to resolve to
vulnerable 5.x releases, so update the version marker in requirements.txt to
require lxml 6.1.0 or newer for python_version >= "3.12" while leaving the
Python <3.12 constraint unchanged; adjust the existing lxml dependency entries
only, using the lxml requirement lines as the reference point.

In `@setup.py`:
- Around line 41-43: Remove the outdated flake8 pin from the tests_require list
in setup.py and update it to a Python 3.13-compatible version, using the
existing dependency entry alongside coveralls. Locate the dependency declaration
near the flake8 package name and replace flake8==3.0.4 with flake8>=6.1.0 so the
test/lint environment works on newer Python versions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b66dbd31-6e7f-481f-99a5-e3f1d2afa23b

📥 Commits

Reviewing files that changed from the base of the PR and between 6030779 and 5254072.

📒 Files selected for processing (3)
  • evalai/utils/updates.py
  • requirements.txt
  • setup.py

Comment thread evalai/utils/updates.py
import requests

from distutils.version import StrictVersion
from packaging.version import Version as StrictVersion

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C2 '__version__\s*<\s*latest_version|get_latest_version\(' evalai/main.py evalai/utils/updates.py

Repository: Cloud-CV/evalai-cli

Length of output: 654


🏁 Script executed:

sed -n '6,30p' evalai/utils/updates.py

Repository: Cloud-CV/evalai-cli

Length of output: 395


Return a parsed Version object instead of a string from get_latest_version()

The helper sorts releases using StrictVersion but returns a raw string from the release list. Consequently, evalai/main.py compares __version__ and latest_version as strings, causing lexicographic errors (e.g., 0.9.0 fails to trigger an update against 0.10.0).

Modify get_latest_version() to return a StrictVersion instance (strict_version = StrictVersion(versions[-1]); return strict_version) and update evalai/main.py to parse __version__ before comparison:

- if __version__ < latest_version:
+ if StrictVersion(__version__) < latest_version:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@evalai/utils/updates.py` at line 3, Update get_latest_version() in updates.py
so it returns a parsed StrictVersion object instead of the raw release string,
using the existing StrictVersion alias imported from packaging.version. Then
update the version check in evalai/main.py to parse __version__ with
StrictVersion before comparing it to latest_version, so both sides use version
objects and not lexicographic string comparison.

Comment thread requirements.txt
Comment on lines +6 to +7
lxml>=4.6.2,<5.0; python_version < "3.12"
lxml>=5.0.0; python_version >= "3.12"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C2 --type=py '\bfrom lxml\b|\bimport lxml\b|\biterparse\s*\(|\bETCompatXMLParser\s*\(' .

Repository: Cloud-CV/evalai-cli

Length of output: 157


🌐 Web query:

What is the first patched lxml 5.x release for GHSA-vfmq-68hx-4jfw / PYSEC-2026-87, and is lxml 5.0.0 affected?

💡 Result:

The vulnerability GHSA-vfmq-68hx-4jfw (also tracked as PYSEC-2026-87 and CVE-2026-41066) was fixed in lxml 6.1.0 [1][2][3]. Consequently, there is no patched lxml 5.x release for this issue, as it remained unpatched in the 5.x series [1][4]. Lxml 5.0.0 is affected by this vulnerability because the default configuration for iterparse and ETCompatXMLParser still allowed entity resolution (resolve_entities=True) at that time [1][2]. While lxml 5.0 introduced a change to default resolve_entities='internal' for normal XML and HTML parsers, this change was not applied to iterparse or ETCompatXMLParser until version 6.1.0 [1][2].

Citations:


Bump lxml to >=6.1.0 for Python 3.12+ due to XXE (GHSA-vfmq-68hx-4jfw).

GHSA-vfmq-68hx-4jfw affects lxml 5.0.0–5.x; the first patched release is 6.1.0. Your current pin lxml>=5.0.0; python_version >= "3.12" can resolve to an affected version.

Update both markers to require 6.1.0 minimum:

required change
lxml>=4.6.2,<5.0; python_version < "3.12"
lxml>=6.1.0; python_version >= "3.12"

Even though no explicit lxml imports or iterparse/ETCompatXMLParser calls were found in Python sources, the dependency may be consumed transitively or via non-Python entrypoints. Since the advisory remains unpatched in the 5.x series, pinning to 6.1.0+ eliminates this risk.

[remove: and security flags once updated.]

🧰 Tools
🪛 OSV Scanner (2.4.0)

[HIGH] 6-6: lxml 5.0.0: undefined

(PYSEC-2026-87)


[HIGH] 6-6: lxml 5.0.0: lxml: Default configuration of iterparse() and ETCompatXMLParser() allows XXE to local files

(GHSA-vfmq-68hx-4jfw)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@requirements.txt` around lines 6 - 7, The Python 3.12+ lxml pin is still
allowed to resolve to vulnerable 5.x releases, so update the version marker in
requirements.txt to require lxml 6.1.0 or newer for python_version >= "3.12"
while leaving the Python <3.12 constraint unchanged; adjust the existing lxml
dependency entries only, using the lxml requirement lines as the reference
point.

Source: Linters/SAST tools

Comment thread setup.py
Comment on lines +41 to +43
"packaging",
"coveralls==1.3.0",
"flake8==3.0.4",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Does flake8 3.0.4 support Python 3.12 or Python 3.13?

💡 Result:

No, Flake8 version 3.0.4 does not support Python 3.12 or Python 3.13. Flake8 3.0.4 was released on August 8, 2016 [1][2], long before the release of Python 3.12 or 3.13. Flake8 is tightly coupled to the version of Python on which it runs because it must be able to parse the syntax of the code it checks [3]. Support for Python 3.12 was only introduced much later, starting with Flake8 version 6.1.0, released in July 2023 [4]. Consequently, version 3.0.4 lacks the necessary infrastructure and language feature support to correctly parse or lint Python 3.12 or 3.13 code.

Citations:


Remove the outdated flake8==3.0.4 pin to enable Python 3.13 support

flake8==3.0.4 (released Aug 2016) is incompatible with Python 3.12 and 3.13, as support for these versions was only introduced in flake8 6.1.0. Retaining this pin in tests_require will cause linting failures on target CI environments. Update to flake8>=6.1.0.

    "packaging",
    "coveralls==1.3.0",
    "flake8==3.0.4",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@setup.py` around lines 41 - 43, Remove the outdated flake8 pin from the
tests_require list in setup.py and update it to a Python 3.13-compatible
version, using the existing dependency entry alongside coveralls. Locate the
dependency declaration near the flake8 package name and replace flake8==3.0.4
with flake8>=6.1.0 so the test/lint environment works on newer Python versions.

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.

Python installation error

1 participant