diff options
Diffstat (limited to 'tests/run/methodmangling_T5.py')
-rw-r--r-- | tests/run/methodmangling_T5.py | 299 |
1 files changed, 298 insertions, 1 deletions
diff --git a/tests/run/methodmangling_T5.py b/tests/run/methodmangling_T5.py index 1cfa85310..9e2b7c63a 100644 --- a/tests/run/methodmangling_T5.py +++ b/tests/run/methodmangling_T5.py @@ -1,5 +1,10 @@ # mode: run -# ticket: 5 +# ticket: t5 + +# A small number of extra tests checking: +# 1) this works correctly with pure-Python-mode decorators - methodmangling_pure.py. +# 2) this works correctly with cdef classes - methodmangling_cdef.pyx +# 3) with "error_on_unknown_names" - methodmangling_unknown_names.py class CyTest(object): """ @@ -15,8 +20,23 @@ class CyTest(object): >>> '__x' in dir(cy) False + >>> cy._CyTest__y + 2 + + >>> '_CyTest___more_than_two' in dir(cy) + True + >>> '___more_than_two' in dir(cy) + False + >>> '___more_than_two_special___' in dir(cy) + True """ __x = 1 + ___more_than_two = 3 + ___more_than_two_special___ = 4 + + def __init__(self): + self.__y = 2 + def __private(self): return 8 def get(self): @@ -88,8 +108,285 @@ class _UnderscoreTest(object): 1 >>> ut.get() 1 + >>> ut._UnderscoreTest__UnderscoreNested().ret1() + 1 + >>> ut._UnderscoreTest__UnderscoreNested.__name__ + '__UnderscoreNested' + >>> ut._UnderscoreTest__prop + 1 """ __x = 1 def get(self): return self.__x + + class __UnderscoreNested(object): + def ret1(self): + return 1 + + @property + def __prop(self): + return self.__x + +class C: + error = """Traceback (most recent call last): +... +TypeError: +""" + __doc__ = """ +>>> instance = C() + +Instance methods have their arguments mangled +>>> instance.method1(__arg=1) # doctest: +IGNORE_EXCEPTION_DETAIL +{error} +>>> instance.method1(_C__arg=1) +1 +>>> instance.method2(__arg=1) # doctest: +IGNORE_EXCEPTION_DETAIL +{error} +>>> instance.method2(_C__arg=1) +1 + +Works when optional argument isn't passed +>>> instance.method2() +None + +Where args are in the function's **kwargs dict, names aren't mangled +>>> instance.method3(__arg=1) # doctest: +1 +>>> instance.method3(_C__arg=1) # doctest: +IGNORE_EXCEPTION_DETAIL +Traceback (most recent call last): +... +KeyError: + +Lambda functions behave in the same way: +>>> instance.method_lambda(__arg=1) # doctest: +IGNORE_EXCEPTION_DETAIL +{error} +>>> instance.method_lambda(_C__arg=1) +1 + +Class methods - have their arguments mangled +>>> instance.class_meth(__arg=1) # doctest: +IGNORE_EXCEPTION_DETAIL +{error} +>>> instance.class_meth(_C__arg=1) +1 +>>> C.class_meth(__arg=1) # doctest: +IGNORE_EXCEPTION_DETAIL +{error} +>>> C.class_meth(_C__arg=1) +1 + +Static methods - have their arguments mangled +>>> instance.static_meth(__arg=1) # doctest: +IGNORE_EXCEPTION_DETAIL +{error} +>>> instance.static_meth(_C__arg=1) +1 +>>> C.static_meth(__arg=1) # doctest: +IGNORE_EXCEPTION_DETAIL +{error} +>>> C.static_meth(_C__arg=1) +1 + +Functions assigned to the class don't have their arguments mangled +>>> instance.class_assigned_function(__arg=1) +1 +>>> instance.class_assigned_function(_C__arg=1) # doctest: +IGNORE_EXCEPTION_DETAIL +{error} + +Functions assigned to an instance don't have their arguments mangled +>>> instance.instance_assigned_function = free_function2 +>>> instance.instance_assigned_function(__arg=1) +1 +>>> instance.instance_assigned_function(_C__arg=1) # doctest: +IGNORE_EXCEPTION_DETAIL +{error} + +Locals are reported as mangled +>>> list(sorted(k for k in instance.get_locals(1).keys())) +['_C__arg', 'self'] +""".format(error=error) + + def method1(self, __arg): + print(__arg) + + def method2(self, __arg=None): + # __arg is optional + print(__arg) + + def method3(self, **kwargs): + print(kwargs['__arg']) + + method_lambda = lambda self, __arg: __arg + + def get_locals(self, __arg): + return locals() + + @classmethod + def class_meth(cls, __arg): + print(__arg) + + @staticmethod + def static_meth(__arg, dummy_arg=None): + # dummy_arg is to mask https://github.com/cython/cython/issues/3090 + print(__arg) + +def free_function1(x, __arg): + print(__arg) + +def free_function2(__arg, dummy_arg=None): + # dummy_arg is to mask https://github.com/cython/cython/issues/3090 + print(__arg) + +C.class_assigned_function = free_function1 + +__global_arg = True + +_D__arg1 = None +_D__global_arg = False # define these because otherwise Cython gives a compile-time error + # while Python gives a runtime error (which is difficult to test) +def can_find_global_arg(): + """ + >>> can_find_global_arg() + True + """ + return __global_arg + +def cant_find_global_arg(): + """ + Gets _D_global_arg instead + >>> cant_find_global_arg() + False + """ + class D: + def f(self): + return __global_arg + return D().f() + +class CMultiplyNested: + def f1(self, __arg, name=None, return_closure=False): + """ + >>> inst = CMultiplyNested() + >>> for name in [None, '__arg', '_CMultiplyNested__arg', '_D__arg']: + ... try: + ... print(inst.f1(1,name)) + ... except TypeError: + ... print("TypeError") # not concerned about exact details + ... # now test behaviour is the same in closures + ... closure = inst.f1(1, return_closure=True) + ... try: + ... if name is None: + ... print(closure(2)) + ... else: + ... print(closure(**{ name: 2})) + ... except TypeError: + ... print("TypeError") + 2 + 2 + TypeError + TypeError + TypeError + TypeError + 2 + 2 + """ + class D: + def g(self, __arg): + return __arg + if return_closure: + return D().g + if name is not None: + return D().g(**{ name: 2 }) + else: + return D().g(2) + + def f2(self, __arg1): + """ + This finds the global name '_D__arg1' + It's tested in this way because without the global + Python gives a runtime error and Cython a compile error + >>> print(CMultiplyNested().f2(1)) + None + """ + class D: + def g(self): + return __arg1 + return D().g() + + def f3(self, arg, name): + """ + >>> inst = CMultiplyNested() + >>> inst.f3(1, None) + 2 + >>> inst.f3(1, '__arg') # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + TypeError: + >>> inst.f3(1, '_CMultiplyNested__arg') + 2 + """ + def g(__arg, dummy=1): + return __arg + if name is not None: + return g(**{ name: 2}) + else: + return g(2) + + def f4(self, __arg): + """ + >>> CMultiplyNested().f4(1) + 1 + """ + def g(): + return __arg + return g() + + def f5(self, __arg): + """ + Default values are found in the outer scope correcly + >>> CMultiplyNested().f5(1) + 1 + """ + def g(x=__arg): + return x + return g() + + def f6(self, __arg1): + """ + This will find the global name _D__arg1 + >>> print(CMultiplyNested().f6(1)) + None + """ + class D: + def g(self, x=__arg1): + return x + return D().g() + + def f7(self, __arg): + """ + Lookup works in generator expressions + >>> list(CMultiplyNested().f7(1)) + [1] + """ + return (__arg for x in range(1)) + +class __NameWithDunder: + """ + >>> __NameWithDunder.__name__ + '__NameWithDunder' + """ + pass + +class Inherits(__NameWithDunder): + """ + Compile check that it can find the base class + >>> x = Inherits() + """ + pass + +def regular_function(__x, dummy=None): + # as before, dummy stops Cython creating a 1 arg, non-keyword call + return __x + +class CallsRegularFunction: + def call(self): + """ + >>> CallsRegularFunction().call() + 1 + """ + return regular_function(__x=1) # __x shouldn't be mangled as an argument elsewhere |