PKó}9Gb®į«ˆˆ pretext.pyfrom __future__ import print_function try: import builtins except ImportError: import __builtin__ as builtins try: import reprlib except ImportError: import repr as reprlib import sys __all__ = [ 'activate', 'Repr', 'PrefixRepr', 'BytesRepr', 'UnicodeRepr', 'prepr', 'urepr', 'pdisplayhook', 'bdisplayhook', 'udisplayhook', ] class Repr(reprlib.Repr): """A Repr that doesn't truncate it's output >>> myrepr = Repr().repr >>> myrepr('0123456789') "'0123456789'" >>> len(myrepr('0123456789')) 12 >>> len(myrepr('0123456789'*1000)) 10002 """ def __init__(self): self.maxlevel = sys.maxsize self.maxdict = sys.maxsize self.maxlist = sys.maxsize self.maxset = sys.maxsize self.maxfrozenset = sys.maxsize self.maxdeque = sys.maxsize self.maxarray = sys.maxsize self.maxlong = sys.maxsize self.maxstring = sys.maxsize self.maxlong = sys.maxsize self.maxtuple = sys.maxsize self.maxother = sys.maxsize class PrefixRepr(Repr): """A Repr that always prefixes any string, and doesn't truncate For byte strings this mirrors the behaviour of repr() in Python 3.x. For textual strings this mirrors the behaviour of repr() in Python 2.x. >>> prefix_repr = PrefixRepr().repr >>> prefix_repr(b'0123456789') "b'0123456789'" >>> prefix_repr(u'0123456789') "u'0123456789'" """ def __init__(self): Repr.__init__(self) self._old_repr = repr def repr_str(self, obj, level): """Return the repr of a string, prefixed with b or u """ if str is bytes: # Python 2.x return b'b%s' % (self._old_repr(obj),) else: # Python 3.x return u'u%s' % (self._old_repr(obj),) def repr_bytes(self, obj, level): return self._old_repr(obj) def repr_unicode(self, obj, level): return self._old_repr(obj) class BytesRepr(PrefixRepr): """A Repr that prefixes byte strings, and doesn't truncate This mirrors the behaviour of repr() in Python 3.x. >>> py3_repr = BytesRepr().repr >>> py3_repr(b'0123456789') "b'0123456789'" >>> py3_repr(u'0123456789') "'0123456789'" """ def repr_str(self, obj, level): if str is bytes: # Python 2.x return b'b%s' % (self._old_repr(obj),) else: # Python 3.x return self._old_repr(obj) def repr_unicode(self, obj, level): # Only Python 2.x has a builtin type called unicode r = self._old_repr(obj) assert r[0] == 'u' return r[1:] class UnicodeRepr(PrefixRepr): """A Repr that prefixes textual (unicode) strings & doesn't truncate This mirrors the behaviour of repr() in Python 2.x. >>> py2_repr = UnicodeRepr().repr >>> py2_repr(b'0123456789') "'0123456789'" >>> py2_repr(u'0123456789') "u'0123456789'" """ def repr_str(self, obj, level): if str is bytes: return self._old_repr(obj) else: return u'u%s' % (self._old_repr(obj),) def repr_bytes(self, obj, level): r = self._old_repr(obj) assert r[0] == 'b' return r[1:] prepr = PrefixRepr().repr brepr = BytesRepr().repr urepr = UnicodeRepr().repr def _displayhook(value, repr_fn): if value is None: return # Set '_' to None to avoid recursion builtins._ = None text = repr_fn(value) try: sys.stdout.write(text) except UnicodeEncodeError: bytes = text.encode(sys.stdout.encoding, 'backslashreplace') if hasattr(sys.stdout, 'buffer'): sys.stdout.buffer.write(bytes) else: text = bytes.decode(sys.stdout.encoding, 'strict') sys.stdout.write(text) sys.stdout.write("\n") builtins._ = value def pdisplayhook(value): _displayhook(value, prepr) def bdisplayhook(value): _displayhook(value, brepr) def udisplayhook(value): _displayhook(value, urepr) def activate(repr=brepr, displayhook=bdisplayhook): """Replace the builtin repr & sys.displayhook with versions that prefix The defaults causes the repr() of any string to be the same as it would on Python 3.x. So after `activate()` is called `repr(b'foo') == "b'foo'"` and `repr(u'bar') == "'bar'"`. Even on Python 2.x. >>> import pretext; pretext.activate() >>> b'Now your byte-string doctests can work on Python 2.6, 2.7, & 3.3+' b'Now your byte-string doctests can work on Python 2.6, 2.7, & 3.3+' >>> u"By default unicode strings aren't prefixed, just like on Python 3.x" "By default unicode strings aren't prefixed, just like on Python 3.x" >>> pretext.activate(pretext.urepr, pretext.udisplayhook) >>> [b'However you can choose Python 2.x', u'behaviour if you prefer'] ['However you can choose Python 2.x', u'behaviour if you prefer'] >>> pretext.activate(pretext.prepr, pretext.pdisplayhook) >>> (b'Or prefix all the strings', u'all the time') (b'Or prefix all the strings', u'all the time') """ builtins.repr = repr sys.displayhook = displayhook PKµ„9Gę'2–š š 'pretext-0.0.3.dist-info/DESCRIPTION.rstpretext ======= .. image:: https://travis-ci.org/moreati/b-prefix-all-the-doctests.svg :target: https://travis-ci.org/moreati/b-prefix-all-the-doctests This package makes it easy to write doctests that involve strings, and still have those doctests work with Python 2.6, 2.7, and 3.3+. Just `import pretext` and call `pretext.activate()`. By default Python 3.x `repr()` behaviour is used .. code:: python >>> import pretext; pretext.activate() >>> b'Now strings have a consistant repr on Python 2.x & 3.x' b'Now strings have a consistant repr on all Python versions' >>> u'Unicode strings & nested strings work too'.split() ['Unicode', 'strings', '&', 'nested', 'strings', 'work', 'too'] The problem ----------- Suppose you have the following doctest, and you aren't using pretext .. code:: python >>> textfunc() u'I return a textual (unicode) string' >>> bytesfunc() b'I return a byte (binary) string' The ``textfunc()`` case will pass on Python 2.x, but fail on Python 3.x. Because doctest will compare ``repr(textfunc())`` with the expected value, which on Python 3.x will not include a ``u''`` prefix. The ``bytesfunc()`` case will fail on Python 2.x and pass on Python 3.x. Because on Python 2.x ``repr(bytesfunc())`` won't include the ``b''`` prefix. If the tests cases are editted to remove the prefixes, i.e. .. code:: python >>> textfunc() 'I return a textual (unicode) string' >>> bytesfunc() 'I return a byte (binary) string' then the failures will be reversed. ``textfunc()`` will now fail on Python 2.x, ``bytesfunc()`` will fail on Python 3.x. The hack -------- Replace ``repr()`` and ``sys.displayhook`` with versions that always prefix string literals, regardless of the Python version. Now the doctests can - directly show the string values returned by functions/methods, without resorting to ``print()``, or ``.encode()`` etc - successfully test the examples on all Python versions Proof of concept: .. code:: python r""" >>> import sys >>> import pretext >>> myrepr = bar.PrefixRepr() >>> repr = myrepr.repr >>> def _displayhook(value): ... if value is not None: ... sys.stdout.write(myrepr.repr(value)) >>> sys.displayhook = _displayhook >>> u'' u'' >>> b'' b'' >>> bytes() b'' >>> b'\0' b'\x00' >>> b"'" b"'" """ Alternatives ------------ If you're ready to run screaming at the above, there are alternatives e.g. - Wrap byte-string returns in ``bytearray()``. ``repr(bytearray(b'abc')) == "bytearray(b'abc'))"`` on all versions of python that have ``bytearray()`` (2.6 onward) e.g. .. code:: python >>> bytearray(bytesfunc()) bytearray(b'I return a byte (binary) string') - Support Python 3.x exclusively - Use ``print(bytesfunc().decode('ascii'))`` and choose your input values carefully - Use ``#doctest: +SKIP`` - Use ``#doctest: +ELLIPSIS`` PKµ„9G.d’’%pretext-0.0.3.dist-info/metadata.json{"classifiers": ["Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Documentation", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Testing"], "extensions": {"python.details": {"contacts": [{"email": "alex@moreati.org.uk", "name": "Alex Willmer", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/moreati/b-prefix-all-the-doctests"}}}, "generator": "bdist_wheel (0.24.0)", "keywords": ["doctest", "bytes", "unicode", "bytestring", "prefix", "literal", "string", "str"], "license": "Apache Software License 2.0", "metadata_version": "2.0", "name": "pretext", "summary": "Use doctest with bytes, str & unicode on Python 2.x and 3.x", "version": "0.0.3"}PK“„9GOpaŒ// pretext-0.0.3.dist-info/pbr.json{"is_release": false, "git_version": "f09de5d"}PK“„9G(>> import pretext; pretext.activate() >>> b'Now strings have a consistant repr on Python 2.x & 3.x' b'Now strings have a consistant repr on all Python versions' >>> u'Unicode strings & nested strings work too'.split() ['Unicode', 'strings', '&', 'nested', 'strings', 'work', 'too'] The problem ----------- Suppose you have the following doctest, and you aren't using pretext .. code:: python >>> textfunc() u'I return a textual (unicode) string' >>> bytesfunc() b'I return a byte (binary) string' The ``textfunc()`` case will pass on Python 2.x, but fail on Python 3.x. Because doctest will compare ``repr(textfunc())`` with the expected value, which on Python 3.x will not include a ``u''`` prefix. The ``bytesfunc()`` case will fail on Python 2.x and pass on Python 3.x. Because on Python 2.x ``repr(bytesfunc())`` won't include the ``b''`` prefix. If the tests cases are editted to remove the prefixes, i.e. .. code:: python >>> textfunc() 'I return a textual (unicode) string' >>> bytesfunc() 'I return a byte (binary) string' then the failures will be reversed. ``textfunc()`` will now fail on Python 2.x, ``bytesfunc()`` will fail on Python 3.x. The hack -------- Replace ``repr()`` and ``sys.displayhook`` with versions that always prefix string literals, regardless of the Python version. Now the doctests can - directly show the string values returned by functions/methods, without resorting to ``print()``, or ``.encode()`` etc - successfully test the examples on all Python versions Proof of concept: .. code:: python r""" >>> import sys >>> import pretext >>> myrepr = bar.PrefixRepr() >>> repr = myrepr.repr >>> def _displayhook(value): ... if value is not None: ... sys.stdout.write(myrepr.repr(value)) >>> sys.displayhook = _displayhook >>> u'' u'' >>> b'' b'' >>> bytes() b'' >>> b'\0' b'\x00' >>> b"'" b"'" """ Alternatives ------------ If you're ready to run screaming at the above, there are alternatives e.g. - Wrap byte-string returns in ``bytearray()``. ``repr(bytearray(b'abc')) == "bytearray(b'abc'))"`` on all versions of python that have ``bytearray()`` (2.6 onward) e.g. .. code:: python >>> bytearray(bytesfunc()) bytearray(b'I return a byte (binary) string') - Support Python 3.x exclusively - Use ``print(bytesfunc().decode('ascii'))`` and choose your input values carefully - Use ``#doctest: +SKIP`` - Use ``#doctest: +ELLIPSIS`` PKµ„9GšÕΊŠpretext-0.0.3.dist-info/RECORDpretext.py,sha256=06hZqwaU_slfFNet11u_3xHsA4Rh7P4nCzmEyWNr-gY,5256 pretext-0.0.3.dist-info/WHEEL,sha256=AvR0WeTpDaxT645bl5FQxUK6NPsTls2ttpcGJg3j1Xg,110 pretext-0.0.3.dist-info/RECORD,, pretext-0.0.3.dist-info/METADATA,sha256=mJuzRhVyC0o_PMBi1J3y_Iw_q-fCkw5wvhk3IPfPJ-A,4162 pretext-0.0.3.dist-info/top_level.txt,sha256=AdEydxGeSzbdOGBTpuaF1mBJhaZAUr1Cq-dMZGW9p80,8 pretext-0.0.3.dist-info/DESCRIPTION.rst,sha256=Z6gL0YX-OAK4MvdoT5CB9LhWQxeDnPznuq2t6A8X-Uw,2970 pretext-0.0.3.dist-info/metadata.json,sha256=bQQRwV8iQEJjyVG5WUlxoGMPlrG52IROU904960KuaY,1279 pretext-0.0.3.dist-info/pbr.json,sha256=aZs4p3ScB928VdXPOOks5Kxm3WCManGroYJss8aBe8Q,47 PKó}9Gb®į«ˆˆ pretext.pyPKµ„9Gę'2–š š '°pretext-0.0.3.dist-info/DESCRIPTION.rstPKµ„9G.d’’% pretext-0.0.3.dist-info/metadata.jsonPK“„9GOpaŒ// Ń%pretext-0.0.3.dist-info/pbr.jsonPK“„9G(&pretext-0.0.3.dist-info/top_level.txtPKµ„9G‡3onn‰&pretext-0.0.3.dist-info/WHEELPKµ„9G:[ÜBB 2'pretext-0.0.3.dist-info/METADATAPKµ„9GšÕΊŠ²7pretext-0.0.3.dist-info/RECORDPKfx: