110 lines
3.4 KiB
Python
110 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)
|
|
PY38 = sys.version_info >= (3, 8)
|
|
|
|
# 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
|