Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Making code compatible with Python 2 and 3

Making code compatible with Python 2 and 3

A presentation about writing future-proof code that is compatible with Python 2 and 3.

4cae070608be3489262fe419c03498dc?s=128

Adrián Matellanes

November 30, 2016
Tweet

Transcript

  1. Making code compatible with Python 2 and 3

  2. Adrián Matellanes Lead API Developer at @EburyES & Málaga Python

    Organizer twitter.com/_amatellanes github.com/amatellanes
  3. La Térmica - Málaga Saturday, 6 de May, 2017 Call

    For Papers www.opensouthcode.org
  4. Python 2 Release Schedule

  5. 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/
  6. 2.8 Python 2.8 https://www.python.org/dev/peps/pep-0404/

  7. Why upgrade to Python 3?

  8. Why upgrade to Python 3? Python 2.x is legacy Python

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

    COBOL.” —Alex Gaynor, at PyCon AU 2013
  10. 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
  11. __future__ Future statement definitions

  12. __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
  13. six Python 2 and 3 compatibility utilities

  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. six unittest assertions • six.assertCountEqual() • six.assertRaisesRegex() • six.assertRegex() https://pypi.python.org/pypi/six

  20. 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
  21. 2to3 Automated Python 2 to 3 code translation

  22. 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
  23. 2to3 $ 2to3 example.py https://docs.python.org/2.7/library/2to3.html

  24. 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
  25. future Clean single-source support for Python 3 and 2

  26. 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
  27. Writing Python 2-3 compatible code

  28. 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
  29. Examples

  30. 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')
  31. print # Python 2 only: print >> sys.stderr, 'Hello' #

    Python 2 and 3: from __future__ import print_function print('Hello', file=sys.stderr)
  32. Raising exceptions # Python 2 only: raise ValueError, 'dodgy value'

    # Python 2 and 3: raise ValueError('dodgy value')
  33. Catching exceptions # Python 2 only: Try: ... except ValueError,

    e: ... # Python 2 and 3: try: ... except ValueError as e: ...
  34. Division # Integer division (rounding down): # Python 2 only:

    assert 2 / 3 == 0 # Python 2 and 3: assert 2 // 3 == 0
  35. 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
  36. 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
  37. 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'
  38. 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
  39. Dictionaries # Python 2 only: for key in heights.iterkeys(): ...

    # Python 2 and 3: for key in heights: ...
  40. 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): ...
  41. 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): ...
  42. 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
  43. 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
  44. 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')
  45. 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'
  46. reduce # Python 2 only: from __builtin__ import reduce #

    Python 2 and 3: from functools import reduce
  47. Custom iterators # Python 2 only class Upper(object): def next(self):

    # Py2-style return self._iter.next().upper() assert itr.next() == 'H' # Py2-style
  48. 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'
  49. 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
  50. 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
  51. Thank you! Questions?