hy/hy/_compat.py

109 lines
3.4 KiB
Python

# Copyright 2019 the authors.
# This file is part of Hy, which is free software licensed under the Expat
# license. See the LICENSE.
try:
import __builtin__ as builtins
except ImportError:
import builtins # NOQA
import sys, keyword, textwrap
PY3 = sys.version_info[0] >= 3
PY36 = sys.version_info >= (3, 6)
PY37 = sys.version_info >= (3, 7)
# The value of UCS4 indicates whether Unicode strings are stored as UCS-4.
# It is always true on Pythons >= 3.3, which use USC-4 on all systems.
UCS4 = sys.maxunicode == 0x10FFFF
str_type = str if PY3 else unicode # NOQA
bytes_type = bytes if PY3 else str # NOQA
long_type = int if PY3 else long # NOQA
string_types = str if PY3 else basestring # NOQA
#
# Inspired by the same-named `six` functions.
#
if PY3:
raise_src = textwrap.dedent('''
def raise_from(value, from_value):
raise value from from_value
''')
def reraise(exc_type, value, traceback=None):
try:
raise value.with_traceback(traceback)
finally:
traceback = None
code_obj_args = ['argcount', 'kwonlyargcount', 'nlocals', 'stacksize',
'flags', 'code', 'consts', 'names', 'varnames',
'filename', 'name', 'firstlineno', 'lnotab', 'freevars',
'cellvars']
else:
def raise_from(value, from_value=None):
raise value
raise_src = textwrap.dedent('''
def reraise(exc_type, value, traceback=None):
try:
raise exc_type, value, traceback
finally:
traceback = None
''')
code_obj_args = ['argcount', 'nlocals', 'stacksize', 'flags', 'code',
'consts', 'names', 'varnames', 'filename', 'name',
'firstlineno', 'lnotab', 'freevars', 'cellvars']
raise_code = compile(raise_src, __file__, 'exec')
exec(raise_code)
def rename_function(func, new_name):
"""Creates a copy of a function and [re]sets the name at the code-object
level.
"""
c = func.__code__
new_code = type(c)(*[getattr(c, 'co_{}'.format(a))
if a != 'name' else str(new_name)
for a in code_obj_args])
_fn = type(func)(new_code, func.__globals__, str(new_name),
func.__defaults__, func.__closure__)
_fn.__dict__.update(func.__dict__)
return _fn
def isidentifier(x):
if x in ('True', 'False', 'None', 'print'):
# `print` is special-cased here because Python 2's
# keyword.iskeyword will count it as a keyword, but we
# use the __future__ feature print_function, which makes
# it a non-keyword.
return True
if keyword.iskeyword(x):
return False
if PY3:
return x.isidentifier()
if x.rstrip() != x:
return False
import tokenize as T
from io import StringIO
try:
tokens = list(T.generate_tokens(StringIO(x).readline))
except (T.TokenError, IndentationError):
return False
# Some versions of Python 2.7 (including one that made it into
# Ubuntu 18.10) have a Python 3 backport that adds a NEWLINE
# token. Remove it if it's present.
# https://bugs.python.org/issue33899
tokens = [t for t in tokens if t[0] != T.NEWLINE]
return len(tokens) == 2 and tokens[0][0] == T.NAME
try:
FileNotFoundError = FileNotFoundError
except NameError:
FileNotFoundError = IOError