nine-1.0.0/0000775000175000017500000000000012720375100012660 5ustar nandonando00000000000000nine-1.0.0/setup.cfg0000664000175000017500000000013012720375100014473 0ustar nandonando00000000000000[bdist_wheel] universal = 1 [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 nine-1.0.0/PKG-INFO0000664000175000017500000002336612720375100013767 0ustar nandonando00000000000000Metadata-Version: 1.1 Name: nine Version: 1.0.0 Summary: Python 2 / 3 compatibility, like six, but favouring Python 3 Home-page: https://github.com/nandoflorestan/nine Author: Nando Florestan Author-email: nandoflorestan@gmail.com License: Public domain Description: Let's write Python 3 right now! =============================== When the best Python 2/Python 3 compatibility modules -- especially the famous `*six* library invented by Benjamin Peterson `_ -- were created, they were written from the point of view of a Python 2 programmer starting to grok Python 3. But it is 2016. Python 3.5 is here. 3.5!!! If you use *six*, your code is compatible, but stuck in Python 2 idioms. **nine** turns **six** upside down. You write your code using Python 3 idioms -- as much as possible --, and it is the Python 2 "version" that is patched. Needless to say, this approach is more future-proof. When thou writeth Python, thou shalt write Python 3 and, just for a little longer, ensure that the thing worketh on Python 2.7. Honestly you should not spend one thought on Python 2.6 anymore, it is `no longer supported `_ since its final release (2.6.9) in October 2013. Nobody uses 3.0 or 3.1 either. Python 2.7 will finally meet its demise in the year 2020. So, starting now, thine codebase shall look more like 3 than 2. *nine* facilitates this point of view. You can write code that is as 3ish as possible while still supporting 2.6. Very comfortable for new projects. For instance, you don't type ``unicode`` anymore, you type ``str``, and *nine* makes ``str`` point to ``unicode`` on Python 2 (if you use our boilerplate). Also, ``map``, ``zip`` and ``filter`` have Python 3 behaviour, on Python 2, meaning they return iterators, not lists. The author(s) of *nine* donate this module to the public domain. To understand most of the intricacies involved in achieving 2&3 compatibility in a single codebase, I recommend reading this: http://lucumr.pocoo.org/2013/5/21/porting-to-python-3-redux/ Using nine ========== In each of your modules, start by declaring a text encoding and importing Python 3 behaviours from __future__. Then import variables from *nine*, as per this boilerplate:: # -*- coding: utf-8 -*- from __future__ import (absolute_import, division, print_function, unicode_literals) from nine import (IS_PYTHON2, str, basestring, native_str, chr, long, integer_types, class_types, range, range_list, reraise, iterkeys, itervalues, iteritems, map, zip, filter, input, implements_iterator, implements_to_string, implements_repr, nine, nimport) I know that is ugly. What did you expect? *nine* is 3 squared. OK, in many cases you can get away with less:: # -*- coding: utf-8 -*- from __future__ import (absolute_import, division, print_function, unicode_literals) from nine import IS_PYTHON2, nimport, nine, range, str, basestring But in the second case you need to remember to import the missing stuff when you use it, and it is not realistic to expect that you will remember, is it? Unicode ======= Because of the ``unicode_literals`` import, **all string literals in the module become unicode objects**. No need to add a "u" prefix to each string literal. This is the saner approach since in Python 3 strings are unicode objects by default, and you can then indicate ``b"this is a byte string literal"``. The literals that actually need to be byte strings are very rare. But you wouldn't believe how many developers are irrationally afraid of taking this simple step... If you don't know much about Unicode, just read `The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) `_ Importing moved stuff ===================== Many standard library modules were renamed in Python 3, but nine can help. The ``nimport`` function gets the new Python 3 name, but knows to import the old name if running in Python 2. For instance, instead of writing this to import pickle:: # Bad: try: import cPickle as pickle # Python 2.x except ImportError: import pickle # Python 3 automatically uses the C version. ...you can write this:: # Good: pickle = nimport('pickle') For variables that have been moved: In the argument, please separate the module from the variable with a colon:: name2codepoint = nimport('html.entities:name2codepoint') Want StringIO? I recommend you build lists instead. But if you really need it:: # Good: if IS_PYTHON2: from cStringIO import StringIO as BytesIO, StringIO NativeStringIO = BytesIO else: from io import BytesIO, StringIO NativeStringIO = StringIO Our coverage of Python version differences probably isn't exhaustive, but contributions are welcome. When in doubt, `use the source `_! See the `project page at GitHub `_! We also have `continuous integration at Travis-CI `_. The *nine* class decorator ========================== We provide a class decorator for Python 2 and 3 compatibility of magic methods. Magic methods are those that start and end with two underlines. You define the magic methods with their Python 3 names and, on Python 2, they get their corresponding names. You may write: * ``__next__()``. Use the ``next(iterator)`` function to iterate. * ``__str__()``: must return a unicode string. In Python 2, we implement ``__unicode__()`` and ``__bytes__()`` for you, based on your ``__str__()``. * ``__repr__()``: must return a unicode string. * ``__bytes__()``: must return a bytes object. Example:: @nine class MyClass(object): def __str__(self): return "MyClass" # a unicode string Porting steps ============= When you are starting to apply *nine* on Python 2 code to achieve Python 3 compatibility, you can start by following this list of tasks. It isn't exhaustive, just a good start. You can upgrade one ``.py`` module at a time: * Add our header as mentioned above. * Replace ocurrences of the print statement with the print function (this roughly means, add parentheses). * Replace ``str()``, usually with nine's ``native_str()`` or with ``bytes()``. * Replace ``unicode()`` with ``str()`` and ``from nine import str`` * Replace ``__unicode__()`` methods with ``__str__()`` methods; apply the ``@nine`` decorator on the class. * Also apply the ``@nine`` decorator on classes that define ``__repr__()``. * Search for ``range`` and replace with nine's ``range`` or ``range_list`` * Some dict methods return different things in Python 3. Only if you need exactly the same behavior in both versions, replace: * ``d.keys()`` or ``d.iterkeys()`` with nine's ``iterkeys(d)``; * ``d.values()`` or ``d.itervalues()`` with nine's ``itervalues(d)``; and * ``d.items()`` or ``d.iteritems()`` with nine's ``iteritems(d)``. * Notice that ``map()``, ``zip()`` and ``filter()``, in nine's versions, always return iterators independently of Python version. If you had been using *six* or another compatibility library before: * Replace ``string_types`` with nine's ``basestring`` Then run your tests in all the Python versions you wish to support. If I forgot to mention anything, could you `make a pull request `_, for the benefit of other developers? Keywords: python 2,python 3,python2,python3,migration,compatibility,nine,six,2to3,3to2,future Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: Public Domain Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 nine-1.0.0/setup.py0000775000175000017500000000336312720375064014413 0ustar nandonando00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # http://peak.telecommunity.com/DevCenter/setuptools#developer-s-guide # from distutils.core import setup from setuptools import setup, find_packages from codecs import open with open('README.rst', encoding='utf-8') as f: long_description = f.read() requires = [] # Python 2.6 does not have importlib, but a package exists for that from sys import version_info if version_info[:2] < (2, 7): requires.append('importlib') setup( name="nine", version='1.0.0', description="Python 2 / 3 compatibility, like six, but favouring Python 3", long_description=long_description, url='https://github.com/nandoflorestan/nine', author='Nando Florestan', author_email="nandoflorestan@gmail.com", license='Public domain', packages=find_packages(), include_package_data=True, zip_safe=False, test_suite='nine', install_requires=requires, keywords=[ "python 2", 'python 3', 'python2', 'python3', 'migration', 'compatibility', 'nine', 'six', '2to3', '3to2', 'future', ], classifiers=[ # http://pypi.python.org/pypi?:action=list_classifiers "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", 'License :: Public Domain', "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", ], ) nine-1.0.0/nine.egg-info/0000775000175000017500000000000012720375100015303 5ustar nandonando00000000000000nine-1.0.0/nine.egg-info/PKG-INFO0000664000175000017500000002336612720375100016412 0ustar nandonando00000000000000Metadata-Version: 1.1 Name: nine Version: 1.0.0 Summary: Python 2 / 3 compatibility, like six, but favouring Python 3 Home-page: https://github.com/nandoflorestan/nine Author: Nando Florestan Author-email: nandoflorestan@gmail.com License: Public domain Description: Let's write Python 3 right now! =============================== When the best Python 2/Python 3 compatibility modules -- especially the famous `*six* library invented by Benjamin Peterson `_ -- were created, they were written from the point of view of a Python 2 programmer starting to grok Python 3. But it is 2016. Python 3.5 is here. 3.5!!! If you use *six*, your code is compatible, but stuck in Python 2 idioms. **nine** turns **six** upside down. You write your code using Python 3 idioms -- as much as possible --, and it is the Python 2 "version" that is patched. Needless to say, this approach is more future-proof. When thou writeth Python, thou shalt write Python 3 and, just for a little longer, ensure that the thing worketh on Python 2.7. Honestly you should not spend one thought on Python 2.6 anymore, it is `no longer supported `_ since its final release (2.6.9) in October 2013. Nobody uses 3.0 or 3.1 either. Python 2.7 will finally meet its demise in the year 2020. So, starting now, thine codebase shall look more like 3 than 2. *nine* facilitates this point of view. You can write code that is as 3ish as possible while still supporting 2.6. Very comfortable for new projects. For instance, you don't type ``unicode`` anymore, you type ``str``, and *nine* makes ``str`` point to ``unicode`` on Python 2 (if you use our boilerplate). Also, ``map``, ``zip`` and ``filter`` have Python 3 behaviour, on Python 2, meaning they return iterators, not lists. The author(s) of *nine* donate this module to the public domain. To understand most of the intricacies involved in achieving 2&3 compatibility in a single codebase, I recommend reading this: http://lucumr.pocoo.org/2013/5/21/porting-to-python-3-redux/ Using nine ========== In each of your modules, start by declaring a text encoding and importing Python 3 behaviours from __future__. Then import variables from *nine*, as per this boilerplate:: # -*- coding: utf-8 -*- from __future__ import (absolute_import, division, print_function, unicode_literals) from nine import (IS_PYTHON2, str, basestring, native_str, chr, long, integer_types, class_types, range, range_list, reraise, iterkeys, itervalues, iteritems, map, zip, filter, input, implements_iterator, implements_to_string, implements_repr, nine, nimport) I know that is ugly. What did you expect? *nine* is 3 squared. OK, in many cases you can get away with less:: # -*- coding: utf-8 -*- from __future__ import (absolute_import, division, print_function, unicode_literals) from nine import IS_PYTHON2, nimport, nine, range, str, basestring But in the second case you need to remember to import the missing stuff when you use it, and it is not realistic to expect that you will remember, is it? Unicode ======= Because of the ``unicode_literals`` import, **all string literals in the module become unicode objects**. No need to add a "u" prefix to each string literal. This is the saner approach since in Python 3 strings are unicode objects by default, and you can then indicate ``b"this is a byte string literal"``. The literals that actually need to be byte strings are very rare. But you wouldn't believe how many developers are irrationally afraid of taking this simple step... If you don't know much about Unicode, just read `The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) `_ Importing moved stuff ===================== Many standard library modules were renamed in Python 3, but nine can help. The ``nimport`` function gets the new Python 3 name, but knows to import the old name if running in Python 2. For instance, instead of writing this to import pickle:: # Bad: try: import cPickle as pickle # Python 2.x except ImportError: import pickle # Python 3 automatically uses the C version. ...you can write this:: # Good: pickle = nimport('pickle') For variables that have been moved: In the argument, please separate the module from the variable with a colon:: name2codepoint = nimport('html.entities:name2codepoint') Want StringIO? I recommend you build lists instead. But if you really need it:: # Good: if IS_PYTHON2: from cStringIO import StringIO as BytesIO, StringIO NativeStringIO = BytesIO else: from io import BytesIO, StringIO NativeStringIO = StringIO Our coverage of Python version differences probably isn't exhaustive, but contributions are welcome. When in doubt, `use the source `_! See the `project page at GitHub `_! We also have `continuous integration at Travis-CI `_. The *nine* class decorator ========================== We provide a class decorator for Python 2 and 3 compatibility of magic methods. Magic methods are those that start and end with two underlines. You define the magic methods with their Python 3 names and, on Python 2, they get their corresponding names. You may write: * ``__next__()``. Use the ``next(iterator)`` function to iterate. * ``__str__()``: must return a unicode string. In Python 2, we implement ``__unicode__()`` and ``__bytes__()`` for you, based on your ``__str__()``. * ``__repr__()``: must return a unicode string. * ``__bytes__()``: must return a bytes object. Example:: @nine class MyClass(object): def __str__(self): return "MyClass" # a unicode string Porting steps ============= When you are starting to apply *nine* on Python 2 code to achieve Python 3 compatibility, you can start by following this list of tasks. It isn't exhaustive, just a good start. You can upgrade one ``.py`` module at a time: * Add our header as mentioned above. * Replace ocurrences of the print statement with the print function (this roughly means, add parentheses). * Replace ``str()``, usually with nine's ``native_str()`` or with ``bytes()``. * Replace ``unicode()`` with ``str()`` and ``from nine import str`` * Replace ``__unicode__()`` methods with ``__str__()`` methods; apply the ``@nine`` decorator on the class. * Also apply the ``@nine`` decorator on classes that define ``__repr__()``. * Search for ``range`` and replace with nine's ``range`` or ``range_list`` * Some dict methods return different things in Python 3. Only if you need exactly the same behavior in both versions, replace: * ``d.keys()`` or ``d.iterkeys()`` with nine's ``iterkeys(d)``; * ``d.values()`` or ``d.itervalues()`` with nine's ``itervalues(d)``; and * ``d.items()`` or ``d.iteritems()`` with nine's ``iteritems(d)``. * Notice that ``map()``, ``zip()`` and ``filter()``, in nine's versions, always return iterators independently of Python version. If you had been using *six* or another compatibility library before: * Replace ``string_types`` with nine's ``basestring`` Then run your tests in all the Python versions you wish to support. If I forgot to mention anything, could you `make a pull request `_, for the benefit of other developers? Keywords: python 2,python 3,python2,python3,migration,compatibility,nine,six,2to3,3to2,future Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: Public Domain Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 nine-1.0.0/nine.egg-info/top_level.txt0000664000175000017500000000000512720375100020030 0ustar nandonando00000000000000nine nine-1.0.0/nine.egg-info/SOURCES.txt0000664000175000017500000000036512720375100017173 0ustar nandonando00000000000000LICENSE.rst MANIFEST.in README.rst setup.cfg setup.py nine/__init__.py nine/decorator.py nine/test_nine.py nine.egg-info/PKG-INFO nine.egg-info/SOURCES.txt nine.egg-info/dependency_links.txt nine.egg-info/not-zip-safe nine.egg-info/top_level.txtnine-1.0.0/nine.egg-info/dependency_links.txt0000664000175000017500000000000112720375100021351 0ustar nandonando00000000000000 nine-1.0.0/nine.egg-info/not-zip-safe0000664000175000017500000000000112720324367017542 0ustar nandonando00000000000000 nine-1.0.0/nine/0000775000175000017500000000000012720375100013611 5ustar nandonando00000000000000nine-1.0.0/nine/decorator.py0000664000175000017500000000341312720322331016144 0ustar nandonando00000000000000# -*- coding: utf-8 -*- '''*nine* is about Python 2 and 3 compatibility, but I have taken the liberty of including something else in it. The *reify* decorator is so useful that it should come with Python, in the standard library. While it doesn't, the next best thing is for it to be included in a "base" package. And *nine* is a base package! The *reify* decorator comes from Pyramid_ source code. This is fair use because it is only one function out of hundreds. .. _Pyramid: https://pypi.python.org/pypi/pyramid ''' from __future__ import (absolute_import, division, print_function, unicode_literals) class reify(object): """ Use as a class method decorator. It operates almost exactly like the Python ``@property`` decorator, but it puts the result of the method it decorates into the instance dict after the first call, effectively replacing the function it decorates with an instance variable. It is, in Python parlance, a non-data descriptor. An example: .. code-block:: python class Foo(object): @reify def jammy(self): print 'jammy called' return 1 And usage of Foo: .. code-block:: text >>> f = Foo() >>> v = f.jammy 'jammy called' >>> print v 1 >>> f.jammy 1 >>> # jammy func not called the second time; it replaced itself with 1 """ def __init__(self, wrapped): self.wrapped = wrapped try: self.__doc__ = wrapped.__doc__ except: # pragma: no cover pass def __get__(self, inst, objtype=None): if inst is None: return self val = self.wrapped(inst) setattr(inst, self.wrapped.__name__, val) return val nine-1.0.0/nine/__init__.py0000664000175000017500000002067412720372450015740 0ustar nandonando00000000000000# -*- coding: utf-8 -*- """For documentation and usage, please see the file README.rst. This module is donated to the public domain. """ import sys # Test for Python 2, not 3; don't get bitten when Python 4 appears: IS_PYTHON2 = (sys.version_info[0] == 2) IS_PYPY = hasattr(sys, 'pypy_translation_info') del sys from importlib import import_module if IS_PYTHON2: # Rename Python 2 builtins so they become like Python 3 native_str = bytes str = unicode basestring = basestring byte_chr = chr # does not seem to have an equivalent in Python 3. chr = unichr # takes an int and returns the corresponding unicode char integer_types = (int, long) long = long from types import ClassType class_types = (type, ClassType) del ClassType range_list = range range = xrange iterkeys = lambda d: d.iterkeys() itervalues = lambda d: d.itervalues() iteritems = lambda d: d.iteritems() from itertools import ifilter as filter, imap as map, izip as zip # In Python 2, *input* was equivalent to eval(raw_input(prompt)): input = raw_input else: # For Python 3, declare these variables so they can be chain imported: basestring = native_str = str = str chr = chr # No need to do the same to ord() integer_types = (int,) long = int class_types = type range = range range_list = lambda *a: list(range(*a)) iterkeys = lambda d: iter(d.keys()) itervalues = lambda d: iter(d.values()) iteritems = lambda d: iter(d.items()) filter = filter map = map zip = zip input = input if IS_PYTHON2: # Turn code into string to avoid SyntaxError on Python 3: exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') else: def reraise(tp, value, tb=None): if value.__traceback__ is not tb: raise value.with_traceback(tb) raise value # ===== Class decorators ===== if IS_PYTHON2: def implements_to_string(cls): """Class decorator that converts __str__() and __bytes__. You define __str__() and it is moved to __unicode__() on Python 2. Additionally, if you define __bytes__(), it becomes __str__() on Python 2. If __bytes__() is not defined, __str__() executes __unicode__() and encodes the result to utf-8. """ cls.__unicode__ = cls.__str__ cls.__str__ = cls.__bytes__ if hasattr(cls, '__bytes__') \ else lambda x: x.__unicode__().encode('utf-8') return cls def implements_iterator(cls): """Class decorator. next() has been renamed to __next__().""" cls.next = cls.__next__ del cls.__next__ return cls def implements_repr(cls): """Class decorator that wraps __repr__() in Python 2. You implement __repr__() returning a unicode string, and in Python 2, I encode it to utf-8 for you. """ cls.__repr_unicode__ = cls.__repr__ def wrapper(self): return self.__repr_unicode__().encode('utf-8') cls.__repr__ = wrapper return cls def nine(cls): """Class decorator for Python 2 and 3 compatibility of magic methods. You define the magic methods with their Python 3 names and, on Python 2, they get their corresponding names. You may write: * __next__(). Use the next(iterator) function to iterate. * __str__(): must return a unicode string. * __repr__(): must return a unicode string. * __bytes__(): must return a bytes object. (*nine* is all the above class decorators in one.) """ if hasattr(cls, '__str__'): cls = implements_to_string(cls) if hasattr(cls, '__next__'): cls = implements_iterator(cls) if hasattr(cls, '__repr__'): cls = implements_repr(cls) return cls else: # On Python 3, these class decorators do nothing: implements_to_string = implements_iterator = implements_repr = nine = \ lambda cls: cls # http://docs.pythonsprints.com/python3_porting/py-porting.html _moved = { # Mapping from Python 3 to Python 2 location. May need improvement. 'builtins': '__builtin__', 'configparser': 'ConfigParser', 'copyreg': 'copy_reg', '_markupbase': 'markupbase', 'pickle': 'cPickle', 'queue': 'Queue', 'reprlib': 'repr', 'socketserver': 'SocketServer', '_thread': 'thread', '_dummy_thread': 'dummy_thread', 'tkinter': 'Tkinter', 'http.client': 'httplib', 'http.cookiejar': 'cookielib', 'http.cookies': 'Cookie', 'html.entities': 'htmlentitydefs', 'html.entities:entitydefs': 'htmlentitydefs:entitydefs', 'html.entities:name2codepoint': 'htmlentitydefs:name2codepoint', 'html.entities:codepoint2name': 'htmlentitydefs:codepoint2name', 'html:escape': 'cgi:escape', 'html.parser:HTMLParser': 'htmllib:HTMLParser', 'urllib.robotparser': 'robotparser', 'urllib.error:ContentTooShortError': 'urllib:ContentTooShortError', 'urllib.parse': 'urlparse', 'urllib.parse:quote': 'urllib:quote', 'urllib.parse:quote_plus': 'urllib:quote_plus', 'urllib.parse:unquote': 'urllib:unquote', 'urllib.parse:unquote_plus': 'urllib:unquote_plus', 'urllib.parse:urlencode': 'urllib:urlencode', 'urllib.request:getproxies': 'urllib:getproxies', 'urllib.request:pathname2url': 'urllib:pathname2url', 'urllib.request:url2pathname': 'urllib:url2pathname', 'urllib.request:urlcleanup': 'urllib:urlcleanup', 'urllib.request:urlretrieve': 'urllib:urlretrieve', 'urllib.request:URLopener': 'urllib:URLopener', 'urllib.request:FancyURLopener': 'urllib:FancyURLopener', 'urllib.request:urlopen': 'urllib2:urlopen', 'urllib.request:install_opener': 'urllib2:install_opener', 'urllib.request:build_opener': 'urllib2:build_opener', 'urllib.error:URLError': 'urllib2:URLError', 'urllib.error:HTTPError': 'urllib2:HTTPError', 'urllib.request:Request': 'urllib2:Request', 'urllib.request:OpenerDirector': 'urllib2:OpenerDirector', 'urllib.request:BaseHandler': 'urllib2:BaseHandler', 'urllib.request:HTTPDefaultErrorHandler': 'urllib2:HTTPDefaultErrorHandler', 'urllib.request:HTTPRedirectHandler': 'urllib2:HTTPRedirectHandler', 'urllib.request:HTTPCookieProcessor': 'urllib2:HTTPCookieProcessor', 'urllib.request:ProxyHandler': 'urllib2:ProxyHandler', 'urllib.request:HTTPPasswordMgr': 'urllib2:HTTPPasswordMgr', 'urllib.request:HTTPPasswordMgrWithDefaultRealm': 'urllib2:HTTPPasswordMgrWithDefaultRealm', 'urllib.request:AbstractBasicAuthHandler': 'urllib2:AbstractBasicAuthHandler', 'urllib.request:HTTPBasicAuthHandler': 'urllib2:HTTPBasicAuthHandler', 'urllib.request:ProxyBasicAuthHandler': 'urllib2:ProxyBasicAuthHandler', 'urllib.request:AbstractDigestAuthHandler': 'urllib2:AbstractDigestAuthHandler', 'urllib.request:HTTPDigestAuthHandler': 'urllib2:HTTPDigestAuthHandler', 'urllib.request:ProxyDigestAuthHandler': 'urllib2:ProxyDigestAuthHandler', 'urllib.request:HTTPHandler': 'urllib2:HTTPHandler', 'urllib.request:HTTPSHandler': 'urllib2:HTTPSHandler', 'urllib.request:FileHandler': 'urllib2:FileHandler', 'urllib.request:FTPHandler': 'urllib2:FTPHandler', 'urllib.request:CacheFTPHandler': 'urllib2:CacheFTPHandler', 'urllib.request:UnknownHandler': 'urllib2:UnknownHandler', } def nimport(spec): """Given a Python 3 resource spec, imports and returns it. Example usage:: join = nimport('os.path:join') The ":" indicates "join" is a variable in the module "os.path". The spec should provide the **new** location of the module or variable. *nine* is supposed to know the corresponding, old Python 2 location. Bug reports and pull requests are welcome. """ assert spec if IS_PYTHON2: # Get the Python 2 location of the name, first. spec = _moved.get(spec, spec) alist = spec.split(':') if len(alist) > 2: raise ValueError( 'The argument *spec* cannot have more than ' '2 colon-separated parts: "{}"'.format(spec)) elif len(alist) == 2: module, name = alist elif len(alist) == 1: module = alist[0] name = None module = import_module(module) return getattr(module, name) if name else module nine-1.0.0/nine/test_nine.py0000664000175000017500000000135512720324455016166 0ustar nandonando00000000000000# -*- coding: utf-8 -*- '''This module is donated to the public domain.''' from __future__ import (absolute_import, division, print_function, unicode_literals) import unittest class TestNine(unittest.TestCase): def test_import(self): from nine import ( IS_PYTHON2, str, basestring, native_str, integer_types, class_types, range, range_list, reraise, iterkeys, itervalues, iteritems, map, zip, filter, input, implements_iterator, implements_to_string, implements_repr, nine, nimport, _moved) for key in _moved: if key == 'tkinter': continue # travis does not have tk installed :p assert nimport(key) nine-1.0.0/LICENSE.rst0000664000175000017500000000037712720322462014506 0ustar nandonando00000000000000I, the copyright holder of this work, hereby release it into the public domain. This applies worldwide. In case this is not legally possible, I grant any entity the right to use, modify and redistribute this work for any purpose, without any conditions. nine-1.0.0/README.rst0000664000175000017500000001642312720371427014365 0ustar nandonando00000000000000Let's write Python 3 right now! =============================== When the best Python 2/Python 3 compatibility modules -- especially the famous `*six* library invented by Benjamin Peterson `_ -- were created, they were written from the point of view of a Python 2 programmer starting to grok Python 3. But it is 2016. Python 3.5 is here. 3.5!!! If you use *six*, your code is compatible, but stuck in Python 2 idioms. **nine** turns **six** upside down. You write your code using Python 3 idioms -- as much as possible --, and it is the Python 2 "version" that is patched. Needless to say, this approach is more future-proof. When thou writeth Python, thou shalt write Python 3 and, just for a little longer, ensure that the thing worketh on Python 2.7. Honestly you should not spend one thought on Python 2.6 anymore, it is `no longer supported `_ since its final release (2.6.9) in October 2013. Nobody uses 3.0 or 3.1 either. Python 2.7 will finally meet its demise in the year 2020. So, starting now, thine codebase shall look more like 3 than 2. *nine* facilitates this point of view. You can write code that is as 3ish as possible while still supporting 2.6. Very comfortable for new projects. For instance, you don't type ``unicode`` anymore, you type ``str``, and *nine* makes ``str`` point to ``unicode`` on Python 2 (if you use our boilerplate). Also, ``map``, ``zip`` and ``filter`` have Python 3 behaviour, on Python 2, meaning they return iterators, not lists. The author(s) of *nine* donate this module to the public domain. To understand most of the intricacies involved in achieving 2&3 compatibility in a single codebase, I recommend reading this: http://lucumr.pocoo.org/2013/5/21/porting-to-python-3-redux/ Using nine ========== In each of your modules, start by declaring a text encoding and importing Python 3 behaviours from __future__. Then import variables from *nine*, as per this boilerplate:: # -*- coding: utf-8 -*- from __future__ import (absolute_import, division, print_function, unicode_literals) from nine import (IS_PYTHON2, str, basestring, native_str, chr, long, integer_types, class_types, range, range_list, reraise, iterkeys, itervalues, iteritems, map, zip, filter, input, implements_iterator, implements_to_string, implements_repr, nine, nimport) I know that is ugly. What did you expect? *nine* is 3 squared. OK, in many cases you can get away with less:: # -*- coding: utf-8 -*- from __future__ import (absolute_import, division, print_function, unicode_literals) from nine import IS_PYTHON2, nimport, nine, range, str, basestring But in the second case you need to remember to import the missing stuff when you use it, and it is not realistic to expect that you will remember, is it? Unicode ======= Because of the ``unicode_literals`` import, **all string literals in the module become unicode objects**. No need to add a "u" prefix to each string literal. This is the saner approach since in Python 3 strings are unicode objects by default, and you can then indicate ``b"this is a byte string literal"``. The literals that actually need to be byte strings are very rare. But you wouldn't believe how many developers are irrationally afraid of taking this simple step... If you don't know much about Unicode, just read `The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) `_ Importing moved stuff ===================== Many standard library modules were renamed in Python 3, but nine can help. The ``nimport`` function gets the new Python 3 name, but knows to import the old name if running in Python 2. For instance, instead of writing this to import pickle:: # Bad: try: import cPickle as pickle # Python 2.x except ImportError: import pickle # Python 3 automatically uses the C version. ...you can write this:: # Good: pickle = nimport('pickle') For variables that have been moved: In the argument, please separate the module from the variable with a colon:: name2codepoint = nimport('html.entities:name2codepoint') Want StringIO? I recommend you build lists instead. But if you really need it:: # Good: if IS_PYTHON2: from cStringIO import StringIO as BytesIO, StringIO NativeStringIO = BytesIO else: from io import BytesIO, StringIO NativeStringIO = StringIO Our coverage of Python version differences probably isn't exhaustive, but contributions are welcome. When in doubt, `use the source `_! See the `project page at GitHub `_! We also have `continuous integration at Travis-CI `_. The *nine* class decorator ========================== We provide a class decorator for Python 2 and 3 compatibility of magic methods. Magic methods are those that start and end with two underlines. You define the magic methods with their Python 3 names and, on Python 2, they get their corresponding names. You may write: * ``__next__()``. Use the ``next(iterator)`` function to iterate. * ``__str__()``: must return a unicode string. In Python 2, we implement ``__unicode__()`` and ``__bytes__()`` for you, based on your ``__str__()``. * ``__repr__()``: must return a unicode string. * ``__bytes__()``: must return a bytes object. Example:: @nine class MyClass(object): def __str__(self): return "MyClass" # a unicode string Porting steps ============= When you are starting to apply *nine* on Python 2 code to achieve Python 3 compatibility, you can start by following this list of tasks. It isn't exhaustive, just a good start. You can upgrade one ``.py`` module at a time: * Add our header as mentioned above. * Replace ocurrences of the print statement with the print function (this roughly means, add parentheses). * Replace ``str()``, usually with nine's ``native_str()`` or with ``bytes()``. * Replace ``unicode()`` with ``str()`` and ``from nine import str`` * Replace ``__unicode__()`` methods with ``__str__()`` methods; apply the ``@nine`` decorator on the class. * Also apply the ``@nine`` decorator on classes that define ``__repr__()``. * Search for ``range`` and replace with nine's ``range`` or ``range_list`` * Some dict methods return different things in Python 3. Only if you need exactly the same behavior in both versions, replace: * ``d.keys()`` or ``d.iterkeys()`` with nine's ``iterkeys(d)``; * ``d.values()`` or ``d.itervalues()`` with nine's ``itervalues(d)``; and * ``d.items()`` or ``d.iteritems()`` with nine's ``iteritems(d)``. * Notice that ``map()``, ``zip()`` and ``filter()``, in nine's versions, always return iterators independently of Python version. If you had been using *six* or another compatibility library before: * Replace ``string_types`` with nine's ``basestring`` Then run your tests in all the Python versions you wish to support. If I forgot to mention anything, could you `make a pull request `_, for the benefit of other developers? nine-1.0.0/MANIFEST.in0000664000175000017500000000022512720322331014413 0ustar nandonando00000000000000# http://docs.python.org/3/distutils/sourcedist.html#specifying-the-files-to-distribute include *.rst recursive-include nine * global-exclude *.pyc