|Version 4 (modified by 5 years ago) (diff),|
Porting 2.x code so that it runs on 2.x and 3.x from a single codebase (WIP)
Please Note: This is work in progress, I am working on the draft, and I will remove "I am working on the draft" when I have finished working on the draft.
When porting Django so that it works from a single codebase in Python 2.x and 3.x, the following is a rough-and-ready guide which I (Vinay Sajip) followed, and should also apply to the work of porting Django apps to work on the same basis.
- Do run
2to3on the codebase, but pipe the output to a file so that you can examine what changes need to be made, But don't run
2to3to make inplace changes to your code, as the resulting code will typically not run under Python 2.x. Go through the piped output to see where you need to make changes. These will typically fall into a number of categories, as described below.
- In any module which uses Unicode or bytes literals (
b'bar'), do insert
from django.utils.py3 import u, bat the top of the module in the appropriate place, and do replace
b('bar')throughout the source. The same applies to the constants with double quotes (
b"bar", which should be replaced by
- If you need to make any code conditional on 2.x vs. 3.x, you can do
from django.utils.py3 import PY3and use
PY3as a condition (as you might expect, it's a
Trueon 3.x and
- If you see any long constants (such as
from django.utils.py3 import long_typeand replace e.g.
- If you see
(long, int)in the source, do
from django.utils.py3 import integer_typesand replace the tuple with
intappear in a tuple along with other values, remove them from the tuple and replace the tuple with tuple
- If you see octal constants (such as
0777), replace them with the hex value (
0777case), and if possible, place the octal constant in a comment so anyone can see what it was originally.
- If you see code of the type
except ExceptionTypeOrTupleOfExceptionTypes, name_to_bind_to:, see if
name_to _bind_tois used in the scope (exception handling clause, or later in the same function or method). If not used, just change the
except ExceptionTypeOrTupleOfExceptionTypes:and you're done. If used, do one more thing: put the code
name_to_bind_to = sys.exc_info()just before
name_to_bind_tois first used, and make sure that the
sysmodule is imported.
unicodeoccurs in the source (i.e. not in comments), do
from django.utils.py3 import text_typeand replace
basestringoccurs in the source (i.e. not in comments), do
from django.utils.py3 import string_typesand replace
stroccurs in the source (i.e. not in comments), you may need to do
from django.utils.py3 import binary_typeand replace
binary_type. However, don't use this blindly:
str()is sometimes used to convert something to text for display, and these occurrences of
str()shouldn't need changing.
- If you have classes with metaclasses, change them to use the form
MyClassWithMetaClass(with_metaclass(MetaClass, BaseClass):where you can omit
BaseClassif it is
object. Remove the
__metaclass__ = XXXstatement from the class body (replacing it with
passif that's all there was in the class body).
- If you need to reraise an exception, do
from django.utils.py3 import reraiseand invoke it using the form
reraise(type, instance, traceback).
- You will see that
list()around calls to methods named
items. This is sometimes but not always necessary, so examine each on a case-by-case basis. In particular, if
valuesis called on a queryset, you don't want to wrap with
- There are functions
django.utils.py3. Import them if there is any reference to those methods, and replace using the pattern
xmight be an expression rather than just a name).
mapcalls with the list comprehensions suggested by
2to3. If you see
map(None, ...)you will need to import and use
- If you see
xrangein the source, just do
from django.utils.py3 import xrange.
- If you use anything from
urlparse, you will need to import them