= 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 `2to3` on the codebase, but pipe the output to a file so that you can examine what changes need to be made, But don't run `2to3` to 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 ({{{u'foo'}}} or {{{b'bar'}}}), do insert `from django.utils.py3 import u, b` at the top of the module in the appropriate place, and do replace `u'foo'` with `u('foo')` and `b'bar'` with `b('bar')` throughout the source. The same applies to the constants with double quotes (`u"foo"` or `b"bar"`, which should be replaced by `u("foo")` or `b("bar")` respectively). * If you need to make any code conditional on 2.x vs. 3.x, you can do `from django.utils.py3 import PY3` and use `PY3` as a condition (as you might expect, it's a `bool` which is `True` on 3.x and `False` on 2.x. * If you see any long constants (such as `5L`), do `from django.utils.py3 import long_type` and replace e.g. `5L` with `long_type(5)`. * If you see `(int, long)` or `(long, int)` in the source, do `from django.utils.py3 import integer_types` and replace the tuple with `integer_types`. If `long` and `int` appear in tuple along with other values, remove them from the tuple and replace the tuple with tuple `+ integer_types`. * If you see octal constants (such as {{{0777}}}), replace them with the hex value ({{{0x1ff}}} for the {{{0777}}} case), 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_to}}} is used in the scope (exception handling clause, or later in the same function or method). If not used, just change the {{{except:}}} statement to {{{except ExceptionTypeOrTupleOfExceptionTypes:}}} and you're done. If used, do one more thing: put the code {{{name_to_bind_to = sys.exc_info()[1]}}} just before {{{name_to_bind_to}}} is first used, and make sure that the {{{sys}}} module is imported. * If {{{unicode}}} occurs in the source (i.e. not in comments), do {{{from django.utils.py3 import text_type}}} and replace {{{unicode}}} with {{{text_type}}}. * If {{{basestring}}} occurs in the source (i.e. not in comments), do {{{from django.utils.py3 import string_types}}} and replace {{{basestring}}} with {{{string_types}}}. * If {{{str}}} occurs in the source (i.e. not in comments), you may need to do {{{from django.utils.py3 import binary_type}}} and replace {{{str}}} with {{{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 {{{BaseClass}}} if it is {{{object}}}.