Cython 3.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. Cython/Build/BuildExecutable.py +169 -0
  2. Cython/Build/Cache.py +199 -0
  3. Cython/Build/Cythonize.py +323 -0
  4. Cython/Build/Dependencies.py +1306 -0
  5. Cython/Build/Distutils.py +1 -0
  6. Cython/Build/Inline.py +463 -0
  7. Cython/Build/IpythonMagic.py +560 -0
  8. Cython/Build/SharedModule.py +76 -0
  9. Cython/Build/Tests/TestCyCache.py +194 -0
  10. Cython/Build/Tests/TestCythonizeArgsParser.py +481 -0
  11. Cython/Build/Tests/TestDependencies.py +133 -0
  12. Cython/Build/Tests/TestInline.py +177 -0
  13. Cython/Build/Tests/TestIpythonMagic.py +287 -0
  14. Cython/Build/Tests/TestRecythonize.py +212 -0
  15. Cython/Build/Tests/TestStripLiterals.py +155 -0
  16. Cython/Build/Tests/__init__.py +1 -0
  17. Cython/Build/__init__.py +8 -0
  18. Cython/CodeWriter.py +811 -0
  19. Cython/Compiler/AnalysedTreeTransforms.py +97 -0
  20. Cython/Compiler/Annotate.py +326 -0
  21. Cython/Compiler/AutoDocTransforms.py +320 -0
  22. Cython/Compiler/Buffer.py +680 -0
  23. Cython/Compiler/Builtin.py +934 -0
  24. Cython/Compiler/CmdLine.py +259 -0
  25. Cython/Compiler/Code.pxd +148 -0
  26. Cython/Compiler/Code.py +3375 -0
  27. Cython/Compiler/CodeGeneration.py +33 -0
  28. Cython/Compiler/CythonScope.py +187 -0
  29. Cython/Compiler/Dataclass.py +868 -0
  30. Cython/Compiler/DebugFlags.py +24 -0
  31. Cython/Compiler/Errors.py +295 -0
  32. Cython/Compiler/ExprNodes.py +15267 -0
  33. Cython/Compiler/FlowControl.pxd +97 -0
  34. Cython/Compiler/FlowControl.py +1455 -0
  35. Cython/Compiler/FusedNode.py +1002 -0
  36. Cython/Compiler/Future.py +16 -0
  37. Cython/Compiler/Interpreter.py +57 -0
  38. Cython/Compiler/Lexicon.py +340 -0
  39. Cython/Compiler/LineTable.py +114 -0
  40. Cython/Compiler/Main.py +853 -0
  41. Cython/Compiler/MatchCaseNodes.py +259 -0
  42. Cython/Compiler/MemoryView.py +922 -0
  43. Cython/Compiler/ModuleNode.py +4024 -0
  44. Cython/Compiler/Naming.py +374 -0
  45. Cython/Compiler/Nodes.py +10826 -0
  46. Cython/Compiler/Optimize.py +5256 -0
  47. Cython/Compiler/Options.py +835 -0
  48. Cython/Compiler/ParseTreeTransforms.pxd +77 -0
  49. Cython/Compiler/ParseTreeTransforms.py +4509 -0
  50. Cython/Compiler/Parsing.pxd +9 -0
  51. Cython/Compiler/Parsing.py +4789 -0
  52. Cython/Compiler/Pipeline.py +439 -0
  53. Cython/Compiler/PyrexTypes.py +5762 -0
  54. Cython/Compiler/Pythran.py +232 -0
  55. Cython/Compiler/Scanning.pxd +40 -0
  56. Cython/Compiler/Scanning.py +577 -0
  57. Cython/Compiler/StringEncoding.py +347 -0
  58. Cython/Compiler/Symtab.py +3080 -0
  59. Cython/Compiler/Tests/TestBuffer.py +105 -0
  60. Cython/Compiler/Tests/TestBuiltin.py +72 -0
  61. Cython/Compiler/Tests/TestCmdLine.py +586 -0
  62. Cython/Compiler/Tests/TestCode.py +86 -0
  63. Cython/Compiler/Tests/TestFlowControl.py +65 -0
  64. Cython/Compiler/Tests/TestGrammar.py +202 -0
  65. Cython/Compiler/Tests/TestMemView.py +71 -0
  66. Cython/Compiler/Tests/TestParseTreeTransforms.py +285 -0
  67. Cython/Compiler/Tests/TestScanning.py +134 -0
  68. Cython/Compiler/Tests/TestSignatureMatching.py +73 -0
  69. Cython/Compiler/Tests/TestStringEncoding.py +33 -0
  70. Cython/Compiler/Tests/TestTreeFragment.py +63 -0
  71. Cython/Compiler/Tests/TestTreePath.py +103 -0
  72. Cython/Compiler/Tests/TestTypes.py +75 -0
  73. Cython/Compiler/Tests/TestUtilityLoad.py +112 -0
  74. Cython/Compiler/Tests/TestVisitor.py +61 -0
  75. Cython/Compiler/Tests/Utils.py +36 -0
  76. Cython/Compiler/Tests/__init__.py +1 -0
  77. Cython/Compiler/TreeFragment.py +278 -0
  78. Cython/Compiler/TreePath.py +303 -0
  79. Cython/Compiler/TypeInference.py +584 -0
  80. Cython/Compiler/TypeSlots.py +1181 -0
  81. Cython/Compiler/UFuncs.py +311 -0
  82. Cython/Compiler/UtilNodes.py +389 -0
  83. Cython/Compiler/UtilityCode.py +344 -0
  84. Cython/Compiler/Version.py +8 -0
  85. Cython/Compiler/Visitor.pxd +53 -0
  86. Cython/Compiler/Visitor.py +861 -0
  87. Cython/Compiler/__init__.py +1 -0
  88. Cython/Coverage.py +448 -0
  89. Cython/Debugger/Cygdb.py +175 -0
  90. Cython/Debugger/DebugWriter.py +82 -0
  91. Cython/Debugger/Tests/TestLibCython.py +275 -0
  92. Cython/Debugger/Tests/__init__.py +1 -0
  93. Cython/Debugger/Tests/cfuncs.c +8 -0
  94. Cython/Debugger/Tests/codefile +49 -0
  95. Cython/Debugger/Tests/test_libcython_in_gdb.py +578 -0
  96. Cython/Debugger/Tests/test_libpython_in_gdb.py +90 -0
  97. Cython/Debugger/__init__.py +1 -0
  98. Cython/Debugger/libcython.py +1548 -0
  99. Cython/Debugger/libpython.py +2821 -0
  100. Cython/Debugging.py +20 -0
  101. Cython/Distutils/__init__.py +2 -0
  102. Cython/Distutils/build_ext.py +139 -0
  103. Cython/Distutils/extension.py +96 -0
  104. Cython/Distutils/old_build_ext.py +351 -0
  105. Cython/Includes/cpython/__init__.pxd +173 -0
  106. Cython/Includes/cpython/array.pxd +174 -0
  107. Cython/Includes/cpython/bool.pxd +37 -0
  108. Cython/Includes/cpython/buffer.pxd +112 -0
  109. Cython/Includes/cpython/bytearray.pxd +33 -0
  110. Cython/Includes/cpython/bytes.pxd +200 -0
  111. Cython/Includes/cpython/cellobject.pxd +35 -0
  112. Cython/Includes/cpython/ceval.pxd +8 -0
  113. Cython/Includes/cpython/codecs.pxd +121 -0
  114. Cython/Includes/cpython/complex.pxd +60 -0
  115. Cython/Includes/cpython/contextvars.pxd +145 -0
  116. Cython/Includes/cpython/conversion.pxd +36 -0
  117. Cython/Includes/cpython/datetime.pxd +395 -0
  118. Cython/Includes/cpython/descr.pxd +26 -0
  119. Cython/Includes/cpython/dict.pxd +187 -0
  120. Cython/Includes/cpython/exc.pxd +263 -0
  121. Cython/Includes/cpython/fileobject.pxd +57 -0
  122. Cython/Includes/cpython/float.pxd +47 -0
  123. Cython/Includes/cpython/function.pxd +65 -0
  124. Cython/Includes/cpython/genobject.pxd +25 -0
  125. Cython/Includes/cpython/getargs.pxd +12 -0
  126. Cython/Includes/cpython/instance.pxd +25 -0
  127. Cython/Includes/cpython/iterator.pxd +36 -0
  128. Cython/Includes/cpython/iterobject.pxd +24 -0
  129. Cython/Includes/cpython/list.pxd +92 -0
  130. Cython/Includes/cpython/long.pxd +149 -0
  131. Cython/Includes/cpython/longintrepr.pxd +14 -0
  132. Cython/Includes/cpython/mapping.pxd +63 -0
  133. Cython/Includes/cpython/marshal.pxd +66 -0
  134. Cython/Includes/cpython/mem.pxd +120 -0
  135. Cython/Includes/cpython/memoryview.pxd +50 -0
  136. Cython/Includes/cpython/method.pxd +49 -0
  137. Cython/Includes/cpython/module.pxd +208 -0
  138. Cython/Includes/cpython/number.pxd +258 -0
  139. Cython/Includes/cpython/object.pxd +433 -0
  140. Cython/Includes/cpython/pycapsule.pxd +143 -0
  141. Cython/Includes/cpython/pylifecycle.pxd +68 -0
  142. Cython/Includes/cpython/pyport.pxd +8 -0
  143. Cython/Includes/cpython/pystate.pxd +95 -0
  144. Cython/Includes/cpython/pythread.pxd +53 -0
  145. Cython/Includes/cpython/ref.pxd +67 -0
  146. Cython/Includes/cpython/sequence.pxd +134 -0
  147. Cython/Includes/cpython/set.pxd +119 -0
  148. Cython/Includes/cpython/slice.pxd +70 -0
  149. Cython/Includes/cpython/time.pxd +129 -0
  150. Cython/Includes/cpython/tuple.pxd +72 -0
  151. Cython/Includes/cpython/type.pxd +53 -0
  152. Cython/Includes/cpython/unicode.pxd +639 -0
  153. Cython/Includes/cpython/version.pxd +32 -0
  154. Cython/Includes/cpython/weakref.pxd +78 -0
  155. Cython/Includes/libc/__init__.pxd +1 -0
  156. Cython/Includes/libc/complex.pxd +35 -0
  157. Cython/Includes/libc/errno.pxd +127 -0
  158. Cython/Includes/libc/float.pxd +43 -0
  159. Cython/Includes/libc/limits.pxd +28 -0
  160. Cython/Includes/libc/locale.pxd +46 -0
  161. Cython/Includes/libc/math.pxd +209 -0
  162. Cython/Includes/libc/setjmp.pxd +10 -0
  163. Cython/Includes/libc/signal.pxd +64 -0
  164. Cython/Includes/libc/stddef.pxd +9 -0
  165. Cython/Includes/libc/stdint.pxd +105 -0
  166. Cython/Includes/libc/stdio.pxd +80 -0
  167. Cython/Includes/libc/stdlib.pxd +72 -0
  168. Cython/Includes/libc/string.pxd +50 -0
  169. Cython/Includes/libc/threads.pxd +84 -0
  170. Cython/Includes/libc/time.pxd +51 -0
  171. Cython/Includes/libcpp/__init__.pxd +4 -0
  172. Cython/Includes/libcpp/algorithm.pxd +320 -0
  173. Cython/Includes/libcpp/any.pxd +16 -0
  174. Cython/Includes/libcpp/atomic.pxd +59 -0
  175. Cython/Includes/libcpp/barrier.pxd +22 -0
  176. Cython/Includes/libcpp/bit.pxd +29 -0
  177. Cython/Includes/libcpp/cast.pxd +12 -0
  178. Cython/Includes/libcpp/cmath.pxd +518 -0
  179. Cython/Includes/libcpp/complex.pxd +106 -0
  180. Cython/Includes/libcpp/deque.pxd +165 -0
  181. Cython/Includes/libcpp/exception.pxd +86 -0
  182. Cython/Includes/libcpp/execution.pxd +15 -0
  183. Cython/Includes/libcpp/forward_list.pxd +63 -0
  184. Cython/Includes/libcpp/functional.pxd +26 -0
  185. Cython/Includes/libcpp/future.pxd +103 -0
  186. Cython/Includes/libcpp/iterator.pxd +34 -0
  187. Cython/Includes/libcpp/latch.pxd +17 -0
  188. Cython/Includes/libcpp/limits.pxd +61 -0
  189. Cython/Includes/libcpp/list.pxd +117 -0
  190. Cython/Includes/libcpp/map.pxd +252 -0
  191. Cython/Includes/libcpp/memory.pxd +115 -0
  192. Cython/Includes/libcpp/mutex.pxd +130 -0
  193. Cython/Includes/libcpp/numbers.pxd +15 -0
  194. Cython/Includes/libcpp/numeric.pxd +131 -0
  195. Cython/Includes/libcpp/optional.pxd +34 -0
  196. Cython/Includes/libcpp/pair.pxd +1 -0
  197. Cython/Includes/libcpp/queue.pxd +25 -0
  198. Cython/Includes/libcpp/random.pxd +166 -0
  199. Cython/Includes/libcpp/semaphore.pxd +44 -0
  200. Cython/Includes/libcpp/set.pxd +228 -0
  201. Cython/Includes/libcpp/shared_mutex.pxd +72 -0
  202. Cython/Includes/libcpp/span.pxd +87 -0
  203. Cython/Includes/libcpp/stack.pxd +11 -0
  204. Cython/Includes/libcpp/stop_token.pxd +105 -0
  205. Cython/Includes/libcpp/string.pxd +355 -0
  206. Cython/Includes/libcpp/string_view.pxd +181 -0
  207. Cython/Includes/libcpp/typeindex.pxd +15 -0
  208. Cython/Includes/libcpp/typeinfo.pxd +10 -0
  209. Cython/Includes/libcpp/unordered_map.pxd +193 -0
  210. Cython/Includes/libcpp/unordered_set.pxd +152 -0
  211. Cython/Includes/libcpp/utility.pxd +30 -0
  212. Cython/Includes/libcpp/vector.pxd +186 -0
  213. Cython/Includes/openmp.pxd +50 -0
  214. Cython/Includes/posix/__init__.pxd +1 -0
  215. Cython/Includes/posix/dlfcn.pxd +14 -0
  216. Cython/Includes/posix/fcntl.pxd +86 -0
  217. Cython/Includes/posix/ioctl.pxd +4 -0
  218. Cython/Includes/posix/mman.pxd +101 -0
  219. Cython/Includes/posix/resource.pxd +57 -0
  220. Cython/Includes/posix/select.pxd +21 -0
  221. Cython/Includes/posix/signal.pxd +73 -0
  222. Cython/Includes/posix/stat.pxd +98 -0
  223. Cython/Includes/posix/stdio.pxd +37 -0
  224. Cython/Includes/posix/stdlib.pxd +29 -0
  225. Cython/Includes/posix/strings.pxd +9 -0
  226. Cython/Includes/posix/time.pxd +71 -0
  227. Cython/Includes/posix/types.pxd +30 -0
  228. Cython/Includes/posix/uio.pxd +26 -0
  229. Cython/Includes/posix/unistd.pxd +271 -0
  230. Cython/Includes/posix/wait.pxd +38 -0
  231. Cython/Plex/Actions.pxd +24 -0
  232. Cython/Plex/Actions.py +119 -0
  233. Cython/Plex/DFA.pxd +14 -0
  234. Cython/Plex/DFA.py +164 -0
  235. Cython/Plex/Errors.py +48 -0
  236. Cython/Plex/Lexicons.py +178 -0
  237. Cython/Plex/Machines.pxd +36 -0
  238. Cython/Plex/Machines.py +238 -0
  239. Cython/Plex/Regexps.py +539 -0
  240. Cython/Plex/Scanners.pxd +47 -0
  241. Cython/Plex/Scanners.py +360 -0
  242. Cython/Plex/Transitions.pxd +14 -0
  243. Cython/Plex/Transitions.py +239 -0
  244. Cython/Plex/__init__.py +34 -0
  245. Cython/Runtime/__init__.py +1 -0
  246. Cython/Runtime/refnanny.pyx +237 -0
  247. Cython/Shadow.py +690 -0
  248. Cython/Shadow.pyi +521 -0
  249. Cython/StringIOTree.py +170 -0
  250. Cython/Tempita/__init__.py +4 -0
  251. Cython/Tempita/_looper.py +154 -0
  252. Cython/Tempita/_tempita.py +1091 -0
  253. Cython/TestUtils.py +410 -0
  254. Cython/Tests/TestCodeWriter.py +128 -0
  255. Cython/Tests/TestCythonUtils.py +202 -0
  256. Cython/Tests/TestJediTyper.py +223 -0
  257. Cython/Tests/TestShadow.py +114 -0
  258. Cython/Tests/TestStringIOTree.py +67 -0
  259. Cython/Tests/TestTestUtils.py +90 -0
  260. Cython/Tests/__init__.py +1 -0
  261. Cython/Tests/xmlrunner.py +390 -0
  262. Cython/Utility/AsyncGen.c +1002 -0
  263. Cython/Utility/Buffer.c +875 -0
  264. Cython/Utility/BufferFormatFromTypeInfo.pxd +2 -0
  265. Cython/Utility/Builtins.c +776 -0
  266. Cython/Utility/CConvert.pyx +134 -0
  267. Cython/Utility/CMath.c +104 -0
  268. Cython/Utility/CommonStructures.c +118 -0
  269. Cython/Utility/Complex.c +378 -0
  270. Cython/Utility/Coroutine.c +2206 -0
  271. Cython/Utility/CpdefEnums.pyx +103 -0
  272. Cython/Utility/CppConvert.pyx +279 -0
  273. Cython/Utility/CppSupport.cpp +143 -0
  274. Cython/Utility/CythonFunction.c +1794 -0
  275. Cython/Utility/Dataclasses.c +185 -0
  276. Cython/Utility/Dataclasses.py +112 -0
  277. Cython/Utility/Embed.c +125 -0
  278. Cython/Utility/Exceptions.c +1012 -0
  279. Cython/Utility/ExtensionTypes.c +809 -0
  280. Cython/Utility/FunctionArguments.c +965 -0
  281. Cython/Utility/ImportExport.c +987 -0
  282. Cython/Utility/Lock.c +136 -0
  283. Cython/Utility/MemoryView.pxd +187 -0
  284. Cython/Utility/MemoryView.pyx +1481 -0
  285. Cython/Utility/MemoryView_C.c +1046 -0
  286. Cython/Utility/ModuleSetupCode.c +3059 -0
  287. Cython/Utility/NumpyImportArray.c +46 -0
  288. Cython/Utility/ObjectHandling.c +3342 -0
  289. Cython/Utility/Optimize.c +1589 -0
  290. Cython/Utility/Overflow.c +404 -0
  291. Cython/Utility/Printing.c +86 -0
  292. Cython/Utility/Profile.c +709 -0
  293. Cython/Utility/StringTools.c +1259 -0
  294. Cython/Utility/TestCyUtilityLoader.pyx +8 -0
  295. Cython/Utility/TestCythonScope.pyx +75 -0
  296. Cython/Utility/TestUtilityLoader.c +12 -0
  297. Cython/Utility/TypeConversion.c +1284 -0
  298. Cython/Utility/UFuncs.pyx +50 -0
  299. Cython/Utility/UFuncs_C.c +89 -0
  300. Cython/Utility/__init__.py +28 -0
  301. Cython/Utility/arrayarray.h +148 -0
  302. Cython/Utils.py +687 -0
  303. Cython/__init__.py +10 -0
  304. Cython/__init__.pyi +7 -0
  305. Cython/py.typed +0 -0
  306. cython-3.1.0.dist-info/COPYING.txt +19 -0
  307. cython-3.1.0.dist-info/LICENSE.txt +176 -0
  308. cython-3.1.0.dist-info/METADATA +636 -0
  309. cython-3.1.0.dist-info/RECORD +316 -0
  310. cython-3.1.0.dist-info/WHEEL +5 -0
  311. cython-3.1.0.dist-info/entry_points.txt +4 -0
  312. cython-3.1.0.dist-info/top_level.txt +3 -0
  313. cython.py +29 -0
  314. pyximport/__init__.py +4 -0
  315. pyximport/pyxbuild.py +160 -0
  316. pyximport/pyximport.py +482 -0
@@ -0,0 +1,1455 @@
1
+ # cython: auto_pickle=True
2
+
3
+
4
+ import cython
5
+ cython.declare(PyrexTypes=object, ExprNodes=object, Nodes=object, Builtin=object,
6
+ Options=object, TreeVisitor=object, CythonTransform=object,
7
+ InternalError=object, error=object, warning=object,
8
+ fake_rhs_expr=object, TypedExprNode=object)
9
+
10
+ from . import Builtin
11
+ from . import ExprNodes
12
+ from . import Nodes
13
+ from . import Options
14
+ from . import PyrexTypes
15
+
16
+ from .Visitor import TreeVisitor, CythonTransform
17
+ from .Errors import error, warning, InternalError
18
+
19
+
20
+ class TypedExprNode(ExprNodes.ExprNode):
21
+ # Used for declaring assignments of a specified type without a known entry.
22
+ def __init__(self, type, may_be_none=None, pos=None):
23
+ super().__init__(pos)
24
+ self.type = type
25
+ self._may_be_none = may_be_none
26
+
27
+ def may_be_none(self):
28
+ return self._may_be_none != False
29
+
30
+ # Fake rhs to silence "unused variable" warning
31
+ fake_rhs_expr = TypedExprNode(PyrexTypes.unspecified_type)
32
+
33
+
34
+ class ControlBlock:
35
+ """Control flow graph node. Sequence of assignments and name references.
36
+
37
+ children set of children nodes
38
+ parents set of parent nodes
39
+ positions set of position markers
40
+
41
+ stats list of block statements
42
+ gen dict of assignments generated by this block
43
+ bounded set of entries that are definitely bounded in this block
44
+
45
+ Example:
46
+
47
+ a = 1
48
+ b = a + c # 'c' is already bounded or exception here
49
+
50
+ stats = [Assignment(a), NameReference(a), NameReference(c),
51
+ Assignment(b)]
52
+ gen = {Entry(a): Assignment(a), Entry(b): Assignment(b)}
53
+ bounded = {Entry(a), Entry(c)}
54
+
55
+ """
56
+
57
+ def __init__(self):
58
+ self.children = set()
59
+ self.parents = set()
60
+ self.positions = set()
61
+
62
+ self.stats = []
63
+ self.gen = {}
64
+ self.bounded = set()
65
+
66
+ self.i_input = 0
67
+ self.i_output = 0
68
+ self.i_gen = 0
69
+ self.i_kill = 0
70
+ self.i_state = 0
71
+
72
+ def empty(self):
73
+ return (not self.stats and not self.positions)
74
+
75
+ def detach(self):
76
+ """Detach block from parents and children."""
77
+ for child in self.children:
78
+ child.parents.remove(self)
79
+ for parent in self.parents:
80
+ parent.children.remove(self)
81
+ self.parents.clear()
82
+ self.children.clear()
83
+
84
+ def add_child(self, block):
85
+ self.children.add(block)
86
+ block.parents.add(self)
87
+
88
+
89
+ class ExitBlock(ControlBlock):
90
+ """Non-empty exit point block."""
91
+
92
+ def empty(self):
93
+ return False
94
+
95
+
96
+ class AssignmentList:
97
+ def __init__(self):
98
+ self.stats = []
99
+
100
+
101
+ class ControlFlow:
102
+ """Control-flow graph.
103
+
104
+ entry_point ControlBlock entry point for this graph
105
+ exit_point ControlBlock normal exit point
106
+ block ControlBlock current block
107
+ blocks set children nodes
108
+ entries set tracked entries
109
+ loops list stack for loop descriptors
110
+ exceptions list stack for exception descriptors
111
+ in_try_block int track if we're in a try...except or try...finally block
112
+ """
113
+
114
+ def __init__(self):
115
+ self.blocks = set()
116
+ self.entries = set()
117
+ self.loops = []
118
+ self.exceptions = []
119
+
120
+ self.entry_point = ControlBlock()
121
+ self.exit_point = ExitBlock()
122
+ self.blocks.add(self.exit_point)
123
+ self.block = self.entry_point
124
+ self.in_try_block = 0
125
+
126
+ def newblock(self, parent=None):
127
+ """Create floating block linked to `parent` if given.
128
+
129
+ NOTE: Block is NOT added to self.blocks
130
+ """
131
+ block = ControlBlock()
132
+ self.blocks.add(block)
133
+ if parent:
134
+ parent.add_child(block)
135
+ return block
136
+
137
+ def nextblock(self, parent=None):
138
+ """Create block children block linked to current or `parent` if given.
139
+
140
+ NOTE: Block is added to self.blocks
141
+ """
142
+ block = ControlBlock()
143
+ self.blocks.add(block)
144
+ if parent:
145
+ parent.add_child(block)
146
+ elif self.block:
147
+ self.block.add_child(block)
148
+ self.block = block
149
+ return self.block
150
+
151
+ def is_tracked(self, entry):
152
+ if entry.is_anonymous:
153
+ return False
154
+ return (entry.is_local or entry.is_pyclass_attr or entry.is_arg or
155
+ entry.from_closure or entry.in_closure or
156
+ entry.error_on_uninitialized)
157
+
158
+ def is_statically_assigned(self, entry):
159
+ if (entry.is_local and entry.is_variable and
160
+ (entry.type.is_struct_or_union or
161
+ entry.type.is_complex or
162
+ entry.type.is_array or
163
+ entry.type.is_cython_lock_type or
164
+ (entry.type.is_cpp_class and not entry.is_cpp_optional))):
165
+ # stack allocated structured variable => never uninitialised
166
+ return True
167
+ return False
168
+
169
+ def mark_position(self, node):
170
+ """Mark position, will be used to draw graph nodes."""
171
+ if self.block:
172
+ self.block.positions.add(node.pos[:2])
173
+
174
+ def mark_assignment(self, lhs, rhs, entry, rhs_scope=None):
175
+ if self.block and self.is_tracked(entry):
176
+ assignment = NameAssignment(lhs, rhs, entry, rhs_scope=rhs_scope)
177
+ self.block.stats.append(assignment)
178
+ self.block.gen[entry] = assignment
179
+ self.entries.add(entry)
180
+
181
+ def mark_argument(self, lhs, rhs, entry):
182
+ if self.block and self.is_tracked(entry):
183
+ assignment = Argument(lhs, rhs, entry)
184
+ self.block.stats.append(assignment)
185
+ self.block.gen[entry] = assignment
186
+ self.entries.add(entry)
187
+
188
+ def mark_deletion(self, node, entry):
189
+ if self.block and self.is_tracked(entry):
190
+ assignment = NameDeletion(node, entry)
191
+ self.block.stats.append(assignment)
192
+ self.block.gen[entry] = Uninitialized
193
+ self.entries.add(entry)
194
+
195
+ def mark_reference(self, node, entry):
196
+ if self.block and self.is_tracked(entry):
197
+ self.block.stats.append(NameReference(node, entry))
198
+ ## XXX: We don't track expression evaluation order so we can't use
199
+ ## XXX: successful reference as initialization sign.
200
+ ## # Local variable is definitely bound after this reference
201
+ ## if not node.allow_null:
202
+ ## self.block.bounded.add(entry)
203
+ self.entries.add(entry)
204
+
205
+ def normalize(self):
206
+ """Delete unreachable and orphan blocks."""
207
+ queue = {self.entry_point}
208
+ visited = set()
209
+ while queue:
210
+ root = queue.pop()
211
+ visited.add(root)
212
+ for child in root.children:
213
+ if child not in visited:
214
+ queue.add(child)
215
+
216
+ unreachable: set = self.blocks - visited
217
+ block: ControlBlock
218
+ for block in unreachable:
219
+ block.detach()
220
+
221
+ visited.remove(self.entry_point)
222
+
223
+ parent: ControlBlock
224
+ for block in visited:
225
+ if block.empty():
226
+ for parent in block.parents: # Re-parent
227
+ for child in block.children:
228
+ parent.add_child(child)
229
+ block.detach()
230
+ unreachable.add(block)
231
+ self.blocks -= unreachable
232
+
233
+ def initialize(self):
234
+ """Set initial state, map assignments to bits."""
235
+ self.assmts = {}
236
+ assmts: AssignmentList
237
+ block: ControlBlock
238
+
239
+ bit: int = 1
240
+ for entry in self.entries:
241
+ assmts = AssignmentList()
242
+ assmts.mask = assmts.bit = bit
243
+ self.assmts[entry] = assmts
244
+ bit <<= 1
245
+
246
+ for block in self.blocks:
247
+ for stat in block.stats:
248
+ if isinstance(stat, NameAssignment):
249
+ stat.bit = bit
250
+ assmts = self.assmts[stat.entry]
251
+ assmts.stats.append(stat)
252
+ assmts.mask |= bit
253
+ bit <<= 1
254
+
255
+ for block in self.blocks:
256
+ for entry, stat in block.gen.items():
257
+ assmts = self.assmts[entry]
258
+ if stat is Uninitialized:
259
+ block.i_gen |= assmts.bit
260
+ else:
261
+ block.i_gen |= stat.bit
262
+ block.i_kill |= assmts.mask
263
+ block.i_output = block.i_gen
264
+ for entry in block.bounded:
265
+ block.i_kill |= self.assmts[entry].bit
266
+
267
+ for assmts in self.assmts.values():
268
+ self.entry_point.i_gen |= assmts.bit
269
+ self.entry_point.i_output = self.entry_point.i_gen
270
+
271
+ def map_one(self, istate, entry):
272
+ ret = set()
273
+ assmts: AssignmentList = self.assmts[entry]
274
+ if istate & assmts.bit:
275
+ if self.is_statically_assigned(entry):
276
+ ret.add(StaticAssignment(entry))
277
+ elif entry.from_closure:
278
+ ret.add(Unknown)
279
+ else:
280
+ ret.add(Uninitialized)
281
+
282
+ assmt: NameAssignment
283
+ for assmt in assmts.stats:
284
+ if istate & assmt.bit:
285
+ ret.add(assmt)
286
+ return ret
287
+
288
+ def reaching_definitions(self):
289
+ """Per-block reaching definitions analysis."""
290
+ block: ControlBlock
291
+ parent: ControlBlock
292
+
293
+ dirty = True
294
+ while dirty:
295
+ dirty = False
296
+ for block in self.blocks:
297
+ i_input = 0
298
+ for parent in block.parents:
299
+ i_input |= parent.i_output
300
+ i_output = (i_input & ~block.i_kill) | block.i_gen
301
+ if i_output != block.i_output:
302
+ dirty = True
303
+ block.i_input = i_input
304
+ block.i_output = i_output
305
+
306
+
307
+ class LoopDescr:
308
+ def __init__(self, next_block, loop_block):
309
+ self.next_block = next_block
310
+ self.loop_block = loop_block
311
+ self.exceptions = []
312
+
313
+
314
+ class ExceptionDescr:
315
+ """Exception handling helper.
316
+
317
+ entry_point ControlBlock Exception handling entry point
318
+ finally_enter ControlBlock Normal finally clause entry point
319
+ finally_exit ControlBlock Normal finally clause exit point
320
+ """
321
+
322
+ def __init__(self, entry_point, finally_enter=None, finally_exit=None):
323
+ self.entry_point = entry_point
324
+ self.finally_enter = finally_enter
325
+ self.finally_exit = finally_exit
326
+
327
+
328
+ class NameAssignment:
329
+ def __init__(self, lhs, rhs, entry, rhs_scope=None):
330
+ if lhs.cf_state is None:
331
+ lhs.cf_state = set()
332
+ self.lhs = lhs
333
+ self.rhs = rhs
334
+ self.entry = entry
335
+ self.pos = lhs.pos
336
+ self.refs = set()
337
+ self.is_arg = False
338
+ self.is_deletion = False
339
+ self.inferred_type = None
340
+ # For generator expression targets, the rhs can have a different scope than the lhs.
341
+ self.rhs_scope = rhs_scope
342
+
343
+ def __repr__(self):
344
+ return '%s(entry=%r)' % (self.__class__.__name__, self.entry)
345
+
346
+ def infer_type(self):
347
+ self.inferred_type = self.rhs.infer_type(self.rhs_scope or self.entry.scope)
348
+ return self.inferred_type
349
+
350
+ def type_dependencies(self):
351
+ return self.rhs.type_dependencies(self.rhs_scope or self.entry.scope)
352
+
353
+ @property
354
+ def type(self):
355
+ if not self.entry.type.is_unspecified:
356
+ return self.entry.type
357
+ return self.inferred_type
358
+
359
+
360
+ class StaticAssignment(NameAssignment):
361
+ """Initialised at declaration time, e.g. stack allocation."""
362
+ def __init__(self, entry):
363
+ if not entry.type.is_pyobject:
364
+ may_be_none = False
365
+ else:
366
+ may_be_none = None # unknown
367
+ lhs = TypedExprNode(
368
+ entry.type, may_be_none=may_be_none, pos=entry.pos)
369
+ super().__init__(lhs, lhs, entry)
370
+
371
+ def infer_type(self):
372
+ return self.entry.type
373
+
374
+ def type_dependencies(self):
375
+ return ()
376
+
377
+
378
+ class Argument(NameAssignment):
379
+ def __init__(self, lhs, rhs, entry):
380
+ NameAssignment.__init__(self, lhs, rhs, entry)
381
+ self.is_arg = True
382
+
383
+
384
+ class NameDeletion(NameAssignment):
385
+ def __init__(self, lhs, entry):
386
+ NameAssignment.__init__(self, lhs, lhs, entry)
387
+ self.is_deletion = True
388
+
389
+ def infer_type(self):
390
+ inferred_type = self.rhs.infer_type(self.entry.scope)
391
+ if (not inferred_type.is_pyobject
392
+ and inferred_type.can_coerce_to_pyobject(self.entry.scope)):
393
+ return PyrexTypes.py_object_type
394
+ self.inferred_type = inferred_type
395
+ return inferred_type
396
+
397
+
398
+ class Uninitialized:
399
+ """Definitely not initialised yet."""
400
+
401
+
402
+ class Unknown:
403
+ """Coming from outer closure, might be initialised or not."""
404
+
405
+
406
+ class NameReference:
407
+ def __init__(self, node, entry):
408
+ if node.cf_state is None:
409
+ node.cf_state = set()
410
+ self.node = node
411
+ self.entry = entry
412
+ self.pos = node.pos
413
+
414
+ def __repr__(self):
415
+ return '%s(entry=%r)' % (self.__class__.__name__, self.entry)
416
+
417
+
418
+ class ControlFlowState(list):
419
+ # Keeps track of Node's entry assignments
420
+ #
421
+ # cf_is_null [boolean] It is uninitialized
422
+ # cf_maybe_null [boolean] May be uninitialized
423
+ # is_single [boolean] Has only one assignment at this point
424
+
425
+ cf_maybe_null = False
426
+ cf_is_null = False
427
+ is_single = False
428
+
429
+ def __init__(self, state):
430
+ if Uninitialized in state:
431
+ state.discard(Uninitialized)
432
+ self.cf_maybe_null = True
433
+ if not state:
434
+ self.cf_is_null = True
435
+ elif Unknown in state:
436
+ state.discard(Unknown)
437
+ self.cf_maybe_null = True
438
+ else:
439
+ if len(state) == 1:
440
+ self.is_single = True
441
+ # XXX: Remove fake_rhs_expr
442
+ super().__init__(
443
+ [i for i in state if i.rhs is not fake_rhs_expr])
444
+
445
+ def one(self):
446
+ return self[0]
447
+
448
+
449
+ class GVContext:
450
+ """Graphviz subgraph object."""
451
+
452
+ def __init__(self):
453
+ self.blockids = {}
454
+ self.nextid = 0
455
+ self.children = []
456
+ self.sources = {}
457
+
458
+ def add(self, child):
459
+ self.children.append(child)
460
+
461
+ def nodeid(self, block):
462
+ if block not in self.blockids:
463
+ self.blockids[block] = 'block%d' % self.nextid
464
+ self.nextid += 1
465
+ return self.blockids[block]
466
+
467
+ def extract_sources(self, block):
468
+ if not block.positions:
469
+ return ''
470
+ start = min(block.positions)
471
+ stop = max(block.positions)
472
+ srcdescr = start[0]
473
+ if srcdescr not in self.sources:
474
+ self.sources[srcdescr] = list(srcdescr.get_lines())
475
+ lines = self.sources[srcdescr]
476
+ return '\\n'.join([l.strip() for l in lines[start[1] - 1:stop[1]]])
477
+
478
+ def render(self, fp, name, annotate_defs=False):
479
+ """Render graphviz dot graph"""
480
+ fp.write('digraph %s {\n' % name)
481
+ fp.write(' node [shape=box];\n')
482
+ for child in self.children:
483
+ child.render(fp, self, annotate_defs)
484
+ fp.write('}\n')
485
+
486
+ def escape(self, text):
487
+ return text.replace('"', '\\"').replace('\n', '\\n')
488
+
489
+
490
+ class GV:
491
+ """Graphviz DOT renderer."""
492
+
493
+ def __init__(self, name, flow):
494
+ self.name = name
495
+ self.flow = flow
496
+
497
+ def render(self, fp, ctx, annotate_defs=False):
498
+ fp.write(' subgraph %s {\n' % self.name)
499
+ for block in self.flow.blocks:
500
+ label = ctx.extract_sources(block)
501
+ if annotate_defs:
502
+ for stat in block.stats:
503
+ if isinstance(stat, NameAssignment):
504
+ label += '\n %s [%s %s]' % (
505
+ stat.entry.name, 'deletion' if stat.is_deletion else 'definition', stat.pos[1])
506
+ elif isinstance(stat, NameReference):
507
+ if stat.entry:
508
+ label += '\n %s [reference %s]' % (stat.entry.name, stat.pos[1])
509
+ if not label:
510
+ label = 'empty'
511
+ pid = ctx.nodeid(block)
512
+ fp.write(' %s [label="%s"];\n' % (pid, ctx.escape(label)))
513
+ for block in self.flow.blocks:
514
+ pid = ctx.nodeid(block)
515
+ for child in block.children:
516
+ fp.write(' %s -> %s;\n' % (pid, ctx.nodeid(child)))
517
+ fp.write(' }\n')
518
+
519
+
520
+ class MessageCollection:
521
+ """Collect error/warnings messages first then sort"""
522
+ def __init__(self):
523
+ self.messages = set()
524
+
525
+ def error(self, pos, message):
526
+ self.messages.add((pos, True, message))
527
+
528
+ def warning(self, pos, message):
529
+ self.messages.add((pos, False, message))
530
+
531
+ def report(self):
532
+ for pos, is_error, message in sorted(self.messages):
533
+ if is_error:
534
+ error(pos, message)
535
+ else:
536
+ warning(pos, message, 2)
537
+
538
+
539
+ @cython.cfunc
540
+ def check_definitions(flow: ControlFlow, compiler_directives: dict):
541
+ flow.initialize()
542
+ flow.reaching_definitions()
543
+
544
+ # Track down state
545
+ assignments = set()
546
+ # Node to entry map
547
+ references = {}
548
+ assmt_nodes = set()
549
+
550
+ block: ControlBlock
551
+ assmt: NameAssignment
552
+ for block in flow.blocks:
553
+ i_state = block.i_input
554
+ for stat in block.stats:
555
+ i_assmts = flow.assmts[stat.entry]
556
+ state = flow.map_one(i_state, stat.entry)
557
+ if isinstance(stat, NameAssignment):
558
+ stat.lhs.cf_state.update(state)
559
+ assmt_nodes.add(stat.lhs)
560
+ i_state = i_state & ~i_assmts.mask
561
+ if stat.is_deletion:
562
+ i_state |= i_assmts.bit
563
+ else:
564
+ i_state |= stat.bit
565
+ assignments.add(stat)
566
+ if stat.rhs is not fake_rhs_expr:
567
+ stat.entry.cf_assignments.append(stat)
568
+ elif isinstance(stat, NameReference):
569
+ references[stat.node] = stat.entry
570
+ stat.entry.cf_references.append(stat)
571
+ stat.node.cf_state.update(state)
572
+ ## if not stat.node.allow_null:
573
+ ## i_state &= ~i_assmts.bit
574
+ ## # after successful read, the state is known to be initialised
575
+ state.discard(Uninitialized)
576
+ state.discard(Unknown)
577
+ for assmt in state:
578
+ assmt.refs.add(stat)
579
+
580
+ # Check variable usage
581
+ warn_maybe_uninitialized = compiler_directives['warn.maybe_uninitialized']
582
+ warn_unused_result = compiler_directives['warn.unused_result']
583
+ warn_unused = compiler_directives['warn.unused']
584
+ warn_unused_arg = compiler_directives['warn.unused_arg']
585
+
586
+ messages = MessageCollection()
587
+
588
+ # assignment hints
589
+ for node in assmt_nodes:
590
+ if Uninitialized in node.cf_state:
591
+ node.cf_maybe_null = True
592
+ if len(node.cf_state) == 1:
593
+ node.cf_is_null = True
594
+ else:
595
+ node.cf_is_null = False
596
+ elif Unknown in node.cf_state:
597
+ node.cf_maybe_null = True
598
+ else:
599
+ node.cf_is_null = False
600
+ node.cf_maybe_null = False
601
+
602
+ # Find uninitialized references and cf-hints
603
+ for node, entry in references.items():
604
+ if Uninitialized in node.cf_state:
605
+ node.cf_maybe_null = True
606
+ if (not entry.from_closure and len(node.cf_state) == 1
607
+ and entry.name not in entry.scope.scope_predefined_names):
608
+ node.cf_is_null = True
609
+ if (node.allow_null or entry.from_closure
610
+ or entry.is_pyclass_attr or entry.type.is_error):
611
+ pass # Can be uninitialized here
612
+ elif node.cf_is_null and not entry.in_closure:
613
+ if entry.error_on_uninitialized or (
614
+ Options.error_on_uninitialized and (
615
+ entry.type.is_pyobject or entry.type.is_unspecified)):
616
+ messages.error(
617
+ node.pos,
618
+ "local variable '%s' referenced before assignment"
619
+ % entry.name)
620
+ else:
621
+ messages.warning(
622
+ node.pos,
623
+ "local variable '%s' referenced before assignment"
624
+ % entry.name)
625
+ elif warn_maybe_uninitialized:
626
+ msg = "local variable '%s' might be referenced before assignment" % entry.name
627
+ if entry.in_closure:
628
+ msg += " (maybe initialized inside a closure)"
629
+ messages.warning(
630
+ node.pos,
631
+ msg)
632
+ elif Unknown in node.cf_state:
633
+ # TODO: better cross-closure analysis to know when inner functions
634
+ # are being called before a variable is being set, and when
635
+ # a variable is known to be set before even defining the
636
+ # inner function, etc.
637
+ node.cf_maybe_null = True
638
+ else:
639
+ node.cf_is_null = False
640
+ node.cf_maybe_null = False
641
+
642
+ # Unused result
643
+ for assmt in assignments:
644
+ if (not assmt.refs and not assmt.entry.is_pyclass_attr
645
+ and not assmt.entry.in_closure):
646
+ if assmt.entry.cf_references and warn_unused_result:
647
+ if assmt.is_arg:
648
+ messages.warning(assmt.pos, "Unused argument value '%s'" %
649
+ assmt.entry.name)
650
+ else:
651
+ messages.warning(assmt.pos, "Unused result in '%s'" %
652
+ assmt.entry.name)
653
+ assmt.lhs.cf_used = False
654
+
655
+ # Unused entries
656
+ for entry in flow.entries:
657
+ if (not entry.cf_references
658
+ and not entry.is_pyclass_attr):
659
+ if entry.name != '_' and not entry.name.startswith('unused'):
660
+ # '_' is often used for unused variables, e.g. in loops
661
+ if entry.is_arg:
662
+ if warn_unused_arg:
663
+ messages.warning(entry.pos, "Unused argument '%s'" %
664
+ entry.name)
665
+ else:
666
+ if warn_unused:
667
+ messages.warning(entry.pos, "Unused entry '%s'" %
668
+ entry.name)
669
+ entry.cf_used = False
670
+
671
+ messages.report()
672
+
673
+ for node in assmt_nodes:
674
+ node.cf_state = ControlFlowState(node.cf_state)
675
+ for node in references:
676
+ node.cf_state = ControlFlowState(node.cf_state)
677
+
678
+
679
+ class AssignmentCollector(TreeVisitor):
680
+ def __init__(self):
681
+ super().__init__()
682
+ self.assignments = []
683
+
684
+ def visit_Node(self):
685
+ self._visitchildren(self, None, None)
686
+
687
+ def visit_SingleAssignmentNode(self, node):
688
+ self.assignments.append((node.lhs, node.rhs))
689
+
690
+ def visit_CascadedAssignmentNode(self, node):
691
+ for lhs in node.lhs_list:
692
+ self.assignments.append((lhs, node.rhs))
693
+
694
+
695
+ class ControlFlowAnalysis(CythonTransform):
696
+
697
+ def find_in_stack(self, env):
698
+ if env == self.env:
699
+ return self.flow
700
+ for e, flow in reversed(self.stack):
701
+ if e is env:
702
+ return flow
703
+ assert False
704
+
705
+ def visit_ModuleNode(self, node):
706
+ dot_output = self.current_directives['control_flow.dot_output']
707
+ self.gv_ctx = GVContext() if dot_output else None
708
+
709
+ from .Optimize import ConstantFolding
710
+ self.constant_folder = ConstantFolding()
711
+
712
+ # Set of NameNode reductions
713
+ self.reductions = set()
714
+
715
+ self.in_inplace_assignment = False
716
+ self.env = node.scope
717
+ self.flow = ControlFlow()
718
+ self.stack = [] # a stack of (env, flow) tuples
719
+ self.object_expr = TypedExprNode(PyrexTypes.py_object_type, may_be_none=True)
720
+ self.visitchildren(node)
721
+
722
+ check_definitions(self.flow, self.current_directives)
723
+
724
+ if dot_output:
725
+ annotate_defs = self.current_directives['control_flow.dot_annotate_defs']
726
+ with open(dot_output, 'w') as fp:
727
+ self.gv_ctx.render(fp, 'module', annotate_defs=annotate_defs)
728
+ return node
729
+
730
+ def visit_FuncDefNode(self, node):
731
+ for arg in node.args:
732
+ if arg.default:
733
+ self.visitchildren(arg)
734
+ self.visitchildren(node, ('decorators',))
735
+ self.stack.append((self.env, self.flow))
736
+ self.env = node.local_scope
737
+ self.flow = ControlFlow()
738
+
739
+ # Collect all entries
740
+ for entry in node.local_scope.entries.values():
741
+ if self.flow.is_tracked(entry):
742
+ self.flow.entries.add(entry)
743
+
744
+ self.mark_position(node)
745
+ # Function body block
746
+ self.flow.nextblock()
747
+
748
+ for arg in node.args:
749
+ self._visit(arg)
750
+ if node.star_arg:
751
+ self.flow.mark_argument(node.star_arg,
752
+ TypedExprNode(Builtin.tuple_type,
753
+ may_be_none=False),
754
+ node.star_arg.entry)
755
+ if node.starstar_arg:
756
+ self.flow.mark_argument(node.starstar_arg,
757
+ TypedExprNode(Builtin.dict_type,
758
+ may_be_none=False),
759
+ node.starstar_arg.entry)
760
+ self._visit(node.body)
761
+ # Workaround for generators
762
+ if node.is_generator:
763
+ self._visit(node.gbody.body)
764
+
765
+ # Exit point
766
+ if self.flow.block:
767
+ self.flow.block.add_child(self.flow.exit_point)
768
+
769
+ # Cleanup graph
770
+ self.flow.normalize()
771
+ check_definitions(self.flow, self.current_directives)
772
+ self.flow.blocks.add(self.flow.entry_point)
773
+
774
+ if self.gv_ctx is not None:
775
+ self.gv_ctx.add(GV(node.local_scope.name, self.flow))
776
+
777
+ self.env, self.flow = self.stack.pop()
778
+ return node
779
+
780
+ def visit_DefNode(self, node):
781
+ node.used = True
782
+ return self.visit_FuncDefNode(node)
783
+
784
+ def visit_GeneratorBodyDefNode(self, node):
785
+ return node
786
+
787
+ def visit_CTypeDefNode(self, node):
788
+ return node
789
+
790
+ def mark_assignment(self, lhs, rhs=None, rhs_scope=None):
791
+ if not self.flow.block:
792
+ return
793
+ if self.flow.exceptions:
794
+ exc_descr = self.flow.exceptions[-1]
795
+ self.flow.block.add_child(exc_descr.entry_point)
796
+ self.flow.nextblock()
797
+
798
+ if not rhs:
799
+ rhs = self.object_expr
800
+ if lhs.is_name:
801
+ if lhs.entry is not None:
802
+ entry = lhs.entry
803
+ else:
804
+ entry = self.env.lookup(lhs.name)
805
+ if entry is None: # TODO: This shouldn't happen...
806
+ return
807
+ self.flow.mark_assignment(lhs, rhs, entry, rhs_scope=rhs_scope)
808
+ elif lhs.is_sequence_constructor:
809
+ for i, arg in enumerate(lhs.args):
810
+ if arg.is_starred:
811
+ # "a, *b = x" assigns a list to "b"
812
+ item_node = TypedExprNode(Builtin.list_type, may_be_none=False, pos=arg.pos)
813
+ elif rhs is self.object_expr:
814
+ item_node = rhs
815
+ else:
816
+ item_node = rhs.inferable_item_node(i)
817
+ self.mark_assignment(arg, item_node, rhs_scope=rhs_scope)
818
+ else:
819
+ self._visit(lhs)
820
+
821
+ if self.flow.exceptions:
822
+ exc_descr = self.flow.exceptions[-1]
823
+ self.flow.block.add_child(exc_descr.entry_point)
824
+ self.flow.nextblock()
825
+
826
+ def mark_position(self, node):
827
+ """Mark position if DOT output is enabled."""
828
+ if self.current_directives['control_flow.dot_output']:
829
+ self.flow.mark_position(node)
830
+
831
+ def visit_FromImportStatNode(self, node):
832
+ for name, target in node.items:
833
+ if name != "*":
834
+ self.mark_assignment(target)
835
+ self.visitchildren(node)
836
+ return node
837
+
838
+ def visit_AssignmentNode(self, node):
839
+ raise InternalError("Unhandled assignment node %s" % type(node))
840
+
841
+ def visit_SingleAssignmentNode(self, node):
842
+ self._visit(node.rhs)
843
+ self.mark_assignment(node.lhs, node.rhs)
844
+ return node
845
+
846
+ def visit_CascadedAssignmentNode(self, node):
847
+ self._visit(node.rhs)
848
+ for lhs in node.lhs_list:
849
+ self.mark_assignment(lhs, node.rhs)
850
+ return node
851
+
852
+ def visit_ParallelAssignmentNode(self, node):
853
+ collector = AssignmentCollector()
854
+ collector.visitchildren(node)
855
+ for lhs, rhs in collector.assignments:
856
+ self._visit(rhs)
857
+ for lhs, rhs in collector.assignments:
858
+ self.mark_assignment(lhs, rhs)
859
+ return node
860
+
861
+ def visit_InPlaceAssignmentNode(self, node):
862
+ self.in_inplace_assignment = True
863
+ self.visitchildren(node)
864
+ self.in_inplace_assignment = False
865
+ self.mark_assignment(node.lhs, self.constant_folder(node.create_binop_node()))
866
+ return node
867
+
868
+ def visit_DelStatNode(self, node):
869
+ for arg in node.args:
870
+ if arg.is_name:
871
+ entry = arg.entry or self.env.lookup(arg.name)
872
+ if entry.in_closure or entry.from_closure:
873
+ error(arg.pos,
874
+ "can not delete variable '%s' "
875
+ "referenced in nested scope" % entry.name)
876
+ if not node.ignore_nonexisting:
877
+ self._visit(arg) # mark reference
878
+ self.flow.mark_deletion(arg, entry)
879
+ else:
880
+ self._visit(arg)
881
+ return node
882
+
883
+ def visit_CArgDeclNode(self, node):
884
+ entry = self.env.lookup(node.name)
885
+ if entry:
886
+ may_be_none = not node.not_none
887
+ self.flow.mark_argument(
888
+ node, TypedExprNode(entry.type, may_be_none), entry)
889
+ return node
890
+
891
+ def visit_NameNode(self, node):
892
+ if self.flow.block:
893
+ entry = node.entry or self.env.lookup(node.name)
894
+ if entry:
895
+ self.flow.mark_reference(node, entry)
896
+
897
+ if entry in self.reductions and not self.in_inplace_assignment:
898
+ error(node.pos,
899
+ "Cannot read reduction variable in loop body")
900
+
901
+ return node
902
+
903
+ def visit_StatListNode(self, node):
904
+ if self.flow.block:
905
+ for stat in node.stats:
906
+ self._visit(stat)
907
+ if not self.flow.block:
908
+ stat.is_terminator = True
909
+ break
910
+ return node
911
+
912
+ def visit_Node(self, node):
913
+ self.visitchildren(node)
914
+ self.mark_position(node)
915
+ return node
916
+
917
+ def visit_SizeofVarNode(self, node):
918
+ return node
919
+
920
+ def visit_TypeidNode(self, node):
921
+ return node
922
+
923
+ def visit_IfStatNode(self, node):
924
+ next_block = self.flow.newblock()
925
+ parent = self.flow.block
926
+ # If clauses
927
+ for clause in node.if_clauses:
928
+ parent = self.flow.nextblock(parent)
929
+ self._visit(clause.condition)
930
+ self.flow.nextblock()
931
+ self._visit(clause.body)
932
+ if self.flow.block:
933
+ self.flow.block.add_child(next_block)
934
+ # Else clause
935
+ if node.else_clause:
936
+ self.flow.nextblock(parent=parent)
937
+ self._visit(node.else_clause)
938
+ if self.flow.block:
939
+ self.flow.block.add_child(next_block)
940
+ else:
941
+ parent.add_child(next_block)
942
+
943
+ if next_block.parents:
944
+ self.flow.block = next_block
945
+ else:
946
+ self.flow.block = None
947
+ return node
948
+
949
+ def visit_AssertStatNode(self, node):
950
+ """Essentially an if-condition that wraps a RaiseStatNode.
951
+ """
952
+ self.mark_position(node)
953
+ next_block = self.flow.newblock()
954
+ parent = self.flow.block
955
+ # failure case
956
+ parent = self.flow.nextblock(parent)
957
+ self._visit(node.condition)
958
+ self.flow.nextblock()
959
+ self._visit(node.exception)
960
+ if self.flow.block:
961
+ self.flow.block.add_child(next_block)
962
+ parent.add_child(next_block)
963
+ if next_block.parents:
964
+ self.flow.block = next_block
965
+ else:
966
+ self.flow.block = None
967
+ return node
968
+
969
+ def visit_WhileStatNode(self, node):
970
+ condition_block = self.flow.nextblock()
971
+ next_block = self.flow.newblock()
972
+ # Condition block
973
+ self.flow.loops.append(LoopDescr(next_block, condition_block))
974
+ if node.condition:
975
+ self._visit(node.condition)
976
+ # Body block
977
+ self.flow.nextblock()
978
+ self._visit(node.body)
979
+ self.flow.loops.pop()
980
+ # Loop it
981
+ if self.flow.block:
982
+ self.flow.block.add_child(condition_block)
983
+ self.flow.block.add_child(next_block)
984
+ # Else clause
985
+ if node.else_clause:
986
+ self.flow.nextblock(parent=condition_block)
987
+ self._visit(node.else_clause)
988
+ if self.flow.block:
989
+ self.flow.block.add_child(next_block)
990
+ else:
991
+ condition_block.add_child(next_block)
992
+
993
+ if next_block.parents:
994
+ self.flow.block = next_block
995
+ else:
996
+ self.flow.block = None
997
+ return node
998
+
999
+ def mark_forloop_target(self, node):
1000
+ # TODO: Remove redundancy with range optimization...
1001
+ is_special = False
1002
+ sequence = node.iterator.sequence
1003
+ target = node.target
1004
+ env = node.iterator.expr_scope or self.env
1005
+ if isinstance(sequence, ExprNodes.SimpleCallNode):
1006
+ function = sequence.function
1007
+ if sequence.self is None and function.is_name:
1008
+ entry = env.lookup(function.name)
1009
+ if not entry or entry.is_builtin:
1010
+ if function.name == 'reversed' and len(sequence.args) == 1:
1011
+ sequence = sequence.args[0]
1012
+ elif function.name == 'enumerate' and len(sequence.args) == 1:
1013
+ if target.is_sequence_constructor and len(target.args) == 2:
1014
+ iterator = sequence.args[0]
1015
+ if iterator.is_name:
1016
+ iterator_type = iterator.infer_type(env)
1017
+ if iterator_type.is_builtin_type:
1018
+ # assume that builtin types have a length within Py_ssize_t
1019
+ self.mark_assignment(
1020
+ target.args[0],
1021
+ ExprNodes.IntNode(target.pos, value='PY_SSIZE_T_MAX',
1022
+ type=PyrexTypes.c_py_ssize_t_type),
1023
+ rhs_scope=node.iterator.expr_scope)
1024
+ target = target.args[1]
1025
+ sequence = sequence.args[0]
1026
+ if isinstance(sequence, ExprNodes.SimpleCallNode):
1027
+ function = sequence.function
1028
+ if sequence.self is None and function.is_name:
1029
+ entry = env.lookup(function.name)
1030
+ if not entry or entry.is_builtin:
1031
+ if function.name in ('range', 'xrange'):
1032
+ is_special = True
1033
+ for arg in sequence.args[:2]:
1034
+ self.mark_assignment(target, arg, rhs_scope=node.iterator.expr_scope)
1035
+ if len(sequence.args) > 2:
1036
+ self.mark_assignment(target, self.constant_folder(
1037
+ ExprNodes.binop_node(node.pos,
1038
+ '+',
1039
+ sequence.args[0],
1040
+ sequence.args[2])),
1041
+ rhs_scope=node.iterator.expr_scope)
1042
+
1043
+ if not is_special:
1044
+ # A for-loop basically translates to subsequent calls to
1045
+ # __getitem__(), so using an IndexNode here allows us to
1046
+ # naturally infer the base type of pointers, C arrays,
1047
+ # Python strings, etc., while correctly falling back to an
1048
+ # object type when the base type cannot be handled.
1049
+
1050
+ self.mark_assignment(target, node.item, rhs_scope=node.iterator.expr_scope)
1051
+
1052
+ def mark_parallel_forloop_assignment(self, node):
1053
+ target = node.target
1054
+ for arg in node.args[:2]:
1055
+ self.mark_assignment(target, arg)
1056
+ if len(node.args) > 2:
1057
+ self.mark_assignment(target, self.constant_folder(
1058
+ ExprNodes.binop_node(node.pos,
1059
+ '+',
1060
+ node.args[0],
1061
+ node.args[2])))
1062
+ if not node.args:
1063
+ # Almost certainly an error
1064
+ self.mark_assignment(target)
1065
+
1066
+ def visit_AsyncForStatNode(self, node):
1067
+ return self.visit_ForInStatNode(node)
1068
+
1069
+ def visit_ForInStatNode(self, node):
1070
+ condition_block = self.flow.nextblock()
1071
+ next_block = self.flow.newblock()
1072
+ # Condition with iterator
1073
+ self.flow.loops.append(LoopDescr(next_block, condition_block))
1074
+ self._visit(node.iterator)
1075
+ # Target assignment
1076
+ self.flow.nextblock()
1077
+
1078
+ if isinstance(node, Nodes.ForInStatNode):
1079
+ self.mark_forloop_target(node)
1080
+ elif isinstance(node, Nodes.AsyncForStatNode):
1081
+ # not entirely correct, but good enough for now
1082
+ self.mark_assignment(node.target, node.item)
1083
+ elif isinstance(node, Nodes.ParallelRangeNode): # Parallel
1084
+ self.mark_parallel_forloop_assignment(node)
1085
+ else:
1086
+ assert False, type(node)
1087
+
1088
+ # Body block
1089
+ if isinstance(node, Nodes.ParallelRangeNode):
1090
+ # In case of an invalid
1091
+ self._delete_privates(node, exclude=node.target.entry)
1092
+
1093
+ self.flow.nextblock()
1094
+ self._visit(node.body)
1095
+ self.flow.loops.pop()
1096
+
1097
+ # Loop it
1098
+ if self.flow.block:
1099
+ self.flow.block.add_child(condition_block)
1100
+ # Else clause
1101
+ if node.else_clause:
1102
+ self.flow.nextblock(parent=condition_block)
1103
+ self._visit(node.else_clause)
1104
+ if self.flow.block:
1105
+ self.flow.block.add_child(next_block)
1106
+ else:
1107
+ condition_block.add_child(next_block)
1108
+
1109
+ if next_block.parents:
1110
+ self.flow.block = next_block
1111
+ else:
1112
+ self.flow.block = None
1113
+ return node
1114
+
1115
+ def _delete_privates(self, node, exclude=None):
1116
+ for private_node in node.assigned_nodes:
1117
+ if not exclude or private_node.entry is not exclude:
1118
+ self.flow.mark_deletion(private_node, private_node.entry)
1119
+
1120
+ def visit_ParallelRangeNode(self, node):
1121
+ reductions = self.reductions
1122
+
1123
+ # if node.target is None or not a NameNode, an error will have
1124
+ # been previously issued
1125
+ if hasattr(node.target, 'entry'):
1126
+ self.reductions = set(reductions)
1127
+
1128
+ for private_node in node.assigned_nodes:
1129
+ private_node.entry.error_on_uninitialized = True
1130
+ pos, reduction = node.assignments[private_node.entry]
1131
+ if reduction:
1132
+ self.reductions.add(private_node.entry)
1133
+
1134
+ node = self.visit_ForInStatNode(node)
1135
+
1136
+ self.reductions = reductions
1137
+ return node
1138
+
1139
+ def visit_ParallelWithBlockNode(self, node):
1140
+ for private_node in node.assigned_nodes:
1141
+ private_node.entry.error_on_uninitialized = True
1142
+
1143
+ self._delete_privates(node)
1144
+ self.visitchildren(node)
1145
+ self._delete_privates(node)
1146
+
1147
+ return node
1148
+
1149
+ def visit_ForFromStatNode(self, node):
1150
+ condition_block = self.flow.nextblock()
1151
+ next_block = self.flow.newblock()
1152
+ # Condition with iterator
1153
+ self.flow.loops.append(LoopDescr(next_block, condition_block))
1154
+ self._visit(node.bound1)
1155
+ self._visit(node.bound2)
1156
+ if node.step is not None:
1157
+ self._visit(node.step)
1158
+ # Target assignment
1159
+ self.flow.nextblock()
1160
+ self.mark_assignment(node.target, node.bound1)
1161
+ if node.step is not None:
1162
+ self.mark_assignment(node.target, self.constant_folder(
1163
+ ExprNodes.binop_node(node.pos, '+', node.bound1, node.step)))
1164
+ # Body block
1165
+ self.flow.nextblock()
1166
+ self._visit(node.body)
1167
+ self.flow.loops.pop()
1168
+ # Loop it
1169
+ if self.flow.block:
1170
+ self.flow.block.add_child(condition_block)
1171
+ # Else clause
1172
+ if node.else_clause:
1173
+ self.flow.nextblock(parent=condition_block)
1174
+ self._visit(node.else_clause)
1175
+ if self.flow.block:
1176
+ self.flow.block.add_child(next_block)
1177
+ else:
1178
+ condition_block.add_child(next_block)
1179
+
1180
+ if next_block.parents:
1181
+ self.flow.block = next_block
1182
+ else:
1183
+ self.flow.block = None
1184
+ return node
1185
+
1186
+ def visit_LoopNode(self, node):
1187
+ raise InternalError("Generic loops are not supported")
1188
+
1189
+ def visit_WithTargetAssignmentStatNode(self, node):
1190
+ self.mark_assignment(node.lhs, node.with_node.enter_call)
1191
+ return node
1192
+
1193
+ def visit_WithStatNode(self, node):
1194
+ self._visit(node.manager)
1195
+ self._visit(node.enter_call)
1196
+ self._visit(node.body)
1197
+ return node
1198
+
1199
+ def visit_TryExceptStatNode(self, node):
1200
+ # After exception handling
1201
+ next_block = self.flow.newblock()
1202
+ # Body block
1203
+ self.flow.newblock()
1204
+ # Exception entry point
1205
+ entry_point = self.flow.newblock()
1206
+ self.flow.exceptions.append(ExceptionDescr(entry_point))
1207
+ self.flow.nextblock()
1208
+ ## XXX: links to exception handling point should be added by
1209
+ ## XXX: children nodes
1210
+ self.flow.block.add_child(entry_point)
1211
+ self.flow.nextblock()
1212
+ self.flow.in_try_block += 1
1213
+ self._visit(node.body)
1214
+ self.flow.in_try_block -= 1
1215
+ self.flow.exceptions.pop()
1216
+
1217
+ # After exception
1218
+ if self.flow.block:
1219
+ if node.else_clause:
1220
+ self.flow.nextblock()
1221
+ self._visit(node.else_clause)
1222
+ if self.flow.block:
1223
+ self.flow.block.add_child(next_block)
1224
+
1225
+ for clause in node.except_clauses:
1226
+ self.flow.block = entry_point
1227
+ if clause.pattern:
1228
+ for pattern in clause.pattern:
1229
+ self._visit(pattern)
1230
+ else:
1231
+ # TODO: handle * pattern
1232
+ pass
1233
+ entry_point = self.flow.newblock(parent=self.flow.block)
1234
+ self.flow.nextblock()
1235
+ if clause.target:
1236
+ self.mark_assignment(clause.target)
1237
+ self._visit(clause.body)
1238
+ if self.flow.block:
1239
+ self.flow.block.add_child(next_block)
1240
+
1241
+ if self.flow.exceptions:
1242
+ entry_point.add_child(self.flow.exceptions[-1].entry_point)
1243
+
1244
+ if next_block.parents:
1245
+ self.flow.block = next_block
1246
+ else:
1247
+ self.flow.block = None
1248
+ return node
1249
+
1250
+ def visit_TryFinallyStatNode(self, node):
1251
+ body_block = self.flow.nextblock()
1252
+
1253
+ # Exception entry point
1254
+ entry_point = self.flow.newblock()
1255
+ self.flow.block = entry_point
1256
+ self._visit(node.finally_except_clause)
1257
+
1258
+ if self.flow.block and self.flow.exceptions:
1259
+ self.flow.block.add_child(self.flow.exceptions[-1].entry_point)
1260
+
1261
+ # Normal execution
1262
+ finally_enter = self.flow.newblock()
1263
+ self.flow.block = finally_enter
1264
+ self._visit(node.finally_clause)
1265
+ finally_exit = self.flow.block
1266
+
1267
+ descr = ExceptionDescr(entry_point, finally_enter, finally_exit)
1268
+ self.flow.exceptions.append(descr)
1269
+ if self.flow.loops:
1270
+ self.flow.loops[-1].exceptions.append(descr)
1271
+ self.flow.block = body_block
1272
+ body_block.add_child(entry_point)
1273
+ self.flow.nextblock()
1274
+ self.flow.in_try_block += 1
1275
+ self._visit(node.body)
1276
+ self.flow.in_try_block -= 1
1277
+ self.flow.exceptions.pop()
1278
+ if self.flow.loops:
1279
+ self.flow.loops[-1].exceptions.pop()
1280
+
1281
+ if self.flow.block:
1282
+ self.flow.block.add_child(finally_enter)
1283
+ if finally_exit:
1284
+ self.flow.block = self.flow.nextblock(parent=finally_exit)
1285
+ else:
1286
+ self.flow.block = None
1287
+ return node
1288
+
1289
+ def visit_RaiseStatNode(self, node):
1290
+ self.mark_position(node)
1291
+ self.visitchildren(node)
1292
+ if self.flow.exceptions:
1293
+ self.flow.block.add_child(self.flow.exceptions[-1].entry_point)
1294
+ self.flow.block = None
1295
+ if self.flow.in_try_block:
1296
+ node.in_try_block = True
1297
+ return node
1298
+
1299
+ def visit_ReraiseStatNode(self, node):
1300
+ self.mark_position(node)
1301
+ if self.flow.exceptions:
1302
+ self.flow.block.add_child(self.flow.exceptions[-1].entry_point)
1303
+ self.flow.block = None
1304
+ return node
1305
+
1306
+ def visit_ReturnStatNode(self, node):
1307
+ self.mark_position(node)
1308
+ self.visitchildren(node)
1309
+
1310
+ outer_exception_handlers = iter(self.flow.exceptions[::-1])
1311
+ for handler in outer_exception_handlers:
1312
+ if handler.finally_enter:
1313
+ self.flow.block.add_child(handler.finally_enter)
1314
+ if handler.finally_exit:
1315
+ # 'return' goes to function exit, or to the next outer 'finally' clause
1316
+ exit_point = self.flow.exit_point
1317
+ for next_handler in outer_exception_handlers:
1318
+ if next_handler.finally_enter:
1319
+ exit_point = next_handler.finally_enter
1320
+ break
1321
+ handler.finally_exit.add_child(exit_point)
1322
+ break
1323
+ else:
1324
+ if self.flow.block:
1325
+ self.flow.block.add_child(self.flow.exit_point)
1326
+ self.flow.block = None
1327
+ return node
1328
+
1329
+ def visit_BreakStatNode(self, node):
1330
+ if not self.flow.loops:
1331
+ #error(node.pos, "break statement not inside loop")
1332
+ return node
1333
+ loop = self.flow.loops[-1]
1334
+ self.mark_position(node)
1335
+ for exception in loop.exceptions[::-1]:
1336
+ if exception.finally_enter:
1337
+ self.flow.block.add_child(exception.finally_enter)
1338
+ if exception.finally_exit:
1339
+ exception.finally_exit.add_child(loop.next_block)
1340
+ break
1341
+ else:
1342
+ self.flow.block.add_child(loop.next_block)
1343
+ self.flow.block = None
1344
+ return node
1345
+
1346
+ def visit_ContinueStatNode(self, node):
1347
+ if not self.flow.loops:
1348
+ #error(node.pos, "continue statement not inside loop")
1349
+ return node
1350
+ loop = self.flow.loops[-1]
1351
+ self.mark_position(node)
1352
+ for exception in loop.exceptions[::-1]:
1353
+ if exception.finally_enter:
1354
+ self.flow.block.add_child(exception.finally_enter)
1355
+ if exception.finally_exit:
1356
+ exception.finally_exit.add_child(loop.loop_block)
1357
+ break
1358
+ else:
1359
+ self.flow.block.add_child(loop.loop_block)
1360
+ self.flow.block = None
1361
+ return node
1362
+
1363
+ def visit_ComprehensionNode(self, node):
1364
+ if node.expr_scope:
1365
+ self.stack.append((self.env, self.flow))
1366
+ self.env = node.expr_scope
1367
+ # Skip append node here
1368
+ self._visit(node.loop)
1369
+ if node.expr_scope:
1370
+ self.env, _ = self.stack.pop()
1371
+ return node
1372
+
1373
+ def visit_ScopedExprNode(self, node):
1374
+ # currently this is written to deal with these two types
1375
+ # (with comprehensions covered in their own function)
1376
+ assert isinstance(node, (ExprNodes.IteratorNode, ExprNodes.AsyncIteratorNode)), node
1377
+ if node.expr_scope:
1378
+ self.stack.append((self.env, self.flow))
1379
+ self.flow = self.find_in_stack(node.expr_scope)
1380
+ self.env = node.expr_scope
1381
+ self.visitchildren(node)
1382
+ if node.expr_scope:
1383
+ self.env, self.flow = self.stack.pop()
1384
+ return node
1385
+
1386
+ def visit_PyClassDefNode(self, node):
1387
+ self.visitchildren(node, ('dict', 'metaclass',
1388
+ 'mkw', 'bases', 'class_result'))
1389
+ self.flow.mark_assignment(node.target, node.classobj,
1390
+ self.env.lookup(node.target.name))
1391
+ self.stack.append((self.env, self.flow))
1392
+ self.env = node.scope
1393
+ self.flow.nextblock()
1394
+ if node.doc_node:
1395
+ self.flow.mark_assignment(node.doc_node, fake_rhs_expr, node.doc_node.entry)
1396
+ self.visitchildren(node, ('body',))
1397
+ self.flow.nextblock()
1398
+ self.env, _ = self.stack.pop()
1399
+ return node
1400
+
1401
+ def visit_CClassDefNode(self, node):
1402
+ # just make sure the nodes scope is findable in-case there is a list comprehension in it
1403
+ self.stack.append((node.scope, self.flow))
1404
+ self.visitchildren(node)
1405
+ self.stack.pop()
1406
+ return node
1407
+
1408
+ def visit_AmpersandNode(self, node):
1409
+ if node.operand.is_name:
1410
+ # Fake assignment to silence warning
1411
+ self.mark_assignment(node.operand, fake_rhs_expr)
1412
+ self.visitchildren(node)
1413
+ return node
1414
+
1415
+ def visit_BoolBinopNode(self, node):
1416
+ # Note - I don't believe BoolBinopResultNode needs special handling beyond this
1417
+ assert len(node.subexprs) == 2 # operand1 and operand2 only
1418
+
1419
+ next_block = self.flow.newblock()
1420
+ parent = self.flow.block
1421
+
1422
+ self._visit(node.operand1)
1423
+
1424
+ self.flow.nextblock()
1425
+ self._visit(node.operand2)
1426
+ if self.flow.block:
1427
+ self.flow.block.add_child(next_block)
1428
+
1429
+ parent.add_child(next_block)
1430
+
1431
+ if next_block.parents:
1432
+ self.flow.block = next_block
1433
+ else:
1434
+ self.flow.block = None
1435
+ return node
1436
+
1437
+ def visit_CondExprNode(self, node):
1438
+ assert len(node.subexprs) == 3
1439
+ self._visit(node.test)
1440
+ parent = self.flow.block
1441
+ next_block = self.flow.newblock()
1442
+ self.flow.nextblock()
1443
+ self._visit(node.true_val)
1444
+ if self.flow.block:
1445
+ self.flow.block.add_child(next_block)
1446
+ self.flow.nextblock(parent=parent)
1447
+ self._visit(node.false_val)
1448
+ if self.flow.block:
1449
+ self.flow.block.add_child(next_block)
1450
+
1451
+ if next_block.parents:
1452
+ self.flow.block = next_block
1453
+ else:
1454
+ self.flow.block = None
1455
+ return node