Slide 1

Slide 1 text

Making code compatible with Python 2 and 3

Slide 2

Slide 2 text

Adrián Matellanes Lead API Developer at @EburyES & Málaga Python Organizer twitter.com/_amatellanes github.com/amatellanes

Slide 3

Slide 3 text

La Térmica - Málaga Saturday, 6 de May, 2017 Call For Papers www.opensouthcode.org

Slide 4

Slide 4 text

Python 2 Release Schedule

Slide 5

Slide 5 text

The End Of Life date for Python 2.7 Python 2.7 will retire in 2020 https://pythonclock.org/ https://www.python.org/dev/peps/pep-0373/

Slide 6

Slide 6 text

2.8 Python 2.8 https://www.python.org/dev/peps/pep-0404/

Slide 7

Slide 7 text

Why upgrade to Python 3?

Slide 8

Slide 8 text

Why upgrade to Python 3? Python 2.x is legacy Python 3.x is the present and future of the language

Slide 9

Slide 9 text

Why upgrade to Python 3? “Python 2 is the next COBOL.” —Alex Gaynor, at PyCon AU 2013

Slide 10

Slide 10 text

Why upgrade to Python 3? ● Python 3 support for 360 most downloaded packages on PyPI http://py3readiness.org/ ● Python 3 "Wall of Superpowers" https://python3wos.appspot.com/ ● caniusepython3 https://pypi.python.org/pypi/caniusepython3 $ caniusepython3 -r requirements.txt test-requirement.txt

Slide 11

Slide 11 text

__future__ Future statement definitions

Slide 12

Slide 12 text

__future__ nested_scopes PEP 227: Statically Nested Scopes generators PEP 255: Simple Generators division PEP 238: Changing the Division Operator absolute_import PEP 328: Imports: Multi-Line and Absolute/Relative with_statement PEP 343: The “with” Statement print_function PEP 3105: Make print a function unicode_literals PEP 3112: Bytes literals in Python 3000 https://docs.python.org/2.7/library/__future__.html

Slide 13

Slide 13 text

six Python 2 and 3 compatibility utilities

Slide 14

Slide 14 text

six Constants ● six.PY2, six.PY3 ● six.class_types ● six.integer_types ● six.string_types ● six.text_type ● six.binary_type ● six.MAXSIZE https://pypi.python.org/pypi/six

Slide 15

Slide 15 text

six Object model compatibility ● six.next(it) ● six.callable(obj) ● six.iterkeys(dict, **kwargs) ● six.itervalues(dict, **kwargs) ● six.viewkeys(dict) ● six.viewvalues(dict) ● … https://pypi.python.org/pypi/six

Slide 16

Slide 16 text

six Renamed modules and attributes compatibility ● six.moves Name Python 2 name Python 3 name html_parser HTMLParser html.parser >>> from six.moves import html_parser https://pypi.python.org/pypi/six

Slide 17

Slide 17 text

six Syntax compatibility ● six.exec_(code, globals=None, locals=None) ● six.print_(*args, *, file=sys.stdout, end="\n", sep=" ", flush=False) ● six.with_metaclass(metaclass, *bases), @six.add_metaclass(metaclass) ● … https://pypi.python.org/pypi/six

Slide 18

Slide 18 text

six Binary and text data ● six.b(data) ● six.u(text) ● six.StringIO ● six.BytesIO ● @six.python_2_unicode_compatible ● ... https://pypi.python.org/pypi/six

Slide 19

Slide 19 text

six unittest assertions ● six.assertCountEqual() ● six.assertRaisesRegex() ● six.assertRegex() https://pypi.python.org/pypi/six

Slide 20

Slide 20 text

six Django Support ● A customized version of six is bundled with Django as of version 1.4.2. ● django.utils.six https://docs.djangoproject.com/en/1.10/topics/python3/#writing-compatible-code-with-six

Slide 21

Slide 21 text

2to3 Automated Python 2 to 3 code translation

Slide 22

Slide 22 text

2to3 # example.py def greet(name): print 'Hello, {0}!'.format(name) print 'What's your name?' name = raw_input() greet(name) https://docs.python.org/2.7/library/2to3.html

Slide 23

Slide 23 text

2to3 $ 2to3 example.py https://docs.python.org/2.7/library/2to3.html

Slide 24

Slide 24 text

2to3 # example.py def greet(name): print('Hello, {0}!'.format(name)) print('What's your name?') name = input() greet(name) https://docs.python.org/2.7/library/2to3.html

Slide 25

Slide 25 text

future Clean single-source support for Python 3 and 2

Slide 26

Slide 26 text

future ● future.builtins ● past.builtins ● future.standard_library and future.moves ● futurize and pasteurize ● Support for directly importing 30 standard library modules under their Python 3 names on Python 2 https://pypi.python.org/pypi/future

Slide 27

Slide 27 text

Writing Python 2-3 compatible code

Slide 28

Slide 28 text

Writing Python 2-3 compatible code ● Only worry about supporting Python 2.7 ● Using __future__ ● Make sure you have good test coverage ● Use pylint to help make sure you don’t regress on your Python 3 support ● Use caniusepython3 to find out which of your dependencies are blocking Python 3 ● tox can help test against multiple versions of Python ● Use 2to3 to rewrite your code to run only under Python 3

Slide 29

Slide 29 text

Examples

Slide 30

Slide 30 text

print # Python 2 only: print 'Hello' # Python 2 and 3: print('Hello') # Python 2 and 3: from __future__ import print_function # at top of module print('Hello', 'Guido')

Slide 31

Slide 31 text

print # Python 2 only: print >> sys.stderr, 'Hello' # Python 2 and 3: from __future__ import print_function print('Hello', file=sys.stderr)

Slide 32

Slide 32 text

Raising exceptions # Python 2 only: raise ValueError, 'dodgy value' # Python 2 and 3: raise ValueError('dodgy value')

Slide 33

Slide 33 text

Catching exceptions # Python 2 only: Try: ... except ValueError, e: ... # Python 2 and 3: try: ... except ValueError as e: ...

Slide 34

Slide 34 text

Division # Integer division (rounding down): # Python 2 only: assert 2 / 3 == 0 # Python 2 and 3: assert 2 // 3 == 0

Slide 35

Slide 35 text

Division # True division (float division): # Python 3 only: assert 3 / 2 == 1.5 # Python 2 and 3: from __future__ import division # at top of module assert 3 / 2 == 1.5

Slide 36

Slide 36 text

Unicode string literals # Python 2 and 3 s1 = u'The Zen of Python' s2 = u'きたないのよりきれいな方がいい \n' # Python 2 and 3 from __future__ import unicode_literals http://python-future.org/unicode_literals

Slide 37

Slide 37 text

Byte-string literals # Python 2 only s = 'This must be a byte-string' # Python 2 and 3 s = b'This must be a byte-string'

Slide 38

Slide 38 text

StringIO # Python 2 only: from StringIO import StringIO # or: from cStringIO import StringIO # Python 2 and 3: from io import BytesIO # for handling byte strings from io import StringIO # for handling unicode strings

Slide 39

Slide 39 text

Dictionaries # Python 2 only: for key in heights.iterkeys(): ... # Python 2 and 3: for key in heights: ...

Slide 40

Slide 40 text

Dictionaries # Python 2 only: for value in heights.itervalues(): ... # Idiomatic Python 3 for value in heights.values(): # extra memory overhead on Py2 ... # Python 2 and 3: option 2 from six import itervalues for key in itervalues(heights): ...

Slide 41

Slide 41 text

Dictionaries # Python 2 only: for (key, value) in heights.iteritems(): ... # Python 2 and 3: option 1 for (key, value) in heights.items(): # inefficient on Py2 ... # Python 2 and 3: option 3 from six import iteritems for (key, value) in iteritems(heights): ...

Slide 42

Slide 42 text

imap # Python 2 only: from itertools import imap myiter = imap(func, mylist) # Python 3 only: myiter = map(func, mylist) # Python 2 and 3: option 2 try: import itertools.imap as map except ImportError: pass

Slide 43

Slide 43 text

Metaclasses # Python 2 only: class Form(BaseForm): __metaclass__ = FormType # Python 3 only: class Form(BaseForm, metaclass=FormType): pass # Python 2 and 3: from six import with_metaclass class Form(with_metaclass(FormType, BaseForm)): pass

Slide 44

Slide 44 text

Custom __str__ methods # Python 2 only: class MyClass(object): def __unicode__(self): return 'Unicode string: \u5b54\u5b50' def __str__(self): return unicode(self).encode('utf-8')

Slide 45

Slide 45 text

Custom __str__ methods # Python 2 and 3: from future.utils import python_2_unicode_compatible @python_2_unicode_compatible class MyClass(object): def __str__(self): return u'Unicode string: \u5b54\u5b50'

Slide 46

Slide 46 text

reduce # Python 2 only: from __builtin__ import reduce # Python 2 and 3: from functools import reduce

Slide 47

Slide 47 text

Custom iterators # Python 2 only class Upper(object): def next(self): # Py2-style return self._iter.next().upper() assert itr.next() == 'H' # Py2-style

Slide 48

Slide 48 text

Custom iterators # Python 2 and 3: from future.utils import implements_iterator @implements_iterator class Upper(object): def __next__(self): # Py3-style iterator interface return next(self._iter).upper() assert next(itr) == 'H'

Slide 49

Slide 49 text

urllib module # Python 2 only: from urllib import urlencode # Python 3 only: from urllib.parse import urlencode # Python 2 and 3: easiest option from future.standard_library import install_aliases install_aliases() from urllib.parse import urlencode

Slide 50

Slide 50 text

References ● “Supporting Python 3: An in-depth guide” Lennart Regebro ● “Dive Into Python 3” Appendix A: Porting Code to Python 3 using 2to3 Mark Pilgrim

Slide 51

Slide 51 text

Thank you! Questions?