Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions docs/community/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,52 @@ Once you've made a pull request take a look at the build status in the GitHub in

Sometimes, in order to ensure your code works on various different versions of Django, Python or third party libraries, you'll need to run slightly different code depending on the environment. Any code that branches in this way should be isolated into the `compat.py` module, and should provide a single common interface that the rest of the codebase can use.

### Deprecating a feature

REST framework follows a formal [deprecation policy][deprecation-policy]: a feature deprecated during the development of `3.X` keeps working until `3.X+2` removes it. In practice this means that a pull request that deprecates something should not change any behavior, only warn about the change to come.

The warning classes live in `rest_framework/deprecation.py`, and are named after the release that removes the feature. For example, during the development of 3.0 you'll find:

* `RemovedInDRF31Warning`, a `DeprecationWarning`, aliased as `RemovedInNextDRFVersionWarning`. Features flagged with it were deprecated one cycle ago, and are removed in the next release.
* `RemovedInDRF32Warning`, a `PendingDeprecationWarning`, aliased as `RemovedAfterNextDRFVersionWarning`. This is the class **new deprecations** should use.

To deprecate a feature:

1. Keep the existing behavior working, and raise the pending deprecation warning from the deprecated code path. Refer to the warning class by its concrete name rather than the alias, so that it's easy to grep for everything that has to go when the removal comes around.

```python
import warnings

from rest_framework.deprecation import RemovedInDRF32Warning

warnings.warn(
"The `foo` argument is deprecated and will be removed in DRF 3.2. "
"Use `bar` instead.",
RemovedInDRF32Warning,
stacklevel=2,
)
```

The message should say what is deprecated, which release removes it, and what to use instead. Pick a `stacklevel` that points the warning at the user's code rather than at REST framework internals.

2. If the deprecation can be worked around ahead of time, consider adding a setting that opts into the new behavior early, and mention it in the warning message.

3. Add a test asserting that the warning is raised, and update the existing tests of the old behavior to expect it:

```python
with pytest.warns(RemovedInDRF32Warning, match="Use `bar` instead"):
...
```

4. Update the documentation for the feature, flagging the deprecation with an admonition:

```
!!! warning
The `foo` argument is deprecated and will be removed in DRF 3.2. Use `bar` instead.
Comment thread
browniebroke marked this conversation as resolved.
```

Removing a deprecated feature is the mirror image, and happens as part of the release process. When 3.1 is released, everything raising a `RemovedInDRF31Warning` is deleted, along with the class itself and the documentation of the deprecated behavior, `RemovedInDRF32Warning` is escalated to subclass `DeprecationWarning`, a fresh `RemovedInDRF33Warning` is added for the next cycle's deprecations, and the two aliases are moved on to point at them.

## Documentation

The documentation for REST framework is built from the [Markdown][markdown] source files in [the docs directory][docs].
Expand Down Expand Up @@ -244,3 +290,4 @@ The documentation theme styles `info`, `warning`, `tip` and `danger` admonition
[repo]: https://github.com/encode/django-rest-framework
[how-to-fork]: https://help.github.com/articles/fork-a-repo/
[admonition]: https://python-markdown.github.io/extensions/admonition/
[deprecation-policy]: release-notes.md#deprecation-policy
3 changes: 2 additions & 1 deletion docs/community/project-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ Our PyPI releases is automated in GitHub actions on tag pushes. The following te
- `pyproject.toml` Python & Django version trove classifiers
- `README` Python & Django versions
- `docs` Python & Django versions
- Ensure the pull request increments the version to `*.*.*` in [`restframework/__init__.py`](https://github.com/encode/django-rest-framework/blob/main/rest_framework/__init__.py).
- Ensure the pull request increments the version to `*.*.*` in [`rest_framework/__init__.py`](https://github.com/encode/django-rest-framework/blob/main/rest_framework/__init__.py).
- Ensure the deprecation warnings in [`rest_framework/deprecation.py`](https://github.com/encode/django-rest-framework/blob/main/rest_framework/deprecation.py) have been rotated for the release, following the [deprecation policy](release-notes.md#deprecation-policy).
- Ensure documentation validates
- Build and serve docs `mkdocs serve`
- Validate links `pylinkvalidate.py -P http://127.0.0.1:8000`
Expand Down
21 changes: 21 additions & 0 deletions docs/community/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,29 @@ The timeline for deprecation of a feature present in version 1.0 would work as f

* Version 1.3 would remove the deprecated bits of API entirely.

Two aliases always point at the warning classes for the current release cycle, so you can refer to them without updating your configuration on every release:

* `RemovedInNextDRFVersionWarning` — features that the next feature release removes. Always a `DeprecationWarning`.
* `RemovedAfterNextDRFVersionWarning` — features that the release after next removes. Always a `PendingDeprecationWarning`.

Note that in line with Django's policy, any parts of the framework not mentioned in the documentation should generally be considered private API, and may be subject to change.

### Surfacing deprecations in your test suite

Deprecations are much easier to deal with one release at a time, so it's worth failing your test suite on anything that is due to be removed in the next release, while keeping the earlier warnings visible.

If you run your tests with pytest, add the following to your `pyproject.toml`:

```toml
[tool.pytest]
filterwarnings = [
"error::rest_framework.deprecation.RemovedInNextDRFVersionWarning",
"always::rest_framework.deprecation.RemovedAfterNextDRFVersionWarning",
]
```

When a warning does fire, the message names the release that removes the feature and what to use instead. Once you have dealt with them all, drop the `always` filter down to `error` too, and you'll be ready for the release after next as well.

## Upgrading

To upgrade Django REST framework to the latest version, use pip:
Expand Down
25 changes: 25 additions & 0 deletions rest_framework/deprecation.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,27 @@
"""
Warning classes used to flag features that are scheduled for removal.

Each `RemovedInDRF3XXWarning` class is named after the release that removes
the feature it flags, so `RemovedInDRF320Warning` marks an API that will stop
working in DRF 3.20.

New deprecations target the release after next, and therefore start out as a
`PendingDeprecationWarning`, which is silent by default. One release later,
they are escalated to a `DeprecationWarning`, before being removed in the
following release. See the deprecation policy for the full timeline:
https://www.django-rest-framework.org/community/release-notes/#deprecation-policy
"""


class RemovedInDRF319Warning(DeprecationWarning):
pass


class RemovedInDRF320Warning(PendingDeprecationWarning):
pass


# Aliases that always track the current release cycle, so that projects can
# filter on them without editing their configuration on every DRF release.
RemovedInNextDRFVersionWarning = RemovedInDRF319Warning
RemovedAfterNextDRFVersionWarning = RemovedInDRF320Warning