diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index eab43780d45..4152555506a 100755 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -3800,6 +3800,8 @@ def analyse_attribute(self, env, obj_type = None): def analyse_as_python_attribute(self, env, obj_type = None): if obj_type is None: obj_type = self.obj.type + # mangle private '__*' Python attributes used inside of a class + self.attribute = env.mangle_class_private_name(self.attribute) self.member = self.attribute self.type = py_object_type self.is_py_attr = 1 diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index fcffcccc93b..ce7d5a73a68 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1222,6 +1222,7 @@ def create_local_scope(self, env): if self.needs_closure: lenv = ClosureScope(name=self.entry.name, outer_scope = genv, + parent_scope = env, scope_name=self.entry.cname) else: lenv = LocalScope(name=self.entry.name, diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 80a7bd9ec9b..9714108419c 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -329,6 +329,11 @@ def mangle_internal(self, name): return self.mangle(prefix) #return self.parent_scope.mangle(prefix, self.name) + def mangle_class_private_name(self, name): + if self.parent_scope: + return self.parent_scope.mangle_class_private_name(name) + return name + def next_id(self, name=None): # Return a cname fragment that is unique for this module counters = self.global_scope().id_counters @@ -1535,8 +1540,8 @@ class ClosureScope(LocalScope): is_closure_scope = True - def __init__(self, name, scope_name, outer_scope): - LocalScope.__init__(self, name, outer_scope) + def __init__(self, name, scope_name, outer_scope, parent_scope=None): + LocalScope.__init__(self, name, outer_scope, parent_scope) self.closure_cname = "%s%s" % (Naming.closure_scope_prefix, scope_name) # def mangle_closure_cnames(self, scope_var): @@ -1626,9 +1631,22 @@ class PyClassScope(ClassScope): is_py_class_scope = 1 + def mangle_class_private_name(self, name): + return self.mangle_special_name(name) + + def mangle_special_name(self, name): + if name and name.startswith('__') and not name.endswith('__'): + name = EncodedString('_%s%s' % (self.class_name, name)) + return name + + def lookup_here(self, name): + name = self.mangle_special_name(name) + return ClassScope.lookup_here(self, name) + def declare_var(self, name, type, pos, cname = None, visibility = 'private', api = 0, in_pxd = 0, is_cdef = 0): + name = self.mangle_special_name(name) if type is unspecified_type: type = py_object_type # Add an entry for a class attribute. diff --git a/tests/bugs.txt b/tests/bugs.txt index 2aa38548d28..e3d87cf10c1 100644 --- a/tests/bugs.txt +++ b/tests/bugs.txt @@ -1,7 +1,6 @@ # This file contains tests corresponding to unresolved bugs, # which will be skipped in the normal testing run. -methodmangling_T5 class_attribute_init_values_T18 numpy_ValueError_T172 unsignedbehaviour_T184 diff --git a/tests/run/methodmangling_T5.py b/tests/run/methodmangling_T5.py new file mode 100644 index 00000000000..7a1b7e2748e --- /dev/null +++ b/tests/run/methodmangling_T5.py @@ -0,0 +1,78 @@ +# mode: run +# ticket: 5 + +class CyTest(object): + """ + >>> cy = CyTest() + >>> '_CyTest__private' in dir(cy) + True + >>> cy._CyTest__private() + 8 + >>> '__private' in dir(cy) + False + >>> '_CyTest__x' in dir(cy) + True + + >>> '__x' in dir(cy) + False + """ + __x = 1 + def __private(self): return 8 + + def get(self): + """ + >>> CyTest().get() + (1, 1, 8) + """ + return self._CyTest__x, self.__x, self.__private() + + def get_inner(self): + """ + >>> CyTest().get_inner() + (1, 1, 8) + """ + def get(o): + return o._CyTest__x, o.__x, o.__private() + return get(self) + +class CyTestSub(CyTest): + """ + >>> cy = CyTestSub() + >>> '_CyTestSub__private' in dir(cy) + True + >>> cy._CyTestSub__private() + 9 + >>> '_CyTest__private' in dir(cy) + True + >>> cy._CyTest__private() + 8 + >>> '__private' in dir(cy) + False + + >>> '_CyTestSub__x' in dir(cy) + False + >>> '_CyTestSub__y' in dir(cy) + True + >>> '_CyTest__x' in dir(cy) + True + >>> '__x' in dir(cy) + False + """ + __y = 2 + def __private(self): return 9 + + def get(self): + """ + >>> CyTestSub().get() + (1, 2, 2, 9) + """ + return self._CyTest__x, self._CyTestSub__y, self.__y, self.__private() + + def get_inner(self): + """ + >>> CyTestSub().get_inner() + (1, 2, 2, 9) + """ + def get(o): + return o._CyTest__x, o._CyTestSub__y, o.__y, o.__private() + return get(self) diff --git a/tests/run/methodmangling_T5.pyx b/tests/run/methodmangling_T5.pyx deleted file mode 100644 index 44b165b8354..00000000000 --- a/tests/run/methodmangling_T5.pyx +++ /dev/null @@ -1,22 +0,0 @@ -# ticket: 5 -# this is ticket #5 - -__doc__ = u""" ->>> class PyTest(object): -... def __private(self): pass - ->>> py = PyTest() ->>> '_PyTest__private' in dir(py) -True ->>> '__private' in dir(py) -False - ->>> cy = CyTest() ->>> '_PyTest__private' in dir(cy) -True ->>> '__private' in dir(cy) -False -""" - -class CyTest(object): - def __private(self): pass