From a25c92ceb9ab5be49901443d7184c149797192df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 2 Aug 2024 01:03:05 -0500 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Add=20support=20for=20`pip?= =?UTF-8?q?=20install=20"fastapi[standard]"`=20with=20standard=20dependenc?= =?UTF-8?q?ies=20and=20`python=20-m=20fastapi`=20(#11935)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ♻️ Add support for `pip install "fastapi[standard]"` and make `fastapi` not include the optional standard dependencies * 📝 Update docs to include new fastapi[standard] * ✨ Add new stub fastapi command that tells people to install fastapi[standard] * ✅ Add tests for new stub CLI * 🔧 Add new command fastapi in main fastapi project, for when fastapi-cli is not installed * ✏️ Fix types * 📝 Add note about quotes when installing fastapi[standard] * 📝 Update docs about standard extra dependencies * ⬆️ Upgrade fastapi-cli --- README.md | 41 +++++++++---------- docs/em/docs/index.md | 2 +- docs/em/docs/tutorial/index.md | 2 +- docs/en/docs/deployment/docker.md | 2 +- docs/en/docs/deployment/manually.md | 2 +- docs/en/docs/deployment/versions.md | 14 +++---- docs/en/docs/fastapi-cli.md | 2 +- docs/en/docs/index.md | 41 +++++++++---------- docs/en/docs/tutorial/index.md | 6 +-- docs/en/docs/tutorial/security/first-steps.md | 4 +- fastapi/__main__.py | 3 ++ fastapi/cli.py | 13 ++++++ pdm_build.py | 27 ++---------- pyproject.toml | 36 +++------------- tests/test_fastapi_cli.py | 32 +++++++++++++++ 15 files changed, 114 insertions(+), 113 deletions(-) create mode 100644 fastapi/__main__.py create mode 100644 fastapi/cli.py create mode 100644 tests/test_fastapi_cli.py diff --git a/README.md b/README.md index 739e77627..aa70ff2da 100644 --- a/README.md +++ b/README.md @@ -135,13 +135,15 @@ FastAPI stands on the shoulders of giants:
```console -$ pip install fastapi +$ pip install "fastapi[standard]" ---> 100% ```
+**Note**: Make sure you put `"fastapi[standard]"` in quotes to ensure it works in all terminals. + ## Example ### Create it @@ -452,11 +454,15 @@ To understand more about it, see the section email_validator - for email validation. -* pydantic-settings - for settings management. -* pydantic-extra-types - for extra types to be used with Pydantic. Used by Starlette: @@ -466,33 +472,26 @@ Used by Starlette: Used by FastAPI / Starlette: -* uvicorn - for the server that loads and serves your application. +* uvicorn - for the server that loads and serves your application. This includes `uvicorn[standard]`, which includes some dependencies (e.g. `uvloop`) needed for high performance serving. * `fastapi-cli` - to provide the `fastapi` command. -When you install `fastapi` it comes these standard dependencies. - -Additional optional dependencies: - -* orjson - Required if you want to use `ORJSONResponse`. -* ujson - Required if you want to use `UJSONResponse`. +### Without `standard` Dependencies -## `fastapi-slim` +If you don't want to include the `standard` optional dependencies, you can install with `pip install fastapi` instead of `pip install "fastapi[standard]"`. -If you don't want the extra standard optional dependencies, install `fastapi-slim` instead. +### Additional Optional Dependencies -When you install with: +There are some additional dependencies you might want to install. -```bash -pip install fastapi -``` +Additional optional Pydantic dependencies: -...it includes the same code and dependencies as: +* pydantic-settings - for settings management. +* pydantic-extra-types - for extra types to be used with Pydantic. -```bash -pip install "fastapi-slim[standard]" -``` +Additional optional FastAPI dependencies: -The standard extra dependencies are the ones mentioned above. +* orjson - Required if you want to use `ORJSONResponse`. +* ujson - Required if you want to use `UJSONResponse`. ## License diff --git a/docs/em/docs/index.md b/docs/em/docs/index.md index a7d1d06f9..dc8c4f023 100644 --- a/docs/em/docs/index.md +++ b/docs/em/docs/index.md @@ -133,7 +133,7 @@ FastAPI 🧍 🔛 ⌚ 🐘:
```console -$ pip install fastapi +$ pip install "fastapi[standard]" ---> 100% ``` diff --git a/docs/em/docs/tutorial/index.md b/docs/em/docs/tutorial/index.md index 26b4c1913..fd6900db0 100644 --- a/docs/em/docs/tutorial/index.md +++ b/docs/em/docs/tutorial/index.md @@ -58,7 +58,7 @@ $ pip install "fastapi[all]" 👉 ⚫️❔ 👆 🔜 🎲 🕐 👆 💚 🛠️ 👆 🈸 🏭: ``` - pip install fastapi + pip install "fastapi[standard]" ``` ❎ `uvicorn` 👷 💽: diff --git a/docs/en/docs/deployment/docker.md b/docs/en/docs/deployment/docker.md index 5cd24eb46..157a3c003 100644 --- a/docs/en/docs/deployment/docker.md +++ b/docs/en/docs/deployment/docker.md @@ -113,7 +113,7 @@ You would of course use the same ideas you read in [About FastAPI versions](vers For example, your `requirements.txt` could look like: ``` -fastapi>=0.112.0,<0.113.0 +fastapi[standard]>=0.113.0,<0.114.0 pydantic>=2.7.0,<3.0.0 ``` diff --git a/docs/en/docs/deployment/manually.md b/docs/en/docs/deployment/manually.md index 51989d819..ad9f62f91 100644 --- a/docs/en/docs/deployment/manually.md +++ b/docs/en/docs/deployment/manually.md @@ -103,7 +103,7 @@ But you can also install an ASGI server manually: That including `uvloop`, the high-performance drop-in replacement for `asyncio`, that provides the big concurrency performance boost. - When you install FastAPI with something like `pip install fastapi` you already get `uvicorn[standard]` as well. + When you install FastAPI with something like `pip install "fastapi[standard]"` you already get `uvicorn[standard]` as well. === "Hypercorn" diff --git a/docs/en/docs/deployment/versions.md b/docs/en/docs/deployment/versions.md index 4be9385dd..24430b0cf 100644 --- a/docs/en/docs/deployment/versions.md +++ b/docs/en/docs/deployment/versions.md @@ -12,23 +12,23 @@ You can create production applications with **FastAPI** right now (and you have The first thing you should do is to "pin" the version of **FastAPI** you are using to the specific latest version that you know works correctly for your application. -For example, let's say you are using version `0.45.0` in your app. +For example, let's say you are using version `0.112.0` in your app. If you use a `requirements.txt` file you could specify the version with: ```txt -fastapi==0.45.0 +fastapi[standard]==0.112.0 ``` -that would mean that you would use exactly the version `0.45.0`. +that would mean that you would use exactly the version `0.112.0`. Or you could also pin it with: ```txt -fastapi>=0.45.0,<0.46.0 +fastapi[standard]>=0.112.0,<0.113.0 ``` -that would mean that you would use the versions `0.45.0` or above, but less than `0.46.0`, for example, a version `0.45.2` would still be accepted. +that would mean that you would use the versions `0.112.0` or above, but less than `0.113.0`, for example, a version `0.112.2` would still be accepted. If you use any other tool to manage your installations, like Poetry, Pipenv, or others, they all have a way that you can use to define specific versions for your packages. @@ -78,10 +78,10 @@ So, you can just let **FastAPI** use the correct Starlette version. Pydantic includes the tests for **FastAPI** with its own tests, so new versions of Pydantic (above `1.0.0`) are always compatible with FastAPI. -You can pin Pydantic to any version above `1.0.0` that works for you and below `2.0.0`. +You can pin Pydantic to any version above `1.0.0` that works for you. For example: ```txt -pydantic>=1.2.0,<2.0.0 +pydantic>=2.7.0,<3.0.0 ``` diff --git a/docs/en/docs/fastapi-cli.md b/docs/en/docs/fastapi-cli.md index a6facde3a..0fc072ed2 100644 --- a/docs/en/docs/fastapi-cli.md +++ b/docs/en/docs/fastapi-cli.md @@ -2,7 +2,7 @@ **FastAPI CLI** is a command line program that you can use to serve your FastAPI app, manage your FastAPI project, and more. -When you install FastAPI (e.g. with `pip install fastapi`), it includes a package called `fastapi-cli`, this package provides the `fastapi` command in the terminal. +When you install FastAPI (e.g. with `pip install "fastapi[standard]"`), it includes a package called `fastapi-cli`, this package provides the `fastapi` command in the terminal. To run your FastAPI app for development, you can use the `fastapi dev` command: diff --git a/docs/en/docs/index.md b/docs/en/docs/index.md index 73357542f..4fe0cd6cc 100644 --- a/docs/en/docs/index.md +++ b/docs/en/docs/index.md @@ -131,13 +131,15 @@ FastAPI stands on the shoulders of giants:
```console -$ pip install fastapi +$ pip install "fastapi[standard]" ---> 100% ```
+**Note**: Make sure you put `"fastapi[standard]"` in quotes to ensure it works in all terminals. + ## Example ### Create it @@ -448,11 +450,15 @@ To understand more about it, see the section email_validator - for email validation. -* pydantic-settings - for settings management. -* pydantic-extra-types - for extra types to be used with Pydantic. Used by Starlette: @@ -462,33 +468,26 @@ Used by Starlette: Used by FastAPI / Starlette: -* uvicorn - for the server that loads and serves your application. +* uvicorn - for the server that loads and serves your application. This includes `uvicorn[standard]`, which includes some dependencies (e.g. `uvloop`) needed for high performance serving. * `fastapi-cli` - to provide the `fastapi` command. -When you install `fastapi` it comes these standard dependencies. - -Additional optional dependencies: - -* orjson - Required if you want to use `ORJSONResponse`. -* ujson - Required if you want to use `UJSONResponse`. +### Without `standard` Dependencies -## `fastapi-slim` +If you don't want to include the `standard` optional dependencies, you can install with `pip install fastapi` instead of `pip install "fastapi[standard]"`. -If you don't want the extra standard optional dependencies, install `fastapi-slim` instead. +### Additional Optional Dependencies -When you install with: +There are some additional dependencies you might want to install. -```bash -pip install fastapi -``` +Additional optional Pydantic dependencies: -...it includes the same code and dependencies as: +* pydantic-settings - for settings management. +* pydantic-extra-types - for extra types to be used with Pydantic. -```bash -pip install "fastapi-slim[standard]" -``` +Additional optional FastAPI dependencies: -The standard extra dependencies are the ones mentioned above. +* orjson - Required if you want to use `ORJSONResponse`. +* ujson - Required if you want to use `UJSONResponse`. ## License diff --git a/docs/en/docs/tutorial/index.md b/docs/en/docs/tutorial/index.md index 74fe06acd..f22dc01dd 100644 --- a/docs/en/docs/tutorial/index.md +++ b/docs/en/docs/tutorial/index.md @@ -76,7 +76,7 @@ The first step is to install FastAPI:
```console -$ pip install fastapi +$ pip install "fastapi[standard]" ---> 100% ``` @@ -84,9 +84,9 @@ $ pip install fastapi
!!! note - When you install with `pip install fastapi` it comes with some default optional standard dependencies. + When you install with `pip install "fastapi[standard]"` it comes with some default optional standard dependencies. - If you don't want to have those optional dependencies, you can instead install `pip install fastapi-slim`. + If you don't want to have those optional dependencies, you can instead install `pip install fastapi`. ## Advanced User Guide diff --git a/docs/en/docs/tutorial/security/first-steps.md b/docs/en/docs/tutorial/security/first-steps.md index a769cc0e6..d8682a054 100644 --- a/docs/en/docs/tutorial/security/first-steps.md +++ b/docs/en/docs/tutorial/security/first-steps.md @@ -45,9 +45,9 @@ Copy the example in a file `main.py`: ## Run it !!! info - The `python-multipart` package is automatically installed with **FastAPI** when you run the `pip install fastapi` command. + The `python-multipart` package is automatically installed with **FastAPI** when you run the `pip install "fastapi[standard]"` command. - However, if you use the `pip install fastapi-slim` command, the `python-multipart` package is not included by default. To install it manually, use the following command: + However, if you use the `pip install fastapi` command, the `python-multipart` package is not included by default. To install it manually, use the following command: `pip install python-multipart` diff --git a/fastapi/__main__.py b/fastapi/__main__.py new file mode 100644 index 000000000..fc36465f5 --- /dev/null +++ b/fastapi/__main__.py @@ -0,0 +1,3 @@ +from fastapi.cli import main + +main() diff --git a/fastapi/cli.py b/fastapi/cli.py new file mode 100644 index 000000000..8d3301e9d --- /dev/null +++ b/fastapi/cli.py @@ -0,0 +1,13 @@ +try: + from fastapi_cli.cli import main as cli_main + +except ImportError: # pragma: no cover + cli_main = None # type: ignore + + +def main() -> None: + if not cli_main: # type: ignore[truthy-function] + message = 'To use the fastapi command, please install "fastapi[standard]":\n\n\tpip install "fastapi[standard]"\n' + print(message) + raise RuntimeError(message) # noqa: B904 + cli_main() diff --git a/pdm_build.py b/pdm_build.py index 45922d471..c83222b33 100644 --- a/pdm_build.py +++ b/pdm_build.py @@ -1,5 +1,5 @@ import os -from typing import Any, Dict, List +from typing import Any, Dict from pdm.backend.hooks import Context @@ -11,29 +11,10 @@ def pdm_build_initialize(context: Context) -> None: # Get custom config for the current package, from the env var config: Dict[str, Any] = context.config.data["tool"]["tiangolo"][ "_internal-slim-build" - ]["packages"][TIANGOLO_BUILD_PACKAGE] + ]["packages"].get(TIANGOLO_BUILD_PACKAGE) + if not config: + return project_config: Dict[str, Any] = config["project"] - # Get main optional dependencies, extras - optional_dependencies: Dict[str, List[str]] = metadata.get( - "optional-dependencies", {} - ) - # Get custom optional dependencies name to always include in this (non-slim) package - include_optional_dependencies: List[str] = config.get( - "include-optional-dependencies", [] - ) # Override main [project] configs with custom configs for this package for key, value in project_config.items(): metadata[key] = value - # Get custom build config for the current package - build_config: Dict[str, Any] = ( - config.get("tool", {}).get("pdm", {}).get("build", {}) - ) - # Override PDM build config with custom build config for this package - for key, value in build_config.items(): - context.config.build_config[key] = value - # Get main dependencies - dependencies: List[str] = metadata.get("dependencies", []) - # Add optional dependencies to the default dependencies for this (non-slim) package - for include_optional in include_optional_dependencies: - optional_dependencies_group = optional_dependencies.get(include_optional, []) - dependencies.extend(optional_dependencies_group) diff --git a/pyproject.toml b/pyproject.toml index 9ab6e6d69..3601e6322 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ Repository = "https://github.com/fastapi/fastapi" [project.optional-dependencies] standard = [ - "fastapi-cli >=0.0.2", + "fastapi-cli[standard] >=0.0.5", # For the test client "httpx >=0.23.0", # For templates @@ -73,7 +73,7 @@ standard = [ ] all = [ - "fastapi-cli >=0.0.2", + "fastapi-cli[standard] >=0.0.5", # # For the test client "httpx >=0.23.0", # For templates @@ -98,6 +98,9 @@ all = [ "pydantic-extra-types >=2.0.0", ] +[project.scripts] +fastapi = "fastapi.cli:main" + [tool.pdm] version = { source = "file", path = "fastapi/__init__.py" } distribution = true @@ -115,35 +118,6 @@ source-includes = [ [tool.tiangolo._internal-slim-build.packages.fastapi-slim.project] name = "fastapi-slim" -[tool.tiangolo._internal-slim-build.packages.fastapi] -include-optional-dependencies = ["standard"] - -[tool.tiangolo._internal-slim-build.packages.fastapi.project.optional-dependencies] -all = [ - # # For the test client - "httpx >=0.23.0", - # For templates - "jinja2 >=2.11.2", - # For forms and file uploads - "python-multipart >=0.0.7", - # For Starlette's SessionMiddleware, not commonly used with FastAPI - "itsdangerous >=1.1.0", - # For Starlette's schema generation, would not be used with FastAPI - "pyyaml >=5.3.1", - # For UJSONResponse - "ujson >=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0", - # For ORJSONResponse - "orjson >=3.2.1", - # To validate email fields - "email_validator >=2.0.0", - # Uvicorn with uvloop - "uvicorn[standard] >=0.12.0", - # Settings management - "pydantic-settings >=2.0.0", - # Extra Pydantic data types - "pydantic-extra-types >=2.0.0", -] - [tool.mypy] strict = true diff --git a/tests/test_fastapi_cli.py b/tests/test_fastapi_cli.py new file mode 100644 index 000000000..20c928157 --- /dev/null +++ b/tests/test_fastapi_cli.py @@ -0,0 +1,32 @@ +import subprocess +import sys +from unittest.mock import patch + +import fastapi.cli +import pytest + + +def test_fastapi_cli(): + result = subprocess.run( + [ + sys.executable, + "-m", + "coverage", + "run", + "-m", + "fastapi", + "dev", + "non_existent_file.py", + ], + capture_output=True, + encoding="utf-8", + ) + assert result.returncode == 1, result.stdout + assert "Using path non_existent_file.py" in result.stdout + + +def test_fastapi_cli_not_installed(): + with patch.object(fastapi.cli, "cli_main", None): + with pytest.raises(RuntimeError) as exc_info: + fastapi.cli.main() + assert "To use the fastapi command, please install" in str(exc_info.value)