Skip to content

Add Python 3.13/3.14 support and docs updates#4248

Merged
pcnudde merged 14 commits intoNVIDIA:mainfrom
pcnudde:codex/flare-2740-python-support
Mar 11, 2026
Merged

Add Python 3.13/3.14 support and docs updates#4248
pcnudde merged 14 commits intoNVIDIA:mainfrom
pcnudde:codex/flare-2740-python-support

Conversation

@pcnudde
Copy link
Collaborator

@pcnudde pcnudde commented Mar 2, 2026

Summary

  • add Python 3.13 and 3.14 support metadata and CI matrix coverage
  • update auto-format workflow to run with Python 3.14 and black 26.1.0
  • remove deprecated distutils usage in setup.py for Python 3.12+
  • update installation and release-note docs for Python 3.13/3.14 support
  • keep TensorFlow integration test path on Python 3.12 for compatibility

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 2, 2026

Greptile Summary

This PR adds official Python 3.13 and 3.14 support to NVFlare by updating packaging metadata, CI matrices, runtime code, tests, and documentation. It is a well-scoped compatibility lift that addresses several concrete Python version-specific issues: the break-in-finally SyntaxError promoted in Python 3.14 (server_runner.py), deprecated distutils usage removed in Python 3.12+ (setup.py), a hardcoded version-string check that raised ValueError on 3.13+ (component_builder_test.py), and a long-standing broken test assertion that now passes correctly (stats_def_test.py).

Key changes:

  • setup.cfg: Adds Python 3.13/3.14 classifiers; splits tenseal HE dependency into per-version markers (0.3.15 for <3.13, 0.3.16 for 3.13–<3.14, nothing on 3.14); restricts openmined.psi to python_version < "3.14"; bumps black to 25.9.0.
  • setup.py: Replaces distutils.dir_util.copy_tree with shutil.copytree(..., dirs_exist_ok=True), removing the last distutils import.
  • .github/workflows/premerge.yml: Adds a dedicated style-check job on Python 3.14 (via ./runtest.sh -s) and extends the unit-tests and wheel-build matrices from 3.9–3.12 to 3.9–3.14.
  • nvflare/private/fed/server/server_runner.py: Moves break outside the finally block (Python 3.14 SyntaxError fix) by de-indenting the abort-signal check.
  • nvflare/private/fed/client/client_runner.py: Replaces a try/finally abort-signal pattern with except BaseException+re-raise, plus a post-execute check, to avoid the finally-control-flow concern.
  • tests/: Adds pytest.importorskip("tenseal") guards for HE recipe tests; fixes the version utility and the stats encoder assertion.
  • docs/installation.rst: Adds explicit notes that the HE and PSI extras are currently unavailable on Python 3.14, and updates the venv link to the generic Python 3 docs URL.

Confidence Score: 4/5

  • This PR is safe to merge; changes are well-scoped compatibility fixes with no breaking behaviour changes for existing supported Python versions.
  • All runtime changes (server_runner.py, client_runner.py) are semantically equivalent to the originals on the common execution path and address a hard Python 3.14 SyntaxError. Test and packaging changes are correct. The only minor concerns are a subtle behavioural edge case in server_runner.py (exception-propagation path bypasses abort check) and the fact that the style-check CI job delegates pip install entirely to runtest.sh without an explicit pip upgrade, both of which are low-risk in practice.
  • No files require special attention beyond the inline comments left on nvflare/private/fed/server/server_runner.py and runtest.sh.

Important Files Changed

Filename Overview
.github/workflows/premerge.yml Adds dedicated style-check job on Python 3.14, extends CI matrix to Python 3.13/3.14, and switches unit-test run to ./runtest.sh --numprocesses=auto -u; logically sound though the style-check job delegates dep install to runtest.sh (no explicit pip upgrade).
nvflare/private/fed/server/server_runner.py Moves break outside the finally block (a Python 3.14 SyntaxError fix) by de-indenting the abort-signal check; semantics are preserved for the common path.
nvflare/private/fed/client/client_runner.py Replaces try/finally with except BaseException+re-raise pattern to handle the abort-signal check without relying on finally for control flow; logical equivalent of the original with clear intent.
setup.cfg Adds Python 3.13/3.14 classifiers, SPDX license identifier, version-gated tenseal entries for HE extra, restricts openmined.psi to Python < 3.14, and bumps black to 25.9.0.
setup.py Removes deprecated distutils.dir_util.copy_tree import, replaces it with shutil.copytree(..., dirs_exist_ok=True), and cleans up redundant os.makedirs call.
runtest.sh Refactors argument handling to support `--numprocesses=<N
tests/unit_test/fuel/utils/component_builder_test.py Replaces hardcoded version string comparisons (which raised ValueError on Python 3.13+) with a clean tuple-based (major, minor) > (3, 9) comparison.
tests/unit_test/app_common/statistics/stats_def_test.py Fixes a previously broken assertion (str.__eq__(dict) returned NotImplemented which is always truthy) by round-tripping through json.loads and using a deterministic enumerate-based sample count.
tests/unit_test/recipe/server_memory_gc_rounds_test.py Adds pytest.importorskip("tenseal") guards to HE recipe tests so they are gracefully skipped on Python 3.14 where tenseal has no wheel.
pyproject.toml Adds py313 and py314 to black's target-version list; black 25.9.0 must recognise py314 for the auto-format/style-check jobs to succeed.
docs/installation.rst Updates Python 3.9+ note to mention 3.13/3.14 coverage, adds explicit notes for HE and PSI extras being unavailable on Python 3.14, and fixes the venv doc link to the generic Python 3 URL.
.github/workflows/auto-format.yml Bumps formatter runner to Python 3.14 and black to 25.9.0, consistent with the test_support pin in setup.cfg.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[PR: Python 3.13/3.14 Support] --> B[Packaging & Metadata]
    A --> C[CI Workflows]
    A --> D[Runtime Fixes]
    A --> E[Test Fixes]
    A --> F[Docs]

    B --> B1[setup.cfg: add classifiers\nPy3.13 + 3.14]
    B --> B2[setup.cfg: HE extra\ntenseal 0.3.15 for <3.13\ntenseal 0.3.16 for 3.13–3.14\nnone for 3.14+]
    B --> B3[setup.cfg: PSI extra\nopenmined.psi <3.14 only]
    B --> B4[setup.py: replace distutils\ncopy_tree → shutil.copytree]
    B --> B5[pyproject.toml: add\npy313/py314 black targets]

    C --> C1[premerge.yml: new\nstyle-check job on Py3.14]
    C --> C2[premerge.yml: expand\nunit-tests matrix to Py3.13/3.14]
    C --> C3[auto-format.yml: bump\nrunner to Py3.14 + black 25.9.0]
    C --> C4[runtest.sh: --numprocesses flag\n+ refactored arg parsing]

    D --> D1[server_runner.py:\nbreak outside finally\n→ Python 3.14 SyntaxError fix]
    D --> D2[client_runner.py:\nfinally → except BaseException\n+ post-execute abort check]

    E --> E1[component_builder_test.py:\ntuple version comparison\nreplaces hardcoded list]
    E --> E2[stats_def_test.py:\nfix broken str.__eq__ assertion\nuse json.loads round-trip]
    E --> E3[server_memory_gc_rounds_test.py:\npytest.importorskip tenseal]

    F --> F1[installation.rst:\nHE/PSI Py3.14 notes\nvenv link fix]
    F --> F2[release_notes/flare_272.rst:\nPy3.13/3.14 section]
Loading

Last reviewed commit: 22228ff

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

28 files reviewed, 9 comments

Edit Code Review Agent Settings | Greptile

@pcnudde
Copy link
Collaborator Author

pcnudde commented Mar 3, 2026

/build

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 3, 2026

Additional Comments (1)

tests/unit_test/app_common/statistics/stats_def_test.py
Old test was always-passing no-op — this fix is good, but worth documenting

The previous version called json.dumps(...) and then invoked str.__eq__(dict_literal). In Python 3, str.__eq__ returns NotImplemented when compared to a non-string type, and assert NotImplemented evaluates to True, so the assertion was never actually verifying anything. The new version (json.loads(json.dumps(...)) == dict) is the first time this test has meaningfully validated the serialization output — well caught.

It is worth adding a brief comment noting why json.loads is used here (to get a proper round-trip comparison instead of raw JSON string equality), to make the intent immediately clear to future readers.

@pcnudde pcnudde changed the title FLARE-2740: add Python 3.13/3.14 support and docs updates Add Python 3.13/3.14 support and docs updates Mar 3, 2026
@pcnudde
Copy link
Collaborator Author

pcnudde commented Mar 3, 2026

/build

@pcnudde
Copy link
Collaborator Author

pcnudde commented Mar 6, 2026

/build

Copy link
Collaborator

@chesterxgchen chesterxgchen left a comment

Choose a reason for hiding this comment

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

although most of these changes are trivial, I think the real tests are if the number of our examples are able to continue function with these changes, Its hard to tell from this PR

@pcnudde
Copy link
Collaborator Author

pcnudde commented Mar 7, 2026

although most of these changes are trivial, I think the real tests are if the number of our examples are able to continue function with these changes, Its hard to tell from this PR

That is true, but all unit tests and the ci/cd test pass (still on 3.12). At some point we need to trust those. But we need to decide what version we want to run cicd and the QA tests on

@pcnudde
Copy link
Collaborator Author

pcnudde commented Mar 9, 2026

/build

Copy link
Collaborator

@YuanTingHsieh YuanTingHsieh left a comment

Choose a reason for hiding this comment

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

if certain lib only supports 3.13 (not 3.14), I can change our Jenkins CI to use 3.13 to test.

@pcnudde
Copy link
Collaborator Author

pcnudde commented Mar 10, 2026

/build

@pcnudde pcnudde requested a review from YuanTingHsieh March 10, 2026 22:55
@pcnudde pcnudde merged commit aaf5b13 into NVIDIA:main Mar 11, 2026
28 checks passed
@pcnudde pcnudde deleted the codex/flare-2740-python-support branch March 11, 2026 02:33
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.

3 participants