clean up special casing of non-portable builtin types, add option for disabling error...
authorStefan Behnel <scoder@users.berlios.de>
Sat, 16 Apr 2011 10:05:10 +0000 (12:05 +0200)
committerStefan Behnel <scoder@users.berlios.de>
Sat, 16 Apr 2011 10:05:10 +0000 (12:05 +0200)
Cython/Compiler/Code.py
Cython/Compiler/ModuleNode.py
Cython/Compiler/Options.py
Cython/Compiler/Symtab.py

index c70318a2fa1baf2d011196716ad80afb1e49b374..c23e11fa1917fe55c366d8cde8ab044325f71b11 100644 (file)
@@ -23,6 +23,14 @@ try:
 except ImportError:
     from builtins import str as basestring
 
+
+non_portable_builtins_map = {
+    'bytes'         : ('PY_MAJOR_VERSION < 3',  'str'),
+    'unicode'       : ('PY_MAJOR_VERSION >= 3', 'str'),
+    'xrange'        : ('PY_MAJOR_VERSION >= 3', 'range'),
+    'BaseException' : ('PY_VERSION_HEX < 0x02050000', 'Exception'),
+    }
+
 class UtilityCode(object):
     # Stores utility code to add during code generation.
     #
@@ -651,31 +659,22 @@ class GlobalState(object):
         return "%s%s%d" % (Naming.const_prefix, prefix, n)
 
     def add_cached_builtin_decl(self, entry):
-        if Options.cache_builtins:
+        if entry.is_builtin and entry.is_const:
             if self.should_declare(entry.cname, entry):
                 self.put_pyobject_decl(entry)
                 w = self.parts['cached_builtins']
-                conditional_name = False
-                if entry.name == 'xrange':
-                    # replaced by range() in Py3
-                    conditional_name = True
-                    w.putln('#if PY_MAJOR_VERSION >= 3')
-                    self.put_cached_builtin_init(
-                        entry.pos, StringEncoding.EncodedString('range'),
-                        entry.cname)
-                elif entry.name == 'BaseException':
-                    # replace BaseException by Exception in Py<2.5
-                    conditional_name = True
-                    w.putln('#if PY_VERSION_HEX < 0x02050000')
+                condition = None
+                if entry.name in non_portable_builtins_map:
+                    condition, replacement = non_portable_builtins_map[entry.name]
+                    w.putln('#if %s' % condition)
                     self.put_cached_builtin_init(
-                        entry.pos, StringEncoding.EncodedString('Exception'),
+                        entry.pos, StringEncoding.EncodedString(replacement),
                         entry.cname)
-                if conditional_name:
                     w.putln('#else')
                 self.put_cached_builtin_init(
                     entry.pos, StringEncoding.EncodedString(entry.name),
                     entry.cname)
-                if conditional_name:
+                if condition:
                     w.putln('#endif')
 
     def put_cached_builtin_init(self, pos, name, cname):
index 459a89d7ec18ec57ef3bcb830a86bd4900aab0f4..7ce1c841f70bd350f3b3ca02e3bd6ddc29308fdc 100644 (file)
@@ -2134,19 +2134,21 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
         else:
             objstruct = "struct %s" % type.objstruct_cname
         module_name = type.module_name
+        condition = None
         if module_name not in ('__builtin__', 'builtins'):
             module_name = '"%s"' % module_name
         else:
             module_name = '__Pyx_BUILTIN_MODULE_NAME'
-        if type.name in self.py3_type_name_map:
-            code.putln("#if PY_MAJOR_VERSION >= 3")
-            code.putln('%s = __Pyx_ImportType(%s, "%s", sizeof(%s), 1); %s' % (
-                    type.typeptr_cname,
-                    module_name,
-                    self.py3_type_name_map[type.name],
-                    objstruct,
-                    error_code))
-            code.putln("#else")
+            if type.name in Code.non_portable_builtins_map:
+                condition, replacement = Code.non_portable_builtins_map[entry.name]
+                code.putln("#if %s" % condition)
+                code.putln('%s = __Pyx_ImportType(%s, "%s", sizeof(%s), 1); %s' % (
+                        type.typeptr_cname,
+                        module_name,
+                        replacement,
+                        objstruct,
+                        error_code))
+                code.putln("#else")
         code.putln('%s = __Pyx_ImportType(%s, "%s", sizeof(%s), %i); %s' % (
                 type.typeptr_cname,
                 module_name,
@@ -2154,7 +2156,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
                 objstruct,
                 not type.is_external or type.is_subclassed,
                 error_code))
-        if type.name in self.py3_type_name_map:
+        if condition:
             code.putln("#endif")
 
     def generate_type_ready_code(self, env, entry, code):
index 2e3663aebbf84ad44cbdfcab646b8e2f36d6b48c..a38cc373262285a8416cefa29190b5d178012ea2 100644 (file)
@@ -2,7 +2,10 @@
 #  Cython - Compilation-wide options and pragma declarations
 #
 
-cache_builtins = True  #  Perform lookups on builtin names only once
+# Perform lookups on builtin names only once, at module initialisation
+# time.  This will prevent the module from getting imported if a
+# builtin name that it uses cannot be found during initialisation.
+cache_builtins = True
 
 embed_pos_in_docstring = False
 gcc_branch_hints = True
@@ -25,6 +28,12 @@ fast_fail = False
 # Make all warnings into errors.
 warning_errors = False
 
+# Make unknown names an error.  Python raises a NameError when
+# encountering unknown names at runtime, whereas this option makes
+# them a compile time error.  If you want full Python compatibility,
+# you should disable this option and also 'cache_builtins'.
+error_on_unknown_names = True
+
 # This will convert statements of the form "for i in range(...)"
 # to "for i from ..." when i is a cdef'd integer type, and the direction
 # (i.e. sign of step) can be determined.
index d52317bbb770d4f406612141444d058c82074946..6d6f7e1c5543a431a1e632f6b9fa9bf9cf01c28a 100644 (file)
@@ -760,7 +760,10 @@ class BuiltinScope(Scope):
             if self.outer_scope is not None:
                 return self.outer_scope.declare_builtin(name, pos)
             else:
-                warning(pos, "undeclared name not builtin: %s" % name, 2)
+                if Options.error_on_unknown_names:
+                    error(pos, "undeclared name not builtin: %s" % name)
+                else:
+                    warning(pos, "undeclared name not builtin: %s" % name, 2)
 
     def declare_builtin_cfunction(self, name, type, cname, python_equiv = None,
             utility_code = None):
@@ -802,6 +805,8 @@ class BuiltinScope(Scope):
         var_entry.is_readonly = 1
         var_entry.is_builtin = 1
         var_entry.utility_code = utility_code
+        if Options.cache_builtins:
+            var_entry.is_const = True
         entry.as_variable = var_entry
 
         return type
@@ -911,7 +916,7 @@ class ModuleScope(Scope):
         return self.outer_scope.lookup(name, language_level = self.context.language_level)
 
     def declare_builtin(self, name, pos):
-        if not hasattr(builtins, name) and name not in ('xrange', 'BaseException'):
+        if not hasattr(builtins, name) and name not in Code.non_portable_builtins_map:
             # 'xrange' and 'BaseException' are special cased in Code.py
             if self.has_import_star:
                 entry = self.declare_var(name, py_object_type, pos)
@@ -922,7 +927,10 @@ class ModuleScope(Scope):
             ##     return entry
             else:
                 # unknown - assume it's builtin and look it up at runtime
-                warning(pos, "undeclared name not builtin: %s" % name, 2)
+                if Options.error_on_unknown_names:
+                    error(pos, "undeclared name not builtin: %s" % name)
+                else:
+                    warning(pos, "undeclared name not builtin: %s" % name, 2)
                 entry = self.declare(name, None, py_object_type, pos, 'private')
                 entry.is_builtin = 1
                 return entry
@@ -933,7 +941,7 @@ class ModuleScope(Scope):
         entry = self.declare(None, None, py_object_type, pos, 'private')
         if Options.cache_builtins:
             entry.is_builtin = 1
-            entry.is_const = 1
+            entry.is_const = 1 # cached
             entry.name = name
             entry.cname = Naming.builtin_prefix + name
             self.cached_builtins.append(entry)