Source code for rdflib.py3compat
"""
Utility functions and objects to ease Python 3 compatibility.
"""
import sys
import re
import codecs
import warnings
try:
from functools import wraps
assert wraps
except ImportError:
# No-op wraps decorator
def wraps(f):
def dec(newf):
return newf
return dec
[docs]def cast_bytes(s, enc='utf-8'):
if isinstance(s, unicode):
return s.encode(enc)
return s
PY3 = (sys.version_info[0] >= 3)
def _modify_str_or_docstring(str_change_func):
@wraps(str_change_func)
def wrapper(func_or_str):
if isinstance(func_or_str, str):
func = None
doc = func_or_str
else:
func = func_or_str
doc = func.__doc__
doc = str_change_func(doc)
if func:
func.__doc__ = doc
return func
return doc
return wrapper
if PY3:
# Python 3:
# ---------
def b(s):
return s.encode('ascii')
def ascii(stream):
return codecs.getreader('ascii')(stream)
def bopen(*args, **kwargs):
return open(*args, mode = 'rb', **kwargs)
bytestype = bytes
# Abstract u'abc' syntax:
@_modify_str_or_docstring
def format_doctest_out(s):
"""Python 2 version
"%(u)s'abc'" --> "'abc'"
"%(b)s'abc'" --> "b'abc'"
"55%(L)s" --> "55"
"unicode(x)" --> "str(x)"
Accepts a string or a function, so it can be used as a decorator."""
# s may be None if processed by Py2exe
if s is None:
return ''
return s % {'u': '', 'b': 'b', 'L': '', 'unicode': 'str'}
def type_cmp(a, b):
"""Python 2 style comparison based on type"""
ta, tb = type(a).__name__, type(b).__name__
# Ugly hack: some tests rely on tuple sorting before unicode, and I
# don't know if that's important. Better retain it for now.
if ta == 'str':
ta = 'unicode'
if tb == 'str':
tb = 'unicode'
# return 1 if ta > tb else -1 if ta < tb else 0
if ta > tb:
return 1
elif ta < tb:
return -1
else:
return 0
def sign(n):
if n < 0:
return -1
if n > 0:
return 1
return 0
else:
# Python 2
# --------
[docs] def ascii(stream):
return stream
bopen = open
bytestype = str
# Abstract u'abc' syntax:
@_modify_str_or_docstring
[docs] def type_cmp(a, b):
# return 1 if a > b else -1 if a < b else 0
if a > b:
return 1
elif a < b:
return -1
else:
return 0
[docs] def sign(n):
return cmp(n, 0)
r_unicodeEscape = re.compile(r'(\\u[0-9A-Fa-f]{4}|\\U[0-9A-Fa-f]{8})')
def _unicodeExpand(s):
return r_unicodeEscape.sub(lambda m: unichr(int(m.group(0)[2:], 16)), s)
narrow_build = False
try:
unichr(0x10FFFF)
except ValueError:
narrow_build = True
if narrow_build:
def _unicodeExpand(s):
try:
return r_unicodeEscape.sub(
lambda m: unichr(int(m.group(0)[2:], 16)), s)
except ValueError:
warnings.warn(
'Encountered a unicode char > 0xFFFF in a narrow python build. '
'Trying to degrade gracefully, but this can cause problems '
'later when working with the string:\n%s' % s)
return r_unicodeEscape.sub(
lambda m: codecs.decode(m.group(0), 'unicode_escape'), s)
[docs]def decodeStringEscape(s):
"""
s is byte-string - replace \ escapes in string
"""
if not PY3:
s = s.decode('string-escape')
else:
s = s.replace('\\t', '\t')
s = s.replace('\\n', '\n')
s = s.replace('\\r', '\r')
s = s.replace('\\b', '\b')
s = s.replace('\\f', '\f')
s = s.replace('\\"', '"')
s = s.replace("\\'", "'")
s = s.replace('\\\\', '\\')
return s
#return _unicodeExpand(s) # hmm - string escape doesn't do unicode escaping
[docs]def decodeUnicodeEscape(s):
"""
s is a unicode string
replace \n and \u00AC unicode escapes
"""
if not PY3:
s = s.encode('utf-8').decode('string-escape')
s = _unicodeExpand(s)
else:
s = s.replace('\\t', '\t')
s = s.replace('\\n', '\n')
s = s.replace('\\r', '\r')
s = s.replace('\\b', '\b')
s = s.replace('\\f', '\f')
s = s.replace('\\"', '"')
s = s.replace("\\'", "'")
s = s.replace('\\\\', '\\')
s = _unicodeExpand(s) # hmm - string escape doesn't do unicode escaping
return s