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