-
-
Notifications
You must be signed in to change notification settings - Fork 174
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
base: 6.0
Are you sure you want to change the base?
add deprecation chapter #1850
Changes from 1 commit
973020c
bd6eac9
ff7461b
6027930
e6acc83
b909069
b0923fb
7ca4ede
66a58dc
c859367
4e32566
c7cad7a
a9fc020
151aa67
cff3ad6
d0286e4
1c733a3
49e96f8
8608dff
155c344
295f3f5
5db3027
cd5e77e
0bacab8
245f3b8
32a29d7
dab9e49
1803a60
ce2454c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,311 @@ | ||
--- | ||
myst: | ||
html_meta: | ||
"description": "A guide how to do depracations, including Python, ZCML and templates in Plone." | ||
ale-rt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"property=og:description": "A guide how to do depracations, including Python, ZCML and templates in Plone." | ||
ale-rt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"property=og:title": "Deprecation" | ||
"keywords": "depracation, zcml, template, jbot" | ||
ale-rt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
--- | ||
|
||
(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 | ||
MrTango marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
- need to get rid of old code, | ||
- want to unify api style (consistent api), | ||
MrTango marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- 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](http://semver.org) is recommended. | ||
MrTango marked this conversation as resolved.
Show resolved
Hide resolved
stevepiercy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### Help Programmers, No annoyance | ||
|
||
Deprecation has to support the consumers of the code - the programmers using it. | ||
stevepiercy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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. | ||
MrTango marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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. | ||
MrTango marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 (folder with `__init__.py`) | ||
stevepiercy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
- 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 python egg | ||
stevepiercy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
: 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. | ||
|
||
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. Everything up to this point is a Conceptual Guide, and needs to be split into a separate file placed in the 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. |
||
## 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 just sets a configuration option in `zope.conf`. | ||
MrTango marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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/2/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/2/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/2/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](http://docs.zope.org/zope.deprecation/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](http://docs.zope.org/zope.deprecation/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 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
|
||
|
||
|
||
MrTango marked this conversation as resolved.
Show resolved
Hide resolved
|
||
In the old package folders `__init__.py` we add a mapping dict with the name `jbot_deprecations` of old and new template location: | ||
MrTango marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```python | ||
jbot_deprecations = { | ||
"plone.locking.browser.info.pt": "plone.app.layout.viewlets.info.pt" | ||
} | ||
``` | ||
|
||
add a jbot-deprecations config to the `configure.zcml` file | ||
MrTango marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
create a `jbot_deprecations.zcml` file: | ||
|
||
MrTango marked this conversation as resolved.
Show resolved
Hide resolved
|
||
```{code-block} xml | ||
:emphasize-lines: 6,9-12 | ||
:linenos: | ||
|
||
<configure | ||
xmlns="http://namespaces.zope.org/zope" | ||
xmlns="http://namespaces.zope.org/zope" | ||
MrTango marked this conversation as resolved.
Show resolved
Hide resolved
|
||
xmlns:browser="http://namespaces.zope.org/browser" | ||
xmlns:browser="http://namespaces.zope.org/browser" | ||
MrTango marked this conversation as resolved.
Show resolved
Hide resolved
|
||
xmlns:zcml="http://namespaces.zope.org/zcml" | ||
> | ||
|
||
<configure zcml:condition="have jbot-deprecations"> | ||
<include package="z3c.jbot" file="meta.zcml" /> | ||
<browser:jbotDeprecated dictionary=".jbot_deprecations" /> | ||
</configure> | ||
MrTango marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
</configure> | ||
``` | ||
|
||
this will load the defined `jbot_deprecations` dictionary. | ||
MrTango marked this conversation as resolved.
Show resolved
Hide resolved
|
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 | ||
|
Uh oh!
There was an error while loading. Please reload this page.