From 16e908d56e4b73183a95880df358c1076c89be11 Mon Sep 17 00:00:00 2001 From: Bob Tolbert Date: Sun, 29 Jun 2014 11:19:22 -0600 Subject: [PATCH] In order to fix #608, we need to know which symbols can't be assigned. Python has the keyword.iskeyword method we can leverage for Python keywords, but we also need to address Hy builtins like 'get' or 'slice'. And to make behavior compatible with Python 2 or 3, we also make a special case to prevent assignment to False, True or None as well as the Hy versions: false, true, null, and nil. For non-Hy modules, we also check to make sure the symbol is not part of the compiler. This allows shadow.hy to override "+" but prevents general use from re-defn-ing 'get' or 'do'. --- hy/compiler.py | 22 ++++++++++++++++++++++ tests/native_tests/language.hy | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/hy/compiler.py b/hy/compiler.py index a82d89e..b1fd266 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -47,6 +47,7 @@ import importlib import codecs import ast import sys +import keyword from collections import defaultdict @@ -73,6 +74,21 @@ def load_stdlib(): _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', + 'true', 'false', 'nil', 'null'] + 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 = {} @@ -1751,6 +1767,12 @@ class HyASTCompiler(object): def _compile_assign(self, name, result, start_line, start_column): + + str_name = "%s" % name + if _is_hy_builtin(str_name, self.module_name): + raise HyTypeError(name, + "Can't assign to a builtin: `%s'" % str_name) + result = self.compile(result) ld_name = self.compile(name) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 7db5320..b7888e2 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -28,6 +28,28 @@ (setv (get foo 0) 12) (assert (= (get foo 0) 12))) +(defn test-setv-builtin [] + "NATIVE: test that setv doesn't work on builtins" + (try (eval '(setv False 1)) + (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (try (eval '(setv True 0)) + (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (try (eval '(setv None 1)) + (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (try (eval '(setv false 1)) + (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (try (eval '(setv true 0)) + (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (try (eval '(setv nil 1)) + (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (try (eval '(setv null 1)) + (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (try (eval '(defn defclass [] (print "hello"))) + (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (try (eval '(defn get [] (print "hello"))) + (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (try (eval '(defn lambda [] (print "hello"))) + (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e)))))) (defn test-for-loop [] "NATIVE: test for loops"