Supporting Python 3--does not use 2TO3 conversion to support Python 2 and Python 3

Source: Internet
Author: User
Tags integer division unsupported





Python 2 and Python 3 are not supported with 2TO3 conversion


Although the official document of Python 3 has been written to support Python 2 and Python 3 code, it is appropriate in this case. Especially if you can't give up support for Python 2.5 and earlier, because Python 2.6 introduces quite a lot of forward compatibility.



It's possible to make the same code run under earlier versions of Python as well and then you start getting into the "con Torted "Writing style the Python 3 documentation mentions. I'll take up tricks to does this and the Sixmodule I mention at the end of this chapter would help a lot. It has been do even for some quite big projects, but I would in general no recommend it for a large project. For small projects or parts of bigger projects, for example bootstrapping scripts, supporting old versions of Python Witho UT using 2to3is often the best solution.



Python 2.7 have some small improvements on Python 3 compatibility, but it's likely so if you want to run the same code UN Der both Python 2 and Python 3 you'll have to support Python 2.6 for some time to come.



Many of the changes you need'll be do by 2to3, so-to-start converting your code you actually want to first run 2to3 on Your code and make your code run under Python 3. It is generally easier, or at least less monotonous, to introduce Python 2 compatibility in Python 3 code, than to Introdu Ce python 3 compatibility in Python 2 code.



Once you had the project running under Python 3, try to run it under Python 2.6. At the this stage, you could run into syntax errors. They should come from only changes in the print statement. Once You has fixed them you can fix the remaining errors and then lastly your do the same for earlier versions of Python, If you need to support them as well.


Supporting the print () function


One of the major causes of syntax errors is the change of print from a statement to a function. The simple cases is not problematic and you can simply put parentheses around the text that should be printed. The following would print exactly the same in all versions of Python:


>>> print ("This works with all versions of python!") This works under all versions of python!


However, if you use any more advanced feature of the print you either end to with a syntax error or not printing what do you inte nded. Python 2 ' s trailing comma have in Python 3 become a parameter, so if you use trailing commas to avoid the newline after a P Rint, this would in Python 3 look like print (' Textto print ', end= ") which is a syntax error under Python 2.



Under Python 2.6 There is a __future__ import to make print into a function. So to avoid any syntax errors and other differences your should start any file where do I print () with from __future__ I Mportprint_function. The __future__ import works under Python 2.6 and later, so for Python 2.5 and earlier are the other options. Can either convert the more complex print to something simpler, or can use a separate print function that works UN Der both Python 2 and Python 3.



Writing your own print function sounds more complicated than it is. The trick is to use Sys.stdout.write () and formatting according to the parameters passed in to the function. However, it is even easier to use the Print_ () function from the six module that I'll present at the end of this chapter .


Handling Exceptions


If you are in your exception handling need access to the exception itself you need to use an exception variable. In Python 2, the syntax for this is:


>>> Try: ... a = 1/0 ... Except Zerodivisionerror, E: ... print E.args[0]integer division or modulo by zero


However, this syntax are confusing when you need to catch more than one exception. You need to parentheses around the list of exceptions:


>>> try: A = 1/"Dinsdale" ... except (Zerodivisionerror, TypeError): ... print "You can ' t divide by zero or by strings! " You can ' t divide by zero or by strings!


If you forget the parentheses only zerodivisionerror is caught and the raised exception'll be stored in a variable named TypeError. That's not expect. Therefore, in Python 3, the syntax have changed to use asinstead of a comma, which avoids this mistake. It'll also give you a syntax the error if you don't have the parentheses when catching more than one exception:


>>> try: A = 1/' 0 ' ... except (Zerodivisionerror, TypeError) as E: ... print (e.args[0]) unsupported opera nd type (s) for/: ' int ' and ' str '


The above syntax also works in Python 2.6 where both the old syntax with a comma and the new syntax using as works. If you need to support Python versions lower than Python 2.6 and you need access to the exception that is raised, you can Get and all versions through the Exc_info () function:


>>> import sys>>> Try: ... a = 1/' 0 ' ... except (Zerodivisionerror, TypeError): .... E = Sys.exc_inf O () [1] ... print (e.args[0]) unsupported operand type (s) for/: ' int ' and ' str '


Another difference is this Exceptions is no longer iterable. In Python 2 The arguments to the exception is available by iterating over the exception or subscripting the exception.


>>> Try: ... f = 1/0 ...  Except Zerodivisionerror, E: ... for M in e: ... print m ... print E[0]integer division or modulo by Zerointeger division or modulo by zero


In Python 3 your need to use the exception attribute args instead:


>>> try: F = "1" + 1 ... Except TypeError as E: ... for M in E.args: ... print (e.args[0]) Can ' t convert ' int ' object to str i Mplicitlycan ' t convert ' int ' object to str implicitly


A message attribute is added to the built-in exceptions in Python 2.5. It is however deprecated already in Python 2.6 and removed in Python 3. Python 2.6 and Python 2.7 would warn you on this when using The-3 flag.


Import errors


One of the big changes is the reorganization of the standard library and as a result the typical errors you'll get at th Is stage is mostly import errors. Getting around it is very easy. You simply try-to-import from the Python 3 locations and if-fails you import from the Python 2 locations. For modules that has been renamed, you can just import them as the new name.


>>> try: Import Configparser ... except importerror: ... import configparser as Configparser


Some of the new modules is mergers of several old modules and in this case the above would not work if you need things fro M several of the old modules. You can also does this with modules that has sub-modules. Import Httplib ashttp.client is a syntax error. The Urllib and URLLIB2 modules has not a been merged, but a reorganized into several sub-modules. So there your need to the import each name and separately. This often means your need to make changes to your code as well. In Python 2 retrieving a Web page looks like this:


>>> import urllib>>> import urlparse>>>>>> url = ' http://docs.python.org/library/ ' >>> parts = urlparse.urlparse (URL) >>> parts = parts._replace (path= '/3.0 ' +parts.path) >>> page = Urllib.urlopen (Parts.geturl ())


After conversion with 2to3 it would look like this:


>>> import Urllib.request, Urllib.parse, urllib.error>>> import urllib.parse>>>>> > url = ' http://docs.python.org/library/' >>> parts = urllib.parse.urlparse (URL) >>> parts = parts._ Replace (path= '/3.0 ' +parts.path) >>> page = Urllib.request.urlopen (Parts.geturl ())


Yes, Urllib.parse'll be imported twice and urllib.error imported even though it isn ' t used. That's how this fixer does it and solving that would are a lot of extra effort, so it's imports more than are needed. We need to fix up the code to import the names we use directly instead of the modules they is located in, so the version That's runs under both Python 2 and Python 3 ends up like this:


>>> try: ... from urllib.request import Urlopen ... from urllib.parse import Urlparse ... except Importerror :... from urlparse import Urlparse ... from urllib import urlopen...>>> url = ' Http://docs.python.org/libr ary/' >>> parts = urlparse (URL) >>> parts = parts._replace (path= '/3.0 ' +parts.path) >>> page = Urlopen (Parts.geturl ())
Integer incompatibilities


There is both big changes in the integer handling in Python 3. The first one is, the int and the long types have been merged. That means so you can ' t specify, that's an integer should is a long by adding the L-suffix any more. 1L is a syntax error in Python 3.



If you are having an integer being a long in Python 2 and still is compatible with Python 3, the following code would sol ve it. It defines up along variable to being the same as the Int class under Python 3, and it can then is used explicitly to make Su Re the integer is a long.


>>> Import sys>>> if Sys.version_info > (3,): ... long = int>>> long (1) 1L


Another change is, the syntax for octal literals have changed. In Python 2 any number starting with a 0 are an octal, which can be confusing if you decide to format your numbers by start ing them with zeros. In Python 3 The syntax have instead changed to the less confusing 0o, similar to 0x used for hex numbers. Starting numbers with 0 are a syntax error to prevent if trying the old octal syntax by mistake.



Octal is used almost exclusively when setting permissions under Unix, but the in turn is quite a common task. There is a easy to do works under both Python 2 and Python 3:use the decimal or hex value and put the octal value in A comment:


>>> f = 420 # 644 in octal, ' rw-r--r--'
More bytes, strings and Unicode


It's no surprise that the trickiest issue we had in supporting Python 2 and Python 3 without 2to3 is handling binary data , just as it is when Using2to3. When we choose don't to use 2to3 the problem are made more tricky by making Unicode a issue as well. When using the 2to3 to support Python 2 and Python 3, 2to3 would convert any Unicode literals to straight string literals. Without 2to3 we have no such luxury and since the Unicode Literalu ' are gone in Python 3 we need to find a-a-to-say that We want a Unicode string that works with all versions of Python.



Here, only supporting Python 3.3 would make things much easier for you, because in Python 3.3, the U "literal is back! In the case you can mostly the ignore this section.



But if you need to support Python 3.1 or 3.2, the best-of-do-a Unicode string maker function just like Theb () function in Common migration problems and for Unicode strings instead of binary bytes. The natural name for this function is of Courseu (). We would then use B () instead of the B ' literal, and U () instead of the U ' literal.


Import Sysif Sys.version_info < (3,): Import Codecs def u (x): Return Codecs.unicode_escape_decode (x) [0]els E:def u (x): Return x


This would return a Unicode object in Python 2:


>>> from Makeunicode Import u>>> u (' gif89a ') u ' gif89a '


While it'll return a string object in Python 3:


>>> from Makeunicode Import u>>> u (' gif89a ') ' gif89a '


Here I use the Unicode_escape encoding, because and encodings could fail if you save the file with a different encoding than the one specified in the function. Using Unicode_escape is a bit more work this just typing in the characters and saving the file but it'll be work on diff Erent versions of Python as well as different operating system platforms.



The Unicode_escape encoding would convert all the various ways of entering Unicode characters. The ' \x00 ' syntax, the ' \u0000 ' and even the ' \n{name} ' syntax:


>>> from Makeunicode import u>>> print (U (' \u00dcnic\u00f6de ')) ünic?de>>> print (U (' \xdcnic \n{latin Small Letter O with Diaeresis}de ')) Ünic?de


If you are need to the support Python 2.6 or later there are also from __future__import unicode_literals. This would turn all strings literals in the file into Unicode literals:


>>> from __future__ import unicode_literals>>> type ("A-standard string literal") <type ' Unicode ' >>>> type (b "A binary literal") <type ' str ' >


Both with the __future__ import and the U () function the the binary data type are still called Str and the text type is STI ll called Unicode under Python 2, while under Python 3 they is called bytes and Str.



The best-of-the-around-is-to define-variables; Text_type Andbinary_type, depending on Python version and then we test against that variable.


>>> from __future__ import unicode_literals>>> import sys>>> if sys.version_info < (3,):...     text_type = unicode...     binary_type = str... else:...     text_type = str...     binary_type = bytes>>> isinstance(‘U\xf1ic\xf6de‘, text_type)True


For the handling of binary data you can use the same techniques as discussed inCommonmigration problems.


The Times three is "six"


There is many many more unusual and sometimes subtle differences between python 2 and python 3. Although the techniques mentioned here works for a very them, I definitely recommend you to look at Benjamin petersons mo Dule "Six" [1] It contains a PY3 constant to use when checking for the version of Python, and it contains the above mention Ed B () and U () functions, although the U () function doesn ' t specify an encoding, so is restricted to using ASCII Char Acters. It also have many helpful constants like Text_typeand Binary_type and a print_ () function that works both under PYTHON&NBSP ; 2 and python 3.



It also contains imports of much of the reorganized standard library, so instead of the try/except construct from the Begi Nning of this chapter your can import the module from the six module instead. However it notably doesn ' t support the reorganization of the Urllib and URLLIB2 modules, so there your still need to use th e try/except import technique.



The six module even contains helpers for unusual problems, like using metaclasses and the attributes of functions, which a LSO has been renamed. Although it requires Python 2.4 or later you can use many of the techniques in it even under earlier versions of Python, I f you need to support them.



If you is attempting to support Python 2 and Python 3 without conversion you'll definitely find it very helpful.



Footnotes


[1] Http://pypi.python.org/pypi/six





This address: http://my.oschina.net/soarwilldo/blog/522927



In the lake to smell the Zhang Note:



Original http://python3porting.com/noconv.html



Boot page supporting Python 3: (Support Python3): in-Depth guide



Directory supporting Python 3 (Python 3 supported)--Directory






Supporting Python 3--does not use 2TO3 conversion to support Python 2 and Python 3


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.