Skip to content

Commit d32e277

Browse files
committed
Python: some doc, a generator, and a corotuine
1 parent 488a7f4 commit d32e277

File tree

2 files changed

+76
-19
lines changed

2 files changed

+76
-19
lines changed

python/ql/test/experimental/dataflow/coverage/classes.py

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
1-
# These are included so that we can easily evaluate the test code
1+
# User-defined methods, both instance methods and class methods, can be called in many non-standard ways
2+
# i.e. differently from simply `c.f()` or `C.f()`. For example, a user-defined `__await__` function will
3+
# be called by the syntactic constru
4+
# This should cover all the class calls that we hope to support. It is based on https://docs.python.org/3/reference/datamodel.html.
5+
#
6+
# Intended sources should be the variable `SOURCE` and intended sinks should be.
7+
# arguments to the function `SINK` (see python/ql/test/experimental/dataflow/testConfig.qll).
8+
#
9+
# Functions whose name ends with "_with_local_flow" will also be tested for local flow.
10+
11+
12+
# These are included so that we can easily evaluate the test code.
213
SOURCE = "source"
314
def SINK(x):
415
print(x)
@@ -17,17 +28,26 @@ def f(a, b):
1728
# An instance method object combines a class, a class instance and any callable object (normally a user-defined function).
1829
class C(object):
1930

20-
def method(self, a, cls):
31+
def method(self, x, cls):
2132
assert cls is self.__class__
22-
return a
33+
return x
2334

2435
@classmethod
25-
def classmethod(cls, a):
26-
return a
36+
def classmethod(cls, x):
37+
return x
2738

2839
@staticmethod
29-
def staticmethod():
30-
return a
40+
def staticmethod(x):
41+
return x
42+
43+
def gen(self, x, count):
44+
n = count
45+
while n > 0:
46+
yield x
47+
n -= 1
48+
49+
async def coro(self, x):
50+
return x
3151

3252
c = C()
3353

@@ -50,9 +70,39 @@ def staticmethod():
5070

5171
# Generator functions
5272
# 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.
73+
def gen(x, count):
74+
n = count
75+
while n > 0:
76+
yield x
77+
n -= 1
78+
79+
iter = gen(SOURCE, 1)
80+
SINK(iter.__next__()) # Returns SOURCE, path not found
81+
SINK(iter.__next__()) # throws StopIteration
82+
83+
oiter = c.gen(SOURCE, 1)
84+
SINK(oiter.__next__())
85+
SINK(oiter.__next__())
5386

5487
# Coroutine functions
5588
# 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.
89+
async def coro(x):
90+
return x
91+
92+
import asyncio
93+
SINK(asyncio.run(coro(SOURCE)))
94+
SINK(asyncio.run(c.coro(SOURCE)))
95+
96+
class A:
97+
98+
def __await__(self, x):
99+
yield x
100+
101+
async def agen(x):
102+
a = A()
103+
return await a(SOURCE)
104+
105+
SINK(asyncio.run(agen(SOURCE))) # fails with TypeError: 'A' object is not callable (possible query?)
56106

57107
# Asynchronous generator functions
58108
# 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.

python/ql/test/experimental/dataflow/coverage/dataflow.expected

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
1-
| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:14:6:14:17 | ControlFlowNode for f() |
2-
| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:38:6:38:24 | ControlFlowNode for Attribute() |
3-
| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:39:6:39:27 | ControlFlowNode for Attribute() |
4-
| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:40:6:40:27 | ControlFlowNode for func_obj() |
5-
| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:47:6:47:26 | ControlFlowNode for Attribute() |
6-
| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:48:6:48:26 | ControlFlowNode for Attribute() |
7-
| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:49:6:49:26 | ControlFlowNode for c_func_obj() |
8-
| classes.py:14:8:14:13 | ControlFlowNode for SOURCE | classes.py:14:6:14:17 | ControlFlowNode for f() |
9-
| classes.py:38:15:38:20 | ControlFlowNode for SOURCE | classes.py:38:6:38:24 | ControlFlowNode for Attribute() |
10-
| classes.py:39:18:39:23 | ControlFlowNode for SOURCE | classes.py:39:6:39:27 | ControlFlowNode for Attribute() |
11-
| classes.py:47:20:47:25 | ControlFlowNode for SOURCE | classes.py:47:6:47:26 | ControlFlowNode for Attribute() |
12-
| classes.py:48:20:48:25 | ControlFlowNode for SOURCE | classes.py:48:6:48:26 | ControlFlowNode for Attribute() |
1+
| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:25:6:25:17 | ControlFlowNode for f() |
2+
| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:58:6:58:24 | ControlFlowNode for Attribute() |
3+
| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:59:6:59:27 | ControlFlowNode for Attribute() |
4+
| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:60:6:60:27 | ControlFlowNode for func_obj() |
5+
| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:67:6:67:26 | ControlFlowNode for Attribute() |
6+
| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:68:6:68:26 | ControlFlowNode for Attribute() |
7+
| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:69:6:69:26 | ControlFlowNode for c_func_obj() |
8+
| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:80:6:80:20 | ControlFlowNode for Attribute() |
9+
| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:81:6:81:20 | ControlFlowNode for Attribute() |
10+
| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:84:6:84:21 | ControlFlowNode for Attribute() |
11+
| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:85:6:85:21 | ControlFlowNode for Attribute() |
12+
| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:93:6:93:30 | ControlFlowNode for Attribute() |
13+
| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:94:6:94:32 | ControlFlowNode for Attribute() |
14+
| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:105:6:105:30 | ControlFlowNode for Attribute() |
15+
| classes.py:25:8:25:13 | ControlFlowNode for SOURCE | classes.py:25:6:25:17 | ControlFlowNode for f() |
16+
| classes.py:58:15:58:20 | ControlFlowNode for SOURCE | classes.py:58:6:58:24 | ControlFlowNode for Attribute() |
17+
| classes.py:59:18:59:23 | ControlFlowNode for SOURCE | classes.py:59:6:59:27 | ControlFlowNode for Attribute() |
18+
| classes.py:67:20:67:25 | ControlFlowNode for SOURCE | classes.py:67:6:67:26 | ControlFlowNode for Attribute() |
19+
| classes.py:68:20:68:25 | ControlFlowNode for SOURCE | classes.py:68:6:68:26 | ControlFlowNode for Attribute() |
1320
| test.py:20:9:20:14 | ControlFlowNode for SOURCE | test.py:21:10:21:10 | ControlFlowNode for x |
1421
| test.py:25:9:25:16 | ControlFlowNode for Str | test.py:26:10:26:10 | ControlFlowNode for x |
1522
| test.py:29:9:29:17 | ControlFlowNode for Str | test.py:30:10:30:10 | ControlFlowNode for x |

0 commit comments

Comments
 (0)