From 89ffbec3a8ce1a56cd737246be44082e7095a004 Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Sun, 11 Sep 2022 16:55:43 +0200 Subject: [PATCH 1/2] Add deprecation policy --- docs/deprecation.md | 60 +++++++++++++++++++++++++++++++++++++++++++++ mkdocs.yml | 3 ++- 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 docs/deprecation.md diff --git a/docs/deprecation.md b/docs/deprecation.md new file mode 100644 index 000000000..c6c2de7c9 --- /dev/null +++ b/docs/deprecation.md @@ -0,0 +1,60 @@ +# Deprecation Policy + +The goal of this policy is to reduce the impact of changes on users and developers of the project by providing +clear guidelines and a well-defined process for deprecating functionalities. This policy applies to both features +and API interfaces. + +This policy describes what Starlette should follow pre-1.0, and what Starlette will follow post-1.0. + +## Deprecation Types + +We'll consider two kinds of deprecations: **Python version** deprecations and **feature** deprecations. + +### Python Version Deprecation + +Starlette will aim to support a Python version until the [EOL date of that version](https://endoflife.date/python). +When a Python version reaches EOL, Starlette will drop support for that version in the next minor release. + +!!! warning + Pre-1.0 Starlette will also drop support in the next minor release. + +The drop of Python version support will be documented in the release notes, but the user will **not** be warned it. + +### Feature Deprecation + +Starlette will deprecate a feature in the next minor release after the feature is marked as deprecated. + +The deprecation of a feature needs to be followed by a warning message using `warnings.warn` in the code that +uses the deprecated feature. The warning message should include the version in which the feature will be removed. + +The format of the message should follow: + +> *`code` is deprecated and will be removed in version `version`.* + +The `code` can be a *function*, *module* or *feature* name, and the `version` should be the next major release. + +The deprecation warning may include an advice on how to replace the deprecated feature. + +> *Use `alternative` instead.* + +As a full example, imagine we are in version 1.0.0, and we want to deprecate the `potato` function. +We would add the follow warning: + +```python +def potato(): + warnings.warn( + "potato is deprecated and will be removed in version 2.0.0. " + "Use banana instead.", + DeprecationWarning, + ) + +def banana(): + ... +``` + +!!! warning + Pre-1.0 Starlette will consider two minor releases instead of the next major release. + + If a feature is deprecated in version 0.1.0, it will be removed in version 0.3.0. + +The drop of a feature will be documented in the release notes, and the user will be warned about it. diff --git a/mkdocs.yml b/mkdocs.yml index 6b1d6cc46..f73fe0b16 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -45,6 +45,7 @@ nav: - Test Client: 'testclient.md' - Third Party Packages: 'third-party-packages.md' - Contributing: 'contributing.md' + - Deprecation Policy: 'deprecation.md' - Release Notes: 'release-notes.md' markdown_extensions: @@ -53,7 +54,7 @@ markdown_extensions: - pymdownx.highlight - pymdownx.superfences - pymdownx.tabbed: - alternate_style: true + alternate_style: true extra_javascript: - 'js/chat.js' From 92549102bd3545e171da6d32a21df0acdcaff5e6 Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Sat, 24 Sep 2022 08:40:06 +0200 Subject: [PATCH 2/2] Apply policy on code source --- docs/deprecation.md | 14 ++++++++++---- setup.cfg | 6 +++--- starlette/concurrency.py | 3 +-- starlette/exceptions.py | 3 ++- starlette/middleware/wsgi.py | 2 +- starlette/routing.py | 12 +++++++----- starlette/status.py | 3 ++- starlette/testclient.py | 9 +++++---- tests/test_applications.py | 5 +++-- tests/test_status.py | 6 ++++-- 10 files changed, 38 insertions(+), 25 deletions(-) diff --git a/docs/deprecation.md b/docs/deprecation.md index c6c2de7c9..c78469270 100644 --- a/docs/deprecation.md +++ b/docs/deprecation.md @@ -6,9 +6,17 @@ and API interfaces. This policy describes what Starlette should follow pre-1.0, and what Starlette will follow post-1.0. +## Starlette Versions + +Starlette follows [Semantic Versioning](https://semver.org/), with some additional constraints. +While we are pre-1.0, we treat any SemVer changes requiring a: + +- **major** bump as a change requiring a **minor** bump. +- **minor** bump as a change requiring a **patch** bump. + ## Deprecation Types -We'll consider two kinds of deprecations: **Python version** deprecations and **feature** deprecations. +We'll consider two kinds of deprecations: **Python version** and **feature** deprecations. ### Python Version Deprecation @@ -53,8 +61,6 @@ def banana(): ``` !!! warning - Pre-1.0 Starlette will consider two minor releases instead of the next major release. - - If a feature is deprecated in version 0.1.0, it will be removed in version 0.3.0. + Pre-1.0 Starlette will remove deprecated code in the stable release (1.0). The drop of a feature will be documented in the release notes, and the user will be warned about it. diff --git a/setup.cfg b/setup.cfg index 23cf32cc0..4c9156317 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,12 +28,12 @@ xfail_strict=True filterwarnings= # Turn warnings that aren't filtered into exceptions error - ignore: run_until_first_complete is deprecated and will be removed in a future version.:DeprecationWarning - ignore: starlette\.middleware\.wsgi is deprecated and will be removed in a future release\.*:DeprecationWarning + ignore: run_until_first_complete is deprecated, and will be removed in version 1\.0\.0\.:DeprecationWarning + ignore: starlette\.middleware\.wsgi is deprecated, and will be removed in version 1\.0\.0.*:DeprecationWarning ignore: Async generator 'starlette\.requests\.Request\.stream' was garbage collected before it had been exhausted.*:ResourceWarning ignore: path is deprecated.*:DeprecationWarning:certifi ignore: Use 'content=<...>' to upload raw bytes/text content.:DeprecationWarning - ignore: The `allow_redirects` argument is deprecated. Use `follow_redirects` instead.:DeprecationWarning + ignore: `allow_redirects` argument is deprecated, and will be removed in version 1\.0\.0\. Use `follow_redirects` instead.:DeprecationWarning ignore: 'cgi' is deprecated and slated for removal in Python 3\.13:DeprecationWarning [coverage:run] diff --git a/starlette/concurrency.py b/starlette/concurrency.py index 5c76cb3df..9f7ff7469 100644 --- a/starlette/concurrency.py +++ b/starlette/concurrency.py @@ -17,8 +17,7 @@ async def run_until_first_complete(*args: typing.Tuple[typing.Callable, dict]) -> None: warnings.warn( - "run_until_first_complete is deprecated " - "and will be removed in a future version.", + "run_until_first_complete is deprecated, and will be removed in version 1.0.0.", DeprecationWarning, ) diff --git a/starlette/exceptions.py b/starlette/exceptions.py index 87da73591..fbc15dfb4 100644 --- a/starlette/exceptions.py +++ b/starlette/exceptions.py @@ -41,7 +41,8 @@ def __getattr__(name: str) -> typing.Any: # pragma: no cover from starlette.middleware.exceptions import ExceptionMiddleware warnings.warn( - f"{__deprecated__} is deprecated on `starlette.exceptions`. " + f"{__deprecated__} is deprecated on `starlette.exceptions`, " + "and will be removed in version 1.0.0. " f"Import it from `starlette.middleware.exceptions` instead.", category=DeprecationWarning, stacklevel=3, diff --git a/starlette/middleware/wsgi.py b/starlette/middleware/wsgi.py index 9dbd06528..800240657 100644 --- a/starlette/middleware/wsgi.py +++ b/starlette/middleware/wsgi.py @@ -9,7 +9,7 @@ from starlette.types import Receive, Scope, Send warnings.warn( - "starlette.middleware.wsgi is deprecated and will be removed in a future release. " + "starlette.middleware.wsgi is deprecated, and will be removed in version 1.0.0. " "Please refer to https://github.com/abersheeran/a2wsgi as a replacement.", DeprecationWarning, ) diff --git a/starlette/routing.py b/starlette/routing.py index 479d4ae65..a1955d5d3 100644 --- a/starlette/routing.py +++ b/starlette/routing.py @@ -45,7 +45,7 @@ def iscoroutinefunction_or_partial(obj: typing.Any) -> bool: # pragma: no cover """ warnings.warn( "iscoroutinefunction_or_partial is deprecated, " - "and will be removed in a future release.", + "and will be removed in version 1.0.0.", DeprecationWarning, ) while isinstance(obj, functools.partial): @@ -597,8 +597,9 @@ def __init__( elif inspect.isasyncgenfunction(lifespan): warnings.warn( - "async generator function lifespans are deprecated, " - "use an @contextlib.asynccontextmanager function instead", + "Usage of async generator function lifespans is deprecated, " + "and will be removed in version 1.0.0. " + "Use an @contextlib.asynccontextmanager function instead.", DeprecationWarning, ) self.lifespan_context = asynccontextmanager( @@ -606,8 +607,9 @@ def __init__( ) elif inspect.isgeneratorfunction(lifespan): warnings.warn( - "generator function lifespans are deprecated, " - "use an @contextlib.asynccontextmanager function instead", + "Usage of generator function lifespans is deprecated, " + "and will be removed in version 1.0.0. " + "Use an @contextlib.asynccontextmanager function instead.", DeprecationWarning, ) self.lifespan_context = _wrap_gen_lifespan_context( diff --git a/starlette/status.py b/starlette/status.py index 1689328a4..c08a3264e 100644 --- a/starlette/status.py +++ b/starlette/status.py @@ -187,7 +187,8 @@ def __getattr__(name: str) -> int: deprecated = __deprecated__.get(name) if deprecated: warnings.warn( - f"'{name}' is deprecated. Use '{deprecation_changes[name]}' instead.", + f"'{name}' is deprecated, and will be removed in version 1.0.0. " + f"Use '{deprecation_changes[name]}' instead.", category=DeprecationWarning, stacklevel=3, ) diff --git a/starlette/testclient.py b/starlette/testclient.py index 455440ce5..86c50bb34 100644 --- a/starlette/testclient.py +++ b/starlette/testclient.py @@ -407,11 +407,12 @@ def _choose_redirect_arg( bool, httpx._client.UseClientDefault ] = httpx._client.USE_CLIENT_DEFAULT if allow_redirects is not None: - message = ( - "The `allow_redirects` argument is deprecated. " - "Use `follow_redirects` instead." + warnings.warn( + "`allow_redirects` argument is deprecated, " + "and will be removed in version 1.0.0. " + "Use `follow_redirects` instead.", + DeprecationWarning, ) - warnings.warn(message, DeprecationWarning) redirect = allow_redirects if follow_redirects is not None: redirect = follow_redirects diff --git a/tests/test_applications.py b/tests/test_applications.py index 2cee601b0..0ebb1c375 100644 --- a/tests/test_applications.py +++ b/tests/test_applications.py @@ -380,8 +380,9 @@ async def lifespan(app): deprecated_lifespan = pytest.mark.filterwarnings( r"ignore" - r":(async )?generator function lifespans are deprecated, use an " - r"@contextlib\.asynccontextmanager function instead" + r":Usage of (async )?generator function lifespans is deprecated, " + r"and will be removed in version 1.0.0. " + r"Use an @contextlib\.asynccontextmanager function instead." r":DeprecationWarning" r":starlette.routing" ) diff --git a/tests/test_status.py b/tests/test_status.py index 04719e87e..4b1930dcd 100644 --- a/tests/test_status.py +++ b/tests/test_status.py @@ -8,12 +8,14 @@ ( ( "WS_1004_NO_STATUS_RCVD", - "'WS_1004_NO_STATUS_RCVD' is deprecated. " + "'WS_1004_NO_STATUS_RCVD' is deprecated, " + "and will be removed in version 1.0.0. " "Use 'WS_1005_NO_STATUS_RCVD' instead.", ), ( "WS_1005_ABNORMAL_CLOSURE", - "'WS_1005_ABNORMAL_CLOSURE' is deprecated. " + "'WS_1005_ABNORMAL_CLOSURE' is deprecated, " + "and will be removed in version 1.0.0. " "Use 'WS_1006_ABNORMAL_CLOSURE' instead.", ), ),