-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathREADME.in
192 lines (112 loc) · 10.5 KB
/
README.in
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
.. NOTE: only edit README.in, and use generate_readme.py to enrich it with the table of fixers
.. image:: https://ci.appveyor.com/api/projects/status/v4rmlu4o3a45q523/branch/master
:target: https://ci.appveyor.com/project/pakal/django-compat-patcher
=====================
django-compat-patcher
=====================
*Compatibility Matters*
DCP is a companion application which adds backward/forward compatibility patches to Django, so that your app ecosystem doesn't get broken by trivial changes made to the core of the framework. You can thus mix bleeding-edge applications with others that are still stuck at much older Django versions.
To know more about the whole concept of Compat Patchers, see the documentation of the underlying `Compat Patcher Core <https://compat-patcher-core.readthedocs.io/en/latest/index.html>`_.
Note that DCP is aimed at project maintainers. If you are developing a reusable Django application, you can't expect all your users to integrate DCP as well. In this case, to support a wide range of Django versions, you should rather use a toolkit like `Django-compat <https://github.com/arteria/django-compat>`_. You may think of DCP as a "runtime 2to3 and 3to2 for the Django core framework', whereas Django-Compat is rather a "*six* module for Django". If you only seek to upgrade your own codebase to newer Django versions, look at `Django Codemod <https://github.com/browniebroke/django-codemod>`_ too (which is like a "static 2to3 for Django-dependent code repositories").
Feel free to ask for (or contribute) new fixers, for backwards or forwards compatibility, depending on the compatibility troubles you encounter on your own projects. See `docs/django_deprecation_timeline_notes.rst` for a list of breaking changes in Django history, and their current status in DCP.
How to setup
==================
Django-compat-patcher is currently tested on python 3.7/3.8/3.9/3.10/3.11, with Django versions 1.8/1.9/1.10/1.11/2.0/2.1/2.2/3.0/3.1/3.2/4.0/4.1/4.2, where these combinations make sense (e.g. Django2+ dropped support for Python2).
First add :code:`django-compat-patcher` to your project requirements, or install it directly with :code:`pip install django-compat-patcher`.
The Django settings of your project are not altered by compatibility shims, so they should be kept up-to-date with your installed Django version (eg. now use `TEMPLATES`, `MIDDLEWARE`, and not deprecated equivalents). In particular, always put real package names in your INSTALLED_APPS, not their potential "import aliases".
Despite DCP patching, you might encounter errors raised by the Django check framework, like the following. Use the `SILENCED_SYSTEM_CHECKS <https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-SILENCED_SYSTEM_CHECKS>`_ setting to bypass such blocking checks.
::
(fields.E900) IPAddressField has been removed except for support in historical migrations. HINT: Use GenericIPAddressField instead.
(fields.E160) The options auto_now, auto_now_add, and default are mutually exclusive. Only one of these options may be present.
(fields.E903) NullBooleanField is removed except for support in historical migrations.
Activation method 1 - with code
*********************************
You can activate patching with::
import django_compat_patcher
django_compat_patcher.patch()
This code should be placed before any use of Django (eg. in your :code:`manage.py` or your :code:`wsgi.py` script), but after the :code:`DJANGO_SETTINGS_MODULE` environment variable has been set.
In particular, some fixers only work if they are applied before the loading of INSTALLED_APPS (so before django.setup() gets called).
**Note for Pytest-Django users**
Pytest-Django triggers `django.setup()` early during test suite execution, so to place your `django_compat_patcher.patch()` before,
you might have to use a `pytest plugin as explained here <https://pytest-django.readthedocs.io/en/latest/configuring_django.html?highlight=plugin#changing-your-app-before-django-gets-set-up>`_. Use plugin `pytest-pythonpath` if your plugin is not at repository root.
Activation method 2 - with python wrapper
**********************************************
You may force the patching of Django at python startup using https://pypi.org/project/main-wrapper/::
pip install main-wrapper
export DJANGO_SETTINGS_MODULE=<your-settings-module> # Mandatory
export DCP_INCLUDE_FIXER_IDS='[]' # Disable the "all fixers" set by default
export DCP_INCLUDE_FIXER_FAMILIES='["django3.0"]' # Enable just this family of fixers
python-main-wrapper django_compat_patcher:patch <your-normal-command-line>
This unintrusive method is especially useful to repeatedly launch the unit-tests of a library, with different settings, and thus
determine how many fixers it needs to function properly under latest Django version::
python-main-wrapper django_compat_patcher:patch manage.py test
python-main-wrapper django_compat_patcher:patch pytest
python-main-wrapper django_compat_patcher:patch -m <some-module>
See `python-main-wrapper -h` for more details on this launcher.
Tweaking DCP settings
==========================
By default, DCP emits logs and warnings when patching the code, and applies all "relevant" fixers,
i.e all that support your currently installed django version, and are not deemed **unsafe**.
Unsafe fixers are the few ones which might conflict with modern third-party libraries , e.g. if these
add their own workarounds for Django incompatibilites (see `DCP_EXCLUDE_FIXER_IDS` default below).
This behaviour can be customized via the Django settings documented below.
Note however, that some fixers depend on other fixers, so it's advised to be consistent and always include contiguous series of fixers around your current version (ex. if you use Django1.11, apply fixers from Django1.8 up to Django1.11, or up to Django2.X if you want some forward compatibility as well). DCP filters out, by himself, fixers which are useless for your Django version.
You might also provide a `settings` dictionary directly to `patch()`, in which case the DCP django settings of your project will be **entirely** ignored (only DCP library defaults will be used as fallbacks)::
django_compat_patcher.patch(settings=dict(DCP_INCLUDE_FIXER_IDS=["my_fixer_id"]))
It is also possible to override only one or more of these settings by using environment variables with the same name (e.g. "DCP_INCLUDE_FIXER_IDS"),
in JSON format (so a string must be passed as '"*"' for example, or a boolean as 'true'; beware of single quotes, forbidden for JSON strings).
Note that exclusion filters have precedence over inclusion ones.
DCP_INCLUDE_FIXER_IDS
*********************
List of fixer identifiers to include. If :code:`"*"` is used, then all fixers are included.
In rare case of name conflicts when using several registries at once, you may use qualified qualified fixer names like "fixer_family|fixer_id".
| **Default:** :code:`{DCP_INCLUDE_FIXER_IDS!r}`
| **Type:** List of strings, or :code:`"*"`
| **Example:** :code:`DCP_INCLUDE_FIXER_IDS = ['fix_deletion_templatetags_future_url']`
DCP_INCLUDE_FIXER_FAMILIES
**************************
List of fixer families to include. If :code:`"*"` is used, then all families are included.
Note: If you want to include only specific families, remember to replace the value :code:`"*" from :code:`DCP_INCLUDE_FIXER_IDS` by, for example, an empty list.
| **Default:** :code:`{DCP_INCLUDE_FIXER_FAMILIES!r}`
| **Type:** List of strings, or :code:`"*"`
| **Choices:** :code:`("djangoX.Y")` where :code:`X` and :code:`Y` are respectively the major and minor versions
| **Example:** :code:`DCP_INCLUDE_FIXER_FAMILIES = ["django1.9"]`
DCP_EXCLUDE_FIXER_IDS
*********************
List of fixer identifiers to exclude. If :code:`"*"` is used, then all fixers are excluded.
In rare case of name conflicts when using several registries at once, you may use qualified qualified fixer names like "fixer_family|fixer_id".
Note: The "EXCLUDE" filters are applied AFTER the "INCLUDE" ones, and so take precedence.
| **Default:** :code:`{DCP_EXCLUDE_FIXER_IDS!r}`
| **Type:** List of strings, or :code:`"*"`
| **Example:** :code:`DCP_EXCLUDE_FIXER_IDS = ['fix_deletion_templatetags_future_url']`
DCP_EXCLUDE_FIXER_FAMILIES
**************************
List of fixer families to exclude. If :code:`"*"` is used, then all families are excluded.
Note: The "EXCLUDE" filters are applied AFTER the "INCLUDE" ones, and so take precedence.
| **Default:** :code:`{DCP_EXCLUDE_FIXER_FAMILIES!r}`
| **Type:** List of strings, or :code:`"*"`
| **Choices:** :code:`("djangoX.Y")` where :code:`X` and :code:`Y` are respectively the major and minor versions
| **Example:** :code:`DCP_EXCLUDE_FIXER_FAMILIES = ["django1.6", "django1.9"]`
DCP_PATCH_INJECTED_OBJECTS
***************************
By default, the patcher sets an attribute (with value :code:`True`) on injected objects (callables, classes, modules, attributes...) when possible,
with this attribute name, to differentiate them from original objects. Set this setting to True to automatically choose the attribute name, or False to disable the feature.
| **Default:** :code:`{DCP_PATCH_INJECTED_OBJECTS!r}`
| **Type:** Str (or Boolean)
| **Example:** :code:`DCP_PATCH_INJECTED_OBJECTS = False`
DCP_ENABLE_WARNINGS
***************************
If True, compatibility shims emit python warnings (:code:`warnings.warn(...)`) when they are imported/used,
to help detect deprecated code. These warnings are mostly subclasses of :code:`DeprecationWarning` (ex. :code:`RemovedInDjango19Warning`).
Once emitted, the handling of warnings depends on your setup (python command line flags, logging config...), see the `official doc on warnings <https://docs.python.org/3/library/warnings.html>`_ for more information.
| **Default:** :code:`{DCP_ENABLE_WARNINGS!r}`
| **Type:** Boolean
| **Example:** :code:`DCP_ENABLE_WARNINGS = False`
DCP_LOGGING_LEVEL
***************************
The patch() system of DCP can output to *STDERR* which fixers are getting applied, and provide debug information (ex. for which reason a specific fixer was discarded).
This setting sets the logging level of that information stream, which is typically only viewed at django startup. A value :code:`None` disables DCP logging entirely.
Note that DCP does NOT actually use stdlib loggers, because it mostly performs operations before Django logging has been setup (ex. using the LOGGING setting), so log entries would most probably get discarded.
| **Default:** :code:`{DCP_LOGGING_LEVEL!r}`
| **Type:** Logging level string, or None
| **Example:** :code:`DCP_LOGGING_LEVEL = "DEBUG"`