From bfb3302ab7fb706bce4ccc1da58acad0c58feb89 Mon Sep 17 00:00:00 2001 From: Vitja Makarov Date: Mon, 6 Dec 2010 21:04:00 +0300 Subject: [PATCH] Allow pyfunction redefinition --- Cython/Compiler/Nodes.py | 7 +-- Cython/Compiler/Symtab.py | 53 +++++++++++++---------- tests/errors/e_redeclmeth.pyx | 8 ---- tests/run/pyfuncion_redefine_T489.pyx | 62 +++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 33 deletions(-) delete mode 100644 tests/errors/e_redeclmeth.pyx create mode 100644 tests/run/pyfuncion_redefine_T489.pyx diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 63528fbc..53062f75 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -2148,9 +2148,10 @@ class DefNode(FuncDefNode): entry = env.lookup_here(name) if entry and entry.type.is_cfunction and not self.is_wrapper: warning(self.pos, "Overriding cdef method with def method.", 5) - entry = env.declare_pyfunction(name, self.pos) + entry = env.declare_pyfunction(name, self.pos, allow_redefine=not self.is_wrapper) self.entry = entry - prefix = env.scope_prefix + prefix = env.next_id(env.scope_prefix) + entry.func_cname = \ Naming.pyfunc_prefix + prefix + name entry.pymethdef_cname = \ @@ -2225,7 +2226,7 @@ class DefNode(FuncDefNode): def needs_assignment_synthesis(self, env, code=None): # Should enable for module level as well, that will require more testing... - if self.entry.is_lambda: + if self.entry.is_anonymous: return True if env.is_module_scope: if code is None: diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 4253bbef..a619c671 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -75,7 +75,7 @@ class Entry(object): # is_cfunction boolean Is a C function # is_cmethod boolean Is a C method of an extension type # is_unbound_cmethod boolean Is an unbound C method of an extension type - # is_lambda boolean Is a lambda function + # is_anonymous boolean Is a anonymous pyfunction entry # is_type boolean Is a type definition # is_cclass boolean Is an extension class # is_cpp_class boolean Is a C++ class @@ -138,7 +138,7 @@ class Entry(object): is_cfunction = 0 is_cmethod = 0 is_unbound_cmethod = 0 - is_lambda = 0 + is_anonymous = 0 is_type = 0 is_cclass = 0 is_cpp_class = 0 @@ -513,18 +513,36 @@ class Scope(object): def declare_builtin(self, name, pos): return self.outer_scope.declare_builtin(name, pos) - - def declare_pyfunction(self, name, pos): - # Add an entry for a Python function. - entry = self.lookup_here(name) + + def _declare_pyfunction(self, name, pos, visibility='extern', entry=None): if entry and not entry.type.is_cfunction: - # This is legal Python, but for now will produce invalid C. error(pos, "'%s' already declared" % name) - entry = self.declare_var(name, py_object_type, pos, visibility='extern') + error(entry.pos, "Previous declaration is here") + entry = self.declare_var(name, py_object_type, pos, visibility=visibility) entry.signature = pyfunction_signature self.pyfunc_entries.append(entry) return entry + def declare_pyfunction(self, name, pos, allow_redefine=False, visibility='extern'): + # Add an entry for a Python function. + entry = self.lookup_here(name) + if not allow_redefine: + return self._declare_pyfunction(name, pos, visibility=visibility, entry=entry) + if entry: + if entry.type.is_unspecified: + entry.type = py_object_type + elif not entry.type.is_pyobject: + return self._declare_pyfunction(name, pos, visibility=visibility, entry=entry) + else: # declare entry stub + self.declare_var(name, py_object_type, pos, visibility=visibility) + entry = self.declare_var(None, py_object_type, pos, + cname=name, visibility='private') + entry.name = EncodedString(name) + entry.qualified_name = self.qualify_name(name) + entry.signature = pyfunction_signature + entry.is_anonymous = True + return entry + def declare_lambda_function(self, func_cname, pos): # Add an entry for an anonymous Python function. entry = self.declare_var(None, py_object_type, pos, @@ -532,7 +550,7 @@ class Scope(object): entry.name = EncodedString(func_cname) entry.func_cname = func_cname entry.signature = pyfunction_signature - entry.is_lambda = True + entry.is_anonymous = True return entry def add_lambda_def(self, def_node): @@ -1337,17 +1355,8 @@ class ClosureScope(LocalScope): # return "%s->%s" % (self.cur_scope_cname, name) # return "%s->%s" % (self.closure_cname, name) - def declare_pyfunction(self, name, pos): - # Add an entry for a Python function. - entry = self.lookup_here(name) - if entry and not entry.type.is_cfunction: - # This is legal Python, but for now may produce invalid C. - error(pos, "'%s' already declared" % name) - entry = self.declare_var(name, py_object_type, pos) - entry.signature = pyfunction_signature - self.pyfunc_entries.append(entry) - return entry - + def declare_pyfunction(self, name, pos, allow_redefine=False): + return LocalScope.declare_pyfunction(self, name, pos, allow_redefine, visibility='private') class StructOrUnionScope(Scope): # Namespace of a C struct or union. @@ -1521,7 +1530,7 @@ class CClassScope(ClassScope): return entry - def declare_pyfunction(self, name, pos): + def declare_pyfunction(self, name, pos, allow_redefine=False): # Add an entry for a method. if name in ('__eq__', '__ne__', '__lt__', '__gt__', '__le__', '__ge__'): error(pos, "Special method %s must be implemented via __richcmp__" % name) @@ -1765,7 +1774,7 @@ class PropertyScope(Scope): is_property_scope = 1 - def declare_pyfunction(self, name, pos): + def declare_pyfunction(self, name, pos, allow_redefine=False): # Add an entry for a method. signature = get_property_accessor_signature(name) if signature: diff --git a/tests/errors/e_redeclmeth.pyx b/tests/errors/e_redeclmeth.pyx deleted file mode 100644 index 0952ee03..00000000 --- a/tests/errors/e_redeclmeth.pyx +++ /dev/null @@ -1,8 +0,0 @@ -class C: - def f(self): - pass - def f(self): - pass -_ERRORS = u""" -4:1: 'f' already declared -""" diff --git a/tests/run/pyfuncion_redefine_T489.pyx b/tests/run/pyfuncion_redefine_T489.pyx new file mode 100644 index 00000000..7add3880 --- /dev/null +++ b/tests/run/pyfuncion_redefine_T489.pyx @@ -0,0 +1,62 @@ +""" +>>> xxx +[0, 1, 2, 3] +""" + +xxx = [] +foo = 0 +xxx.append(foo) +def foo(): + return 1 +xxx.append(foo()) +def foo(): + return 2 +xxx.append(foo()) +foo = 3 +xxx.append(foo) + +def closure_scope(a): + """ + >>> closure_scope(0) + [0, 1, 'X', -4, 3] + """ + ret = [] + foo = a + 0 + ret.append(foo) + def foo(): + return a + 1 + ret.append(foo()) + def foo(): + return 'X' + ret.append(foo()) + def foo(b): + return a - b + ret.append(foo(4)) + foo = a + 3 + ret.append(foo) + return ret + +class ClassScope(object): + """ + >>> obj = ClassScope() + [0, 1, 2, 3] + """ + x = [] + def __init__(self): + r = [] + for x in self.x: + if isinstance(x, int): + r.append(x) + else: + r.append(x(self)) + print r + foo = 0 + x.append(foo) + def foo(self): + return 1 + x.append(foo) + def foo(self): + return 2 + x.append(foo) + foo = 3 + x.append(foo) -- 2.26.2