Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
973020c
add deprecation chapter
MrTango Feb 11, 2025
bd6eac9
fix broken links in deprecation chapter
MrTango Feb 11, 2025
ff7461b
Update docs/backend/deprecation.md
MrTango Feb 11, 2025
6027930
Update docs/backend/deprecation.md
MrTango Feb 11, 2025
e6acc83
Update docs/backend/deprecation.md
MrTango Feb 11, 2025
b909069
Update docs/backend/deprecation.md
MrTango Feb 11, 2025
b0923fb
Update docs/backend/deprecation.md
MrTango Feb 11, 2025
7ca4ede
Update docs/backend/deprecation.md
MrTango Feb 11, 2025
66a58dc
Update docs/backend/deprecation.md
MrTango Feb 11, 2025
c859367
Update docs/backend/deprecation.md
MrTango Feb 11, 2025
4e32566
Update docs/backend/deprecation.md
MrTango Feb 11, 2025
c7cad7a
Update docs/backend/deprecation.md
MrTango Feb 11, 2025
a9fc020
remove meta.zcml attribute from z3c.jbot include
MrTango Feb 11, 2025
151aa67
Update docs/backend/deprecation.md
MrTango Feb 11, 2025
cff3ad6
Fix ty
ale-rt Feb 11, 2025
d0286e4
Fix typo
ale-rt Feb 11, 2025
1c733a3
Fix typo
ale-rt Feb 11, 2025
49e96f8
Update docs/backend/deprecation.md
MrTango Feb 12, 2025
8608dff
Update docs/backend/deprecation.md
MrTango Feb 12, 2025
155c344
Update docs/backend/deprecation.md
MrTango Feb 12, 2025
295f3f5
Update docs/backend/deprecation.md
MrTango Feb 12, 2025
5db3027
Update docs/backend/deprecation.md
MrTango Feb 12, 2025
cd5e77e
Merge branch '6.0' into chapter-deprecation
MrTango Feb 12, 2025
0bacab8
Apply suggestions from code review
stevepiercy Feb 16, 2025
245f3b8
Merge branch '6.0' into chapter-deprecation
stevepiercy Feb 16, 2025
32a29d7
Merge branch '6.0' into chapter-deprecation
stevepiercy Feb 17, 2025
dab9e49
Merge branch '6.0' into chapter-deprecation
jensens Feb 17, 2025
1803a60
Merge branch '6.0' into chapter-deprecation
jensens Feb 18, 2025
ce2454c
Merge branch '6.0' into chapter-deprecation
ale-rt Feb 20, 2025
ad3b50e
Merge branch '6.0' into chapter-deprecation
MrTango Jul 11, 2025
09173ee
Merge branch '6.0' into chapter-deprecation
jensens Dec 4, 2025
46b14eb
split deprecation guide into two file according to diataxis principles
jensens Dec 4, 2025
ef52530
Update docs/developer-guide/deprecation.md
jensens Dec 5, 2025
25cb25b
Merge branch '6.0' into chapter-deprecation
stevepiercy Dec 9, 2025
9bedd0e
- Fix English grammar and syntax
stevepiercy Dec 9, 2025
78165e0
Refine some grammar
stevepiercy Dec 13, 2025
a1e40f4
- Add MyST syntax to link to Python objects, improve rendering
stevepiercy Dec 14, 2025
f0e9b8c
Merge branch '6.0' into chapter-deprecation
jensens Dec 15, 2025
6fa028f
Update docs/developer-guide/deprecation.md
stevepiercy Dec 15, 2025
0dd2608
Merge branch '6.0' into chapter-deprecation
stevepiercy Dec 15, 2025
3dd2949
Update docs/developer-guide/deprecation.md
jensens Dec 19, 2025
6a29889
Update docs/developer-guide/deprecation.md
jensens Dec 19, 2025
0dab30b
Update docs/developer-guide/deprecation.md
jensens Dec 19, 2025
b05efb9
Update docs/developer-guide/deprecation.md
jensens Dec 19, 2025
397b9d9
Merge branch '6.0' into chapter-deprecation
jensens Dec 19, 2025
a4959b6
Update docs/developer-guide/deprecation.md
jensens Dec 24, 2025
6b176b5
Update docs/developer-guide/deprecation.md
jensens Dec 24, 2025
cf28296
Update docs/developer-guide/deprecation.md
jensens Dec 24, 2025
416c5eb
Update docs/developer-guide/deprecation.md
jensens Dec 24, 2025
8f0b50f
Merge branch '6.0' into chapter-deprecation
jensens Dec 24, 2025
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
86 changes: 86 additions & 0 deletions docs/conceptual-guides/deprecation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
myst:
html_meta:
"description": "Understanding deprecation in Plone - rationale, philosophy, and use cases"
"property=og:description": "Understanding deprecation in Plone - rationale, philosophy, and use cases"
"property=og:title": "Deprecation"
"keywords": "deprecation, Plone, Python, Node.js, React, philosophy, rationale, use cases"
---

(conceptual-deprecation-label)=

# Deprecation

This chapter describes the rationale and philosophy of deprecations in Plone.
It is meant as a guide for how to think about deprecations in Plone core packages.

```{seealso}
For implementation details and code examples, see {doc}`/developer-guide/deprecation`.
```


(why-deprecation-label)=

## Why deprecation

Developers may need to get rid of old code, unify to a consistent API style, fix typos in names, move code or templates around, resolve technical debt, address security issues, or adapt to changes in external dependencies.

When refactoring code, it's often necessary to move modules, functions, classes, and methods.
It's critical not to break third party code imports from the old place.
It's also important that usage of old functions or methods must work for a while to allow developers to migrate or update their code.

Deprecated methods are usually removed with the next major release of Plone.
Plone follows the [semantic versioning guideline](https://semver.org).


## Help programmers without annoyance

Developers should use code deprecations to support the consumers of the code, that is, their fellow Plone developers.
From the consumer's point of view, Plone core code is an API.
Any change may annoy them, but they feel better when deprecation warnings tell them how to adapt their code to the changes.

Deprecations must always log at the level of warning.

Deprecations should always answer the following questions.

- Why is the code gone from the old place?
- What should the developer do instead?

A short message is enough, such as the following examples.

- "Replaced by new API `xyz`, found at `abc.cde`".
- "Moved to `xyz`, because of `abc`".
- "Name had a typo, new name is `xyz`".

All logging must be done only once, in other words, on the first usage or import.
It must not flood the logs.


## Use cases

The following use cases describe when to deprecate.

Rename
: Developers may want to rename classes, methods, functions, or global or class variables to get a more consistent API or because of a typo.
Renaming alone is not enough to deprecate code.
Always provide a deprecated version that logs a verbose deprecation warning with information for where to import from in the future.

Move objects
: For reasons described in {ref}`why-deprecation-label`, developers may need to move code around.
When imported from the old place, it logs a verbose deprecation warning with information of where to import from in the future.

Deprecation of a whole Python or npm package
: A whole {ref}`Python package <python:tut-packages>` or [npm package](https://www.npmjs.com/) may be moved to a new location.

- All imports still work.
- Log deprecation warnings on first import.
- The ZCML still exists, but is empty or includes the ZCML from the new place, if there's no auto import for `meta.zcml`.

Deprecation of a whole released or installable package
: Plone developers provide a major release with no "real" code, but only backward compatible imports of the public API.
This should be done the way described above for a whole package.
The README should clearly state why it was moved and where to find the code now.

Deprecation of a GenericSetup profile
: These may have been renamed for consistency or are superfluous after an update.
Code does not need to break to support this.
3 changes: 2 additions & 1 deletion docs/conceptual-guides/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ This part of the documentation provides explanation of concepts to deepen and br
:maxdepth: 2

choose-user-interface
compare-buildout-pip
distributions
compare-buildout-pip
deprecation
package-management
package-dependencies
make-backend-build
Expand Down
234 changes: 234 additions & 0 deletions docs/developer-guide/deprecation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
---
myst:
html_meta:
"description": "How to implement deprecations in Plone, including Python, ZCML and templates."
"property=og:description": "How to implement deprecations in Plone, including Python, ZCML and templates."
"property=og:title": "Implement deprecations"
"keywords": "deprecation, zcml, template, jbot, Plone, Python"
---

(developer-deprecation-label)=

# Deprecate code

This chapter describes how to enable deprecation warnings and best practices for deprecating code in Plone, Zope, and Python.

```{seealso}
For background on deprecation philosophy and use cases, see {doc}`/conceptual-guides/deprecation`.
```


## Enable deprecation warnings

This section describes how to enable deprecation warnings in Python, both in an interpreter and code, and when running tests.




(deprecation-warning-python-label)=

### Python

Enable warnings

: Warnings are written to `stderr` by default, but `DeprecationWarning` output is surpressed by default.

Output can be enabled by starting the Python interpreter with the {ref}`-W[all|module|once] <python:using-on-warnings>` argument.

As an alternative, the environment variable {envvar}`python:PYTHONWARNINGS` can be set to `default`, in other words, `PYTHONWARNINGS=default`.

It's possible to enable output in code, too.

```python
import warnings
warnings.simplefilter("module")
```

Configure logging

: Once output is enabled, it's possible to use {func}`python:logging.captureWarnings` to redirect warnings to the logger.

```python
import logging
logging.captureWarnings(True)
```

### Running tests

In Plone, test deprecation warnings are not shown by default.
The {file}`zope.conf` setting is not taken into account.

To enable deprecation warnings, use the `-W` command.

Given you're using a modern buildout with a virtual environment as recommended, the command would be the following.

```shell
./bin/python -W module ./bin/test
```


## Deprecation best practices

It's recommended to follow these best practices when deprecating code.


### Vanilla deprecation messages

Python offers a built-in exception {exc}`DeprecationWarning` which can be issued using the standard library's {mod}`warnings` module.

Its basic usage is the following example.

```python
import warnings
warnings.warn("deprecated", DeprecationWarning)
```


### Move an entire module

Given a package {file}`old.pkg` with a module {file}`foo.py`, to move it to a package {file}`new.pkg` as {file}`bar.py`, go through the following steps.

[`zope.deprecation` Moving modules](https://zopedeprecation.readthedocs.io/en/latest/api.html#moving-modules) offers a helper.

1. Move the {file}`foo.py` as {file}`bar.py` to the {file}`new.pkg`.
1. At the old place, create a new {file}`foo.py`, and add to it the following lines of code.

```python
from zope.deprecation import moved
moved("new.pkg.bar", "Version 2.0")
```

1. Now you can still import the module at the old place, but get a deprecation warning.

```console
DeprecationWarning: old.pkg.foo has moved to new.pkg.bar.
Import of old.pkg.foo will become unsupported in Version 2.0
```


### Move an entire package

To move an entire package, the process is exactly the same as moving a module, but instead, create a file for each module in the package.


### Deprecate methods and properties

Use the `@deprecate` decorator from [`zope.deprecation` Deprecating methods and properties](https://zopedeprecation.readthedocs.io/en/latest/api.html#deprecating-methods-and-properties) to deprecate methods in a module.

```python
from zope.deprecation import deprecate

@deprecate("Old method is no longer supported, use new_method instead.")
def old_method():
return "some value"
```

The `@deprecated` wrapper method deprecates properties.

```python
from zope.deprecation import deprecated

foo = None
foo = deprecated(foo, "foo is no more, use bar instead")
```


### Move functions and classes

This example describes how to move some classes or functions from a Python file at {file}`old/foo/bar.py` to {file}`new/baz/baaz.py`.
Here, `zope.deferredimport` offers a deprecation helper.
It also avoids circular imports at initialization time.

```python
import zope.deferredimport
zope.deferredimport.initialize()

zope.deferredimport.deprecated(
"Import from new.baz.baaz instead",
SomeOldClass="new.baz:baaz.SomeMovedClass",
some_old_function="new.baz:baaz.some_moved_function",
)

def some_function_which_is_not_touched_at_all():
pass
```

### Deprecate a GenericSetup profile

In GenericSetup, the `post_handler` attribute in ZCML can be used to call a function after the profile was applied.
Use this feature to issue a warning.

First, register the same profile twice, under both the new name and old.

```xml
<genericsetup:registerProfile
name="default"
title="My Fancy Package"
directory="profiles/default"
description="..."
provides="Products.GenericSetup.interfaces.EXTENSION"
/>

<genericsetup:registerProfile
name="some_confusing_name"
title="My Fancy Package (deprecated)"
directory="profiles/some_confusing_name"
description="... (use profile default instead)"
provides="Products.GenericSetup.interfaces.EXTENSION"
post_handler=".setuphandlers.deprecate_profile_some_confusing_name"
/>
```

Then in {file}`setuphandlers.py`, add a function.

```python
import warnings

def deprecate_profile_some_confusing_name(tool):
warnings.warn(
'The profile with id "some_confusing_name" was renamed to "default".',
DeprecationWarning
)
```

### Deprecate a template position
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### Deprecate a template position
### Deprecate a template location


Sometimes you need to move templates to new locations.
Since add-ons often use [`z3c.jbot`](https://github.com/zopefoundation/z3c.jbot) to override templates by their position, you'll need to point them to the new position as well as make sure that the override still works with the old position.

To deprecate a template, follow these steps.

1. In the old package folder's {file}`__init__.py`, add a dictionary `jbot_deprecations` that maps the old template locations to their new counterparts.

```python
jbot_deprecations = {
"plone.locking.browser.info.pt": "plone.app.layout.viewlets.info.pt"
}
```

1. Add this deprecation snippet to the package {file}`configure.zcml` file.

```{code-block} xml
:emphasize-lines: 7-14
:linenos:

<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:zcml="http://namespaces.zope.org/zcml"
>

<include
package="z3c.jbot"
file="meta.zcml"
zcml:condition="installed z3c.jbot"
/>
<browser:jbotDeprecated
zcml:condition="have jbot-deprecations"
dictionary=".jbot_deprecations"
/>

</configure>
```

If a `z3c.jbot` version that supports deprecation is found, trying to override the template with the old location will trigger a deprecation warning that will instruct the user to rename its override file.
1 change: 1 addition & 0 deletions docs/developer-guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ develop-volto-add-ons-index
create-a-distribution
standardize-python-project-configuration
native-namespace
deprecation
```