|
5 | 5 | # These tests should cover all the class calls that we hope to support.
|
6 | 6 | # It is based on https://docs.python.org/3/reference/datamodel.html, and headings refer there.
|
7 | 7 | #
|
8 |
| -# Intended sources should be the variable `SOURCE` and intended sinks should be |
9 |
| -# arguments to the function `SINK` (see python/ql/test/experimental/dataflow/testConfig.qll). |
10 |
| -# |
11 |
| -# Functions whose name ends with "_with_local_flow" will also be tested for local flow. |
12 |
| -# |
13 | 8 | # All functions starting with "test_" should run and print `"OK"`.
|
14 | 9 | # This can be checked by running validTest.py.
|
15 | 10 |
|
16 |
| -# These are defined so that we can evaluate the test code. |
17 |
| -NONSOURCE = "not a source" |
18 |
| -SOURCE = "source" |
19 |
| - |
20 |
| -def is_source(x): |
21 |
| - return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j |
22 |
| - |
23 |
| -def SINK(x): |
24 |
| - if is_source(x): |
25 |
| - print("OK") |
26 |
| - else: |
27 |
| - print("Unexpected flow", x) |
28 |
| - |
29 |
| -def SINK_F(x): |
30 |
| - if is_source(x): |
31 |
| - print("Unexpected flow", x) |
32 |
| - else: |
33 |
| - print("OK") |
34 |
| - |
35 |
| -# Callable types |
36 |
| -# These are the types to which the function call operation (see section Calls) can be applied: |
37 |
| - |
38 |
| -# User-defined functions |
39 |
| -# A user-defined function object is created by a function definition (see section Function definitions). It should be called with an argument list containing the same number of items as the function's formal parameter list. |
40 |
| -def f(a, b): |
41 |
| - return a |
42 |
| - |
43 |
| -SINK(f(SOURCE, 3)) |
44 |
| - |
45 |
| -# Instance methods |
46 |
| -# An instance method object combines a class, a class instance and any callable object (normally a user-defined function). |
47 |
| -class C(object): |
48 |
| - |
49 |
| - def method(self, x, cls): |
50 |
| - assert cls is self.__class__ |
51 |
| - return x |
52 |
| - |
53 |
| - @classmethod |
54 |
| - def classmethod(cls, x): |
55 |
| - return x |
56 |
| - |
57 |
| - @staticmethod |
58 |
| - def staticmethod(x): |
59 |
| - return x |
60 |
| - |
61 |
| - def gen(self, x, count): |
62 |
| - n = count |
63 |
| - while n > 0: |
64 |
| - yield x |
65 |
| - n -= 1 |
66 |
| - |
67 |
| - async def coro(self, x): |
68 |
| - return x |
69 |
| - |
70 |
| -c = C() |
71 |
| - |
72 |
| -# When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its __self__ attribute is the instance, and the method object is said to be bound. The new method’s __func__ attribute is the original function object. |
73 |
| -func_obj = c.method.__func__ |
74 |
| - |
75 |
| -# When an instance method object is called, the underlying function (__func__) is called, inserting the class instance (__self__) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1). |
76 |
| -SINK(c.method(SOURCE, C)) |
77 |
| -SINK(C.method(c, SOURCE, C)) |
78 |
| -SINK(func_obj(c, SOURCE, C)) |
79 |
| - |
80 |
| - |
81 |
| -# When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method. |
82 |
| -c_func_obj = C.classmethod.__func__ |
83 |
| - |
84 |
| -# When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function. |
85 |
| -SINK(c.classmethod(SOURCE)) |
86 |
| -SINK(C.classmethod(SOURCE)) |
87 |
| -SINK(c_func_obj(C, SOURCE)) |
88 |
| - |
89 |
| -# Generator functions |
90 |
| -# A function or method which uses the yield statement (see section The yield statement) is called a generator function. Such a function, when called, always returns an iterator object which can be used to execute the body of the function: calling the iterator’s iterator.__next__() method will cause the function to execute until it provides a value using the yield statement. When the function executes a return statement or falls off the end, a StopIteration exception is raised and the iterator will have reached the end of the set of values to be returned. |
91 |
| -def gen(x, count): |
92 |
| - n = count |
93 |
| - while n > 0: |
94 |
| - yield x |
95 |
| - n -= 1 |
96 |
| - |
97 |
| -iter = gen(SOURCE, 1) |
98 |
| -SINK(iter.__next__()) |
99 |
| -# SINK_F(iter.__next__()) # throws StopIteration, FP |
100 |
| - |
101 |
| -oiter = c.gen(SOURCE, 1) |
102 |
| -SINK(oiter.__next__()) |
103 |
| -# SINK_F(oiter.__next__()) # throws StopIteration, FP |
104 |
| - |
105 |
| -# Coroutine functions |
106 |
| -# A function or method which is defined using async def is called a coroutine function. Such a function, when called, returns a coroutine object. It may contain await expressions, as well as async with and async for statements. See also the Coroutine Objects section. |
107 |
| -async def coro(x): |
108 |
| - return x |
109 |
| - |
110 |
| -import asyncio |
111 |
| -SINK(asyncio.run(coro(SOURCE))) |
112 |
| -SINK(asyncio.run(c.coro(SOURCE))) |
113 |
| - |
114 |
| -class A: |
115 |
| - |
116 |
| - def __await__(self): |
117 |
| - # yield SOURCE -- see https://groups.google.com/g/dev-python/c/_lrrc-vp9TI?pli=1 |
118 |
| - return (yield from asyncio.coroutine(lambda: SOURCE)()) |
119 |
| - |
120 |
| -async def agen(x): |
121 |
| - a = A() |
122 |
| - return await a |
123 |
| - |
124 |
| -SINK(asyncio.run(agen(SOURCE))) |
125 |
| - |
126 |
| -# Asynchronous generator functions |
127 |
| -# A function or method which is defined using async def and which uses the yield statement is called a asynchronous generator function. Such a function, when called, returns an asynchronous iterator object which can be used in an async for statement to execute the body of the function. |
128 |
| - |
129 |
| -# Calling the asynchronous iterator’s aiterator.__anext__() method will return an awaitable which when awaited will execute until it provides a value using the yield expression. When the function executes an empty return statement or falls off the end, a StopAsyncIteration exception is raised and the asynchronous iterator will have reached the end of the set of values to be yielded. |
130 |
| - |
131 |
| -# Built-in functions |
132 |
| -# A built-in function object is a wrapper around a C function. Examples of built-in functions are len() and math.sin() (math is a standard built-in module). The number and type of the arguments are determined by the C function. Special read-only attributes: __doc__ is the function’s documentation string, or None if unavailable; __name__ is the function’s name; __self__ is set to None (but see the next item); __module__ is the name of the module the function was defined in or None if unavailable. |
133 |
| - |
134 |
| -# Built-in methods |
135 |
| -# This is really a different disguise of a built-in function, this time containing an object passed to the C function as an implicit extra argument. An example of a built-in method is alist.append(), assuming alist is a list object. In this case, the special read-only attribute __self__ is set to the object denoted by alist. |
136 |
| - |
137 |
| -# Classes |
138 |
| -# Classes are callable. These objects normally act as factories for new instances of themselves, but variations are possible for class types that override __new__(). The arguments of the call are passed to __new__() and, in the typical case, to __init__() to initialize the new instance. |
139 |
| - |
140 |
| -# Class Instances |
141 |
| -# Instances of arbitrary classes can be made callable by defining a __call__() method in their class. |
142 |
| - |
143 |
| -# If a class sets __iter__() to None, calling iter() on its instances will raise a TypeError (without falling back to __getitem__()). |
144 |
| - |
145 |
| -# 3.3.1. Basic customization |
146 |
| - |
147 |
| -class Customized: |
148 |
| - |
149 |
| - a = NONSOURCE |
150 |
| - b = NONSOURCE |
151 |
| - |
152 |
| - def __new__(cls): |
153 |
| - cls.a = SOURCE |
154 |
| - return super().__new__(cls) |
155 |
| - |
156 |
| - def __init__(self): |
157 |
| - self.b = SOURCE |
158 |
| - |
159 |
| -# testing __new__ and __init__ |
160 |
| -customized = Customized() |
161 |
| -SINK(Customized.a) |
162 |
| -SINK_F(Customized.b) |
163 |
| -SINK(customized.a) |
164 |
| -SINK(customized.b) |
165 |
| - |
166 | 11 | def OK():
|
167 | 12 | print("OK")
|
168 | 13 |
|
@@ -1254,6 +1099,8 @@ def test_exit():
|
1254 | 1099 | pass
|
1255 | 1100 |
|
1256 | 1101 | # 3.4.1. Awaitable Objects
|
| 1102 | +import asyncio |
| 1103 | + |
1257 | 1104 | # object.__await__(self)
|
1258 | 1105 | class With_await:
|
1259 | 1106 |
|
|
0 commit comments