From f075cae22515a9b44054c3fd11e4465e681a8719 Mon Sep 17 00:00:00 2001 From: esubaalew Date: Thu, 26 Mar 2026 11:47:52 +0300 Subject: [PATCH] Fix multipart version comparison for newer releases Use numeric version parsing instead of string comparison so valid python-multipart versions like 0.0.100 are not treated as older than 0.0.12. Add regression tests for the multipart check and the version parser helper. --- fastapi/dependencies/utils.py | 16 +++++++++++++++- tests/test_multipart_installation.py | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index 6b14dac8dc55e..915e0842c44e5 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -91,12 +91,26 @@ ) +def _version_str_to_tuple(version: str) -> tuple[int, ...]: + version_tuple = [] + for part in version.split("."): + numeric_prefix = "" + for char in part: + if not char.isdigit(): + break + numeric_prefix += char + if not numeric_prefix: + break + version_tuple.append(int(numeric_prefix)) + return tuple(version_tuple) + + def ensure_multipart_is_installed() -> None: try: from python_multipart import __version__ # Import an attribute that can be mocked/deleted in testing - assert __version__ > "0.0.12" + assert _version_str_to_tuple(__version__) > (0, 0, 12) except (ImportError, AssertionError): try: # __version__ is available in both multiparts, and can be mocked diff --git a/tests/test_multipart_installation.py b/tests/test_multipart_installation.py index 9c3e47c495990..585e673112446 100644 --- a/tests/test_multipart_installation.py +++ b/tests/test_multipart_installation.py @@ -3,6 +3,7 @@ import pytest from fastapi import FastAPI, File, Form, UploadFile from fastapi.dependencies.utils import ( + _version_str_to_tuple, multipart_incorrect_install_error, multipart_not_installed_error, ) @@ -147,3 +148,21 @@ def test_old_multipart_installed(monkeypatch): @app.post("/") async def root(username: str = Form()): return username # pragma: nocover + + +def test_new_multipart_version_without_multipart_alias(monkeypatch): + monkeypatch.setattr("python_multipart.__version__", "0.0.100") + with warnings.catch_warnings(record=True): + warnings.simplefilter("always") + app = FastAPI() + + @app.post("/") + async def root(username: str = Form()): + return username # pragma: nocover + + +def test_version_str_to_tuple(): + assert _version_str_to_tuple("0.0.12") == (0, 0, 12) + assert _version_str_to_tuple("0.0.100") == (0, 0, 100) + assert _version_str_to_tuple("1.2.3a1") == (1, 2, 3) + assert _version_str_to_tuple("") == ()