Mangle names that coincide with Python keywords

This commit is contained in:
Kodi Arfer 2017-11-15 15:43:46 -08:00
parent 52edad28e2
commit d252bb0e94
5 changed files with 51 additions and 51 deletions

View File

@ -18,7 +18,7 @@ except ImportError:
(x >> 8) & 0xff, (x >> 8) & 0xff,
(x >> 16) & 0xff, (x >> 16) & 0xff,
(x >> 24) & 0xff])) (x >> 24) & 0xff]))
import sys import sys, keyword
PY3 = sys.version_info[0] >= 3 PY3 = sys.version_info[0] >= 3
PY35 = sys.version_info >= (3, 5) PY35 = sys.version_info >= (3, 5)
@ -37,15 +37,22 @@ else:
raise t(*args) raise t(*args)
def isidentifier(x): 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: if PY3:
return x.isidentifier() return x.isidentifier()
else: if x.rstrip() != x:
if x.rstrip() != x: return False
return False import tokenize as T
import tokenize as T from StringIO import StringIO
from StringIO import StringIO try:
try: tokens = list(T.generate_tokens(StringIO(x).readline))
tokens = list(T.generate_tokens(StringIO(x).readline)) except T.TokenError:
except T.TokenError: return False
return False return len(tokens) == 2 and tokens[0][0] == T.NAME
return len(tokens) == 2 and tokens[0][0] == T.NAME

View File

@ -61,20 +61,6 @@ def load_stdlib():
_stdlib[e] = module _stdlib[e] = module
# True, False and None included here since they
# are assignable in Python 2.* but become
# keywords in Python 3.*
def _is_hy_builtin(name, module_name):
extras = ['True', 'False', 'None']
if name in extras or keyword.iskeyword(name):
return True
# for non-Hy modules, check for pre-existing name in
# _compile_table
if not module_name.startswith("hy."):
return name in _compile_table
return False
_compile_table = {} _compile_table = {}
_decoratables = (ast.FunctionDef, ast.ClassDef) _decoratables = (ast.FunctionDef, ast.ClassDef)
if PY35: if PY35:
@ -386,7 +372,6 @@ def ends_with_else(expr):
class HyASTCompiler(object): class HyASTCompiler(object):
def __init__(self, module_name): def __init__(self, module_name):
self.allow_builtins = module_name.startswith("hy.core")
self.anon_var_count = 0 self.anon_var_count = 0
self.imports = defaultdict(set) self.imports = defaultdict(set)
self.module_name = module_name self.module_name = module_name
@ -1803,10 +1788,11 @@ class HyASTCompiler(object):
def _compile_assign(self, name, result): def _compile_assign(self, name, result):
str_name = "%s" % name str_name = "%s" % name
if (_is_hy_builtin(str_name, self.module_name) and if str_name in (["None"] + (["True", "False"] if PY3 else [])):
not self.allow_builtins): # Python 2 allows assigning to True and False, although
# this is rarely wise.
raise HyTypeError(name, raise HyTypeError(name,
"Can't assign to a builtin: `%s'" % str_name) "Can't assign to `%s'" % str_name)
result = self.compile(result) result = self.compile(result)
ld_name = self.compile(name) ld_name = self.compile(name)
@ -2082,8 +2068,6 @@ class HyASTCompiler(object):
body += self._compile_assign(symb, docstring) body += self._compile_assign(symb, docstring)
body += body.expr_as_stmt() body += body.expr_as_stmt()
allow_builtins = self.allow_builtins
self.allow_builtins = True
if expressions and isinstance(expressions[0], HyList) \ if expressions and isinstance(expressions[0], HyList) \
and not isinstance(expressions[0], HyExpression): and not isinstance(expressions[0], HyExpression):
expr = expressions.pop(0) expr = expressions.pop(0)
@ -2095,8 +2079,6 @@ class HyASTCompiler(object):
for expression in expressions: for expression in expressions:
body += self.compile(rewire_init(macroexpand(expression, self))) body += self.compile(rewire_init(macroexpand(expression, self)))
self.allow_builtins = allow_builtins
if not body.stmts: if not body.stmts:
body += asty.Pass(expressions) body += asty.Pass(expressions)

View File

@ -596,13 +596,11 @@ def test_invalid_list_comprehension():
def test_bad_setv(): def test_bad_setv():
"""Ensure setv handles error cases""" """Ensure setv handles error cases"""
cant_compile("(setv if* 1)")
cant_compile("(setv (a b) [1 2])") cant_compile("(setv (a b) [1 2])")
def test_defn(): def test_defn():
"""Ensure that defn works correctly in various corner cases""" """Ensure that defn works correctly in various corner cases"""
cant_compile("(defn if* [] 1)")
cant_compile("(defn \"hy\" [] 1)") cant_compile("(defn \"hy\" [] 1)")
cant_compile("(defn :hy [] 1)") cant_compile("(defn :hy [] 1)")
can_compile("(defn &hy [] 1)") can_compile("(defn &hy [] 1)")
@ -611,7 +609,6 @@ def test_defn():
def test_setv_builtins(): def test_setv_builtins():
"""Ensure that assigning to a builtin fails, unless in a class""" """Ensure that assigning to a builtin fails, unless in a class"""
cant_compile("(setv None 42)") cant_compile("(setv None 42)")
cant_compile("(defn get [&rest args] 42)")
can_compile("(defclass A [] (defn get [self] 42))") can_compile("(defclass A [] (defn get [self] 42))")
can_compile(""" can_compile("""
(defclass A [] (defclass A []

View File

@ -65,19 +65,19 @@
(defn test-setv-builtin [] (defn test-setv-builtin []
"NATIVE: test that setv doesn't work on builtins" "NATIVE: test that setv doesn't work on names Python can't assign to
(try (eval '(setv False 1)) and that we can't mangle"
(except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e)))))
(try (eval '(setv True 0))
(except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e)))))
(try (eval '(setv None 1)) (try (eval '(setv None 1))
(except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (except [e [TypeError]] (assert (in "Can't assign to" (str e)))))
(try (eval '(defn defclass [] (print "hello"))) (try (eval '(defn None [] (print "hello")))
(except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (except [e [TypeError]] (assert (in "Can't assign to" (str e)))))
(try (eval '(defn get [] (print "hello"))) (when PY3
(except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (try (eval '(setv False 1))
(try (eval '(defn fn [] (print "hello"))) (except [e [TypeError]] (assert (in "Can't assign to" (str e)))))
(except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e)))))) (try (eval '(setv True 0))
(except [e [TypeError]] (assert (in "Can't assign to" (str e)))))
(try (eval '(defn True [] (print "hello")))
(except [e [TypeError]] (assert (in "Can't assign to" (str e)))))))
(defn test-setv-pairs [] (defn test-setv-pairs []
@ -223,14 +223,14 @@
; don't be fooled by constructs that look like else ; don't be fooled by constructs that look like else
(setv s "") (setv s "")
(setv (get (globals) "else") True) (setv else True)
(for [x "abcde"] (for [x "abcde"]
(+= s x) (+= s x)
[else (+= s "_")]) [else (+= s "_")])
(assert (= s "a_b_c_d_e_")) (assert (= s "a_b_c_d_e_"))
(setv s "") (setv s "")
(setv (get (globals) "else") True) (setv else True)
(with [(pytest.raises TypeError)] (with [(pytest.raises TypeError)]
(for [x "abcde"] (for [x "abcde"]
(+= s x) (+= s x)
@ -329,7 +329,7 @@
; don't be fooled by constructs that look like else clauses ; don't be fooled by constructs that look like else clauses
(setv x 2) (setv x 2)
(setv a []) (setv a [])
(setv (get (globals) "else") True) (setv else True)
(while x (while x
(.append a x) (.append a x)
(-= x 1) (-= x 1)

View File

@ -115,6 +115,20 @@
(assert (= x "aabb"))) (assert (= x "aabb")))
(defn test-python-keyword []
(setv if 3)
(assert (= if 3))
(assert (= hyx_if 3)))
(defn test-operator []
(setv + 3)
(assert (= + 3))
(if PY3
(assert (= hyx_Δplus_signΔ 3))
(assert (= hyx_Xplus_signX 3))))
(defn test-late-mangling [] (defn test-late-mangling []
; Mangling should only happen during compilation. ; Mangling should only happen during compilation.
(assert (!= 'foo? 'is_foo)) (assert (!= 'foo? 'is_foo))