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_dependencies_utils.py b/tests/test_dependencies_utils.py index 9257d1c9ee6cc..92d735bb6285b 100644 --- a/tests/test_dependencies_utils.py +++ b/tests/test_dependencies_utils.py @@ -1,4 +1,11 @@ -from fastapi.dependencies.utils import get_typed_annotation +from fastapi.dependencies.utils import _version_str_to_tuple, get_typed_annotation + + +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("") == () def test_get_typed_annotation(): diff --git a/tests/test_multipart_installation.py b/tests/test_multipart_installation.py index 9c3e47c495990..4bf1a67a936ff 100644 --- a/tests/test_multipart_installation.py +++ b/tests/test_multipart_installation.py @@ -147,3 +147,15 @@ 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") + monkeypatch.delattr("multipart.__version__", raising=False) + app = FastAPI() + + @app.post("/") + async def root(username: str = Form()): + return username # pragma: nocover