Skip to content

Commit 810568c

Browse files
authored
Merge pull request #10171 from RasmusWL/variable-accesss
Python: Fixes for variable access
2 parents b94e0d3 + 07457b2 commit 810568c

File tree

14 files changed

+333
-64
lines changed

14 files changed

+333
-64
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Reads of global/non-local variables (without annotations) inside functions defined on classes now works properly in the case where the class had an attribute defined with the same name as the non-local variable.

python/ql/test/experimental/dataflow/typetracking/test.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,27 @@ def test_long_import_chain_full_path():
158158
from foo.bar import baz # $ tracked_foo_bar_baz
159159
x = baz # $ tracked_foo_bar_baz
160160
do_stuff(x) # $ tracked_foo_bar_baz
161+
162+
# ------------------------------------------------------------------------------
163+
# Global variable to method body flow
164+
# ------------------------------------------------------------------------------
165+
166+
some_value = get_tracked() # $ tracked
167+
other_value = get_tracked() # $ tracked
168+
print(some_value) # $ tracked
169+
print(other_value) # $ tracked
170+
171+
class MyClass(object):
172+
# Since we define some_value method on the class, flow for some_value gets blocked
173+
# into the methods
174+
def some_value(self):
175+
print(some_value) # $ tracked
176+
print(other_value) # $ tracked
177+
178+
def other_name(self):
179+
print(some_value) # $ tracked
180+
print(other_value) # $ tracked
181+
182+
def with_global_modifier(self):
183+
global some_value
184+
print(some_value) # $ tracked

python/ql/test/library-tests/PointsTo/new/ImpliesDataflow.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,5 @@
1212
| code/q_super.py:37:14:37:17 | ControlFlowNode for self | code/q_super.py:27:32:27:35 | ControlFlowNode for self |
1313
| code/q_super.py:48:5:48:17 | ControlFlowNode for ClassExpr | code/q_super.py:51:25:51:29 | ControlFlowNode for Attribute |
1414
| code/q_super.py:63:5:63:17 | ControlFlowNode for ClassExpr | code/q_super.py:66:19:66:23 | ControlFlowNode for Attribute |
15-
| code/r_regressions.py:46:1:46:14 | ControlFlowNode for FunctionExpr | code/r_regressions.py:52:9:52:12 | ControlFlowNode for fail |
1615
| code/t_type.py:3:1:3:16 | ControlFlowNode for ClassExpr | code/t_type.py:6:1:6:9 | ControlFlowNode for type() |
1716
| code/t_type.py:3:1:3:16 | ControlFlowNode for ClassExpr | code/t_type.py:13:5:13:13 | ControlFlowNode for type() |
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
| in_class.py:0:0:0:0 | Module in_class | Global Variable MyClass | in_class.py:5:7:5:13 | MyClass |
2+
| in_class.py:0:0:0:0 | Module in_class | Global Variable MyClass | in_class.py:33:6:33:12 | MyClass |
3+
| in_class.py:0:0:0:0 | Module in_class | Global Variable MyClass | in_class.py:41:7:41:13 | MyClass |
4+
| in_class.py:0:0:0:0 | Module in_class | Global Variable NameError | in_class.py:17:16:17:24 | NameError |
5+
| in_class.py:0:0:0:0 | Module in_class | Global Variable bar | in_class.py:2:1:2:3 | bar |
6+
| in_class.py:0:0:0:0 | Module in_class | Global Variable bar | in_class.py:14:15:14:17 | bar |
7+
| in_class.py:0:0:0:0 | Module in_class | Global Variable baz | in_class.py:16:19:16:21 | baz |
8+
| in_class.py:0:0:0:0 | Module in_class | Global Variable foo | in_class.py:1:1:1:3 | foo |
9+
| in_class.py:0:0:0:0 | Module in_class | Global Variable foo | in_class.py:13:15:13:17 | foo |
10+
| in_class.py:0:0:0:0 | Module in_class | Global Variable foo | in_class.py:31:17:31:19 | foo |
11+
| in_class.py:0:0:0:0 | Module in_class | Global Variable mc | in_class.py:33:1:33:2 | mc |
12+
| in_class.py:0:0:0:0 | Module in_class | Global Variable mc | in_class.py:35:1:35:2 | mc |
13+
| in_class.py:0:0:0:0 | Module in_class | Global Variable mc | in_class.py:38:7:38:8 | mc |
14+
| in_class.py:0:0:0:0 | Module in_class | Global Variable object | in_class.py:5:15:5:20 | object |
15+
| in_class.py:0:0:0:0 | Module in_class | Global Variable object | in_class.py:30:15:30:20 | object |
16+
| in_class.py:0:0:0:0 | Module in_class | Global Variable print | in_class.py:9:9:9:13 | print |
17+
| in_class.py:0:0:0:0 | Module in_class | Global Variable print | in_class.py:12:9:12:13 | print |
18+
| in_class.py:0:0:0:0 | Module in_class | Global Variable print | in_class.py:13:9:13:13 | print |
19+
| in_class.py:0:0:0:0 | Module in_class | Global Variable print | in_class.py:14:9:14:13 | print |
20+
| in_class.py:0:0:0:0 | Module in_class | Global Variable print | in_class.py:16:13:16:17 | print |
21+
| in_class.py:0:0:0:0 | Module in_class | Global Variable print | in_class.py:20:9:20:13 | print |
22+
| in_class.py:0:0:0:0 | Module in_class | Global Variable print | in_class.py:21:9:21:13 | print |
23+
| in_class.py:0:0:0:0 | Module in_class | Global Variable print | in_class.py:22:9:22:13 | print |
24+
| in_class.py:0:0:0:0 | Module in_class | Global Variable print | in_class.py:25:9:25:13 | print |
25+
| in_class.py:0:0:0:0 | Module in_class | Global Variable print | in_class.py:34:1:34:5 | print |
26+
| in_class.py:0:0:0:0 | Module in_class | Global Variable print | in_class.py:37:1:37:5 | print |
27+
| in_class.py:0:0:0:0 | Module in_class | Global Variable print | in_class.py:38:1:38:5 | print |
28+
| in_class.py:0:0:0:0 | Module in_class | Global Variable print | in_class.py:40:1:40:5 | print |
29+
| in_class.py:0:0:0:0 | Module in_class | Global Variable print | in_class.py:41:1:41:5 | print |
30+
| in_class.py:5:1:5:22 | Class MyClass | Local Variable Sub | in_class.py:30:11:30:13 | Sub |
31+
| in_class.py:5:1:5:22 | Class MyClass | Local Variable baz | in_class.py:6:5:6:7 | baz |
32+
| in_class.py:5:1:5:22 | Class MyClass | Local Variable baz | in_class.py:28:15:28:17 | baz |
33+
| in_class.py:5:1:5:22 | Class MyClass | Local Variable ex | in_class.py:28:5:28:6 | ex |
34+
| in_class.py:5:1:5:22 | Class MyClass | Local Variable foo | in_class.py:8:9:8:11 | foo |
35+
| in_class.py:5:1:5:22 | Class MyClass | Local Variable func | in_class.py:24:9:24:12 | func |
36+
| in_class.py:5:1:5:22 | Class MyClass | Local Variable func | in_class.py:28:10:28:13 | func |
37+
| in_class.py:5:1:5:22 | Class MyClass | Local Variable use | in_class.py:11:9:11:11 | use |
38+
| in_class.py:8:5:8:18 | Function foo | Local Variable self | in_class.py:8:13:8:16 | self |
39+
| in_class.py:11:5:11:18 | Function use | Local Variable self | in_class.py:11:13:11:16 | self |
40+
| in_class.py:11:5:11:18 | Function use | Local Variable self | in_class.py:21:15:21:18 | self |
41+
| in_class.py:11:5:11:18 | Function use | Local Variable self | in_class.py:22:15:22:18 | self |
42+
| in_class.py:24:5:24:18 | Function func | Local Variable arg | in_class.py:24:14:24:16 | arg |
43+
| in_class.py:24:5:24:18 | Function func | Local Variable arg | in_class.py:25:27:25:29 | arg |
44+
| in_class.py:30:5:30:22 | Class Sub | Local Variable stuff | in_class.py:31:9:31:13 | stuff |
45+
| test.py:0:0:0:0 | Module test | Global Variable C | test.py:30:7:30:7 | C |
46+
| test.py:0:0:0:0 | Module test | Global Variable base | test.py:30:9:30:12 | base |
47+
| test.py:0:0:0:0 | Module test | Global Variable func0 | test.py:5:5:5:9 | func0 |
48+
| test.py:0:0:0:0 | Module test | Global Variable func1 | test.py:8:5:8:9 | func1 |
49+
| test.py:0:0:0:0 | Module test | Global Variable func2 | test.py:15:5:15:9 | func2 |
50+
| test.py:0:0:0:0 | Module test | Global Variable func3 | test.py:22:5:22:9 | func3 |
51+
| test.py:0:0:0:0 | Module test | Global Variable func4 | test.py:38:5:38:9 | func4 |
52+
| test.py:0:0:0:0 | Module test | Global Variable func5 | test.py:44:5:44:9 | func5 |
53+
| test.py:0:0:0:0 | Module test | Global Variable func6 | test.py:47:5:47:9 | func6 |
54+
| test.py:0:0:0:0 | Module test | Global Variable global0 | test.py:2:1:2:7 | global0 |
55+
| test.py:0:0:0:0 | Module test | Global Variable global0 | test.py:13:5:13:11 | global0 |
56+
| test.py:0:0:0:0 | Module test | Global Variable global1 | test.py:3:1:3:7 | global1 |
57+
| test.py:0:0:0:0 | Module test | Global Variable global1 | test.py:13:33:13:39 | global1 |
58+
| test.py:0:0:0:0 | Module test | Global Variable global_local | test.py:12:5:12:16 | global_local |
59+
| test.py:0:0:0:0 | Module test | Global Variable range | test.py:52:17:52:21 | range |
60+
| test.py:0:0:0:0 | Module test | Global Variable seq | test.py:48:26:48:28 | seq |
61+
| test.py:0:0:0:0 | Module test | Global Variable use_in_loop | test.py:51:5:51:15 | use_in_loop |
62+
| test.py:5:1:5:26 | Function func0 | Local Variable param0 | test.py:5:11:5:16 | param0 |
63+
| test.py:5:1:5:26 | Function func0 | Local Variable param0 | test.py:6:12:6:17 | param0 |
64+
| test.py:5:1:5:26 | Function func0 | Local Variable param1 | test.py:5:19:5:24 | param1 |
65+
| test.py:5:1:5:26 | Function func0 | Local Variable param1 | test.py:6:21:6:26 | param1 |
66+
| test.py:8:1:8:12 | Function func1 | Local Variable local0 | test.py:10:5:10:10 | local0 |
67+
| test.py:8:1:8:12 | Function func1 | Local Variable local0 | test.py:13:15:13:20 | local0 |
68+
| test.py:8:1:8:12 | Function func1 | Local Variable local1 | test.py:11:5:11:10 | local1 |
69+
| test.py:8:1:8:12 | Function func1 | Local Variable local1 | test.py:13:24:13:29 | local1 |
70+
| test.py:15:1:15:12 | Function func2 | Local Variable inner1 | test.py:17:9:17:14 | inner1 |
71+
| test.py:15:1:15:12 | Function func2 | Local Variable inner1 | test.py:20:12:20:17 | inner1 |
72+
| test.py:15:1:15:12 | Function func2 | Local Variable local2 | test.py:16:5:16:10 | local2 |
73+
| test.py:15:1:15:12 | Function func2 | Local Variable local2 | test.py:18:18:18:23 | local2 |
74+
| test.py:17:5:17:23 | Function inner1 | Local Variable local3 | test.py:18:9:18:14 | local3 |
75+
| test.py:17:5:17:23 | Function inner1 | Local Variable local3 | test.py:19:16:19:21 | local3 |
76+
| test.py:17:5:17:23 | Function inner1 | Local Variable param2 | test.py:17:16:17:21 | param2 |
77+
| test.py:22:1:22:26 | Function func3 | Local Variable inner_outer | test.py:24:9:24:19 | inner_outer |
78+
| test.py:22:1:22:26 | Function func3 | Local Variable local4 | test.py:23:5:23:10 | local4 |
79+
| test.py:22:1:22:26 | Function func3 | Local Variable local4 | test.py:26:29:26:34 | local4 |
80+
| test.py:22:1:22:26 | Function func3 | Local Variable local4 | test.py:28:23:28:28 | local4 |
81+
| test.py:22:1:22:26 | Function func3 | Local Variable param4 | test.py:22:11:22:16 | param4 |
82+
| test.py:22:1:22:26 | Function func3 | Local Variable param4 | test.py:26:47:26:52 | param4 |
83+
| test.py:22:1:22:26 | Function func3 | Local Variable param4 | test.py:28:32:28:37 | param4 |
84+
| test.py:22:1:22:26 | Function func3 | Local Variable param5 | test.py:22:19:22:24 | param5 |
85+
| test.py:22:1:22:26 | Function func3 | Local Variable param5 | test.py:28:41:28:46 | param5 |
86+
| test.py:24:5:24:22 | Function inner_outer | Local Variable inner2 | test.py:25:13:25:18 | inner2 |
87+
| test.py:24:5:24:22 | Function inner_outer | Local Variable inner2 | test.py:28:16:28:21 | inner2 |
88+
| test.py:24:5:24:22 | Function inner_outer | Local Variable local5 | test.py:26:20:26:25 | local5 |
89+
| test.py:24:5:24:22 | Function inner_outer | Local Variable local5 | test.py:27:9:27:14 | local5 |
90+
| test.py:25:9:25:27 | Function inner2 | Local Variable param3 | test.py:25:20:25:25 | param3 |
91+
| test.py:25:9:25:27 | Function inner2 | Local Variable param3 | test.py:26:38:26:43 | param3 |
92+
| test.py:30:1:30:14 | Class C | Local Variable class_local | test.py:32:5:32:15 | class_local |
93+
| test.py:30:1:30:14 | Class C | Local Variable meth | test.py:34:9:34:12 | meth |
94+
| test.py:34:5:34:19 | Function meth | Local Variable mlocal | test.py:35:9:35:14 | mlocal |
95+
| test.py:34:5:34:19 | Function meth | Local Variable mlocal | test.py:36:16:36:21 | mlocal |
96+
| test.py:34:5:34:19 | Function meth | Local Variable self | test.py:34:14:34:17 | self |
97+
| test.py:34:5:34:19 | Function meth | Local Variable self | test.py:35:18:35:21 | self |
98+
| test.py:38:1:38:18 | Function func4 | Local Variable Local | test.py:39:11:39:15 | Local |
99+
| test.py:38:1:38:18 | Function func4 | Local Variable Local | test.py:42:12:42:16 | Local |
100+
| test.py:38:1:38:18 | Function func4 | Local Variable param6 | test.py:38:11:38:16 | param6 |
101+
| test.py:38:1:38:18 | Function func4 | Local Variable param6 | test.py:41:20:41:25 | param6 |
102+
| test.py:39:5:39:16 | Class Local | Local Variable meth_inner | test.py:40:13:40:22 | meth_inner |
103+
| test.py:40:9:40:29 | Function meth_inner | Local Variable self | test.py:40:24:40:27 | self |
104+
| test.py:44:1:44:15 | Function func5 | Local Variable seq | test.py:44:11:44:13 | seq |
105+
| test.py:44:1:44:15 | Function func5 | Local Variable seq | test.py:45:24:45:26 | seq |
106+
| test.py:45:12:45:27 | Function listcomp | Local Variable .0 | test.py:45:12:45:27 | .0 |
107+
| test.py:45:12:45:27 | Function listcomp | Local Variable .0 | test.py:45:12:45:27 | .0 |
108+
| test.py:45:12:45:27 | Function listcomp | Local Variable x | test.py:45:13:45:13 | x |
109+
| test.py:45:12:45:27 | Function listcomp | Local Variable x | test.py:45:19:45:19 | x |
110+
| test.py:47:1:47:16 | Function func6 | Local Variable y | test.py:47:11:47:11 | y |
111+
| test.py:47:1:47:16 | Function func6 | Local Variable z | test.py:47:14:47:14 | z |
112+
| test.py:47:1:47:16 | Function func6 | Local Variable z | test.py:48:15:48:15 | z |
113+
| test.py:48:12:48:29 | Function listcomp | Local Variable .0 | test.py:48:12:48:29 | .0 |
114+
| test.py:48:12:48:29 | Function listcomp | Local Variable .0 | test.py:48:12:48:29 | .0 |
115+
| test.py:48:12:48:29 | Function listcomp | Local Variable y | test.py:48:13:48:13 | y |
116+
| test.py:48:12:48:29 | Function listcomp | Local Variable y | test.py:48:21:48:21 | y |
117+
| test.py:51:1:51:21 | Function use_in_loop | Local Variable seq | test.py:51:17:51:19 | seq |
118+
| test.py:51:1:51:21 | Function use_in_loop | Local Variable seq | test.py:53:14:53:16 | seq |
119+
| test.py:51:1:51:21 | Function use_in_loop | Local Variable v | test.py:53:9:53:9 | v |
120+
| test.py:51:1:51:21 | Function use_in_loop | Local Variable v | test.py:54:9:54:9 | v |
121+
| test.py:52:5:52:25 | Function listcomp | Local Variable .0 | test.py:52:5:52:25 | .0 |
122+
| test.py:52:5:52:25 | Function listcomp | Local Variable .0 | test.py:52:5:52:25 | .0 |
123+
| test.py:52:5:52:25 | Function listcomp | Local Variable v | test.py:52:6:52:6 | v |
124+
| test.py:52:5:52:25 | Function listcomp | Local Variable v | test.py:52:12:52:12 | v |
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import python
2+
3+
from Variable v, Scope s, Name n
4+
where
5+
n = v.getAnAccess() and
6+
s = v.getScope()
7+
select s, v, n
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
| Local Variable local2 | Function func2 | Function inner1 |
2-
| Local Variable local4 | Function func3 | Function inner2 |
3-
| Local Variable local4 | Function func3 | Function inner_outer |
4-
| Local Variable local5 | Function inner_outer | Function inner2 |
5-
| Local Variable param4 | Function func3 | Function inner2 |
6-
| Local Variable param4 | Function func3 | Function inner_outer |
7-
| Local Variable param5 | Function func3 | Function inner_outer |
8-
| Local Variable param6 | Function func4 | Function meth_inner |
9-
| Local Variable z | Function func6 | Function listcomp |
1+
| Local Variable local2 | test.py:15:1:15:12 | Function func2 | test.py:17:5:17:23 | Function inner1 |
2+
| Local Variable local4 | test.py:22:1:22:26 | Function func3 | test.py:24:5:24:22 | Function inner_outer |
3+
| Local Variable local4 | test.py:22:1:22:26 | Function func3 | test.py:25:9:25:27 | Function inner2 |
4+
| Local Variable local5 | test.py:24:5:24:22 | Function inner_outer | test.py:25:9:25:27 | Function inner2 |
5+
| Local Variable param4 | test.py:22:1:22:26 | Function func3 | test.py:24:5:24:22 | Function inner_outer |
6+
| Local Variable param4 | test.py:22:1:22:26 | Function func3 | test.py:25:9:25:27 | Function inner2 |
7+
| Local Variable param5 | test.py:22:1:22:26 | Function func3 | test.py:24:5:24:22 | Function inner_outer |
8+
| Local Variable param6 | test.py:38:1:38:18 | Function func4 | test.py:40:9:40:29 | Function meth_inner |
9+
| Local Variable z | test.py:47:1:47:16 | Function func6 | test.py:48:12:48:29 | Function listcomp |

python/ql/test/library-tests/variables/scopes/free.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ where
55
v.escapes() and
66
inner = v.getAnAccess().getScope() and
77
inner != v.getScope()
8-
select v.toString(), v.getScope().toString(), inner.toString()
8+
select v, v.getScope(), inner
Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
1-
| Global Variable C | Module test |
2-
| Global Variable __name__ | Module test |
3-
| Global Variable __package__ | Module test |
4-
| Global Variable base | Module test |
5-
| Global Variable func0 | Module test |
6-
| Global Variable func1 | Module test |
7-
| Global Variable func2 | Module test |
8-
| Global Variable func3 | Module test |
9-
| Global Variable func4 | Module test |
10-
| Global Variable func5 | Module test |
11-
| Global Variable func6 | Module test |
12-
| Global Variable global0 | Module test |
13-
| Global Variable global1 | Module test |
14-
| Global Variable global_local | Module test |
15-
| Global Variable range | Module test |
16-
| Global Variable seq | Module test |
17-
| Global Variable use_in_loop | Module test |
1+
| Global Variable C | test.py:0:0:0:0 | Module test |
2+
| Global Variable MyClass | in_class.py:0:0:0:0 | Module in_class |
3+
| Global Variable NameError | in_class.py:0:0:0:0 | Module in_class |
4+
| Global Variable __name__ | in_class.py:0:0:0:0 | Module in_class |
5+
| Global Variable __name__ | test.py:0:0:0:0 | Module test |
6+
| Global Variable __package__ | in_class.py:0:0:0:0 | Module in_class |
7+
| Global Variable __package__ | test.py:0:0:0:0 | Module test |
8+
| Global Variable bar | in_class.py:0:0:0:0 | Module in_class |
9+
| Global Variable base | test.py:0:0:0:0 | Module test |
10+
| Global Variable baz | in_class.py:0:0:0:0 | Module in_class |
11+
| Global Variable foo | in_class.py:0:0:0:0 | Module in_class |
12+
| Global Variable func0 | test.py:0:0:0:0 | Module test |
13+
| Global Variable func1 | test.py:0:0:0:0 | Module test |
14+
| Global Variable func2 | test.py:0:0:0:0 | Module test |
15+
| Global Variable func3 | test.py:0:0:0:0 | Module test |
16+
| Global Variable func4 | test.py:0:0:0:0 | Module test |
17+
| Global Variable func5 | test.py:0:0:0:0 | Module test |
18+
| Global Variable func6 | test.py:0:0:0:0 | Module test |
19+
| Global Variable global0 | test.py:0:0:0:0 | Module test |
20+
| Global Variable global1 | test.py:0:0:0:0 | Module test |
21+
| Global Variable global_local | test.py:0:0:0:0 | Module test |
22+
| Global Variable mc | in_class.py:0:0:0:0 | Module in_class |
23+
| Global Variable object | in_class.py:0:0:0:0 | Module in_class |
24+
| Global Variable print | in_class.py:0:0:0:0 | Module in_class |
25+
| Global Variable range | test.py:0:0:0:0 | Module test |
26+
| Global Variable seq | test.py:0:0:0:0 | Module test |
27+
| Global Variable use_in_loop | test.py:0:0:0:0 | Module test |
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import python
22

33
from GlobalVariable l
4-
select l.toString(), l.getScope().toString()
4+
select l, l.getScope()
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
foo = "foo-text"
2+
bar = "bar-text"
3+
4+
5+
class MyClass(object):
6+
baz = "baz"
7+
8+
def foo(self):
9+
print("foo()")
10+
11+
def use(self):
12+
print("! use()")
13+
print(foo) # foo-text
14+
print(bar) # bar-text
15+
try:
16+
print(baz)
17+
except NameError:
18+
pass
19+
20+
print("=== access on self ===")
21+
print(self.foo)
22+
print(self.baz)
23+
24+
def func(arg):
25+
print("! func()", arg)
26+
return 42
27+
28+
ex = func(baz)
29+
30+
class Sub(object):
31+
stuff = foo
32+
33+
mc = MyClass()
34+
print()
35+
mc.use()
36+
37+
print("\n! mc.ex")
38+
print(mc.ex)
39+
40+
print("\n! MyClass.Sub.stuff")
41+
print(MyClass.Sub().stuff) # foo-text

0 commit comments

Comments
 (0)