-
-
Notifications
You must be signed in to change notification settings - Fork 176
add deprecation chapter #1850
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
MrTango
wants to merge
29
commits into
6.0
Choose a base branch
from
chapter-deprecation
base: 6.0
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
add deprecation chapter #1850
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
973020c
add deprecation chapter
MrTango bd6eac9
fix broken links in deprecation chapter
MrTango ff7461b
Update docs/backend/deprecation.md
MrTango 6027930
Update docs/backend/deprecation.md
MrTango e6acc83
Update docs/backend/deprecation.md
MrTango b909069
Update docs/backend/deprecation.md
MrTango b0923fb
Update docs/backend/deprecation.md
MrTango 7ca4ede
Update docs/backend/deprecation.md
MrTango 66a58dc
Update docs/backend/deprecation.md
MrTango c859367
Update docs/backend/deprecation.md
MrTango 4e32566
Update docs/backend/deprecation.md
MrTango c7cad7a
Update docs/backend/deprecation.md
MrTango a9fc020
remove meta.zcml attribute from z3c.jbot include
MrTango 151aa67
Update docs/backend/deprecation.md
MrTango cff3ad6
Fix ty
ale-rt d0286e4
Fix typo
ale-rt 1c733a3
Fix typo
ale-rt 49e96f8
Update docs/backend/deprecation.md
MrTango 8608dff
Update docs/backend/deprecation.md
MrTango 155c344
Update docs/backend/deprecation.md
MrTango 295f3f5
Update docs/backend/deprecation.md
MrTango 5db3027
Update docs/backend/deprecation.md
MrTango cd5e77e
Merge branch '6.0' into chapter-deprecation
MrTango 0bacab8
Apply suggestions from code review
stevepiercy 245f3b8
Merge branch '6.0' into chapter-deprecation
stevepiercy 32a29d7
Merge branch '6.0' into chapter-deprecation
stevepiercy dab9e49
Merge branch '6.0' into chapter-deprecation
jensens 1803a60
Merge branch '6.0' into chapter-deprecation
jensens ce2454c
Merge branch '6.0' into chapter-deprecation
ale-rt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,318 @@ | ||
--- | ||
myst: | ||
html_meta: | ||
"description": "A guide how to do deprecations, including Python, ZCML and templates in Plone." | ||
"property=og:description": "A guide how to do deprecations, including Python, ZCML and templates in Plone." | ||
"property=og:title": "Deprecation" | ||
"keywords": "deprecation, zcml, template, jbot" | ||
--- | ||
|
||
(backend-deprecation-label)= | ||
# Deprecation | ||
|
||
## Introduction | ||
|
||
This document describes rationales, configuration and best practices of deprecations in Plone, Zope and Python. | ||
It is meant as a styleguide on how to apply deprecations in Plone core packages. | ||
It also has a value as a general overview on how to deprecate in Python. | ||
|
||
|
||
### Why Deprecation | ||
|
||
At some point we: | ||
|
||
- need to get rid of old code, | ||
- want to unify API style (consistent API), | ||
- fix typos in namings, | ||
- move code or templates around (inside package or to another package). | ||
|
||
While refactoring code, moving modules, functions, classes and methods is often needed. | ||
To not break third party code imports from the old place or usage of old functions/ methods must work for while. | ||
Deprecated methods are usually removed with the next major release of Plone. | ||
|
||
Following the [semantic versioning guideline](https://semver.org) is recommended. | ||
|
||
### Help Programmers, No annoyance | ||
|
||
The developers should use code deprecations to support the consumers of the code. | ||
From their point of view, Plone core code is an API to them. | ||
Any change is annoying to them anyway, but they feel better if deprecation warnings are telling them what to do. | ||
|
||
Deprecations must always log at level *warning* and have to answers the question: | ||
|
||
**"Why is the code gone from the old place? What to do instead?"** | ||
|
||
A short message is enough., i.e.: | ||
|
||
- "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 has to be done once, i.e. on first usage or first import. | ||
It must not flood the logs. | ||
|
||
### Use Cases | ||
|
||
Renaming | ||
|
||
: We may want to rename classes, methods, functions or global or class variables in order to get a more consistent API or because of a typo, etc. | ||
We never just rename, we always provide a deprecated version logging a verbose deprecation warning with information where to | ||
import from in future. | ||
|
||
Moving a module, class, function, etc to another place | ||
|
||
: For some reason, i.e. merging packages, consistent API or resolving cirular import problems, we need to move code around. | ||
When imported from the old place it logs a verbose deprecation warning with information where to import from in future. | ||
|
||
Deprecation of a whole package | ||
|
||
: A whole [package](https://docs.python.org/3/tutorial/modules.html#packages) | ||
|
||
- all imports still working, logging deprecation warnings on first import | ||
- ZCML still exists, but is empty (or includes the zcml from the new place if theres no auto import (i.e. for meta.zcml). | ||
|
||
Deprecation of a whole released/ installable package. | ||
|
||
: We will provide a last major release with no 'real' code, only backward compatible (bbb) imports of public API are provided. | ||
This will be done the way described above for a whole package. | ||
The README clearly states why it was moved and where to find the code now. | ||
|
||
Deprecation of a GenericSetup profile | ||
|
||
: They may got renamed for consistency or are superfluos after an update. | ||
Code does not need to break to support this. | ||
|
||
## Enable Deprecation Warnings | ||
|
||
### Zope | ||
|
||
Zope does configure logging and warnings, so the steps below (under section Python) are not needed. | ||
|
||
Using `plone.recipe.zope2instance` add the option `deprecation-warnings = on` to the buildouts `[instance]` section. | ||
|
||
```ini | ||
[buildout] | ||
parts = instance | ||
|
||
[instance] | ||
recipe = plone.recipe.zope2instance | ||
... | ||
deprecation-warnings = on | ||
... | ||
``` | ||
|
||
This adds this line to the `zope.conf` file: | ||
|
||
``` | ||
debug-mode on | ||
``` | ||
|
||
Without the recipe this can be set manually as well: | ||
In `zope.conf` custom filters for warnings can be defined. | ||
|
||
```xml | ||
... | ||
<warnfilter> | ||
action always | ||
category exceptions.DeprecationWarning | ||
</warnfilter> | ||
... | ||
``` | ||
|
||
### 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 [-W \[all|module|once\]](https://docs.python.org/3/using/cmdline.html#cmdoption-W) option. | ||
|
||
It is possible to enable output in code too: | ||
|
||
```python | ||
import warnings | ||
warnings.simplefilter("module") | ||
``` | ||
|
||
Configure Logging | ||
|
||
: Once output is enabled it is possible to [redirect warnings to the logger](https://docs.python.org/3/library/logging.html#logging.captureWarnings): | ||
|
||
```python | ||
import logging | ||
logging.captureWarnings(True) | ||
``` | ||
|
||
### Running tests | ||
|
||
In Plone tests deprecation warnings are not shown by default. | ||
The `zope.conf` setting is not taken into account. | ||
|
||
In order to enable deprecation warnings, | ||
the Python way with the `-W` command option must to be used. | ||
|
||
Given youre using a modern buildout with virtualenv as recommended, | ||
the call looks like so: | ||
|
||
```bash | ||
./bin/python -W module ./bin/test | ||
``` | ||
|
||
## Deprecation Best Practice | ||
|
||
### Vanilla Deprecation Messages | ||
|
||
Python offers a built-in `DeprecationWarning` which can be issued using standard libraries `warnings` module. | ||
|
||
For details read the [official documentation about warnings](https://docs.python.org/3/library/warnings.html). | ||
|
||
In short it works like so | ||
|
||
```python | ||
import warnings | ||
warnings.warn('deprecated', DeprecationWarning) | ||
``` | ||
|
||
### Moving Whole Modules | ||
|
||
Given a package `old.pkg` with a module `foo.py` need to be moved to a package `new.pkg` as `bar.py`. | ||
|
||
[zope.deprecation Moving modules](https://zopedeprecation.readthedocs.io/en/latest/api.html#moving-modules) offers a helper. | ||
|
||
1. Move the `foo.py` as `bar.py` to the `new.pkg`. | ||
2. At the old place create a new `foo.py` and add to it | ||
|
||
```python | ||
from zope.deprecation import moved | ||
moved('new.pkg.bar', 'Version 2.0') | ||
``` | ||
|
||
Now you can still import the namespace from `bar` at the old place, but get a deprecation warning: | ||
|
||
> DeprecationWarning: old.pkg.foo has moved to new.pkg.bar. | ||
> Import of old.pkg.foo will become unsupported in Version 2.0 | ||
|
||
### Moving Whole Packages | ||
|
||
This is the same as moving a module, just create for each module a file. | ||
|
||
### Deprecating methods and properties | ||
|
||
You can 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 is for deprecating properties: | ||
|
||
```python | ||
from zope.deprecation import deprecated | ||
|
||
foo = None | ||
foo = deprecated(foo, 'foo is no more, use bar instead') | ||
``` | ||
|
||
### Moving functions and classes | ||
|
||
Given we have a Python file at `old/foo/bar.py` and want to move some classes or functions to `new/baz/baaz.py`. | ||
|
||
Here `zope.deferredimport` offers a deprecation helper. | ||
It also avoids circular imports on 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 | ||
``` | ||
|
||
### Deprecating a GenericSetup profile | ||
|
||
Starting with GenericSetup 1.8.2 (part of Plone > 5.0.2) the `post_handler` attribute in ZCML can be used to call a function after the profile was applied. | ||
We use this feature to issue a warning. | ||
|
||
First we register the same profile twice. Under the new name and under the old name: | ||
|
||
```xml | ||
<genericsetup:registerProfile | ||
name="default" | ||
title="My Fance Package" | ||
directory="profiles/default" | ||
description="..." | ||
provides="Products.GenericSetup.interfaces.EXTENSION" | ||
/> | ||
|
||
<genericsetup:registerProfile | ||
name="some_confusing_name" | ||
title="My Fance Package (deprecated)" | ||
directory="profiles/some_confusing_name" | ||
description="... (use profile default instaed)" | ||
provides="Products.GenericSetup.interfaces.EXTENSION" | ||
post_handler=".setuphandlers.deprecate_profile_some_confusing_name" | ||
/> | ||
``` | ||
|
||
And in `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 | ||
) | ||
``` | ||
|
||
### Deprecating a template position | ||
|
||
Sometimes we need to move templates to new locations. Since addons often use [z3c.jbot](https://github.com/zopefoundation/z3c.jbot) to override templates by their position, we need to point them to the new position as well as make sure that the override still works with the old position. | ||
|
||
|
||
MrTango marked this conversation as resolved.
Show resolved
Hide resolved
|
||
To deprecate a package: | ||
|
||
1. In the old package folders `__init__.py` add a dictionary `jbot_deprecations` that maps the old template locations to their new counterparts, e.g.: | ||
|
||
```python | ||
jbot_deprecations = { | ||
"plone.locking.browser.info.pt": "plone.app.layout.viewlets.info.pt" | ||
} | ||
``` | ||
|
||
2. Add this deprecation snippet to the package `configure.zcml` file: | ||
|
||
```{code-block} xml | ||
:emphasize-lines: 6,9-12 | ||
:linenos: | ||
|
||
<configure | ||
xmlns="http://namespaces.zope.org/zope" | ||
xmlns:browser="http://namespaces.zope.org/browser" | ||
MrTango marked this conversation as resolved.
Show resolved
Hide resolved
|
||
xmlns:zcml="http://namespaces.zope.org/zcml" | ||
> | ||
|
||
<include | ||
zcml:condition="installed z3c.jbot" | ||
package="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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ behaviors | |
configuration-registry | ||
content-types/index | ||
control-panels | ||
deprecation | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you all think that this insertion point in the TOC is the most appropiate place for this subject? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not. See #1850 (review). |
||
fields | ||
global-utils | ||
indexing | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything up to this point is a Conceptual Guide, and needs to be split into a separate file placed in the
conceptual-guides
directory.Everything after this point is a How-to Guide and should be moved under Developer guide. This section is a recent addition, and more suitable for this content.