summaryrefslogtreecommitdiff
path: root/tests/run/methodmangling_T5.py
blob: 9e2b7c63aefa4b6d9f6051afada033f05e75665b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
# mode: run
# 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):
    """
    >>> 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
    >>> 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):
        """
        >>> 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)

class _UnderscoreTest(object):
    """
    >>> ut = _UnderscoreTest()
    >>> '__x' in dir(ut)
    False
    >>> '_UnderscoreTest__x' in dir(ut)
    True
    >>> ut._UnderscoreTest__x
    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