119 lines
3.5 KiB
Python
119 lines
3.5 KiB
Python
""":mod:`wand.compat` --- Compatibility layer
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
This module provides several subtle things to support
|
|
multiple Python versions (2.6, 2.7, 3.2--3.5) and VM implementations
|
|
(CPython, PyPy).
|
|
|
|
"""
|
|
import contextlib
|
|
import io
|
|
import sys
|
|
import types
|
|
|
|
__all__ = ('PY3', 'binary', 'binary_type', 'encode_filename', 'file_types',
|
|
'nested', 'string_type', 'text', 'text_type', 'xrange')
|
|
|
|
|
|
#: (:class:`bool`) Whether it is Python 3.x or not.
|
|
PY3 = sys.version_info >= (3,)
|
|
|
|
#: (:class:`type`) Type for representing binary data. :class:`str` in Python 2
|
|
#: and :class:`bytes` in Python 3.
|
|
binary_type = bytes if PY3 else str
|
|
|
|
#: (:class:`type`) Type for text data. :class:`basestring` in Python 2
|
|
#: and :class:`str` in Python 3.
|
|
string_type = str if PY3 else basestring # noqa
|
|
|
|
#: (:class:`type`) Type for representing Unicode textual data.
|
|
#: :class:`unicode` in Python 2 and :class:`str` in Python 3.
|
|
text_type = str if PY3 else unicode # noqa
|
|
|
|
|
|
def binary(string, var=None):
|
|
"""Makes ``string`` to :class:`str` in Python 2.
|
|
Makes ``string`` to :class:`bytes` in Python 3.
|
|
|
|
:param string: a string to cast it to :data:`binary_type`
|
|
:type string: :class:`bytes`, :class:`str`, :class:`unicode`
|
|
:param var: an optional variable name to be used for error message
|
|
:type var: :class:`str`
|
|
|
|
"""
|
|
if isinstance(string, text_type):
|
|
return string.encode()
|
|
elif isinstance(string, binary_type):
|
|
return string
|
|
if var:
|
|
raise TypeError('{0} must be a string, not {1!r}'.format(var, string))
|
|
raise TypeError('expected a string, not ' + repr(string))
|
|
|
|
|
|
if PY3:
|
|
def text(string):
|
|
if isinstance(string, bytes):
|
|
return string.decode('utf-8')
|
|
return string
|
|
else:
|
|
def text(string):
|
|
"""Makes ``string`` to :class:`str` in Python 3.
|
|
Does nothing in Python 2.
|
|
|
|
:param string: a string to cast it to :data:`text_type`
|
|
:type string: :class:`bytes`, :class:`str`, :class:`unicode`
|
|
|
|
"""
|
|
return string
|
|
|
|
|
|
#: The :func:`xrange()` function. Alias for :func:`range()` in Python 3.
|
|
xrange = range if PY3 else xrange # noqa
|
|
|
|
|
|
#: (:class:`type`, :class:`tuple`) Types for file objects that have
|
|
#: ``fileno()``.
|
|
file_types = io.RawIOBase if PY3 else (io.RawIOBase, types.FileType)
|
|
|
|
|
|
def encode_filename(filename):
|
|
"""If ``filename`` is a :data:`text_type`, encode it to
|
|
:data:`binary_type` according to filesystem's default encoding.
|
|
|
|
"""
|
|
if isinstance(filename, text_type):
|
|
return filename.encode(sys.getfilesystemencoding())
|
|
return filename
|
|
|
|
|
|
try:
|
|
nested = contextlib.nested
|
|
except AttributeError:
|
|
# http://hg.python.org/cpython/file/v2.7.6/Lib/contextlib.py#l88
|
|
@contextlib.contextmanager
|
|
def nested(*managers):
|
|
exits = []
|
|
vars = []
|
|
exc = (None, None, None)
|
|
try:
|
|
for mgr in managers:
|
|
exit = mgr.__exit__
|
|
enter = mgr.__enter__
|
|
vars.append(enter())
|
|
exits.append(exit)
|
|
yield vars
|
|
except:
|
|
exc = sys.exc_info()
|
|
finally:
|
|
while exits:
|
|
exit = exits.pop()
|
|
try:
|
|
if exit(*exc):
|
|
exc = (None, None, None)
|
|
except:
|
|
exc = sys.exc_info()
|
|
if exc != (None, None, None):
|
|
# PEP 3109
|
|
e = exc[0](exc[1])
|
|
e.__traceback__ = e[2]
|
|
raise e
|