Skip to content

Commit 76359f2

Browse files
MrTangoale-rtstevepiercyjensensmauritsvanrees
authored
add deprecation chapter (#1850)
* add deprecation chapter * fix broken links in deprecation chapter * Update docs/backend/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * Update docs/backend/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * Update docs/backend/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * Update docs/backend/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * Update docs/backend/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * Update docs/backend/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * Update docs/backend/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * Update docs/backend/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * Update docs/backend/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * Update docs/backend/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * remove meta.zcml attribute from z3c.jbot include * Update docs/backend/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * Fix ty * Fix typo * Fix typo * Update docs/backend/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * Update docs/backend/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * Update docs/backend/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * Update docs/backend/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * Update docs/backend/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * Apply suggestions from code review Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> Co-authored-by: Jens W. Klein <jk@kleinundpartner.at> * split deprecation guide into two file according to diataxis principles * Update docs/developer-guide/deprecation.md Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> * - Fix English grammar and syntax - Generalize for npm and Volto - Clean up MyST style and formatting * Refine some grammar * - Add MyST syntax to link to Python objects, improve rendering - Clean up grammar, MyST syntax - Indent code within list items. - Use ruff style code example formatting. * Update docs/developer-guide/deprecation.md Co-authored-by: Jens W. Klein <jk@kleinundpartner.at> * Update docs/developer-guide/deprecation.md Co-authored-by: Maurits van Rees <maurits@vanrees.org> * Update docs/developer-guide/deprecation.md Co-authored-by: Maurits van Rees <maurits@vanrees.org> * Update docs/developer-guide/deprecation.md Co-authored-by: Maurits van Rees <maurits@vanrees.org> * Update docs/developer-guide/deprecation.md Co-authored-by: Maurits van Rees <maurits@vanrees.org> * Update docs/developer-guide/deprecation.md Co-authored-by: Steve Piercy <web@stevepiercy.com> * Update docs/developer-guide/deprecation.md Co-authored-by: Steve Piercy <web@stevepiercy.com> * Update docs/developer-guide/deprecation.md Co-authored-by: Steve Piercy <web@stevepiercy.com> * Update docs/developer-guide/deprecation.md Co-authored-by: Steve Piercy <web@stevepiercy.com> * Apply suggestion from @gforcada Co-authored-by: Gil Forcada Codinachs <gil.gnome@gmail.com> * s/position/location * Fix order of items in the table of contents * Include one more line to emphasize Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> --------- Co-authored-by: Alessandro Pisa <alessandro.pisa@gmail.com> Co-authored-by: Steve Piercy <web@stevepiercy.com> Co-authored-by: Jens W. Klein <jk@kleinundpartner.at> Co-authored-by: Maurits van Rees <maurits@vanrees.org> Co-authored-by: Gil Forcada Codinachs <gil.gnome@gmail.com>
1 parent 97b7b14 commit 76359f2

4 files changed

Lines changed: 322 additions & 0 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
myst:
3+
html_meta:
4+
"description": "Understanding deprecation in Plone - rationale, philosophy, and use cases"
5+
"property=og:description": "Understanding deprecation in Plone - rationale, philosophy, and use cases"
6+
"property=og:title": "Deprecation"
7+
"keywords": "deprecation, Plone, Python, Node.js, React, philosophy, rationale, use cases"
8+
---
9+
10+
(conceptual-deprecation-label)=
11+
12+
# Deprecation
13+
14+
This chapter describes the rationale and philosophy of deprecations in Plone.
15+
It is meant as a guide for how to think about deprecations in Plone core packages.
16+
17+
```{seealso}
18+
For implementation details and code examples, see {doc}`/developer-guide/deprecation`.
19+
```
20+
21+
22+
(why-deprecation-label)=
23+
24+
## Why deprecation
25+
26+
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.
27+
28+
When refactoring code, it's often necessary to move modules, functions, classes, and methods.
29+
It's critical not to break third party code imports from the old place.
30+
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.
31+
32+
Deprecated methods are usually removed with the next major release of Plone.
33+
Plone follows the [semantic versioning guideline](https://semver.org).
34+
35+
36+
## Help programmers without annoyance
37+
38+
Developers should use code deprecations to support the consumers of the code, that is, their fellow Plone developers.
39+
From the consumer's point of view, Plone core code is an API.
40+
Any change may annoy them, but they feel better when deprecation warnings tell them how to adapt their code to the changes.
41+
42+
Deprecations must always log at the level of warning.
43+
44+
Deprecations should always answer the following questions.
45+
46+
- Why is the code gone from the old place?
47+
- What should the developer do instead?
48+
49+
A short message is enough, such as the following examples.
50+
51+
- "Replaced by new API `xyz`, found at `abc.cde`".
52+
- "Moved to `xyz`, because of `abc`".
53+
- "Name had a typo, new name is `xyz`".
54+
55+
All logging must be done only once, in other words, on the first usage or import.
56+
It must not flood the logs.
57+
58+
59+
## Use cases
60+
61+
The following use cases describe when to deprecate.
62+
63+
Rename
64+
: Developers may want to rename classes, methods, functions, or global or class variables to get a more consistent API or because of a typo.
65+
Renaming alone is not enough to deprecate code.
66+
Always provide a deprecated version that logs a verbose deprecation warning with information for where to import from in the future.
67+
68+
Move objects
69+
: For reasons described in {ref}`why-deprecation-label`, developers may need to move code around.
70+
When imported from the old place, it logs a verbose deprecation warning with information of where to import from in the future.
71+
72+
Deprecation of a whole Python or npm package
73+
: A whole {ref}`Python package <python:tut-packages>` or [npm package](https://www.npmjs.com/) may be moved to a new location.
74+
75+
- All imports still work.
76+
- Log deprecation warnings on first import.
77+
- The ZCML still exists, but is empty or includes the ZCML from the new place, if there's no auto import for `meta.zcml`.
78+
79+
Deprecation of a whole released or installable package
80+
: Plone developers provide a major release with no "real" code, but only backward compatible imports of the public API.
81+
This should be done the way described above for a whole package.
82+
The README should clearly state why it was moved and where to find the code now.
83+
84+
Deprecation of a GenericSetup profile
85+
: These may have been renamed for consistency or are superfluous after an update.
86+
Code does not need to break to support this.

docs/conceptual-guides/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ This part of the documentation provides explanation of concepts to deepen and br
1818
choose-user-interface
1919
compare-buildout-pip
2020
distributions
21+
deprecation
2122
package-management
2223
package-dependencies
2324
make-backend-build
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
---
2+
myst:
3+
html_meta:
4+
"description": "How to implement deprecations in Plone, including Python, ZCML and templates."
5+
"property=og:description": "How to implement deprecations in Plone, including Python, ZCML and templates."
6+
"property=og:title": "Implement deprecations"
7+
"keywords": "deprecation, zcml, template, jbot, Plone, Python"
8+
---
9+
10+
(developer-deprecation-label)=
11+
12+
# Deprecate code
13+
14+
This chapter describes how to enable deprecation warnings and best practices for deprecating code in Plone, Zope, and Python.
15+
16+
```{seealso}
17+
For background on deprecation philosophy and use cases, see {doc}`/conceptual-guides/deprecation`.
18+
```
19+
20+
21+
## Enable deprecation warnings
22+
23+
This section describes how to enable deprecation warnings in Python, both in an interpreter and code, and when running tests.
24+
25+
26+
27+
28+
(deprecation-warning-python-label)=
29+
30+
### Python
31+
32+
Enable warnings
33+
34+
: Warnings are written to `stderr` by default, but `DeprecationWarning` output is surpressed by default.
35+
36+
Output can be enabled by starting the Python interpreter with the {ref}`-W[all|module|once] <python:using-on-warnings>` argument.
37+
38+
As an alternative, the environment variable {envvar}`python:PYTHONWARNINGS` can be set to `default`, in other words, `PYTHONWARNINGS=default`.
39+
40+
It's possible to enable output in code, too.
41+
42+
```python
43+
import warnings
44+
warnings.simplefilter("module")
45+
```
46+
47+
Configure logging
48+
49+
: Once output is enabled, it's possible to use {func}`python:logging.captureWarnings` to redirect warnings to the logger.
50+
51+
```python
52+
import logging
53+
logging.captureWarnings(True)
54+
```
55+
56+
### Running tests
57+
58+
In Plone, test deprecation warnings are not shown by default.
59+
The {file}`zope.conf` setting is not taken into account.
60+
61+
To enable deprecation warnings, use the `-W` command.
62+
63+
Given you're using a modern buildout with a virtual environment as recommended, the command would be the following.
64+
65+
```shell
66+
./bin/python -W module ./bin/test
67+
```
68+
69+
70+
## Deprecation best practices
71+
72+
It's recommended to follow these best practices when deprecating code.
73+
74+
75+
### Vanilla deprecation messages
76+
77+
Python offers a built-in exception {exc}`DeprecationWarning` which can be issued using the standard library's {mod}`warnings` module.
78+
79+
Its basic usage is the following example.
80+
81+
```python
82+
import warnings
83+
warnings.warn("deprecated", DeprecationWarning)
84+
```
85+
86+
87+
### Move an entire module
88+
89+
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.
90+
91+
[`zope.deprecation` Moving modules](https://zopedeprecation.readthedocs.io/en/latest/api.html#moving-modules) offers a helper.
92+
93+
1. Move the {file}`foo.py` as {file}`bar.py` to the {file}`new.pkg`.
94+
1. At the old place, create a new {file}`foo.py`, and add to it the following lines of code.
95+
96+
```python
97+
from zope.deprecation import moved
98+
moved("new.pkg.bar", "Version 2.0")
99+
```
100+
101+
1. Now you can still import the module at the old place, but get a deprecation warning.
102+
103+
```console
104+
DeprecationWarning: old.pkg.foo has moved to new.pkg.bar.
105+
Import of old.pkg.foo will become unsupported in Version 2.0
106+
```
107+
108+
109+
### Move an entire package
110+
111+
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.
112+
113+
114+
### Deprecate methods and properties
115+
116+
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.
117+
118+
```python
119+
from zope.deprecation import deprecate
120+
121+
@deprecate("Old method is no longer supported, use new_method instead.")
122+
def old_method():
123+
return "some value"
124+
```
125+
126+
The `@deprecated` wrapper method deprecates properties.
127+
128+
```python
129+
from zope.deprecation import deprecated
130+
131+
foo = None
132+
foo = deprecated(foo, "foo is no more, use bar instead")
133+
```
134+
135+
136+
### Move functions and classes
137+
138+
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`.
139+
Here, `zope.deferredimport` offers a deprecation helper.
140+
It also avoids circular imports at initialization time.
141+
142+
```python
143+
import zope.deferredimport
144+
zope.deferredimport.initialize()
145+
146+
zope.deferredimport.deprecated(
147+
"Import from new.baz.baaz instead",
148+
SomeOldClass="new.baz:baaz.SomeMovedClass",
149+
some_old_function="new.baz:baaz.some_moved_function",
150+
)
151+
152+
def some_function_which_is_not_touched_at_all():
153+
pass
154+
```
155+
156+
### Deprecate a GenericSetup profile
157+
158+
In GenericSetup, the `post_handler` attribute in ZCML can be used to call a function after the profile was applied.
159+
Use this feature to issue a warning.
160+
161+
First, register the same profile twice, under both the new name and old.
162+
163+
```xml
164+
<genericsetup:registerProfile
165+
name="default"
166+
title="My Fancy Package"
167+
directory="profiles/default"
168+
description="..."
169+
provides="Products.GenericSetup.interfaces.EXTENSION"
170+
/>
171+
172+
<genericsetup:registerProfile
173+
name="some_confusing_name"
174+
title="My Fancy Package (deprecated)"
175+
directory="profiles/some_confusing_name"
176+
description="... (use profile default instead)"
177+
provides="Products.GenericSetup.interfaces.EXTENSION"
178+
post_handler=".setuphandlers.deprecate_profile_some_confusing_name"
179+
/>
180+
```
181+
182+
Then in {file}`setuphandlers.py`, add a function.
183+
184+
```python
185+
import warnings
186+
187+
def deprecate_profile_some_confusing_name(tool):
188+
warnings.warn(
189+
'The profile with id "some_confusing_name" was renamed to "default".',
190+
DeprecationWarning
191+
)
192+
```
193+
194+
### Deprecate a template location
195+
196+
Sometimes you need to move templates to new locations.
197+
Since add-ons often use [`z3c.jbot`](https://github.com/zopefoundation/z3c.jbot) to override templates by their location, you'll need to point them to the new location as well as make sure that the override still works with the old location.
198+
199+
To deprecate a template, follow these steps.
200+
201+
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.
202+
203+
```python
204+
jbot_deprecations = {
205+
"plone.locking.browser.info.pt": "plone.app.layout.viewlets.info.pt"
206+
}
207+
```
208+
209+
1. Add this deprecation snippet to the package {file}`configure.zcml` file.
210+
211+
```{code-block} xml
212+
:emphasize-lines: 7-15
213+
:linenos:
214+
215+
<configure
216+
xmlns="http://namespaces.zope.org/zope"
217+
xmlns:browser="http://namespaces.zope.org/browser"
218+
xmlns:zcml="http://namespaces.zope.org/zcml"
219+
>
220+
221+
<include
222+
package="z3c.jbot"
223+
file="meta.zcml"
224+
zcml:condition="installed z3c.jbot"
225+
/>
226+
<browser:jbotDeprecated
227+
zcml:condition="have jbot-deprecations"
228+
dictionary=".jbot_deprecations"
229+
/>
230+
231+
</configure>
232+
```
233+
234+
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.

docs/developer-guide/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ develop-volto-add-ons-index
2525
create-a-distribution
2626
standardize-python-project-configuration
2727
native-namespace
28+
deprecation
2829
```

0 commit comments

Comments
 (0)