Cython 3.2.0__cp39-abi3-win32.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 (333) hide show
  1. Cython/Build/BuildExecutable.py +169 -0
  2. Cython/Build/Cache.py +199 -0
  3. Cython/Build/Cythonize.py +350 -0
  4. Cython/Build/Dependencies.py +1314 -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 +94 -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 +11 -0
  18. Cython/CodeWriter.py +815 -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 +680 -0
  23. Cython/Compiler/Builtin.py +984 -0
  24. Cython/Compiler/CmdLine.py +263 -0
  25. Cython/Compiler/Code.pxd +149 -0
  26. Cython/Compiler/Code.py +3746 -0
  27. Cython/Compiler/Code.pyd +0 -0
  28. Cython/Compiler/CodeGeneration.py +33 -0
  29. Cython/Compiler/CythonScope.py +191 -0
  30. Cython/Compiler/Dataclass.py +864 -0
  31. Cython/Compiler/DebugFlags.py +24 -0
  32. Cython/Compiler/Errors.py +297 -0
  33. Cython/Compiler/ExprNodes.py +15562 -0
  34. Cython/Compiler/FlowControl.pxd +97 -0
  35. Cython/Compiler/FlowControl.py +1451 -0
  36. Cython/Compiler/FlowControl.pyd +0 -0
  37. Cython/Compiler/FusedNode.py +971 -0
  38. Cython/Compiler/FusedNode.pyd +0 -0
  39. Cython/Compiler/Future.py +16 -0
  40. Cython/Compiler/Interpreter.py +57 -0
  41. Cython/Compiler/Lexicon.py +421 -0
  42. Cython/Compiler/LineTable.py +114 -0
  43. Cython/Compiler/LineTable.pyd +0 -0
  44. Cython/Compiler/Main.py +857 -0
  45. Cython/Compiler/MatchCaseNodes.py +259 -0
  46. Cython/Compiler/MemoryView.py +905 -0
  47. Cython/Compiler/ModuleNode.py +4235 -0
  48. Cython/Compiler/Naming.py +363 -0
  49. Cython/Compiler/Nodes.py +10831 -0
  50. Cython/Compiler/Optimize.py +5288 -0
  51. Cython/Compiler/Options.py +843 -0
  52. Cython/Compiler/ParseTreeTransforms.pxd +78 -0
  53. Cython/Compiler/ParseTreeTransforms.py +4638 -0
  54. Cython/Compiler/Parsing.pxd +9 -0
  55. Cython/Compiler/Parsing.py +4775 -0
  56. Cython/Compiler/Parsing.pyd +0 -0
  57. Cython/Compiler/Pipeline.py +439 -0
  58. Cython/Compiler/PyrexTypes.py +5870 -0
  59. Cython/Compiler/Pythran.py +232 -0
  60. Cython/Compiler/Scanning.pxd +48 -0
  61. Cython/Compiler/Scanning.py +701 -0
  62. Cython/Compiler/Scanning.pyd +0 -0
  63. Cython/Compiler/StringEncoding.py +298 -0
  64. Cython/Compiler/Symtab.py +3073 -0
  65. Cython/Compiler/Tests/TestBuffer.py +105 -0
  66. Cython/Compiler/Tests/TestBuiltin.py +72 -0
  67. Cython/Compiler/Tests/TestCmdLine.py +586 -0
  68. Cython/Compiler/Tests/TestCode.py +144 -0
  69. Cython/Compiler/Tests/TestFlowControl.py +65 -0
  70. Cython/Compiler/Tests/TestGrammar.py +202 -0
  71. Cython/Compiler/Tests/TestMemView.py +71 -0
  72. Cython/Compiler/Tests/TestParseTreeTransforms.py +285 -0
  73. Cython/Compiler/Tests/TestScanning.py +134 -0
  74. Cython/Compiler/Tests/TestSignatureMatching.py +73 -0
  75. Cython/Compiler/Tests/TestStringEncoding.py +21 -0
  76. Cython/Compiler/Tests/TestTreeFragment.py +63 -0
  77. Cython/Compiler/Tests/TestTreePath.py +103 -0
  78. Cython/Compiler/Tests/TestTypes.py +75 -0
  79. Cython/Compiler/Tests/TestUtilityLoad.py +112 -0
  80. Cython/Compiler/Tests/TestVisitor.py +61 -0
  81. Cython/Compiler/Tests/Utils.py +36 -0
  82. Cython/Compiler/Tests/__init__.py +1 -0
  83. Cython/Compiler/TreeFragment.py +278 -0
  84. Cython/Compiler/TreePath.py +303 -0
  85. Cython/Compiler/TypeInference.py +591 -0
  86. Cython/Compiler/TypeSlots.py +1174 -0
  87. Cython/Compiler/UFuncs.py +311 -0
  88. Cython/Compiler/UtilNodes.py +389 -0
  89. Cython/Compiler/UtilityCode.py +344 -0
  90. Cython/Compiler/Version.py +8 -0
  91. Cython/Compiler/Visitor.pxd +53 -0
  92. Cython/Compiler/Visitor.py +861 -0
  93. Cython/Compiler/Visitor.pyd +0 -0
  94. Cython/Compiler/__init__.py +1 -0
  95. Cython/Coverage.py +448 -0
  96. Cython/Debugger/Cygdb.py +177 -0
  97. Cython/Debugger/DebugWriter.py +82 -0
  98. Cython/Debugger/Tests/TestLibCython.py +275 -0
  99. Cython/Debugger/Tests/__init__.py +1 -0
  100. Cython/Debugger/Tests/cfuncs.c +8 -0
  101. Cython/Debugger/Tests/codefile +49 -0
  102. Cython/Debugger/Tests/test_libcython_in_gdb.py +578 -0
  103. Cython/Debugger/Tests/test_libpython_in_gdb.py +90 -0
  104. Cython/Debugger/__init__.py +1 -0
  105. Cython/Debugger/libcython.py +1548 -0
  106. Cython/Debugger/libpython.py +2821 -0
  107. Cython/Debugging.py +20 -0
  108. Cython/Distutils/__init__.py +2 -0
  109. Cython/Distutils/build_ext.py +139 -0
  110. Cython/Distutils/extension.py +96 -0
  111. Cython/Distutils/old_build_ext.py +351 -0
  112. Cython/Includes/cpython/__init__.pxd +173 -0
  113. Cython/Includes/cpython/array.pxd +178 -0
  114. Cython/Includes/cpython/bool.pxd +37 -0
  115. Cython/Includes/cpython/buffer.pxd +112 -0
  116. Cython/Includes/cpython/bytearray.pxd +33 -0
  117. Cython/Includes/cpython/bytes.pxd +200 -0
  118. Cython/Includes/cpython/cellobject.pxd +35 -0
  119. Cython/Includes/cpython/ceval.pxd +8 -0
  120. Cython/Includes/cpython/codecs.pxd +121 -0
  121. Cython/Includes/cpython/complex.pxd +60 -0
  122. Cython/Includes/cpython/contextvars.pxd +145 -0
  123. Cython/Includes/cpython/conversion.pxd +36 -0
  124. Cython/Includes/cpython/datetime.pxd +395 -0
  125. Cython/Includes/cpython/descr.pxd +26 -0
  126. Cython/Includes/cpython/dict.pxd +187 -0
  127. Cython/Includes/cpython/exc.pxd +263 -0
  128. Cython/Includes/cpython/fileobject.pxd +57 -0
  129. Cython/Includes/cpython/float.pxd +47 -0
  130. Cython/Includes/cpython/function.pxd +65 -0
  131. Cython/Includes/cpython/genobject.pxd +25 -0
  132. Cython/Includes/cpython/getargs.pxd +12 -0
  133. Cython/Includes/cpython/instance.pxd +25 -0
  134. Cython/Includes/cpython/iterator.pxd +36 -0
  135. Cython/Includes/cpython/iterobject.pxd +24 -0
  136. Cython/Includes/cpython/list.pxd +92 -0
  137. Cython/Includes/cpython/long.pxd +149 -0
  138. Cython/Includes/cpython/longintrepr.pxd +14 -0
  139. Cython/Includes/cpython/mapping.pxd +63 -0
  140. Cython/Includes/cpython/marshal.pxd +66 -0
  141. Cython/Includes/cpython/mem.pxd +120 -0
  142. Cython/Includes/cpython/memoryview.pxd +50 -0
  143. Cython/Includes/cpython/method.pxd +49 -0
  144. Cython/Includes/cpython/module.pxd +208 -0
  145. Cython/Includes/cpython/number.pxd +258 -0
  146. Cython/Includes/cpython/object.pxd +433 -0
  147. Cython/Includes/cpython/pycapsule.pxd +143 -0
  148. Cython/Includes/cpython/pylifecycle.pxd +68 -0
  149. Cython/Includes/cpython/pyport.pxd +8 -0
  150. Cython/Includes/cpython/pystate.pxd +95 -0
  151. Cython/Includes/cpython/pythread.pxd +53 -0
  152. Cython/Includes/cpython/ref.pxd +141 -0
  153. Cython/Includes/cpython/sequence.pxd +134 -0
  154. Cython/Includes/cpython/set.pxd +119 -0
  155. Cython/Includes/cpython/slice.pxd +70 -0
  156. Cython/Includes/cpython/time.pxd +129 -0
  157. Cython/Includes/cpython/tuple.pxd +72 -0
  158. Cython/Includes/cpython/type.pxd +53 -0
  159. Cython/Includes/cpython/unicode.pxd +639 -0
  160. Cython/Includes/cpython/version.pxd +32 -0
  161. Cython/Includes/cpython/weakref.pxd +78 -0
  162. Cython/Includes/libc/__init__.pxd +1 -0
  163. Cython/Includes/libc/complex.pxd +35 -0
  164. Cython/Includes/libc/errno.pxd +127 -0
  165. Cython/Includes/libc/float.pxd +43 -0
  166. Cython/Includes/libc/limits.pxd +28 -0
  167. Cython/Includes/libc/locale.pxd +46 -0
  168. Cython/Includes/libc/math.pxd +209 -0
  169. Cython/Includes/libc/setjmp.pxd +10 -0
  170. Cython/Includes/libc/signal.pxd +64 -0
  171. Cython/Includes/libc/stddef.pxd +9 -0
  172. Cython/Includes/libc/stdint.pxd +105 -0
  173. Cython/Includes/libc/stdio.pxd +80 -0
  174. Cython/Includes/libc/stdlib.pxd +72 -0
  175. Cython/Includes/libc/string.pxd +50 -0
  176. Cython/Includes/libc/threads.pxd +234 -0
  177. Cython/Includes/libc/time.pxd +52 -0
  178. Cython/Includes/libcpp/__init__.pxd +4 -0
  179. Cython/Includes/libcpp/algorithm.pxd +320 -0
  180. Cython/Includes/libcpp/any.pxd +16 -0
  181. Cython/Includes/libcpp/atomic.pxd +59 -0
  182. Cython/Includes/libcpp/barrier.pxd +22 -0
  183. Cython/Includes/libcpp/bit.pxd +29 -0
  184. Cython/Includes/libcpp/cast.pxd +12 -0
  185. Cython/Includes/libcpp/cmath.pxd +518 -0
  186. Cython/Includes/libcpp/complex.pxd +106 -0
  187. Cython/Includes/libcpp/condition_variable.pxd +322 -0
  188. Cython/Includes/libcpp/deque.pxd +165 -0
  189. Cython/Includes/libcpp/exception.pxd +86 -0
  190. Cython/Includes/libcpp/execution.pxd +15 -0
  191. Cython/Includes/libcpp/forward_list.pxd +63 -0
  192. Cython/Includes/libcpp/functional.pxd +26 -0
  193. Cython/Includes/libcpp/future.pxd +103 -0
  194. Cython/Includes/libcpp/iterator.pxd +34 -0
  195. Cython/Includes/libcpp/latch.pxd +17 -0
  196. Cython/Includes/libcpp/limits.pxd +61 -0
  197. Cython/Includes/libcpp/list.pxd +117 -0
  198. Cython/Includes/libcpp/map.pxd +252 -0
  199. Cython/Includes/libcpp/memory.pxd +115 -0
  200. Cython/Includes/libcpp/mutex.pxd +387 -0
  201. Cython/Includes/libcpp/numbers.pxd +15 -0
  202. Cython/Includes/libcpp/numeric.pxd +131 -0
  203. Cython/Includes/libcpp/optional.pxd +34 -0
  204. Cython/Includes/libcpp/pair.pxd +1 -0
  205. Cython/Includes/libcpp/queue.pxd +25 -0
  206. Cython/Includes/libcpp/random.pxd +166 -0
  207. Cython/Includes/libcpp/semaphore.pxd +43 -0
  208. Cython/Includes/libcpp/set.pxd +228 -0
  209. Cython/Includes/libcpp/shared_mutex.pxd +96 -0
  210. Cython/Includes/libcpp/span.pxd +87 -0
  211. Cython/Includes/libcpp/stack.pxd +11 -0
  212. Cython/Includes/libcpp/stop_token.pxd +117 -0
  213. Cython/Includes/libcpp/string.pxd +355 -0
  214. Cython/Includes/libcpp/string_view.pxd +183 -0
  215. Cython/Includes/libcpp/typeindex.pxd +15 -0
  216. Cython/Includes/libcpp/typeinfo.pxd +10 -0
  217. Cython/Includes/libcpp/unordered_map.pxd +193 -0
  218. Cython/Includes/libcpp/unordered_set.pxd +152 -0
  219. Cython/Includes/libcpp/utility.pxd +30 -0
  220. Cython/Includes/libcpp/vector.pxd +186 -0
  221. Cython/Includes/numpy/math.pxd +150 -0
  222. Cython/Includes/openmp.pxd +50 -0
  223. Cython/Includes/posix/__init__.pxd +1 -0
  224. Cython/Includes/posix/dlfcn.pxd +14 -0
  225. Cython/Includes/posix/fcntl.pxd +86 -0
  226. Cython/Includes/posix/ioctl.pxd +4 -0
  227. Cython/Includes/posix/mman.pxd +101 -0
  228. Cython/Includes/posix/resource.pxd +57 -0
  229. Cython/Includes/posix/select.pxd +21 -0
  230. Cython/Includes/posix/signal.pxd +73 -0
  231. Cython/Includes/posix/stat.pxd +98 -0
  232. Cython/Includes/posix/stdio.pxd +37 -0
  233. Cython/Includes/posix/stdlib.pxd +29 -0
  234. Cython/Includes/posix/strings.pxd +9 -0
  235. Cython/Includes/posix/time.pxd +71 -0
  236. Cython/Includes/posix/types.pxd +30 -0
  237. Cython/Includes/posix/uio.pxd +26 -0
  238. Cython/Includes/posix/unistd.pxd +271 -0
  239. Cython/Includes/posix/wait.pxd +38 -0
  240. Cython/Plex/Actions.pxd +24 -0
  241. Cython/Plex/Actions.py +119 -0
  242. Cython/Plex/Actions.pyd +0 -0
  243. Cython/Plex/DFA.pxd +14 -0
  244. Cython/Plex/DFA.py +164 -0
  245. Cython/Plex/DFA.pyd +0 -0
  246. Cython/Plex/Errors.py +48 -0
  247. Cython/Plex/Lexicons.py +178 -0
  248. Cython/Plex/Machines.pxd +36 -0
  249. Cython/Plex/Machines.py +238 -0
  250. Cython/Plex/Machines.pyd +0 -0
  251. Cython/Plex/Regexps.py +535 -0
  252. Cython/Plex/Scanners.pxd +45 -0
  253. Cython/Plex/Scanners.py +328 -0
  254. Cython/Plex/Scanners.pyd +0 -0
  255. Cython/Plex/Transitions.pxd +14 -0
  256. Cython/Plex/Transitions.py +239 -0
  257. Cython/Plex/Transitions.pyd +0 -0
  258. Cython/Plex/__init__.py +34 -0
  259. Cython/Runtime/__init__.py +1 -0
  260. Cython/Runtime/refnanny.pyd +0 -0
  261. Cython/Runtime/refnanny.pyx +237 -0
  262. Cython/Shadow.py +690 -0
  263. Cython/Shadow.pyi +521 -0
  264. Cython/StringIOTree.py +170 -0
  265. Cython/StringIOTree.pyd +0 -0
  266. Cython/Tempita/__init__.py +4 -0
  267. Cython/Tempita/_looper.py +154 -0
  268. Cython/Tempita/_tempita.py +1091 -0
  269. Cython/Tempita/_tempita.pyd +0 -0
  270. Cython/TestUtils.py +422 -0
  271. Cython/Tests/TestCodeWriter.py +128 -0
  272. Cython/Tests/TestCythonUtils.py +202 -0
  273. Cython/Tests/TestJediTyper.py +223 -0
  274. Cython/Tests/TestShadow.py +114 -0
  275. Cython/Tests/TestStringIOTree.py +67 -0
  276. Cython/Tests/TestTestUtils.py +90 -0
  277. Cython/Tests/__init__.py +1 -0
  278. Cython/Tests/xmlrunner.py +390 -0
  279. Cython/Utility/AsyncGen.c +1031 -0
  280. Cython/Utility/Buffer.c +865 -0
  281. Cython/Utility/BufferFormatFromTypeInfo.pxd +2 -0
  282. Cython/Utility/Builtins.c +810 -0
  283. Cython/Utility/CConvert.pyx +134 -0
  284. Cython/Utility/CMath.c +104 -0
  285. Cython/Utility/CommonStructures.c +226 -0
  286. Cython/Utility/Complex.c +378 -0
  287. Cython/Utility/Coroutine.c +2300 -0
  288. Cython/Utility/CpdefEnums.pyx +103 -0
  289. Cython/Utility/CppConvert.pyx +282 -0
  290. Cython/Utility/CppSupport.cpp +151 -0
  291. Cython/Utility/CythonFunction.c +1832 -0
  292. Cython/Utility/Dataclasses.c +101 -0
  293. Cython/Utility/Embed.c +121 -0
  294. Cython/Utility/Exceptions.c +1016 -0
  295. Cython/Utility/ExtensionTypes.c +996 -0
  296. Cython/Utility/FunctionArguments.c +1043 -0
  297. Cython/Utility/FusedFunction.pyx +44 -0
  298. Cython/Utility/ImportExport.c +907 -0
  299. Cython/Utility/MemoryView.pxd +188 -0
  300. Cython/Utility/MemoryView.pyx +1482 -0
  301. Cython/Utility/MemoryView_C.c +927 -0
  302. Cython/Utility/ModuleSetupCode.c +3203 -0
  303. Cython/Utility/NumpyImportArray.c +46 -0
  304. Cython/Utility/ObjectHandling.c +3273 -0
  305. Cython/Utility/Optimize.c +1603 -0
  306. Cython/Utility/Overflow.c +384 -0
  307. Cython/Utility/Printing.c +86 -0
  308. Cython/Utility/Profile.c +732 -0
  309. Cython/Utility/StringTools.c +1379 -0
  310. Cython/Utility/Synchronization.c +399 -0
  311. Cython/Utility/TString.c +356 -0
  312. Cython/Utility/TestCyUtilityLoader.pyx +8 -0
  313. Cython/Utility/TestCythonScope.pyx +75 -0
  314. Cython/Utility/TestUtilityLoader.c +12 -0
  315. Cython/Utility/TypeConversion.c +1385 -0
  316. Cython/Utility/UFuncs.pyx +50 -0
  317. Cython/Utility/UFuncs_C.c +89 -0
  318. Cython/Utility/__init__.py +28 -0
  319. Cython/Utility/arrayarray.h +167 -0
  320. Cython/Utils.py +687 -0
  321. Cython/Utils.pyd +0 -0
  322. Cython/__init__.py +10 -0
  323. Cython/__init__.pyi +7 -0
  324. Cython/py.typed +0 -0
  325. cython-3.2.0.dist-info/METADATA +85 -0
  326. cython-3.2.0.dist-info/RECORD +333 -0
  327. cython-3.2.0.dist-info/WHEEL +5 -0
  328. cython-3.2.0.dist-info/entry_points.txt +4 -0
  329. cython-3.2.0.dist-info/top_level.txt +3 -0
  330. cython.py +29 -0
  331. pyximport/__init__.py +4 -0
  332. pyximport/pyxbuild.py +160 -0
  333. pyximport/pyximport.py +482 -0
@@ -0,0 +1,4638 @@
1
+ import cython
2
+ cython.declare(PyrexTypes=object, Naming=object, ExprNodes=object, Nodes=object,
3
+ Options=object, UtilNodes=object, LetNode=object,
4
+ LetRefNode=object, TreeFragment=object, EncodedString=object,
5
+ error=object, warning=object, copy=object, hashlib=object, sys=object,
6
+ itemgetter=object)
7
+
8
+ import copy
9
+ import hashlib
10
+ import sys
11
+ from operator import itemgetter
12
+
13
+ from . import Code
14
+ from . import PyrexTypes
15
+ from . import Naming
16
+ from . import ExprNodes
17
+ from . import Nodes
18
+ from . import Options
19
+ from . import Builtin
20
+ from . import Errors
21
+
22
+ from .Visitor import VisitorTransform, TreeVisitor
23
+ from .Visitor import CythonTransform, EnvTransform, ScopeTrackingTransform
24
+ from .UtilNodes import LetNode, LetRefNode
25
+ from .TreeFragment import TreeFragment
26
+ from .StringEncoding import EncodedString
27
+ from .Errors import error, warning, CompileError, InternalError
28
+
29
+
30
+ class SkipDeclarations:
31
+ """
32
+ Variable and function declarations can often have a deep tree structure,
33
+ and yet most transformations don't need to descend to this depth.
34
+
35
+ Declaration nodes are removed after AnalyseDeclarationsTransform, so there
36
+ is no need to use this for transformations after that point.
37
+ """
38
+ def visit_CTypeDefNode(self, node):
39
+ return node
40
+
41
+ def visit_CVarDefNode(self, node):
42
+ return node
43
+
44
+ def visit_CDeclaratorNode(self, node):
45
+ return node
46
+
47
+ def visit_CBaseTypeNode(self, node):
48
+ return node
49
+
50
+ def visit_CEnumDefNode(self, node):
51
+ return node
52
+
53
+ def visit_CStructOrUnionDefNode(self, node):
54
+ return node
55
+
56
+ def visit_CppClassNode(self, node):
57
+ if node.visibility != "extern":
58
+ # Need to traverse methods.
59
+ self.visitchildren(node)
60
+ return node
61
+
62
+
63
+ class NormalizeTree(CythonTransform):
64
+ """
65
+ This transform fixes up a few things after parsing
66
+ in order to make the parse tree more suitable for
67
+ transforms.
68
+
69
+ a) After parsing, blocks with only one statement will
70
+ be represented by that statement, not by a StatListNode.
71
+ When doing transforms this is annoying and inconsistent,
72
+ as one cannot in general remove a statement in a consistent
73
+ way and so on. This transform wraps any single statements
74
+ in a StatListNode containing a single statement.
75
+
76
+ b) The PassStatNode is a noop and serves no purpose beyond
77
+ plugging such one-statement blocks; i.e., once parsed a
78
+ ` "pass" can just as well be represented using an empty
79
+ StatListNode. This means less special cases to worry about
80
+ in subsequent transforms (one always checks to see if a
81
+ StatListNode has no children to see if the block is empty).
82
+ """
83
+
84
+ def __init__(self, context):
85
+ super().__init__(context)
86
+ self.is_in_statlist = False
87
+ self.is_in_expr = False
88
+
89
+ def visit_ModuleNode(self, node):
90
+ self.visitchildren(node)
91
+ if not isinstance(node.body, Nodes.StatListNode):
92
+ # This can happen when the body only consists of a single (unused) declaration and no statements.
93
+ node.body = Nodes.StatListNode(pos=node.pos, stats=[node.body])
94
+ return node
95
+
96
+ def visit_ExprNode(self, node):
97
+ stacktmp = self.is_in_expr
98
+ self.is_in_expr = True
99
+ self.visitchildren(node)
100
+ self.is_in_expr = stacktmp
101
+ return node
102
+
103
+ def visit_StatNode(self, node, is_listcontainer=False):
104
+ stacktmp = self.is_in_statlist
105
+ self.is_in_statlist = is_listcontainer
106
+ self.visitchildren(node)
107
+ self.is_in_statlist = stacktmp
108
+ if not self.is_in_statlist and not self.is_in_expr:
109
+ return Nodes.StatListNode(pos=node.pos, stats=[node])
110
+ else:
111
+ return node
112
+
113
+ def visit_StatListNode(self, node):
114
+ self.is_in_statlist = True
115
+ self.visitchildren(node)
116
+ self.is_in_statlist = False
117
+ return node
118
+
119
+ def visit_ParallelAssignmentNode(self, node):
120
+ return self.visit_StatNode(node, True)
121
+
122
+ def visit_CEnumDefNode(self, node):
123
+ return self.visit_StatNode(node, True)
124
+
125
+ def visit_CStructOrUnionDefNode(self, node):
126
+ return self.visit_StatNode(node, True)
127
+
128
+ def visit_ExprStatNode(self, node):
129
+ """Eliminate useless string literals"""
130
+ if node.expr.is_string_literal:
131
+ return Nodes.PassStatNode(node.expr.pos)
132
+ else:
133
+ return self.visit_StatNode(node)
134
+
135
+ def visit_CDeclaratorNode(self, node):
136
+ return node
137
+
138
+
139
+ class PostParseError(CompileError): pass
140
+
141
+ # error strings checked by unit tests, so define them
142
+ ERR_CDEF_INCLASS = 'Cannot assign default value to fields in cdef classes, structs or unions'
143
+ ERR_BUF_DEFAULTS = 'Invalid buffer defaults specification (see docs)'
144
+ ERR_INVALID_SPECIALATTR_TYPE = 'Special attributes must not have a type declared'
145
+ class PostParse(ScopeTrackingTransform):
146
+ """
147
+ Basic interpretation of the parse tree, as well as validity
148
+ checking that can be done on a very basic level on the parse
149
+ tree (while still not being a problem with the basic syntax,
150
+ as such).
151
+
152
+ Specifically:
153
+ - Default values to cdef assignments are turned into single
154
+ assignments following the declaration (everywhere but in class
155
+ bodies, where they raise a compile error)
156
+
157
+ - Interpret some node structures into Python runtime values.
158
+ Some nodes take compile-time arguments (currently:
159
+ TemplatedTypeNode[args] and __cythonbufferdefaults__ = {args}),
160
+ which should be interpreted. This happens in a general way
161
+ and other steps should be taken to ensure validity.
162
+
163
+ Type arguments cannot be interpreted in this way.
164
+
165
+ - For __cythonbufferdefaults__ the arguments are checked for
166
+ validity.
167
+
168
+ TemplatedTypeNode has its directives interpreted:
169
+ Any first positional argument goes into the "dtype" attribute,
170
+ any "ndim" keyword argument goes into the "ndim" attribute and
171
+ so on. Also it is checked that the directive combination is valid.
172
+ - __cythonbufferdefaults__ attributes are parsed and put into the
173
+ type information.
174
+
175
+ Note: Currently Parsing.py does a lot of interpretation and
176
+ reorganization that can be refactored into this transform
177
+ if a more pure Abstract Syntax Tree is wanted.
178
+
179
+ - Some invalid uses of := assignment expressions are detected
180
+ """
181
+ def __init__(self, context):
182
+ super().__init__(context)
183
+ self.specialattribute_handlers = {
184
+ '__cythonbufferdefaults__' : self.handle_bufferdefaults
185
+ }
186
+ self.in_pattern_node = False
187
+
188
+ def visit_LambdaNode(self, node):
189
+ # unpack a lambda expression into the corresponding DefNode
190
+ collector = YieldNodeCollector()
191
+ collector.visitchildren(node.result_expr)
192
+ if collector.has_yield or collector.has_await or isinstance(node.result_expr, ExprNodes.YieldExprNode):
193
+ body = Nodes.ExprStatNode(
194
+ node.result_expr.pos, expr=node.result_expr)
195
+ else:
196
+ body = Nodes.ReturnStatNode(
197
+ node.result_expr.pos, value=node.result_expr)
198
+ node.def_node = Nodes.DefNode(
199
+ node.pos, name=node.name,
200
+ args=node.args, star_arg=node.star_arg,
201
+ starstar_arg=node.starstar_arg,
202
+ body=body, doc=None)
203
+ self.visitchildren(node)
204
+ return node
205
+
206
+ def visit_GeneratorExpressionNode(self, node):
207
+ # unpack a generator expression into the corresponding DefNode
208
+ collector = YieldNodeCollector()
209
+ collector.visitchildren(node.loop, attrs=None, exclude=["iterator"])
210
+ node.def_node = Nodes.DefNode(
211
+ node.pos, name=node.name, doc=None,
212
+ args=[], star_arg=None, starstar_arg=None,
213
+ body=node.loop, is_async_def=collector.has_await,
214
+ is_generator_expression=True)
215
+ _AssignmentExpressionChecker.do_checks(node.loop, scope_is_class=self.scope_type in ("pyclass", "cclass"))
216
+ self.visitchildren(node)
217
+ return node
218
+
219
+ def visit_ComprehensionNode(self, node):
220
+ # enforce local scope also in Py2 for async generators (seriously, that's a Py3.6 feature...)
221
+ if not node.has_local_scope:
222
+ collector = YieldNodeCollector()
223
+ collector.visitchildren(node.loop)
224
+ if collector.has_await:
225
+ node.has_local_scope = True
226
+ _AssignmentExpressionChecker.do_checks(node.loop, scope_is_class=self.scope_type in ("pyclass", "cclass"))
227
+ self.visitchildren(node)
228
+ return node
229
+
230
+ # cdef variables
231
+ def handle_bufferdefaults(self, decl):
232
+ if not isinstance(decl.default, ExprNodes.DictNode):
233
+ raise PostParseError(decl.pos, ERR_BUF_DEFAULTS)
234
+ self.scope_node.buffer_defaults_node = decl.default
235
+ self.scope_node.buffer_defaults_pos = decl.pos
236
+
237
+ def visit_CVarDefNode(self, node):
238
+ # This assumes only plain names and pointers are assignable on
239
+ # declaration. Also, it makes use of the fact that a cdef decl
240
+ # must appear before the first use, so we don't have to deal with
241
+ # "i = 3; cdef int i = i" and can simply move the nodes around.
242
+ try:
243
+ self.visitchildren(node)
244
+ stats = [node]
245
+ newdecls = []
246
+ for decl in node.declarators:
247
+ declbase = decl
248
+ while isinstance(declbase, (Nodes.CPtrDeclaratorNode, Nodes.CConstDeclaratorNode)):
249
+ declbase = declbase.base
250
+ if isinstance(declbase, Nodes.CNameDeclaratorNode):
251
+ if declbase.default is not None:
252
+ if self.scope_type in ('cclass', 'pyclass', 'struct'):
253
+ if isinstance(self.scope_node, Nodes.CClassDefNode):
254
+ handler = self.specialattribute_handlers.get(decl.name)
255
+ if handler:
256
+ if decl is not declbase:
257
+ raise PostParseError(decl.pos, ERR_INVALID_SPECIALATTR_TYPE)
258
+ handler(decl)
259
+ continue # Remove declaration
260
+ raise PostParseError(decl.pos, ERR_CDEF_INCLASS)
261
+ first_assignment = self.scope_type != 'module'
262
+ stats.append(Nodes.SingleAssignmentNode(node.pos,
263
+ lhs=ExprNodes.NameNode(node.pos, name=declbase.name),
264
+ rhs=declbase.default, first=first_assignment,
265
+ from_pxd_cvardef=node.in_pxd))
266
+ declbase.default = None
267
+ newdecls.append(decl)
268
+ node.declarators = newdecls
269
+ return stats
270
+ except PostParseError as e:
271
+ # An error in a cdef clause is ok, simply remove the declaration
272
+ # and try to move on to report more errors
273
+ self.context.nonfatal_error(e)
274
+ return None
275
+
276
+ # Split parallel assignments (a,b = b,a) into separate partial
277
+ # assignments that are executed rhs-first using temps. This
278
+ # restructuring must be applied before type analysis so that known
279
+ # types on rhs and lhs can be matched directly. It is required in
280
+ # the case that the types cannot be coerced to a Python type in
281
+ # order to assign from a tuple.
282
+
283
+ def visit_SingleAssignmentNode(self, node):
284
+ self.visitchildren(node)
285
+ return self._visit_assignment_node(node, [node.lhs, node.rhs])
286
+
287
+ def visit_CascadedAssignmentNode(self, node):
288
+ self.visitchildren(node)
289
+ return self._visit_assignment_node(node, node.lhs_list + [node.rhs])
290
+
291
+ def _visit_assignment_node(self, node, expr_list):
292
+ """Flatten parallel assignments into separate single
293
+ assignments or cascaded assignments.
294
+ """
295
+ if sum([ 1 for expr in expr_list
296
+ if expr.is_sequence_constructor or expr.is_string_literal ]) < 2:
297
+ # no parallel assignments => nothing to do
298
+ return node
299
+
300
+ expr_list_list = []
301
+ flatten_parallel_assignments(expr_list, expr_list_list)
302
+ temp_refs = []
303
+ eliminate_rhs_duplicates(expr_list_list, temp_refs)
304
+
305
+ nodes = []
306
+ for expr_list in expr_list_list:
307
+ lhs_list = expr_list[:-1]
308
+ rhs = expr_list[-1]
309
+ if len(lhs_list) == 1:
310
+ node = Nodes.SingleAssignmentNode(rhs.pos,
311
+ lhs = lhs_list[0], rhs = rhs)
312
+ else:
313
+ node = Nodes.CascadedAssignmentNode(rhs.pos,
314
+ lhs_list = lhs_list, rhs = rhs)
315
+ nodes.append(node)
316
+
317
+ if len(nodes) == 1:
318
+ assign_node = nodes[0]
319
+ else:
320
+ assign_node = Nodes.ParallelAssignmentNode(nodes[0].pos, stats = nodes)
321
+
322
+ if temp_refs:
323
+ duplicates_and_temps = [ (temp.expression, temp)
324
+ for temp in temp_refs ]
325
+ sort_common_subsequences(duplicates_and_temps)
326
+ for _, temp_ref in duplicates_and_temps[::-1]:
327
+ assign_node = LetNode(temp_ref, assign_node)
328
+
329
+ return assign_node
330
+
331
+ def _flatten_sequence(self, seq, result):
332
+ for arg in seq.args:
333
+ if arg.is_sequence_constructor:
334
+ self._flatten_sequence(arg, result)
335
+ else:
336
+ result.append(arg)
337
+ return result
338
+
339
+ def visit_DelStatNode(self, node):
340
+ self.visitchildren(node)
341
+ node.args = self._flatten_sequence(node, [])
342
+ return node
343
+
344
+ def visit_ExceptClauseNode(self, node):
345
+ if node.is_except_as:
346
+ # except-as must delete NameNode target at the end
347
+ del_target = Nodes.DelStatNode(
348
+ node.pos,
349
+ args=[ExprNodes.NameNode(
350
+ node.target.pos, name=node.target.name)],
351
+ ignore_nonexisting=True)
352
+ node.body = Nodes.StatListNode(
353
+ node.pos,
354
+ stats=[Nodes.TryFinallyStatNode(
355
+ node.pos,
356
+ body=node.body,
357
+ finally_clause=Nodes.StatListNode(
358
+ node.pos,
359
+ stats=[del_target]))])
360
+ self.visitchildren(node)
361
+ return node
362
+
363
+ def visit_AssertStatNode(self, node):
364
+ """Extract the exception raising into a RaiseStatNode to simplify GIL handling.
365
+ """
366
+ if node.exception is None:
367
+ node.exception = Nodes.RaiseStatNode(
368
+ node.pos,
369
+ exc_type=ExprNodes.NameNode(node.pos, name=EncodedString("AssertionError")),
370
+ exc_value=node.value,
371
+ exc_tb=None,
372
+ cause=None,
373
+ builtin_exc_name="AssertionError",
374
+ wrap_tuple_value=True,
375
+ )
376
+ node.value = None
377
+ self.visitchildren(node)
378
+ return node
379
+
380
+ def visit_ErrorNode(self, node):
381
+ error(node.pos, node.what)
382
+ return None
383
+
384
+ def visit_MatchCaseNode(self, node):
385
+ node.validate_targets()
386
+ self.visitchildren(node)
387
+ return node
388
+
389
+ def visit_MatchNode(self, node):
390
+ node.validate_irrefutable()
391
+ self.visitchildren(node)
392
+ return node
393
+
394
+ def visit_PatternNode(self, node):
395
+ in_pattern_node, self.in_pattern_node = self.in_pattern_node, True
396
+ self.visitchildren(node)
397
+ self.in_pattern_node = in_pattern_node
398
+ return node
399
+
400
+ def visit_JoinedStrNode(self, node):
401
+ if self.in_pattern_node:
402
+ error(node.pos, "f-strings are not accepted for pattern matching")
403
+ self.visitchildren(node)
404
+ return node
405
+
406
+ def visit_DefNode(self, node):
407
+ if (self.scope_type == "cclass" and
408
+ node.name in ["__getreadbuffer__", "__getwritebuffer__", "__getsegcount__", "__getcharbuffer__"]):
409
+ warning(node.pos, f"'{node.name}' relates to the old Python 2 buffer protocol "
410
+ "and is no longer used.", 2)
411
+ return None # drop the node - the arguments are invalid for a def node
412
+ return self.visit_FuncDefNode(node)
413
+
414
+
415
+ class _AssignmentExpressionTargetNameFinder(TreeVisitor):
416
+ def __init__(self):
417
+ super().__init__()
418
+ self.target_names = {}
419
+
420
+ def find_target_names(self, target):
421
+ if target.is_name:
422
+ return [target.name]
423
+ elif target.is_sequence_constructor:
424
+ names = []
425
+ for arg in target.args:
426
+ names.extend(self.find_target_names(arg))
427
+ return names
428
+ # other targets are possible, but it isn't necessary to investigate them here
429
+ return []
430
+
431
+ def visit_ForInStatNode(self, node):
432
+ self.target_names[node] = tuple(self.find_target_names(node.target))
433
+ self.visitchildren(node)
434
+
435
+ def visit_ComprehensionNode(self, node):
436
+ pass # don't recurse into nested comprehensions
437
+
438
+ def visit_LambdaNode(self, node):
439
+ pass # don't recurse into nested lambdas/generator expressions
440
+
441
+ def visit_Node(self, node):
442
+ self.visitchildren(node)
443
+
444
+
445
+ class _AssignmentExpressionChecker(TreeVisitor):
446
+ """
447
+ Enforces rules on AssignmentExpressions within generator expressions and comprehensions
448
+ """
449
+ def __init__(self, loop_node, scope_is_class):
450
+ super().__init__()
451
+
452
+ target_name_finder = _AssignmentExpressionTargetNameFinder()
453
+ target_name_finder.visit(loop_node)
454
+ self.target_names_dict = target_name_finder.target_names
455
+ self.in_iterator = False
456
+ self.in_nested_generator = False
457
+ self.scope_is_class = scope_is_class
458
+ self.current_target_names = ()
459
+ self.all_target_names = set()
460
+ for names in self.target_names_dict.values():
461
+ self.all_target_names.update(names)
462
+
463
+ def _reset_state(self):
464
+ old_state = (self.in_iterator, self.in_nested_generator, self.scope_is_class, self.all_target_names, self.current_target_names)
465
+ # note: not resetting self.in_iterator here, see visit_LambdaNode() below
466
+ self.in_nested_generator = False
467
+ self.scope_is_class = False
468
+ self.current_target_names = ()
469
+ self.all_target_names = set()
470
+ return old_state
471
+
472
+ def _set_state(self, old_state):
473
+ self.in_iterator, self.in_nested_generator, self.scope_is_class, self.all_target_names, self.current_target_names = old_state
474
+
475
+ @classmethod
476
+ def do_checks(cls, loop_node, scope_is_class):
477
+ checker = cls(loop_node, scope_is_class)
478
+ checker.visit(loop_node)
479
+
480
+ def visit_ForInStatNode(self, node):
481
+ if self.in_nested_generator:
482
+ self.visitchildren(node) # once nested, don't do anything special
483
+ return
484
+
485
+ current_target_names = self.current_target_names
486
+ target_name = self.target_names_dict.get(node, None)
487
+ if target_name:
488
+ self.current_target_names += target_name
489
+
490
+ self.in_iterator = True
491
+ self.visit(node.iterator)
492
+ self.in_iterator = False
493
+ self.visitchildren(node, exclude=("iterator",))
494
+
495
+ self.current_target_names = current_target_names
496
+
497
+ def visit_AssignmentExpressionNode(self, node):
498
+ if self.in_iterator:
499
+ error(node.pos, "assignment expression cannot be used in a comprehension iterable expression")
500
+ if self.scope_is_class:
501
+ error(node.pos, "assignment expression within a comprehension cannot be used in a class body")
502
+ if node.target_name in self.current_target_names:
503
+ error(node.pos, "assignment expression cannot rebind comprehension iteration variable '%s'" %
504
+ node.target_name)
505
+ elif node.target_name in self.all_target_names:
506
+ error(node.pos, "comprehension inner loop cannot rebind assignment expression target '%s'" %
507
+ node.target_name)
508
+
509
+ def visit_LambdaNode(self, node):
510
+ # Don't reset "in_iterator" - an assignment expression in a lambda in an
511
+ # iterator is explicitly tested by the Python testcases and banned.
512
+ old_state = self._reset_state()
513
+ # the lambda node's "def_node" is not set up at this point, so we need to recurse into it explicitly.
514
+ self.visit(node.result_expr)
515
+ self._set_state(old_state)
516
+
517
+ def visit_ComprehensionNode(self, node):
518
+ in_nested_generator = self.in_nested_generator
519
+ self.in_nested_generator = True
520
+ self.visitchildren(node)
521
+ self.in_nested_generator = in_nested_generator
522
+
523
+ def visit_GeneratorExpressionNode(self, node):
524
+ in_nested_generator = self.in_nested_generator
525
+ self.in_nested_generator = True
526
+ # def_node isn't set up yet, so we need to visit the loop directly.
527
+ self.visit(node.loop)
528
+ self.in_nested_generator = in_nested_generator
529
+
530
+ def visit_Node(self, node):
531
+ self.visitchildren(node)
532
+
533
+
534
+ def eliminate_rhs_duplicates(expr_list_list, ref_node_sequence):
535
+ """Replace rhs items by LetRefNodes if they appear more than once.
536
+ Creates a sequence of LetRefNodes that set up the required temps
537
+ and appends them to ref_node_sequence. The input list is modified
538
+ in-place.
539
+ """
540
+ seen_nodes = set()
541
+ ref_nodes = {}
542
+ def find_duplicates(node):
543
+ if node.is_literal or node.is_name:
544
+ # no need to replace those; can't include attributes here
545
+ # as their access is not necessarily side-effect free
546
+ return
547
+ if node in seen_nodes:
548
+ if node not in ref_nodes:
549
+ ref_node = LetRefNode(node)
550
+ ref_nodes[node] = ref_node
551
+ ref_node_sequence.append(ref_node)
552
+ else:
553
+ seen_nodes.add(node)
554
+ if node.is_sequence_constructor:
555
+ for item in node.args:
556
+ find_duplicates(item)
557
+
558
+ for expr_list in expr_list_list:
559
+ rhs = expr_list[-1]
560
+ find_duplicates(rhs)
561
+ if not ref_nodes:
562
+ return
563
+
564
+ def substitute_nodes(node):
565
+ if node in ref_nodes:
566
+ return ref_nodes[node]
567
+ elif node.is_sequence_constructor:
568
+ node.args = list(map(substitute_nodes, node.args))
569
+ return node
570
+
571
+ # replace nodes inside of the common subexpressions
572
+ for node in ref_nodes:
573
+ if node.is_sequence_constructor:
574
+ node.args = list(map(substitute_nodes, node.args))
575
+
576
+ # replace common subexpressions on all rhs items
577
+ for expr_list in expr_list_list:
578
+ expr_list[-1] = substitute_nodes(expr_list[-1])
579
+
580
+ def sort_common_subsequences(items):
581
+ """Sort items/subsequences so that all items and subsequences that
582
+ an item contains appear before the item itself. This is needed
583
+ because each rhs item must only be evaluated once, so its value
584
+ must be evaluated first and then reused when packing sequences
585
+ that contain it.
586
+
587
+ This implies a partial order, and the sort must be stable to
588
+ preserve the original order as much as possible, so we use a
589
+ simple insertion sort (which is very fast for short sequences, the
590
+ normal case in practice).
591
+ """
592
+ def contains(seq, x):
593
+ for item in seq:
594
+ if item is x:
595
+ return True
596
+ elif item.is_sequence_constructor and contains(item.args, x):
597
+ return True
598
+ return False
599
+ def lower_than(a,b):
600
+ return b.is_sequence_constructor and contains(b.args, a)
601
+
602
+ for pos, item in enumerate(items):
603
+ key = item[1] # the ResultRefNode which has already been injected into the sequences
604
+ new_pos = pos
605
+ for i in range(pos-1, -1, -1):
606
+ if lower_than(key, items[i][0]):
607
+ new_pos = i
608
+ if new_pos != pos:
609
+ for i in range(pos, new_pos, -1):
610
+ items[i] = items[i-1]
611
+ items[new_pos] = item
612
+
613
+
614
+ def unpack_string_to_character_literals(literal):
615
+ chars = []
616
+ pos = literal.pos
617
+ stype = literal.__class__
618
+ sval = literal.value
619
+ sval_type = sval.__class__
620
+ for char in sval:
621
+ cval = sval_type(char)
622
+ chars.append(stype(pos, value=cval))
623
+ return chars
624
+
625
+
626
+ @cython.cfunc
627
+ def flatten_parallel_assignments(input: list, output: list):
628
+ # The input is a list of expression nodes, representing the LHSs
629
+ # and RHS of one (possibly cascaded) assignment statement. For
630
+ # sequence constructors, rearranges the matching parts of both
631
+ # sides into a list of equivalent assignments between the
632
+ # individual elements. This transformation is applied
633
+ # recursively, so that nested structures get matched as well.
634
+ rhs = input[-1]
635
+ if (not (rhs.is_sequence_constructor or isinstance(rhs, ExprNodes.UnicodeNode))
636
+ or not sum([lhs.is_sequence_constructor for lhs in input[:-1]])):
637
+ output.append(input)
638
+ return
639
+
640
+ complete_assignments = []
641
+
642
+ if rhs.is_sequence_constructor:
643
+ rhs_args = rhs.args
644
+ elif rhs.is_string_literal:
645
+ rhs_args = unpack_string_to_character_literals(rhs)
646
+
647
+ starred_targets: cython.Py_ssize_t
648
+ lhs_size: cython.Py_ssize_t
649
+ rhs_size: cython.Py_ssize_t = len(rhs_args)
650
+ lhs_targets = [[] for _ in range(rhs_size)]
651
+ starred_assignments = []
652
+
653
+ for lhs in input[:-1]:
654
+ if not lhs.is_sequence_constructor:
655
+ if lhs.is_starred:
656
+ error(lhs.pos, "starred assignment target must be in a list or tuple")
657
+ complete_assignments.append(lhs)
658
+ continue
659
+ lhs_size = len(lhs.args)
660
+ starred_targets = 0
661
+ for expr in lhs.args:
662
+ starred_targets += bool(expr.is_starred)
663
+ if starred_targets > 1:
664
+ error(lhs.pos, "more than 1 starred expression in assignment")
665
+ output.append([lhs,rhs])
666
+ continue
667
+ elif lhs_size - starred_targets > rhs_size:
668
+ error(lhs.pos, "need more than %d value%s to unpack"
669
+ % (rhs_size, (rhs_size != 1) and 's' or ''))
670
+ output.append([lhs,rhs])
671
+ continue
672
+ elif starred_targets:
673
+ map_starred_assignment(lhs_targets, starred_assignments,
674
+ lhs.args, rhs_args)
675
+ elif lhs_size < rhs_size:
676
+ error(lhs.pos, "too many values to unpack (expected %d, got %d)"
677
+ % (lhs_size, rhs_size))
678
+ output.append([lhs,rhs])
679
+ continue
680
+ else:
681
+ for targets, expr in zip(lhs_targets, lhs.args):
682
+ targets.append(expr)
683
+
684
+ if complete_assignments:
685
+ complete_assignments.append(rhs)
686
+ output.append(complete_assignments)
687
+
688
+ # recursively flatten partial assignments
689
+ for cascade, rhs in zip(lhs_targets, rhs_args):
690
+ if cascade:
691
+ cascade.append(rhs)
692
+ flatten_parallel_assignments(cascade, output)
693
+
694
+ # recursively flatten starred assignments
695
+ for cascade in starred_assignments:
696
+ if cascade[0].is_sequence_constructor:
697
+ flatten_parallel_assignments(cascade, output)
698
+ else:
699
+ output.append(cascade)
700
+
701
+
702
+ @cython.cfunc
703
+ def map_starred_assignment(lhs_targets: list, starred_assignments: list, lhs_args: list, rhs_args: list):
704
+ # Appends the fixed-position LHS targets to the target list that
705
+ # appear left and right of the starred argument.
706
+ #
707
+ # The starred_assignments list receives a new tuple
708
+ # (lhs_target, rhs_values_list) that maps the remaining arguments
709
+ # (those that match the starred target) to a list.
710
+
711
+ # left side of the starred target
712
+ i: cython.Py_ssize_t
713
+ starred: cython.Py_ssize_t
714
+ lhs_remaining: cython.Py_ssize_t
715
+ for i, (targets, expr) in enumerate(zip(lhs_targets, lhs_args)):
716
+ if expr.is_starred:
717
+ starred = i
718
+ lhs_remaining = len(lhs_args) - i - 1
719
+ break
720
+ targets.append(expr)
721
+ else:
722
+ raise InternalError("no starred arg found when splitting starred assignment")
723
+
724
+ # right side of the starred target
725
+ for i, (targets, expr) in enumerate(zip(lhs_targets[-lhs_remaining:],
726
+ lhs_args[starred + 1:])):
727
+ targets.append(expr)
728
+
729
+ # the starred target itself, must be assigned a (potentially empty) list
730
+ target = lhs_args[starred].target # unpack starred node
731
+ starred_rhs = rhs_args[starred:]
732
+ if lhs_remaining:
733
+ starred_rhs = starred_rhs[:-lhs_remaining]
734
+ if starred_rhs:
735
+ pos = starred_rhs[0].pos
736
+ else:
737
+ pos = target.pos
738
+ starred_assignments.append([
739
+ target, ExprNodes.ListNode(pos=pos, args=starred_rhs)])
740
+
741
+
742
+ class PxdPostParse(CythonTransform, SkipDeclarations):
743
+ """
744
+ Basic interpretation/validity checking that should only be
745
+ done on pxd trees.
746
+
747
+ A lot of this checking currently happens in the parser; but
748
+ what is listed below happens here.
749
+
750
+ - "def" functions are let through only if they fill the
751
+ getbuffer/releasebuffer slots
752
+
753
+ - cdef functions are let through only if they are on the
754
+ top level and are declared "inline"
755
+ """
756
+ ERR_INLINE_ONLY = "function definition in pxd file must be declared 'cdef inline'"
757
+ ERR_NOGO_WITH_INLINE = "inline function definition in pxd file cannot be '%s'"
758
+
759
+ def __call__(self, node):
760
+ self.scope_type = 'pxd'
761
+ return super().__call__(node)
762
+
763
+ def visit_CClassDefNode(self, node):
764
+ old = self.scope_type
765
+ self.scope_type = 'cclass'
766
+ self.visitchildren(node)
767
+ self.scope_type = old
768
+ return node
769
+
770
+ def visit_FuncDefNode(self, node):
771
+ # FuncDefNode always come with an implementation (without
772
+ # an imp they are CVarDefNodes..)
773
+ err = self.ERR_INLINE_ONLY
774
+
775
+ if (isinstance(node, Nodes.DefNode) and self.scope_type == 'cclass'
776
+ and node.name in ('__getbuffer__', '__releasebuffer__')):
777
+ err = None # allow these slots
778
+
779
+ if isinstance(node, Nodes.CFuncDefNode):
780
+ if ('inline' in node.modifiers and
781
+ self.scope_type in ('pxd', 'cclass')):
782
+ node.inline_in_pxd = True
783
+ if node.visibility != 'private':
784
+ err = self.ERR_NOGO_WITH_INLINE % node.visibility
785
+ elif node.api:
786
+ err = self.ERR_NOGO_WITH_INLINE % 'api'
787
+ else:
788
+ err = None # allow inline function
789
+ else:
790
+ err = self.ERR_INLINE_ONLY
791
+
792
+ if err:
793
+ self.context.nonfatal_error(PostParseError(node.pos, err))
794
+ return None
795
+ else:
796
+ return node
797
+
798
+
799
+ class TrackNumpyAttributes(VisitorTransform, SkipDeclarations):
800
+ # TODO: Make name handling as good as in InterpretCompilerDirectives() below - probably best to merge the two.
801
+ def __init__(self):
802
+ super().__init__()
803
+ self.numpy_module_names = set()
804
+
805
+ def visit_CImportStatNode(self, node):
806
+ if node.module_name == "numpy":
807
+ self.numpy_module_names.add(node.as_name or "numpy")
808
+ return node
809
+
810
+ def visit_AttributeNode(self, node):
811
+ self.visitchildren(node)
812
+ obj = node.obj
813
+ if (obj.is_name and obj.name in self.numpy_module_names) or obj.is_numpy_attribute:
814
+ node.is_numpy_attribute = True
815
+ return node
816
+
817
+ visit_Node = VisitorTransform.recurse_to_children
818
+
819
+
820
+ class InterpretCompilerDirectives(CythonTransform):
821
+ """
822
+ After parsing, directives can be stored in a number of places:
823
+ - #cython-comments at the top of the file (stored in ModuleNode)
824
+ - Command-line arguments overriding these
825
+ - @cython.directivename decorators
826
+ - with cython.directivename: statements
827
+ - replaces "cython.compiled" with BoolNode(value=True)
828
+ allowing unreachable blocks to be removed at a fairly early stage
829
+ before cython typing rules are forced on applied
830
+
831
+ This transform is responsible for interpreting these various sources
832
+ and store the directive in two ways:
833
+ - Set the directives attribute of the ModuleNode for global directives.
834
+ - Use a CompilerDirectivesNode to override directives for a subtree.
835
+
836
+ (The first one is primarily to not have to modify with the tree
837
+ structure, so that ModuleNode stay on top.)
838
+
839
+ The directives are stored in dictionaries from name to value in effect.
840
+ Each such dictionary is always filled in for all possible directives,
841
+ using default values where no value is given by the user.
842
+
843
+ The available directives are controlled in Options.py.
844
+
845
+ Note that we have to run this prior to analysis, and so some minor
846
+ duplication of functionality has to occur: We manually track cimports
847
+ and which names the "cython" module may have been imported to.
848
+ """
849
+ unop_method_nodes = {
850
+ 'typeof': ExprNodes.TypeofNode,
851
+
852
+ 'operator.address': ExprNodes.AmpersandNode,
853
+ 'operator.dereference': ExprNodes.DereferenceNode,
854
+ 'operator.preincrement' : ExprNodes.inc_dec_constructor(True, '++'),
855
+ 'operator.predecrement' : ExprNodes.inc_dec_constructor(True, '--'),
856
+ 'operator.postincrement': ExprNodes.inc_dec_constructor(False, '++'),
857
+ 'operator.postdecrement': ExprNodes.inc_dec_constructor(False, '--'),
858
+ 'operator.typeid' : ExprNodes.TypeidNode,
859
+
860
+ # For backwards compatibility.
861
+ 'address': ExprNodes.AmpersandNode,
862
+ }
863
+
864
+ binop_method_nodes = {
865
+ 'operator.comma' : ExprNodes.c_binop_constructor(','),
866
+ }
867
+
868
+ special_methods = {
869
+ 'declare', 'union', 'struct', 'typedef',
870
+ 'sizeof', 'cast', 'pointer', 'compiled',
871
+ 'NULL', 'fused_type', 'parallel',
872
+ }
873
+ special_methods.update(unop_method_nodes)
874
+
875
+ valid_cython_submodules = {
876
+ 'cimports',
877
+ 'dataclasses',
878
+ 'operator',
879
+ 'parallel',
880
+ 'view',
881
+ }
882
+
883
+ valid_parallel_directives = {
884
+ "parallel",
885
+ "prange",
886
+ "threadid",
887
+ #"threadsavailable",
888
+ }
889
+
890
+ def __init__(self, context, compilation_directive_defaults):
891
+ super().__init__(context)
892
+ self.cython_module_names = set()
893
+ self.directive_names = {'staticmethod': 'staticmethod'}
894
+ self.parallel_directives = {}
895
+ directives = copy.deepcopy(Options.get_directive_defaults())
896
+ for key, value in compilation_directive_defaults.items():
897
+ directives[str(key)] = copy.deepcopy(value)
898
+ self.directives = directives
899
+
900
+ def check_directive_scope(self, pos, directive, scope):
901
+ legal_scopes = Options.directive_scopes.get(directive, None)
902
+ if legal_scopes and scope not in legal_scopes:
903
+ self.context.nonfatal_error(PostParseError(pos, 'The %s compiler directive '
904
+ 'is not allowed in %s scope' % (directive, scope)))
905
+ return False
906
+ else:
907
+ if directive not in Options.directive_types:
908
+ error(pos, "Invalid directive: '%s'." % (directive,))
909
+ return True
910
+
911
+ def _check_valid_cython_module(self, pos, module_name):
912
+ if not module_name.startswith("cython."):
913
+ return
914
+ submodule = module_name.split('.', 2)[1]
915
+ if submodule in self.valid_cython_submodules:
916
+ return
917
+
918
+ extra = ""
919
+ # This is very rarely used, so don't waste space on static tuples.
920
+ hints = [
921
+ line.split() for line in """\
922
+ imp cimports
923
+ cimp cimports
924
+ para parallel
925
+ parra parallel
926
+ dataclass dataclasses
927
+ """.splitlines()[:-1]
928
+ ]
929
+ for wrong, correct in hints:
930
+ if module_name.startswith("cython." + wrong):
931
+ extra = "Did you mean 'cython.%s' ?" % correct
932
+ break
933
+ if not extra:
934
+ is_simple_cython_name = submodule in Options.directive_types
935
+ if not is_simple_cython_name and not submodule.startswith("_"):
936
+ # Try to find it in the Shadow module (i.e. the pure Python namespace of cython.*).
937
+ # FIXME: use an internal reference of "cython.*" names instead of Shadow.py
938
+ from .. import Shadow
939
+ is_simple_cython_name = hasattr(Shadow, submodule)
940
+ if is_simple_cython_name:
941
+ extra = "Instead, use 'import cython' and then 'cython.%s'." % submodule
942
+
943
+ error(pos, "'%s' is not a valid cython.* module%s%s" % (
944
+ module_name,
945
+ ". " if extra else "",
946
+ extra,
947
+ ))
948
+
949
+ # Set up processing and handle the cython: comments.
950
+ def visit_ModuleNode(self, node):
951
+ for key in sorted(node.directive_comments):
952
+ if not self.check_directive_scope(node.pos, key, 'module'):
953
+ self.wrong_scope_error(node.pos, key, 'module')
954
+ del node.directive_comments[key]
955
+
956
+ self.module_scope = node.scope
957
+
958
+ self.directives.update(node.directive_comments)
959
+ node.directives = self.directives
960
+ node.parallel_directives = self.parallel_directives
961
+ self.visitchildren(node)
962
+ node.cython_module_names = self.cython_module_names
963
+ return node
964
+
965
+ def visit_CompilerDirectivesNode(self, node):
966
+ old_directives, self.directives = self.directives, node.directives
967
+ self.visitchildren(node)
968
+ self.directives = old_directives
969
+ return node
970
+
971
+ # The following four functions track imports and cimports that
972
+ # begin with "cython"
973
+ def is_cython_directive(self, name):
974
+ return (name in Options.directive_types or
975
+ name in self.special_methods or
976
+ PyrexTypes.parse_basic_type(name))
977
+
978
+ def is_parallel_directive(self, full_name, pos):
979
+ """
980
+ Checks to see if fullname (e.g. cython.parallel.prange) is a valid
981
+ parallel directive. If it is a star import it also updates the
982
+ parallel_directives.
983
+ """
984
+ result = (full_name + ".").startswith("cython.parallel.")
985
+
986
+ if result:
987
+ directive = full_name.split('.')
988
+ if full_name == "cython.parallel":
989
+ self.parallel_directives["parallel"] = "cython.parallel"
990
+ elif full_name == "cython.parallel.*":
991
+ for name in self.valid_parallel_directives:
992
+ self.parallel_directives[name] = "cython.parallel.%s" % name
993
+ elif (len(directive) != 3 or
994
+ directive[-1] not in self.valid_parallel_directives):
995
+ error(pos, "No such directive: %s" % full_name)
996
+
997
+ return result
998
+
999
+ def visit_CImportStatNode(self, node):
1000
+ module_name = node.module_name
1001
+ if module_name == "cython.cimports":
1002
+ error(node.pos, "Cannot cimport the 'cython.cimports' package directly, only submodules.")
1003
+ if module_name.startswith("cython.cimports."):
1004
+ if node.as_name and node.as_name != 'cython':
1005
+ node.module_name = module_name[len("cython.cimports."):]
1006
+ return node
1007
+ error(node.pos,
1008
+ "Python cimports must use 'from cython.cimports... import ...'"
1009
+ " or 'import ... as ...', not just 'import ...'")
1010
+
1011
+ if module_name == "cython":
1012
+ self.cython_module_names.add(node.as_name or "cython")
1013
+ elif module_name.startswith("cython."):
1014
+ if module_name.startswith("cython.parallel."):
1015
+ error(node.pos, node.module_name + " is not a module")
1016
+ else:
1017
+ self._check_valid_cython_module(node.pos, module_name)
1018
+
1019
+ if module_name == "cython.parallel":
1020
+ if node.as_name and node.as_name != "cython":
1021
+ self.parallel_directives[node.as_name] = module_name
1022
+ else:
1023
+ self.cython_module_names.add("cython")
1024
+ self.parallel_directives[
1025
+ "cython.parallel"] = module_name
1026
+ elif node.as_name:
1027
+ self.directive_names[node.as_name] = module_name[7:]
1028
+ else:
1029
+ self.cython_module_names.add("cython")
1030
+ # if this cimport was a compiler directive, we don't
1031
+ # want to leave the cimport node sitting in the tree
1032
+ return None
1033
+ return node
1034
+
1035
+ def visit_FromCImportStatNode(self, node):
1036
+ module_name = node.module_name
1037
+ if module_name == "cython.cimports" or module_name.startswith("cython.cimports."):
1038
+ # only supported for convenience
1039
+ return self._create_cimport_from_import(
1040
+ node.pos, module_name, node.relative_level, node.imported_names)
1041
+ elif not node.relative_level and (
1042
+ module_name == "cython" or module_name.startswith("cython.")):
1043
+ self._check_valid_cython_module(node.pos, module_name)
1044
+ submodule = (module_name + ".")[7:]
1045
+ newimp = []
1046
+ for pos, name, as_name in node.imported_names:
1047
+ full_name = submodule + name
1048
+ qualified_name = "cython." + full_name
1049
+ if self.is_parallel_directive(qualified_name, node.pos):
1050
+ # from cython cimport parallel, or
1051
+ # from cython.parallel cimport parallel, prange, ...
1052
+ self.parallel_directives[as_name or name] = qualified_name
1053
+ elif self.is_cython_directive(full_name):
1054
+ self.directive_names[as_name or name] = full_name
1055
+ elif full_name in ['dataclasses', 'typing']:
1056
+ self.directive_names[as_name or name] = full_name
1057
+ # unlike many directives, still treat it as a regular module
1058
+ newimp.append((pos, name, as_name))
1059
+ else:
1060
+ newimp.append((pos, name, as_name))
1061
+
1062
+ if not newimp:
1063
+ return None
1064
+
1065
+ node.imported_names = newimp
1066
+ return node
1067
+
1068
+ def visit_FromImportStatNode(self, node):
1069
+ import_node = node.module
1070
+ module_name = import_node.module_name.value
1071
+ if module_name == "cython.cimports" or module_name.startswith("cython.cimports."):
1072
+ imported_names = []
1073
+ for name, name_node in node.items:
1074
+ imported_names.append(
1075
+ (name_node.pos, name, None if name == name_node.name else name_node.name))
1076
+ return self._create_cimport_from_import(
1077
+ node.pos, module_name, import_node.level, imported_names)
1078
+ elif module_name == "cython" or module_name.startswith("cython."):
1079
+ self._check_valid_cython_module(import_node.module_name.pos, module_name)
1080
+ submodule = (module_name + ".")[7:]
1081
+ newimp = []
1082
+ for name, name_node in node.items:
1083
+ full_name = submodule + name
1084
+ qualified_name = "cython." + full_name
1085
+ if self.is_parallel_directive(qualified_name, node.pos):
1086
+ self.parallel_directives[name_node.name] = qualified_name
1087
+ elif self.is_cython_directive(full_name):
1088
+ self.directive_names[name_node.name] = full_name
1089
+ else:
1090
+ newimp.append((name, name_node))
1091
+ if not newimp:
1092
+ return None
1093
+ node.items = newimp
1094
+ return node
1095
+
1096
+ def _create_cimport_from_import(self, node_pos, module_name, level, imported_names):
1097
+ if module_name == "cython.cimports" or module_name.startswith("cython.cimports."):
1098
+ module_name = EncodedString(module_name[len("cython.cimports."):]) # may be empty
1099
+
1100
+ if module_name:
1101
+ # from cython.cimports.a.b import x, y, z => from a.b cimport x, y, z
1102
+ return Nodes.FromCImportStatNode(
1103
+ node_pos, module_name=module_name,
1104
+ relative_level=level,
1105
+ imported_names=imported_names)
1106
+ else:
1107
+ # from cython.cimports import x, y, z => cimport x; cimport y; cimport z
1108
+ return [
1109
+ Nodes.CImportStatNode(
1110
+ pos,
1111
+ module_name=dotted_name,
1112
+ as_name=as_name,
1113
+ is_absolute=level == 0)
1114
+ for pos, dotted_name, as_name in imported_names
1115
+ ]
1116
+
1117
+ def visit_SingleAssignmentNode(self, node):
1118
+ if isinstance(node.rhs, ExprNodes.ImportNode):
1119
+ module_name = node.rhs.module_name.value
1120
+ if module_name != "cython" and not module_name.startswith("cython."):
1121
+ return node
1122
+
1123
+ node = Nodes.CImportStatNode(node.pos, module_name=module_name, as_name=node.lhs.name)
1124
+ node = self.visit_CImportStatNode(node)
1125
+ else:
1126
+ self.visitchildren(node)
1127
+
1128
+ return node
1129
+
1130
+ def visit_NameNode(self, node):
1131
+ if node.annotation:
1132
+ self.visitchild(node, 'annotation')
1133
+ if node.name in self.cython_module_names:
1134
+ node.is_cython_module = True
1135
+ else:
1136
+ directive = self.directive_names.get(node.name)
1137
+ if directive is not None:
1138
+ node.cython_attribute = directive
1139
+ if node.as_cython_attribute() == "compiled":
1140
+ return ExprNodes.BoolNode(node.pos, value=True) # replace early so unused branches can be dropped
1141
+ # before they have a chance to cause compile-errors
1142
+ return node
1143
+
1144
+ def visit_AttributeNode(self, node):
1145
+ self.visitchildren(node)
1146
+ if node.as_cython_attribute() == "compiled":
1147
+ return ExprNodes.BoolNode(node.pos, value=True) # replace early so unused branches can be dropped
1148
+ # before they have a chance to cause compile-errors
1149
+ return node
1150
+
1151
+ def visit_AnnotationNode(self, node):
1152
+ # for most transforms annotations are left unvisited (because they're unevaluated)
1153
+ # however, it is important to pick up compiler directives from them
1154
+ if node.expr:
1155
+ self.visit(node.expr)
1156
+ return node
1157
+
1158
+ def visit_NewExprNode(self, node):
1159
+ self.visitchild(node, 'cppclass')
1160
+ self.visitchildren(node)
1161
+ return node
1162
+
1163
+ def try_to_parse_directives(self, node):
1164
+ # If node is the contents of an directive (in a with statement or
1165
+ # decorator), returns a list of (directivename, value) pairs.
1166
+ # Otherwise, returns None
1167
+ if isinstance(node, ExprNodes.CallNode):
1168
+ self.visitchild(node, 'function')
1169
+ optname = node.function.as_cython_attribute()
1170
+ if optname:
1171
+ directivetype = Options.directive_types.get(optname)
1172
+ if directivetype:
1173
+ args, kwds = node.explicit_args_kwds()
1174
+ directives = []
1175
+ key_value_pairs = []
1176
+ if kwds is not None and directivetype is not dict:
1177
+ for keyvalue in kwds.key_value_pairs:
1178
+ key, value = keyvalue
1179
+ sub_optname = "%s.%s" % (optname, key.value)
1180
+ if Options.directive_types.get(sub_optname):
1181
+ directives.append(self.try_to_parse_directive(sub_optname, [value], None, keyvalue.pos))
1182
+ else:
1183
+ key_value_pairs.append(keyvalue)
1184
+ if not key_value_pairs:
1185
+ kwds = None
1186
+ else:
1187
+ kwds.key_value_pairs = key_value_pairs
1188
+ if directives and not kwds and not args:
1189
+ return directives
1190
+ directives.append(self.try_to_parse_directive(optname, args, kwds, node.function.pos))
1191
+ return directives
1192
+ elif isinstance(node, (ExprNodes.AttributeNode, ExprNodes.NameNode)):
1193
+ self.visit(node)
1194
+ optname = node.as_cython_attribute()
1195
+ if optname:
1196
+ directivetype = Options.directive_types.get(optname)
1197
+ if directivetype is bool:
1198
+ arg = ExprNodes.BoolNode(node.pos, value=True)
1199
+ return [self.try_to_parse_directive(optname, [arg], None, node.pos)]
1200
+ elif directivetype is None or directivetype is Options.DEFER_ANALYSIS_OF_ARGUMENTS:
1201
+ return [(optname, None)]
1202
+ else:
1203
+ raise PostParseError(
1204
+ node.pos, "The '%s' directive should be used as a function call." % optname)
1205
+ return None
1206
+
1207
+ def try_to_parse_directive(self, optname, args, kwds, pos):
1208
+ if optname == 'np_pythran' and not self.context.cpp:
1209
+ raise PostParseError(pos, 'The %s directive can only be used in C++ mode.' % optname)
1210
+ elif optname == 'exceptval':
1211
+ # default: exceptval(None, check=True)
1212
+ arg_error = len(args) > 1
1213
+ check = True
1214
+ if kwds and kwds.key_value_pairs:
1215
+ kw = kwds.key_value_pairs[0]
1216
+ if (len(kwds.key_value_pairs) == 1 and
1217
+ kw.key.is_string_literal and kw.key.value == 'check' and
1218
+ isinstance(kw.value, ExprNodes.BoolNode)):
1219
+ check = kw.value.value
1220
+ else:
1221
+ arg_error = True
1222
+ if arg_error:
1223
+ raise PostParseError(
1224
+ pos, 'The exceptval directive takes 0 or 1 positional arguments and the boolean keyword "check"')
1225
+ return ('exceptval', (args[0] if args else None, check))
1226
+
1227
+ directivetype = Options.directive_types.get(optname)
1228
+ if len(args) == 1 and isinstance(args[0], ExprNodes.NoneNode):
1229
+ return optname, Options.get_directive_defaults()[optname]
1230
+ elif directivetype is bool:
1231
+ if kwds is not None or len(args) != 1 or not isinstance(args[0], ExprNodes.BoolNode):
1232
+ raise PostParseError(pos,
1233
+ 'The %s directive takes one compile-time boolean argument' % optname)
1234
+ return (optname, args[0].value)
1235
+ elif directivetype is int:
1236
+ if kwds is not None or len(args) != 1 or not isinstance(args[0], ExprNodes.IntNode):
1237
+ raise PostParseError(pos,
1238
+ 'The %s directive takes one compile-time integer argument' % optname)
1239
+ return (optname, int(args[0].value))
1240
+ elif directivetype is str:
1241
+ if kwds is not None or len(args) != 1 or not isinstance(args[0], ExprNodes.UnicodeNode):
1242
+ raise PostParseError(pos,
1243
+ 'The %s directive takes one compile-time string argument' % optname)
1244
+ return (optname, str(args[0].value))
1245
+ elif directivetype is type:
1246
+ if kwds is not None or len(args) != 1:
1247
+ raise PostParseError(pos,
1248
+ 'The %s directive takes one type argument' % optname)
1249
+ return (optname, args[0])
1250
+ elif directivetype is dict:
1251
+ if len(args) != 0:
1252
+ raise PostParseError(pos,
1253
+ 'The %s directive takes no prepositional arguments' % optname)
1254
+ return optname, kwds.as_python_dict()
1255
+ elif directivetype is list:
1256
+ if kwds and len(kwds.key_value_pairs) != 0:
1257
+ raise PostParseError(pos,
1258
+ 'The %s directive takes no keyword arguments' % optname)
1259
+ return optname, [ str(arg.value) for arg in args ]
1260
+ elif callable(directivetype):
1261
+ if kwds is not None or len(args) != 1 or not isinstance(args[0], ExprNodes.UnicodeNode):
1262
+ raise PostParseError(pos,
1263
+ 'The %s directive takes one compile-time string argument' % optname)
1264
+ return (optname, directivetype(optname, str(args[0].value)))
1265
+ elif directivetype is Options.DEFER_ANALYSIS_OF_ARGUMENTS:
1266
+ # signal to pass things on without processing
1267
+ return (optname, (args, kwds.as_python_dict() if kwds else {}))
1268
+ else:
1269
+ assert False
1270
+
1271
+ def visit_with_directives(self, node, directives, contents_directives):
1272
+ # contents_directives may be None
1273
+ if not directives:
1274
+ assert not contents_directives
1275
+ return self.visit_Node(node)
1276
+
1277
+ old_directives = self.directives
1278
+ new_directives = Options.copy_inherited_directives(old_directives, **directives)
1279
+ if contents_directives is not None:
1280
+ new_contents_directives = Options.copy_inherited_directives(
1281
+ old_directives, **contents_directives)
1282
+ else:
1283
+ new_contents_directives = new_directives
1284
+
1285
+ if new_directives == old_directives:
1286
+ return self.visit_Node(node)
1287
+
1288
+ self.directives = new_directives
1289
+ if (contents_directives is not None and
1290
+ new_contents_directives != new_directives):
1291
+ # we need to wrap the node body in a compiler directives node
1292
+ node.body = Nodes.StatListNode(
1293
+ node.body.pos,
1294
+ stats=[
1295
+ Nodes.CompilerDirectivesNode(
1296
+ node.body.pos,
1297
+ directives=new_contents_directives,
1298
+ body=node.body)
1299
+ ]
1300
+ )
1301
+ retbody = self.visit_Node(node)
1302
+ self.directives = old_directives
1303
+
1304
+ if isinstance(retbody, Nodes.CompilerDirectivesNode):
1305
+ new_directives.update(retbody.directives)
1306
+ retbody = retbody.body
1307
+ if not isinstance(retbody, Nodes.StatListNode):
1308
+ retbody = Nodes.StatListNode(node.pos, stats=[retbody])
1309
+ return Nodes.CompilerDirectivesNode(
1310
+ retbody.pos, body=retbody, directives=new_directives, is_terminator=retbody.is_terminator)
1311
+
1312
+ # Handle decorators
1313
+ def visit_FuncDefNode(self, node):
1314
+ directives, contents_directives = self._extract_directives(node, 'function')
1315
+ return self.visit_with_directives(node, directives, contents_directives)
1316
+
1317
+ def visit_CVarDefNode(self, node):
1318
+ directives, _ = self._extract_directives(node, 'function')
1319
+ for name, value in directives.items():
1320
+ if name == 'locals':
1321
+ node.directive_locals = value
1322
+ elif name not in ('final', 'staticmethod'):
1323
+ self.context.nonfatal_error(PostParseError(
1324
+ node.pos,
1325
+ "Cdef functions can only take cython.locals(), "
1326
+ "staticmethod, or final decorators, got %s." % name))
1327
+ return self.visit_with_directives(node, directives, contents_directives=None)
1328
+
1329
+ def visit_CClassDefNode(self, node):
1330
+ directives, contents_directives = self._extract_directives(node, 'cclass')
1331
+ return self.visit_with_directives(node, directives, contents_directives)
1332
+
1333
+ def visit_CppClassNode(self, node):
1334
+ directives, contents_directives = self._extract_directives(node, 'cppclass')
1335
+ return self.visit_with_directives(node, directives, contents_directives)
1336
+
1337
+ def visit_PyClassDefNode(self, node):
1338
+ directives, contents_directives = self._extract_directives(node, 'class')
1339
+ return self.visit_with_directives(node, directives, contents_directives)
1340
+
1341
+ def _extract_directives(self, node, scope_name):
1342
+ """
1343
+ Returns two dicts - directives applied to this function/class
1344
+ and directives applied to its contents. They aren't always the
1345
+ same (since e.g. cfunc should not be applied to inner functions)
1346
+ """
1347
+ if not node.decorators:
1348
+ return {}, {}
1349
+ # Split the decorators into two lists -- real decorators and directives
1350
+ directives = []
1351
+ realdecs = []
1352
+ both = []
1353
+ current_opt_dict = dict(self.directives)
1354
+ missing = object()
1355
+ # Decorators coming first take precedence.
1356
+ for dec in node.decorators[::-1]:
1357
+ new_directives = self.try_to_parse_directives(dec.decorator)
1358
+ if new_directives is not None:
1359
+ for directive in new_directives:
1360
+ if self.check_directive_scope(node.pos, directive[0], scope_name):
1361
+ name, value = directive
1362
+ if name in ('nogil', 'with_gil'):
1363
+ if value is None:
1364
+ value = True
1365
+ else:
1366
+ args, kwds = value
1367
+ if kwds or len(args) != 1 or not isinstance(args[0], ExprNodes.BoolNode):
1368
+ raise PostParseError(dec.pos, 'The %s directive takes one compile-time boolean argument' % name)
1369
+ value = args[0].value
1370
+ directive = (name, value)
1371
+ if current_opt_dict.get(name, missing) != value:
1372
+ if name == 'cfunc' and 'ufunc' in current_opt_dict:
1373
+ error(dec.pos, "Cannot apply @cfunc to @ufunc, please reverse the decorators.")
1374
+ directives.append(directive)
1375
+ current_opt_dict[name] = value
1376
+ else:
1377
+ warning(dec.pos, "Directive does not change previous value (%s%s)" % (
1378
+ name, '=%r' % value if value is not None else ''))
1379
+ if directive[0] == 'staticmethod':
1380
+ both.append(dec)
1381
+ # Adapt scope type based on decorators that change it.
1382
+ if directive[0] == 'cclass' and scope_name == 'class':
1383
+ scope_name = 'cclass'
1384
+ else:
1385
+ realdecs.append(dec)
1386
+ node.decorators = realdecs[::-1] + both[::-1]
1387
+ # merge or override repeated directives
1388
+ optdict = {}
1389
+ contents_optdict = {}
1390
+ for name, value in directives:
1391
+ if name in optdict:
1392
+ old_value = optdict[name]
1393
+ # keywords and arg lists can be merged, everything
1394
+ # else overrides completely
1395
+ if isinstance(old_value, dict):
1396
+ old_value.update(value)
1397
+ elif isinstance(old_value, list):
1398
+ old_value.extend(value)
1399
+ else:
1400
+ optdict[name] = value
1401
+ else:
1402
+ optdict[name] = value
1403
+ if name not in Options.immediate_decorator_directives:
1404
+ contents_optdict[name] = value
1405
+ return optdict, contents_optdict
1406
+
1407
+ # Handle with-statements
1408
+ def visit_WithStatNode(self, node):
1409
+ directive_dict = {}
1410
+ for directive in self.try_to_parse_directives(node.manager) or []:
1411
+ if directive is None:
1412
+ continue
1413
+ if node.target is not None:
1414
+ self.context.nonfatal_error(
1415
+ PostParseError(node.pos, "Compiler directive with statements cannot contain 'as'"))
1416
+ continue
1417
+ name, value = directive
1418
+ if name in ('nogil', 'gil'):
1419
+ # special case: in pure mode, "with nogil" spells "with cython.nogil"
1420
+ return self._transform_with_gil(node, name)
1421
+ elif name == "critical_section":
1422
+ args, kwds = value
1423
+ return self._transform_critical_section(node, args, kwds)
1424
+ elif self.check_directive_scope(node.pos, name, 'with statement'):
1425
+ directive_dict[name] = value
1426
+ if directive_dict:
1427
+ return self.visit_with_directives(node.body, directive_dict, contents_directives=None)
1428
+ return self.visit_Node(node)
1429
+
1430
+ def _transform_with_gil(self, node, state):
1431
+ assert state in ('gil', 'nogil')
1432
+ manager = node.manager
1433
+ condition = None
1434
+ if isinstance(manager, ExprNodes.SimpleCallNode) and manager.args:
1435
+ if len(manager.args) > 1:
1436
+ self.context.nonfatal_error(
1437
+ PostParseError(node.pos, "Compiler directive %s accepts one positional argument." % state))
1438
+ condition = manager.args[0]
1439
+ elif isinstance(manager, ExprNodes.GeneralCallNode):
1440
+ self.context.nonfatal_error(
1441
+ PostParseError(node.pos, "Compiler directive %s accepts one positional argument." % state))
1442
+ node = Nodes.GILStatNode(node.pos, state=state, body=node.body, condition=condition)
1443
+ return self.visit_Node(node)
1444
+
1445
+ def _transform_critical_section(self, node, args, kwds):
1446
+ if len(args) < 1 or len(args) > 2 or kwds:
1447
+ self.context.nonfatal_error(
1448
+ PostParseError(node.pos, "critical_section directive accepts one or two positional arguments")
1449
+ )
1450
+ node = Nodes.CriticalSectionStatNode(
1451
+ node.pos, args=args, body=node.body
1452
+ )
1453
+ return self.visit_Node(node)
1454
+
1455
+
1456
+ class ParallelRangeTransform(CythonTransform, SkipDeclarations):
1457
+ """
1458
+ Transform cython.parallel stuff. The parallel_directives come from the
1459
+ module node, set there by InterpretCompilerDirectives.
1460
+
1461
+ x = cython.parallel.threadavailable() -> ParallelThreadAvailableNode
1462
+ with nogil, cython.parallel.parallel(): -> ParallelWithBlockNode
1463
+ print cython.parallel.threadid() -> ParallelThreadIdNode
1464
+ for i in cython.parallel.prange(...): -> ParallelRangeNode
1465
+ ...
1466
+ """
1467
+
1468
+ # a list of names, maps 'cython.parallel.prange' in the code to
1469
+ # ['cython', 'parallel', 'prange']
1470
+ parallel_directive = None
1471
+
1472
+ # Indicates whether a namenode in an expression is the cython module
1473
+ namenode_is_cython_module = False
1474
+
1475
+ # Keep track of whether we are the context manager of a 'with' statement
1476
+ in_context_manager_section = False
1477
+
1478
+ # One of 'prange' or 'with parallel'. This is used to disallow closely
1479
+ # nested 'with parallel:' blocks
1480
+ state = None
1481
+
1482
+ directive_to_node = {
1483
+ "cython.parallel.parallel": Nodes.ParallelWithBlockNode,
1484
+ # u"cython.parallel.threadsavailable": ExprNodes.ParallelThreadsAvailableNode,
1485
+ "cython.parallel.threadid": ExprNodes.ParallelThreadIdNode,
1486
+ "cython.parallel.prange": Nodes.ParallelRangeNode,
1487
+ }
1488
+
1489
+ def node_is_parallel_directive(self, node):
1490
+ return node.name in self.parallel_directives or node.is_cython_module
1491
+
1492
+ def get_directive_class_node(self, node):
1493
+ """
1494
+ Figure out which parallel directive was used and return the associated
1495
+ Node class.
1496
+
1497
+ E.g. for a cython.parallel.prange() call we return ParallelRangeNode
1498
+ """
1499
+ if self.namenode_is_cython_module:
1500
+ directive = '.'.join(self.parallel_directive)
1501
+ else:
1502
+ directive = self.parallel_directives[self.parallel_directive[0]]
1503
+ directive = '%s.%s' % (directive,
1504
+ '.'.join(self.parallel_directive[1:]))
1505
+ directive = directive.rstrip('.')
1506
+
1507
+ cls = self.directive_to_node.get(directive)
1508
+ if cls is None and not (self.namenode_is_cython_module and
1509
+ self.parallel_directive[0] != 'parallel'):
1510
+ error(node.pos, "Invalid directive: %s" % directive)
1511
+
1512
+ self.namenode_is_cython_module = False
1513
+ self.parallel_directive = None
1514
+
1515
+ return cls
1516
+
1517
+ def visit_ModuleNode(self, node):
1518
+ """
1519
+ If any parallel directives were imported, copy them over and visit
1520
+ the AST
1521
+ """
1522
+ if node.parallel_directives:
1523
+ self.parallel_directives = node.parallel_directives
1524
+ return self.visit_Node(node)
1525
+
1526
+ # No parallel directives were imported, so they can't be used :)
1527
+ return node
1528
+
1529
+ def visit_NameNode(self, node):
1530
+ if self.node_is_parallel_directive(node):
1531
+ self.parallel_directive = [node.name]
1532
+ self.namenode_is_cython_module = node.is_cython_module
1533
+ return node
1534
+
1535
+ def visit_AttributeNode(self, node):
1536
+ self.visitchildren(node)
1537
+ if self.parallel_directive:
1538
+ self.parallel_directive.append(node.attribute)
1539
+ return node
1540
+
1541
+ def visit_CallNode(self, node):
1542
+ self.visitchild(node, 'function')
1543
+ if not self.parallel_directive:
1544
+ self.visitchildren(node, exclude=('function',))
1545
+ return node
1546
+
1547
+ # We are a parallel directive, replace this node with the
1548
+ # corresponding ParallelSomethingSomething node
1549
+
1550
+ if isinstance(node, ExprNodes.GeneralCallNode):
1551
+ args = node.positional_args.args
1552
+ kwargs = node.keyword_args
1553
+ else:
1554
+ args = node.args
1555
+ kwargs = {}
1556
+
1557
+ parallel_directive_class = self.get_directive_class_node(node)
1558
+ if parallel_directive_class:
1559
+ # Note: in case of a parallel() the body is set by
1560
+ # visit_WithStatNode
1561
+ node = parallel_directive_class(node.pos, args=args, kwargs=kwargs)
1562
+
1563
+ return node
1564
+
1565
+ def visit_WithStatNode(self, node):
1566
+ "Rewrite with cython.parallel.parallel() blocks"
1567
+ newnode = self.visit(node.manager)
1568
+
1569
+ if isinstance(newnode, Nodes.ParallelWithBlockNode):
1570
+ if self.state == 'parallel with':
1571
+ error(node.manager.pos,
1572
+ "Nested parallel with blocks are disallowed")
1573
+
1574
+ self.state = 'parallel with'
1575
+ body = self.visitchild(node, 'body')
1576
+ self.state = None
1577
+
1578
+ newnode.body = body
1579
+ return newnode
1580
+ elif self.parallel_directive:
1581
+ parallel_directive_class = self.get_directive_class_node(node)
1582
+
1583
+ if not parallel_directive_class:
1584
+ # There was an error, stop here and now
1585
+ return None
1586
+
1587
+ if parallel_directive_class is Nodes.ParallelWithBlockNode:
1588
+ error(node.pos, "The parallel directive must be called")
1589
+ return None
1590
+
1591
+ self.visitchild(node, 'body')
1592
+ return node
1593
+
1594
+ def visit_ForInStatNode(self, node):
1595
+ "Rewrite 'for i in cython.parallel.prange(...):'"
1596
+ self.visitchild(node, 'iterator')
1597
+ self.visitchild(node, 'target')
1598
+
1599
+ in_prange = isinstance(node.iterator.sequence,
1600
+ Nodes.ParallelRangeNode)
1601
+ previous_state = self.state
1602
+
1603
+ if in_prange:
1604
+ # This will replace the entire ForInStatNode, so copy the
1605
+ # attributes
1606
+ parallel_range_node = node.iterator.sequence
1607
+
1608
+ parallel_range_node.target = node.target
1609
+ parallel_range_node.body = node.body
1610
+ parallel_range_node.else_clause = node.else_clause
1611
+
1612
+ node = parallel_range_node
1613
+
1614
+ if not isinstance(node.target, ExprNodes.NameNode):
1615
+ error(node.target.pos,
1616
+ "Can only iterate over an iteration variable")
1617
+
1618
+ self.state = 'prange'
1619
+
1620
+ self.visitchild(node, 'body')
1621
+ self.state = previous_state
1622
+ self.visitchild(node, 'else_clause')
1623
+ return node
1624
+
1625
+ def visit(self, node):
1626
+ "Visit a node that may be None"
1627
+ if node is not None:
1628
+ return super().visit(node)
1629
+
1630
+
1631
+ class WithTransform(VisitorTransform, SkipDeclarations):
1632
+ def visit_WithStatNode(self, node):
1633
+ self.visitchildren(node, ['body'])
1634
+ pos = node.pos
1635
+ is_async = node.is_async
1636
+ body, target, manager = node.body, node.target, node.manager
1637
+ manager = node.manager = ExprNodes.ProxyNode(manager)
1638
+ node.enter_call = ExprNodes.SimpleCallNode(
1639
+ pos, function=ExprNodes.AttributeNode(
1640
+ pos, obj=ExprNodes.CloneNode(manager),
1641
+ attribute=EncodedString('__aenter__' if is_async else '__enter__'),
1642
+ is_special_lookup=True),
1643
+ args=[],
1644
+ is_temp=True)
1645
+
1646
+ if is_async:
1647
+ node.enter_call = ExprNodes.AwaitExprNode(pos, arg=node.enter_call)
1648
+
1649
+ if target is not None:
1650
+ body = Nodes.StatListNode(
1651
+ pos, stats=[
1652
+ Nodes.WithTargetAssignmentStatNode(
1653
+ pos, lhs=target, with_node=node),
1654
+ body])
1655
+
1656
+ excinfo_target = ExprNodes.TupleNode(pos, slow=True, args=[
1657
+ ExprNodes.ExcValueNode(pos) for _ in range(3)])
1658
+ except_clause = Nodes.ExceptClauseNode(
1659
+ pos, body=Nodes.IfStatNode(
1660
+ pos, if_clauses=[
1661
+ Nodes.IfClauseNode(
1662
+ pos, condition=ExprNodes.NotNode(
1663
+ pos, operand=ExprNodes.WithExitCallNode(
1664
+ pos, with_stat=node,
1665
+ test_if_run=False,
1666
+ args=excinfo_target,
1667
+ await_expr=ExprNodes.AwaitExprNode(pos, arg=None) if is_async else None)),
1668
+ body=Nodes.ReraiseStatNode(pos),
1669
+ ),
1670
+ ],
1671
+ else_clause=None),
1672
+ pattern=None,
1673
+ target=None,
1674
+ excinfo_target=excinfo_target,
1675
+ )
1676
+
1677
+ node.body = Nodes.TryFinallyStatNode(
1678
+ pos, body=Nodes.TryExceptStatNode(
1679
+ pos, body=body,
1680
+ except_clauses=[except_clause],
1681
+ else_clause=None,
1682
+ ),
1683
+ finally_clause=Nodes.ExprStatNode(
1684
+ pos, expr=ExprNodes.WithExitCallNode(
1685
+ pos, with_stat=node,
1686
+ test_if_run=True,
1687
+ args=ExprNodes.TupleNode(
1688
+ pos, args=[ExprNodes.NoneNode(pos) for _ in range(3)]),
1689
+ await_expr=ExprNodes.AwaitExprNode(pos, arg=None) if is_async else None)),
1690
+ handle_error_case=False,
1691
+ )
1692
+ return node
1693
+
1694
+ def visit_ExprNode(self, node):
1695
+ # With statements are never inside expressions.
1696
+ return node
1697
+
1698
+ visit_Node = VisitorTransform.recurse_to_children
1699
+
1700
+
1701
+ class _GeneratorExpressionArgumentsMarker(TreeVisitor, SkipDeclarations):
1702
+ # called from "MarkClosureVisitor"
1703
+ def __init__(self, gen_expr):
1704
+ super().__init__()
1705
+ self.gen_expr = gen_expr
1706
+
1707
+ def visit_ExprNode(self, node):
1708
+ if not node.is_literal:
1709
+ # Don't bother tagging literal nodes
1710
+ assert (not node.generator_arg_tag) # nobody has tagged this first
1711
+ node.generator_arg_tag = self.gen_expr
1712
+ self.visitchildren(node)
1713
+
1714
+ def visit_Node(self, node):
1715
+ # We're only interested in the expressions that make up the iterator sequence,
1716
+ # so don't go beyond ExprNodes (e.g. into ForFromStatNode).
1717
+ return
1718
+
1719
+ def visit_GeneratorExpressionNode(self, node):
1720
+ node.generator_arg_tag = self.gen_expr
1721
+ # don't visit children, can't handle overlapping tags
1722
+ # (and assume generator expressions don't end up optimized out in a way
1723
+ # that would require overlapping tags)
1724
+
1725
+
1726
+ class _HandleGeneratorArguments(VisitorTransform, SkipDeclarations):
1727
+ # used from within CreateClosureClasses
1728
+
1729
+ def __call__(self, node):
1730
+ from . import Visitor
1731
+ assert isinstance(node, ExprNodes.GeneratorExpressionNode)
1732
+ self.gen_node = node
1733
+
1734
+ self.args = list(node.def_node.args)
1735
+ self.call_parameters = list(node.call_parameters)
1736
+ self.tag_count = 0
1737
+ self.substitutions = {}
1738
+
1739
+ self.visitchildren(node)
1740
+
1741
+ for k, v in self.substitutions.items():
1742
+ # doing another search for replacements here (at the end) allows us to sweep up
1743
+ # CloneNodes too (which are often generated by the optimizer)
1744
+ # (it could arguably be done more efficiently with a single traversal though)
1745
+ Visitor.recursively_replace_node(node, k, v)
1746
+
1747
+ node.def_node.args = self.args
1748
+ node.call_parameters = self.call_parameters
1749
+ return node
1750
+
1751
+ def visit_GeneratorExpressionNode(self, node):
1752
+ # a generator can also be substituted itself, so handle that case
1753
+ new_node = self._handle_ExprNode(node, do_visit_children=False)
1754
+ # However do not traverse into it. A new _HandleGeneratorArguments visitor will be used
1755
+ # elsewhere to do that.
1756
+ return node
1757
+
1758
+ def _handle_ExprNode(self, node, do_visit_children):
1759
+ if (node.generator_arg_tag is not None and self.gen_node is not None and
1760
+ self.gen_node == node.generator_arg_tag):
1761
+ pos = node.pos
1762
+ # The reason for using ".x" as the name is that this is how CPython
1763
+ # tracks internal variables in loops (e.g.
1764
+ # { locals() for v in range(10) }
1765
+ # will produce "v" and ".0"). We don't replicate this behaviour completely
1766
+ # but use it as a starting point
1767
+ name_source = self.tag_count
1768
+ self.tag_count += 1
1769
+ name = EncodedString(".{}".format(name_source))
1770
+ def_node = self.gen_node.def_node
1771
+ if not def_node.local_scope.lookup_here(name):
1772
+ from . import Symtab
1773
+ cname = EncodedString(Naming.genexpr_arg_prefix + Symtab.punycodify_name(str(name_source)))
1774
+ name_decl = Nodes.CNameDeclaratorNode(pos=pos, name=name)
1775
+ type = node.type
1776
+
1777
+ # strip away cv types - they shouldn't be applied to the
1778
+ # function argument or to the closure struct.
1779
+ # It isn't obvious whether the right thing to do would be to capture by reference or by
1780
+ # value (C++ itself doesn't know either for lambda functions and forces a choice).
1781
+ # However, capture by reference involves converting to FakeReference which would require
1782
+ # re-analysing AttributeNodes. Therefore I've picked capture-by-value out of convenience
1783
+ # TODO - could probably be optimized by making the arg a reference but the closure not
1784
+ # (see https://github.com/cython/cython/issues/2468)
1785
+ type = PyrexTypes.remove_cv_ref(type, remove_fakeref=False)
1786
+
1787
+ name_decl.type = type
1788
+ new_arg = Nodes.CArgDeclNode(pos=pos, declarator=name_decl,
1789
+ base_type=None, default=None, annotation=None)
1790
+ new_arg.name = name_decl.name
1791
+ new_arg.type = type
1792
+
1793
+ self.args.append(new_arg)
1794
+ node.generator_arg_tag = None # avoid the possibility of this being caught again
1795
+ self.call_parameters.append(node)
1796
+ new_arg.entry = def_node.declare_argument(def_node.local_scope, new_arg)
1797
+ new_arg.entry.cname = cname
1798
+ new_arg.entry.in_closure = True
1799
+
1800
+ if do_visit_children:
1801
+ # now visit the Nodes's children (but remove self.gen_node to not to further
1802
+ # argument substitution)
1803
+ gen_node, self.gen_node = self.gen_node, None
1804
+ self.visitchildren(node)
1805
+ self.gen_node = gen_node
1806
+
1807
+ # replace the node inside the generator with a looked-up name
1808
+ # (initialized_check can safely be False because the source variable will be checked
1809
+ # before it is captured if the check is required)
1810
+ name_node = ExprNodes.NameNode(pos, name=name, initialized_check=False)
1811
+ name_node.entry = self.gen_node.def_node.gbody.local_scope.lookup(name_node.name)
1812
+ name_node.type = name_node.entry.type
1813
+ self.substitutions[node] = name_node
1814
+ return name_node
1815
+ if do_visit_children:
1816
+ self.visitchildren(node)
1817
+ return node
1818
+
1819
+ def visit_ExprNode(self, node):
1820
+ return self._handle_ExprNode(node, True)
1821
+
1822
+ visit_Node = VisitorTransform.recurse_to_children
1823
+
1824
+
1825
+ class DecoratorTransform(ScopeTrackingTransform, SkipDeclarations):
1826
+ """
1827
+ Transforms method decorators in cdef classes into nested calls or properties.
1828
+
1829
+ Python-style decorator properties are transformed into a PropertyNode
1830
+ with up to the three getter, setter and deleter DefNodes.
1831
+ The functional style isn't supported yet.
1832
+ """
1833
+ _properties = None
1834
+
1835
+ _map_property_attribute = {
1836
+ 'getter': EncodedString('__get__'),
1837
+ 'setter': EncodedString('__set__'),
1838
+ 'deleter': EncodedString('__del__'),
1839
+ }.get
1840
+
1841
+ def visit_CClassDefNode(self, node):
1842
+ if self._properties is None:
1843
+ self._properties = []
1844
+ self._properties.append({})
1845
+ node = super().visit_CClassDefNode(node)
1846
+ self._properties.pop()
1847
+ return node
1848
+
1849
+ def visit_PropertyNode(self, node):
1850
+ # Low-level warning for other code until we can convert all our uses over.
1851
+ level = 2 if isinstance(node.pos[0], str) else 0
1852
+ warning(node.pos, "'property %s:' syntax is deprecated, use '@property'" % node.name, level)
1853
+ return node
1854
+
1855
+ def visit_CFuncDefNode(self, node):
1856
+ node = self.visit_FuncDefNode(node)
1857
+ if not node.decorators:
1858
+ return node
1859
+ elif self.scope_type != 'cclass' or self.scope_node.visibility != "extern":
1860
+ # at the moment cdef functions are very restricted in what decorators they can take
1861
+ # so it's simple to test for the small number of allowed decorators....
1862
+ if not (len(node.decorators) == 1 and node.decorators[0].decorator.is_name and
1863
+ node.decorators[0].decorator.name == "staticmethod"):
1864
+ error(node.decorators[0].pos, "Cdef functions cannot take arbitrary decorators.")
1865
+ return node
1866
+
1867
+ ret_node = node
1868
+ decorator_node = self._find_property_decorator(node)
1869
+ if decorator_node:
1870
+ if decorator_node.decorator.is_name:
1871
+ name = node.declared_name()
1872
+ if name:
1873
+ ret_node = self._add_property(node, name, decorator_node)
1874
+ else:
1875
+ error(decorator_node.pos, "C property decorator can only be @property")
1876
+
1877
+ if node.decorators:
1878
+ return self._reject_decorated_property(node, node.decorators[0])
1879
+ return ret_node
1880
+
1881
+ def visit_DefNode(self, node):
1882
+ scope_type = self.scope_type
1883
+ node = self.visit_FuncDefNode(node)
1884
+ if scope_type != 'cclass' or not node.decorators:
1885
+ return node
1886
+
1887
+ # transform @property decorators
1888
+ decorator_node = self._find_property_decorator(node)
1889
+ if decorator_node is not None:
1890
+ decorator = decorator_node.decorator
1891
+ if decorator.is_name:
1892
+ return self._add_property(node, node.name, decorator_node)
1893
+ else:
1894
+ handler_name = self._map_property_attribute(decorator.attribute)
1895
+ if handler_name:
1896
+ if decorator.obj.name != node.name:
1897
+ # CPython does not generate an error or warning, but not something useful either.
1898
+ error(decorator_node.pos,
1899
+ "Mismatching property names, expected '%s', got '%s'" % (
1900
+ decorator.obj.name, node.name))
1901
+ elif len(node.decorators) > 1:
1902
+ return self._reject_decorated_property(node, decorator_node)
1903
+ else:
1904
+ return self._add_to_property(node, handler_name, decorator_node)
1905
+
1906
+ # we clear node.decorators, so we need to set the
1907
+ # is_staticmethod/is_classmethod attributes now
1908
+ for decorator in node.decorators:
1909
+ func = decorator.decorator
1910
+ if func.is_name:
1911
+ node.is_classmethod |= func.name == 'classmethod'
1912
+ node.is_staticmethod |= func.name == 'staticmethod'
1913
+
1914
+ # transform normal decorators
1915
+ decs = node.decorators
1916
+ node.decorators = None
1917
+ return self.chain_decorators(node, decs, node.name)
1918
+
1919
+ def _find_property_decorator(self, node):
1920
+ properties = self._properties[-1]
1921
+ for decorator_node in node.decorators[::-1]:
1922
+ decorator = decorator_node.decorator
1923
+ if decorator.is_name and decorator.name == 'property':
1924
+ # @property
1925
+ return decorator_node
1926
+ elif decorator.is_attribute and decorator.obj.name in properties:
1927
+ # @prop.setter etc.
1928
+ return decorator_node
1929
+ return None
1930
+
1931
+ @staticmethod
1932
+ def _reject_decorated_property(node, decorator_node):
1933
+ # restrict transformation to outermost decorator as wrapped properties will probably not work
1934
+ for deco in node.decorators:
1935
+ if deco != decorator_node:
1936
+ error(deco.pos, "Property methods with additional decorators are not supported")
1937
+ return node
1938
+
1939
+ def _add_property(self, node, name, decorator_node):
1940
+ if len(node.decorators) > 1:
1941
+ return self._reject_decorated_property(node, decorator_node)
1942
+ node.decorators.remove(decorator_node)
1943
+ properties = self._properties[-1]
1944
+ is_cproperty = isinstance(node, Nodes.CFuncDefNode)
1945
+ body = Nodes.StatListNode(node.pos, stats=[node])
1946
+ if is_cproperty:
1947
+ if name in properties:
1948
+ error(node.pos, "C property redeclared")
1949
+ if 'inline' not in node.modifiers:
1950
+ error(node.pos, "C property method must be declared 'inline'")
1951
+ prop = Nodes.CPropertyNode(node.pos, doc=node.doc, name=name, body=body)
1952
+ elif name in properties:
1953
+ prop = properties[name]
1954
+ if prop.is_cproperty:
1955
+ error(node.pos, "C property redeclared")
1956
+ else:
1957
+ node.name = EncodedString("__get__")
1958
+ prop.pos = node.pos
1959
+ prop.doc = node.doc
1960
+ prop.body.stats = [node]
1961
+ return None
1962
+ else:
1963
+ node.name = EncodedString("__get__")
1964
+ prop = Nodes.PropertyNode(
1965
+ node.pos, name=name, doc=node.doc, body=body)
1966
+ properties[name] = prop
1967
+ return prop
1968
+
1969
+ def _add_to_property(self, node, name, decorator):
1970
+ properties = self._properties[-1]
1971
+ prop = properties[node.name]
1972
+ if prop.is_cproperty:
1973
+ error(node.pos, "C property redeclared")
1974
+ return None
1975
+ node.name = name
1976
+ node.decorators.remove(decorator)
1977
+ stats = prop.body.stats
1978
+ for i, stat in enumerate(stats):
1979
+ if stat.name == name:
1980
+ stats[i] = node
1981
+ break
1982
+ else:
1983
+ stats.append(node)
1984
+ return None
1985
+
1986
+ @staticmethod
1987
+ def chain_decorators(node, decorators, name):
1988
+ """
1989
+ Decorators are applied directly in DefNode and PyClassDefNode to avoid
1990
+ reassignments to the function/class name - except for cdef class methods.
1991
+ For those, the reassignment is required as methods are originally
1992
+ defined in the PyMethodDef struct.
1993
+
1994
+ The IndirectionNode allows DefNode to override the decorator.
1995
+ """
1996
+ decorator_result = ExprNodes.NameNode(node.pos, name=name)
1997
+ for decorator in decorators[::-1]:
1998
+ decorator_result = ExprNodes.SimpleCallNode(
1999
+ decorator.pos,
2000
+ function=decorator.decorator,
2001
+ args=[decorator_result])
2002
+
2003
+ name_node = ExprNodes.NameNode(node.pos, name=name)
2004
+ reassignment = Nodes.SingleAssignmentNode(
2005
+ node.pos,
2006
+ lhs=name_node,
2007
+ rhs=decorator_result)
2008
+
2009
+ reassignment = Nodes.IndirectionNode([reassignment])
2010
+ node.decorator_indirection = reassignment
2011
+ return [node, reassignment]
2012
+
2013
+
2014
+ class CnameDirectivesTransform(CythonTransform, SkipDeclarations):
2015
+ """
2016
+ Only part of the CythonUtilityCode pipeline. Must be run before
2017
+ DecoratorTransform in case this is a decorator for a cdef class.
2018
+ It filters out @cname('my_cname') decorators and rewrites them to
2019
+ CnameDecoratorNodes.
2020
+ """
2021
+
2022
+ def handle_function(self, node):
2023
+ if not getattr(node, 'decorators', None):
2024
+ return self.visit_Node(node)
2025
+
2026
+ for i, decorator in enumerate(node.decorators):
2027
+ decorator = decorator.decorator
2028
+
2029
+ if (isinstance(decorator, ExprNodes.CallNode) and
2030
+ decorator.function.is_name and
2031
+ decorator.function.name == 'cname'):
2032
+ args, kwargs = decorator.explicit_args_kwds()
2033
+
2034
+ if kwargs:
2035
+ raise AssertionError(
2036
+ "cname decorator does not take keyword arguments")
2037
+
2038
+ if len(args) != 1:
2039
+ raise AssertionError(
2040
+ "cname decorator takes exactly one argument")
2041
+
2042
+ if not (args[0].is_literal and args[0].type is Builtin.unicode_type):
2043
+ raise AssertionError(
2044
+ "argument to cname decorator must be a string literal")
2045
+
2046
+ cname = args[0].compile_time_value(None)
2047
+ del node.decorators[i]
2048
+ node = Nodes.CnameDecoratorNode(pos=node.pos, node=node,
2049
+ cname=cname)
2050
+ break
2051
+
2052
+ return self.visit_Node(node)
2053
+
2054
+ visit_FuncDefNode = handle_function
2055
+ visit_CClassDefNode = handle_function
2056
+ visit_CEnumDefNode = handle_function
2057
+ visit_CStructOrUnionDefNode = handle_function
2058
+ visit_CVarDefNode = handle_function
2059
+
2060
+
2061
+ class ForwardDeclareTypes(CythonTransform):
2062
+ """
2063
+ Declare all global cdef names that we allow referencing in other places,
2064
+ before declaring everything (else) in source code order.
2065
+ """
2066
+
2067
+ def visit_CompilerDirectivesNode(self, node):
2068
+ env = self.module_scope
2069
+ old = env.directives
2070
+ env.directives = node.directives
2071
+ self.visitchildren(node)
2072
+ env.directives = old
2073
+ return node
2074
+
2075
+ def visit_ModuleNode(self, node):
2076
+ self.module_scope = node.scope
2077
+ self.module_scope.directives = node.directives
2078
+ self.visitchildren(node)
2079
+ return node
2080
+
2081
+ def visit_CDefExternNode(self, node):
2082
+ old_cinclude_flag = self.module_scope.in_cinclude
2083
+ self.module_scope.in_cinclude = 1
2084
+ self.visitchildren(node)
2085
+ self.module_scope.in_cinclude = old_cinclude_flag
2086
+ return node
2087
+
2088
+ def visit_CEnumDefNode(self, node):
2089
+ node.declare(self.module_scope)
2090
+ return node
2091
+
2092
+ def visit_CStructOrUnionDefNode(self, node):
2093
+ if node.name not in self.module_scope.entries:
2094
+ node.declare(self.module_scope)
2095
+ return node
2096
+
2097
+ def visit_CClassDefNode(self, node):
2098
+ if node.class_name not in self.module_scope.entries:
2099
+ node.declare(self.module_scope)
2100
+ # Expand fused methods of .pxd declared types to construct the final vtable order.
2101
+ type = self.module_scope.entries[node.class_name].type
2102
+ if type is not None and type.is_extension_type and not type.is_builtin_type and type.scope:
2103
+ scope = type.scope
2104
+ for entry in scope.cfunc_entries:
2105
+ if entry.type and entry.type.is_fused:
2106
+ entry.type.get_all_specialized_function_types()
2107
+ return node
2108
+
2109
+ def visit_FuncDefNode(self, node):
2110
+ # no traversal needed
2111
+ return node
2112
+
2113
+ def visit_PyClassDefNode(self, node):
2114
+ # no traversal needed
2115
+ return node
2116
+
2117
+
2118
+ class AnalyseDeclarationsTransform(EnvTransform):
2119
+
2120
+ basic_property = TreeFragment("""
2121
+ property NAME:
2122
+ def __get__(self):
2123
+ return ATTR
2124
+ def __set__(self, value):
2125
+ ATTR = value
2126
+ """, level='c_class', pipeline=[NormalizeTree(None)])
2127
+ basic_pyobject_property = TreeFragment("""
2128
+ property NAME:
2129
+ def __get__(self):
2130
+ return ATTR
2131
+ def __set__(self, value):
2132
+ ATTR = value
2133
+ def __del__(self):
2134
+ ATTR = None
2135
+ """, level='c_class', pipeline=[NormalizeTree(None)])
2136
+ basic_property_ro = TreeFragment("""
2137
+ property NAME:
2138
+ def __get__(self):
2139
+ return ATTR
2140
+ """, level='c_class', pipeline=[NormalizeTree(None)])
2141
+
2142
+ struct_or_union_wrapper = TreeFragment("""
2143
+ cdef class NAME:
2144
+ cdef TYPE value
2145
+ def __init__(self, MEMBER=None):
2146
+ cdef int count
2147
+ count = 0
2148
+ INIT_ASSIGNMENTS
2149
+ if IS_UNION and count > 1:
2150
+ raise ValueError, "At most one union member should be specified."
2151
+ def __str__(self):
2152
+ return STR_FORMAT % MEMBER_TUPLE
2153
+ def __repr__(self):
2154
+ return REPR_FORMAT % MEMBER_TUPLE
2155
+ """, pipeline=[NormalizeTree(None)])
2156
+
2157
+ init_assignment = TreeFragment("""
2158
+ if VALUE is not None:
2159
+ ATTR = VALUE
2160
+ count += 1
2161
+ """, pipeline=[NormalizeTree(None)])
2162
+
2163
+ fused_function = None
2164
+ in_lambda = 0
2165
+
2166
+ def __call__(self, root):
2167
+ # needed to determine if a cdef var is declared after it's used.
2168
+ self.seen_vars_stack = []
2169
+ self.fused_error_funcs = set()
2170
+ super_class = super()
2171
+ self._super_visit_FuncDefNode = super_class.visit_FuncDefNode
2172
+ return super_class.__call__(root)
2173
+
2174
+ def visit_NameNode(self, node):
2175
+ self.seen_vars_stack[-1].add(node.name)
2176
+ return node
2177
+
2178
+ def visit_ModuleNode(self, node):
2179
+ # Pickling support requires injecting module-level nodes.
2180
+ self.extra_module_declarations = []
2181
+ self.seen_vars_stack.append(set())
2182
+ node.analyse_declarations(self.current_env())
2183
+ self.visitchildren(node)
2184
+ self.seen_vars_stack.pop()
2185
+ node.body.stats.extend(self.extra_module_declarations)
2186
+ return node
2187
+
2188
+ def visit_LambdaNode(self, node):
2189
+ self.in_lambda += 1
2190
+ node.analyse_declarations(self.current_env())
2191
+ self.visitchildren(node)
2192
+ self.in_lambda -= 1
2193
+ return node
2194
+
2195
+ def visit_CClassDefNode(self, node):
2196
+ node = self.visit_ClassDefNode(node)
2197
+ if node.scope and 'dataclasses.dataclass' in node.scope.directives:
2198
+ from .Dataclass import handle_cclass_dataclass
2199
+ handle_cclass_dataclass(node, node.scope.directives['dataclasses.dataclass'], self)
2200
+ if node.scope and node.scope.implemented and node.body:
2201
+ stats = []
2202
+ for entry in node.scope.var_entries:
2203
+ if entry.needs_property:
2204
+ property = self.create_Property(entry)
2205
+ property.analyse_declarations(node.scope)
2206
+ self.visit(property)
2207
+ stats.append(property)
2208
+ if stats:
2209
+ node.body.stats += stats
2210
+ if (node.visibility != 'extern'
2211
+ and not node.scope.lookup('__reduce__')
2212
+ and not node.scope.lookup('__reduce_ex__')):
2213
+ self._inject_pickle_methods(node)
2214
+ return node
2215
+
2216
+ def _inject_pickle_methods(self, node):
2217
+ env = self.current_env()
2218
+ if node.scope.directives['auto_pickle'] is False: # None means attempt it.
2219
+ # Old behavior of not doing anything.
2220
+ return
2221
+ auto_pickle_forced = node.scope.directives['auto_pickle'] is True
2222
+
2223
+ all_members = []
2224
+ cls = node.entry.type
2225
+ cinit = None
2226
+ inherited_reduce = None
2227
+ while cls is not None:
2228
+ all_members.extend(e for e in cls.scope.var_entries if e.name not in ('__weakref__', '__dict__'))
2229
+ cinit = cinit or cls.scope.lookup('__cinit__')
2230
+ inherited_reduce = inherited_reduce or cls.scope.lookup('__reduce__') or cls.scope.lookup('__reduce_ex__')
2231
+ cls = cls.base_type
2232
+ all_members.sort(key=lambda e: e.name)
2233
+
2234
+ if inherited_reduce:
2235
+ # This is not failsafe, as we may not know whether a cimported class defines a __reduce__.
2236
+ # This is why we define __reduce_cython__ and only replace __reduce__
2237
+ # (via ExtensionTypes.SetupReduce utility code) at runtime on class creation.
2238
+ return
2239
+
2240
+ non_py = [
2241
+ e for e in all_members
2242
+ if not e.type.is_pyobject and (not e.type.can_coerce_to_pyobject(env)
2243
+ or not e.type.can_coerce_from_pyobject(env))
2244
+ ]
2245
+
2246
+ structs = [e for e in all_members if e.type.is_struct_or_union]
2247
+
2248
+ if cinit or non_py or (structs and not auto_pickle_forced):
2249
+ if cinit:
2250
+ # TODO(robertwb): We could allow this if __cinit__ has no require arguments.
2251
+ msg = 'no default __reduce__ due to non-trivial __cinit__'
2252
+ elif non_py:
2253
+ msg = "%s cannot be converted to a Python object for pickling" % ','.join("self.%s" % e.name for e in non_py)
2254
+ else:
2255
+ # Extern structs may be only partially defined.
2256
+ # TODO(robertwb): Limit the restriction to extern
2257
+ # (and recursively extern-containing) structs.
2258
+ msg = ("Pickling of struct members such as %s must be explicitly requested "
2259
+ "with @auto_pickle(True)" % ','.join("self.%s" % e.name for e in structs))
2260
+
2261
+ if auto_pickle_forced:
2262
+ error(node.pos, msg)
2263
+
2264
+ pickle_func = TreeFragment("""
2265
+ def __reduce_cython__(self):
2266
+ raise TypeError, "%(msg)s"
2267
+ def __setstate_cython__(self, __pyx_state):
2268
+ raise TypeError, "%(msg)s"
2269
+ """ % {'msg': msg},
2270
+ level='c_class', pipeline=[NormalizeTree(None)]).substitute({})
2271
+ pickle_func.analyse_declarations(node.scope)
2272
+ self.visit(pickle_func)
2273
+ node.body.stats.append(pickle_func)
2274
+
2275
+ else:
2276
+ for e in all_members:
2277
+ if not e.type.is_pyobject:
2278
+ e.type.create_to_py_utility_code(env)
2279
+ e.type.create_from_py_utility_code(env)
2280
+
2281
+ all_members_names = [e.name for e in all_members]
2282
+ assignments = '; '.join([
2283
+ '__pyx_result.%s = __pyx_state[%s]' % (v, ix)
2284
+ for ix, v in enumerate(all_members_names)
2285
+ ])
2286
+ checksums = _calculate_pickle_checksums(all_members_names)
2287
+ if len(checksums) != 3:
2288
+ # If we don't have enough checksums to call the check function, we just repeat the last one.
2289
+ checksums = (checksums + [checksums[-1] * 2])[:3]
2290
+
2291
+ unpickle_func_name = f'__pyx_unpickle_{node.punycode_class_name}'
2292
+ num_members = len(all_members_names)
2293
+
2294
+ env.use_utility_code(Code.UtilityCode.load_cached("UpdateUnpickledDict", "ExtensionTypes.c"))
2295
+
2296
+ # TODO(robertwb): Move the state into the third argument
2297
+ # so it can be pickled *after* self is memoized.
2298
+ unpickle_code = f"""
2299
+ cdef extern from *:
2300
+ int __Pyx_CheckUnpickleChecksum(long, long, long, long, const char*) except -1
2301
+ int __Pyx_UpdateUnpickledDict(object, object, Py_ssize_t) except -1
2302
+
2303
+ def {unpickle_func_name}(__pyx_type, long __pyx_checksum, tuple __pyx_state):
2304
+ cdef object __pyx_result
2305
+ __Pyx_CheckUnpickleChecksum(__pyx_checksum, {', '.join(checksums)}, {', '.join(all_members_names).encode('UTF-8')!r})
2306
+ __pyx_result = {node.class_name}.__new__(__pyx_type)
2307
+ if __pyx_state is not None:
2308
+ {unpickle_func_name}__set_state(<{node.class_name}> __pyx_result, __pyx_state)
2309
+ return __pyx_result
2310
+
2311
+ cdef {unpickle_func_name}__set_state({node.class_name} __pyx_result, __pyx_state: tuple):
2312
+ {assignments}
2313
+ __Pyx_UpdateUnpickledDict(__pyx_result, __pyx_state, {num_members:d})
2314
+ """
2315
+
2316
+ env.use_utility_code(Code.UtilityCode.load_cached("CheckUnpickleChecksum", "ExtensionTypes.c"))
2317
+
2318
+ unpickle_func = TreeFragment(unpickle_code, level='module', pipeline=[NormalizeTree(None)]).substitute({})
2319
+ unpickle_func.analyse_declarations(node.entry.scope)
2320
+
2321
+ self.visit(unpickle_func)
2322
+ self.extra_module_declarations.append(unpickle_func)
2323
+
2324
+ members = ', '.join(f'self.{v}' for v in all_members_names) + (',' if len(all_members_names) == 1 else '')
2325
+ # Even better, we could check PyType_IS_GC.
2326
+ any_notnone_members = ' or '.join([f'self.{e.name} is not None' for e in all_members if e.type.is_pyobject] or ['False']),
2327
+
2328
+ pickle_code = f"""
2329
+ def __reduce_cython__(self):
2330
+ cdef tuple state
2331
+ cdef object _dict
2332
+ cdef bint use_setstate
2333
+ state = ({members})
2334
+ _dict = getattr(self, '__dict__', None)
2335
+ if _dict is not None and _dict:
2336
+ state += (_dict,)
2337
+ use_setstate = True
2338
+ else:
2339
+ use_setstate = {any_notnone_members}
2340
+ if use_setstate:
2341
+ return {unpickle_func_name}, (type(self), {checksums[0]}, None), state
2342
+ else:
2343
+ return {unpickle_func_name}, (type(self), {checksums[0]}, state)
2344
+
2345
+ def __setstate_cython__(self, __pyx_state):
2346
+ {unpickle_func_name}__set_state(self, __pyx_state)
2347
+ """
2348
+
2349
+ pickle_func = TreeFragment(pickle_code, level='c_class', pipeline=[NormalizeTree(None)]).substitute({})
2350
+ pickle_func.analyse_declarations(node.scope)
2351
+
2352
+ self.enter_scope(node, node.scope) # functions should be visited in the class scope
2353
+ self.visit(pickle_func)
2354
+ self.exit_scope()
2355
+ node.body.stats.append(pickle_func)
2356
+
2357
+ def _handle_fused_def_decorators(self, old_decorators, env, node):
2358
+ """
2359
+ Create function calls to the decorators and reassignments to
2360
+ the function.
2361
+ """
2362
+ # Delete staticmethod and classmethod decorators, this is
2363
+ # handled directly by the fused function object.
2364
+ decorators = []
2365
+ for decorator in old_decorators:
2366
+ func = decorator.decorator
2367
+ if (not func.is_name or
2368
+ func.name not in ('staticmethod', 'classmethod') or
2369
+ env.lookup_here(func.name)):
2370
+ # not a static or classmethod
2371
+ decorators.append(decorator)
2372
+
2373
+ if decorators:
2374
+ transform = DecoratorTransform(self.context)
2375
+ def_node = node.node
2376
+ _, reassignments = transform.chain_decorators(
2377
+ def_node, decorators, def_node.name)
2378
+ reassignments.analyse_declarations(env)
2379
+ node = [node, reassignments]
2380
+
2381
+ return node
2382
+
2383
+ def _handle_def(self, decorators, env, node):
2384
+ "Handle def or cpdef fused functions"
2385
+ # Create PyCFunction nodes for each specialization
2386
+ node.stats.insert(0, node.py_func)
2387
+ self.visitchild(node, 'py_func')
2388
+ node.update_fused_defnode_entry(env)
2389
+ # For the moment, fused functions do not support METH_FASTCALL
2390
+ node.py_func.entry.signature.use_fastcall = False
2391
+ pycfunc = ExprNodes.PyCFunctionNode.from_defnode(node.py_func, binding=True)
2392
+ pycfunc = ExprNodes.ProxyNode(pycfunc.coerce_to_temp(env))
2393
+ node.resulting_fused_function = pycfunc
2394
+ # Create assignment node for our def function
2395
+ node.fused_func_assignment = self._create_assignment(
2396
+ node.py_func, ExprNodes.CloneNode(pycfunc), env)
2397
+
2398
+ if decorators:
2399
+ node = self._handle_fused_def_decorators(decorators, env, node)
2400
+
2401
+ return node
2402
+
2403
+ def _create_fused_function(self, env, node):
2404
+ "Create a fused function for a DefNode with fused arguments"
2405
+ from . import FusedNode
2406
+
2407
+ if self.fused_function or self.in_lambda:
2408
+ if self.fused_function not in self.fused_error_funcs:
2409
+ if self.in_lambda:
2410
+ error(node.pos, "Fused lambdas not allowed")
2411
+ else:
2412
+ error(node.pos, "Cannot nest fused functions")
2413
+
2414
+ self.fused_error_funcs.add(self.fused_function)
2415
+
2416
+ node.body = Nodes.PassStatNode(node.pos)
2417
+ for arg in node.args:
2418
+ if arg.type.is_fused:
2419
+ arg.type = arg.type.get_fused_types()[0]
2420
+
2421
+ return node
2422
+
2423
+ decorators = getattr(node, 'decorators', None)
2424
+ node = FusedNode.FusedCFuncDefNode(node, env)
2425
+ self.fused_function = node
2426
+ self.visitchildren(node)
2427
+ self.fused_function = None
2428
+ if node.py_func:
2429
+ node = self._handle_def(decorators, env, node)
2430
+
2431
+ return node
2432
+
2433
+ def _handle_fused(self, node):
2434
+ if node.is_generator and node.has_fused_arguments:
2435
+ error(node.pos, "Fused generators not supported")
2436
+ node.has_fused_arguments = False
2437
+ node.gbody.body = Nodes.StatListNode(node.pos, stats=[])
2438
+
2439
+ return node.has_fused_arguments
2440
+
2441
+ def visit_FuncDefNode(self, node):
2442
+ """
2443
+ Analyse a function and its body, as that hasn't happened yet. Also
2444
+ analyse the directive_locals set by @cython.locals().
2445
+
2446
+ Then, if we are a function with fused arguments, replace the function
2447
+ (after it has declared itself in the symbol table!) with a
2448
+ FusedCFuncDefNode, and analyse its children (which are in turn normal
2449
+ functions). If we're a normal function, just analyse the body of the
2450
+ function.
2451
+ """
2452
+ env = self.current_env()
2453
+
2454
+ self.seen_vars_stack.append(set())
2455
+ lenv = node.local_scope
2456
+ node.declare_arguments(lenv)
2457
+
2458
+ # @cython.locals(...)
2459
+ for var, type_node in node.directive_locals.items():
2460
+ if not lenv.lookup_here(var): # don't redeclare args
2461
+ type = type_node.analyse_as_type(lenv)
2462
+ if type and type.is_fused and lenv.fused_to_specific:
2463
+ type = type.specialize(lenv.fused_to_specific)
2464
+ if type:
2465
+ lenv.declare_var(var, type, type_node.pos)
2466
+ else:
2467
+ error(type_node.pos, "Not a type")
2468
+
2469
+ if self._handle_fused(node):
2470
+ node = self._create_fused_function(env, node)
2471
+ else:
2472
+ node.body.analyse_declarations(lenv)
2473
+ node = self._super_visit_FuncDefNode(node)
2474
+
2475
+ self.seen_vars_stack.pop()
2476
+
2477
+ if "ufunc" in lenv.directives:
2478
+ from . import UFuncs
2479
+ return UFuncs.convert_to_ufunc(node)
2480
+ return node
2481
+
2482
+ def visit_DefNode(self, node):
2483
+ node = self.visit_FuncDefNode(node)
2484
+ if not isinstance(node, Nodes.DefNode):
2485
+ return node
2486
+ env = self.current_env()
2487
+ if node.code_object is None:
2488
+ node.code_object = ExprNodes.CodeObjectNode(node)
2489
+ node.code_object.analyse_declarations(env)
2490
+ if node.fused_py_func or node.is_generator_body:
2491
+ return node
2492
+ if not node.needs_assignment_synthesis(env):
2493
+ return node
2494
+ return [node, self._synthesize_assignment(node, env)]
2495
+
2496
+ def visit_CFuncDefNode(self, node):
2497
+ if node.code_object is None and node.py_func is None:
2498
+ node.code_object = ExprNodes.CodeObjectNode.for_cfunc(node)
2499
+ node.code_object.analyse_declarations(self.current_env())
2500
+ return self.visit_FuncDefNode(node)
2501
+
2502
+ def visit_GeneratorBodyDefNode(self, node):
2503
+ return self.visit_FuncDefNode(node)
2504
+
2505
+ def visit_GeneratorDefNode(self, node):
2506
+ # The generator body should use the same code object as the (user facing) generator function that creates it.
2507
+ result = self.visit_DefNode(node)
2508
+ # 'result' will usually be a list of statements, but we still have the original node.
2509
+ node.gbody.code_object = node.code_object
2510
+ return result
2511
+
2512
+ def _synthesize_assignment(self, node, env):
2513
+ # Synthesize assignment node and put it right after defnode
2514
+ genv = env
2515
+ while genv.is_py_class_scope or genv.is_c_class_scope:
2516
+ genv = genv.outer_scope
2517
+
2518
+ binding = env.is_py_class_scope or self.current_directives.get('binding')
2519
+ if genv.is_closure_scope:
2520
+ rhs = node.py_cfunc_node = ExprNodes.InnerFunctionNode.from_defnode(node, binding)
2521
+ else:
2522
+ rhs = ExprNodes.PyCFunctionNode.from_defnode(node, binding)
2523
+
2524
+ node.is_cyfunction = rhs.binding
2525
+ return self._create_assignment(node, rhs, env)
2526
+
2527
+ def _create_assignment(self, def_node, rhs, env):
2528
+ if def_node.decorators:
2529
+ for decorator in def_node.decorators[::-1]:
2530
+ rhs = ExprNodes.SimpleCallNode(
2531
+ decorator.pos,
2532
+ function = decorator.decorator,
2533
+ args = [rhs])
2534
+ def_node.decorators = None
2535
+
2536
+ assmt = Nodes.SingleAssignmentNode(
2537
+ def_node.pos,
2538
+ lhs=ExprNodes.NameNode(def_node.pos, name=def_node.name),
2539
+ rhs=rhs)
2540
+ assmt.analyse_declarations(env)
2541
+ return assmt
2542
+
2543
+ def visit_func_outer_attrs(self, node):
2544
+ # any names in the outer attrs should not be looked up in the function "seen_vars_stack"
2545
+ stack = self.seen_vars_stack.pop()
2546
+ super().visit_func_outer_attrs(node)
2547
+ self.seen_vars_stack.append(stack)
2548
+
2549
+ def visit_ScopedExprNode(self, node):
2550
+ env = self.current_env()
2551
+ node.analyse_declarations(env)
2552
+ # the node may or may not have a local scope
2553
+ if node.expr_scope:
2554
+ self.seen_vars_stack.append(set(self.seen_vars_stack[-1]))
2555
+ self.enter_scope(node, node.expr_scope)
2556
+ node.analyse_scoped_declarations(node.expr_scope)
2557
+ self.visitchildren(node)
2558
+ self.exit_scope()
2559
+ self.seen_vars_stack.pop()
2560
+ else:
2561
+
2562
+ node.analyse_scoped_declarations(env)
2563
+ self.visitchildren(node)
2564
+ return node
2565
+
2566
+ def visit_TempResultFromStatNode(self, node):
2567
+ self.visitchildren(node)
2568
+ node.analyse_declarations(self.current_env())
2569
+ return node
2570
+
2571
+ def visit_CppClassNode(self, node):
2572
+ if node.visibility == 'extern':
2573
+ return None
2574
+ else:
2575
+ return self.visit_ClassDefNode(node)
2576
+
2577
+ def visit_CStructOrUnionDefNode(self, node):
2578
+ # Create a wrapper node if needed.
2579
+ # We want to use the struct type information (so it can't happen
2580
+ # before this phase) but also create new objects to be declared
2581
+ # (so it can't happen later).
2582
+ # Note that we don't return the original node, as it is
2583
+ # never used after this phase.
2584
+ if True: # private (default)
2585
+ return None
2586
+
2587
+ self_value = ExprNodes.AttributeNode(
2588
+ pos = node.pos,
2589
+ obj = ExprNodes.NameNode(pos=node.pos, name="self"),
2590
+ attribute = EncodedString("value"))
2591
+ var_entries = node.entry.type.scope.var_entries
2592
+ attributes = []
2593
+ for entry in var_entries:
2594
+ attributes.append(ExprNodes.AttributeNode(pos = entry.pos,
2595
+ obj = self_value,
2596
+ attribute = entry.name))
2597
+ # __init__ assignments
2598
+ init_assignments = []
2599
+ for entry, attr in zip(var_entries, attributes):
2600
+ # TODO: branch on visibility
2601
+ init_assignments.append(
2602
+ self.init_assignment.substitute(
2603
+ {
2604
+ "VALUE": ExprNodes.NameNode(entry.pos, name = entry.name),
2605
+ "ATTR": attr,
2606
+ },
2607
+ pos=entry.pos,
2608
+ )
2609
+ )
2610
+
2611
+ # create the class
2612
+ str_format = "%s(%s)" % (node.entry.type.name, ("%s, " * len(attributes))[:-2])
2613
+ wrapper_class = self.struct_or_union_wrapper.substitute({
2614
+ "INIT_ASSIGNMENTS": Nodes.StatListNode(node.pos, stats = init_assignments),
2615
+ "IS_UNION": ExprNodes.BoolNode(node.pos, value = not node.entry.type.is_struct),
2616
+ "MEMBER_TUPLE": ExprNodes.TupleNode(node.pos, args=attributes),
2617
+ "STR_FORMAT": ExprNodes.UnicodeNode(node.pos, value = EncodedString(str_format)),
2618
+ "REPR_FORMAT": ExprNodes.UnicodeNode(node.pos, value = EncodedString(str_format.replace("%s", "%r"))),
2619
+ }, pos = node.pos).stats[0]
2620
+ wrapper_class.class_name = node.name
2621
+ wrapper_class.shadow = True
2622
+ class_body = wrapper_class.body.stats
2623
+
2624
+ # fix value type
2625
+ assert isinstance(class_body[0].base_type, Nodes.CSimpleBaseTypeNode)
2626
+ class_body[0].base_type.name = node.name
2627
+
2628
+ # fix __init__ arguments
2629
+ init_method = class_body[1]
2630
+ assert isinstance(init_method, Nodes.DefNode) and init_method.name == '__init__'
2631
+ arg_template = init_method.args[1]
2632
+ if not node.entry.type.is_struct:
2633
+ arg_template.kw_only = True
2634
+ del init_method.args[1]
2635
+ for entry, attr in zip(var_entries, attributes):
2636
+ arg = copy.deepcopy(arg_template)
2637
+ arg.declarator.name = entry.name
2638
+ init_method.args.append(arg)
2639
+
2640
+ # setters/getters
2641
+ for entry, attr in zip(var_entries, attributes):
2642
+ # TODO: branch on visibility
2643
+ if entry.type.is_pyobject:
2644
+ template = self.basic_pyobject_property
2645
+ else:
2646
+ template = self.basic_property
2647
+ property = template.substitute(
2648
+ {
2649
+ "ATTR": attr,
2650
+ },
2651
+ pos=entry.pos,
2652
+ ).stats[0]
2653
+ property.name = entry.name
2654
+ wrapper_class.body.stats.append(property)
2655
+
2656
+ wrapper_class.analyse_declarations(self.current_env())
2657
+ return self.visit_CClassDefNode(wrapper_class)
2658
+
2659
+ # Some nodes are no longer needed after declaration
2660
+ # analysis and can be dropped. The analysis was performed
2661
+ # on these nodes in a separate recursive process from the
2662
+ # enclosing function or module, so we can simply drop them.
2663
+ def visit_CDeclaratorNode(self, node):
2664
+ # necessary to ensure that all CNameDeclaratorNodes are visited.
2665
+ self.visitchildren(node)
2666
+ return node
2667
+
2668
+ def visit_CTypeDefNode(self, node):
2669
+ return node
2670
+
2671
+ def visit_CBaseTypeNode(self, node):
2672
+ return None
2673
+
2674
+ def visit_CEnumDefNode(self, node):
2675
+ if node.visibility == 'public':
2676
+ return node
2677
+ else:
2678
+ return None
2679
+
2680
+ def visit_CNameDeclaratorNode(self, node):
2681
+ if node.name in self.seen_vars_stack[-1]:
2682
+ entry = self.current_env().lookup(node.name)
2683
+ if (entry is None or entry.visibility != 'extern'
2684
+ and not entry.scope.is_c_class_scope):
2685
+ error(node.pos, "cdef variable '%s' declared after it is used" % node.name)
2686
+ self.visitchildren(node)
2687
+ return node
2688
+
2689
+ def visit_CVarDefNode(self, node):
2690
+ # to ensure all CNameDeclaratorNodes are visited.
2691
+ self.visitchildren(node)
2692
+ return None
2693
+
2694
+ def visit_CnameDecoratorNode(self, node):
2695
+ child_node = self.visitchild(node, 'node')
2696
+ if not child_node:
2697
+ return None
2698
+ if type(child_node) is list: # Assignment synthesized
2699
+ node.node = child_node[0]
2700
+ return [node] + child_node[1:]
2701
+ return node
2702
+
2703
+ def create_Property(self, entry):
2704
+ if entry.visibility == 'public':
2705
+ if entry.type.is_pyobject:
2706
+ template = self.basic_pyobject_property
2707
+ else:
2708
+ template = self.basic_property
2709
+ elif entry.visibility == 'readonly':
2710
+ template = self.basic_property_ro
2711
+ property = template.substitute(
2712
+ {
2713
+ "ATTR": ExprNodes.AttributeNode(pos=entry.pos,
2714
+ obj=ExprNodes.NameNode(pos=entry.pos, name="self"),
2715
+ attribute=entry.name),
2716
+ },
2717
+ pos=entry.pos,
2718
+ ).stats[0]
2719
+ property.name = entry.name
2720
+ property.doc = entry.doc
2721
+ return property
2722
+
2723
+ def visit_AssignmentExpressionNode(self, node):
2724
+ self.visitchildren(node)
2725
+ node.analyse_declarations(self.current_env())
2726
+ return node
2727
+
2728
+
2729
+ def _calculate_pickle_checksums(member_names):
2730
+ # Cython 0.x used MD5 for the checksum, which a few Python installations remove for security reasons.
2731
+ # SHA-256 should be ok for years to come, but early Cython 3.0 alpha releases used SHA-1,
2732
+ # which may not be.
2733
+ member_names_string = ' '.join(member_names).encode('utf-8')
2734
+ hash_kwargs = {'usedforsecurity': False} if sys.version_info >= (3, 9) else {}
2735
+ checksums = []
2736
+ for algo_name in ['sha256', 'sha1', 'md5']:
2737
+ try:
2738
+ mkchecksum = getattr(hashlib, algo_name)
2739
+ checksum = mkchecksum(member_names_string, **hash_kwargs).hexdigest()
2740
+ except (AttributeError, ValueError):
2741
+ # The algorithm (i.e. MD5) might not be there at all, or might be blocked at runtime.
2742
+ continue
2743
+ checksums.append('0x' + checksum[:7])
2744
+ return checksums
2745
+
2746
+
2747
+ class CalculateQualifiedNamesTransform(EnvTransform):
2748
+ """
2749
+ Calculate and store the '__qualname__' and the global
2750
+ module name on some nodes.
2751
+ """
2752
+ needs_qualname_assignment = False
2753
+ needs_module_assignment = False
2754
+
2755
+ def visit_ModuleNode(self, node):
2756
+ self.module_name = self.global_scope().qualified_name
2757
+ self.qualified_name = []
2758
+ _super = super()
2759
+ self._super_visit_FuncDefNode = _super.visit_FuncDefNode
2760
+ self._super_visit_ClassDefNode = _super.visit_ClassDefNode
2761
+ self.visitchildren(node)
2762
+ return node
2763
+
2764
+ def _set_qualname(self, node, name=None):
2765
+ if name:
2766
+ qualname = self.qualified_name[:]
2767
+ qualname.append(name)
2768
+ else:
2769
+ qualname = self.qualified_name
2770
+ node.qualname = EncodedString('.'.join(qualname))
2771
+ node.module_name = self.module_name
2772
+
2773
+ def _append_entry(self, entry):
2774
+ if entry.is_pyglobal and not entry.is_pyclass_attr:
2775
+ self.qualified_name = [entry.name]
2776
+ else:
2777
+ self.qualified_name.append(entry.name)
2778
+
2779
+ def visit_ClassNode(self, node):
2780
+ self._set_qualname(node, node.name)
2781
+ self.visitchildren(node)
2782
+ return node
2783
+
2784
+ def visit_PyClassNamespaceNode(self, node):
2785
+ # class name was already added by parent node
2786
+ self._set_qualname(node)
2787
+ self.visitchildren(node)
2788
+ return node
2789
+
2790
+ def visit_PyCFunctionNode(self, node):
2791
+ orig_qualified_name = self.qualified_name[:]
2792
+ if node.def_node.is_wrapper and self.qualified_name and self.qualified_name[-1] == '<locals>':
2793
+ self.qualified_name.pop()
2794
+ self._set_qualname(node)
2795
+ else:
2796
+ self._set_qualname(node, node.def_node.name)
2797
+ self.visitchildren(node)
2798
+ self.qualified_name = orig_qualified_name
2799
+ return node
2800
+
2801
+ def visit_DefNode(self, node):
2802
+ if node.is_wrapper and self.qualified_name:
2803
+ assert self.qualified_name[-1] == '<locals>', self.qualified_name
2804
+ orig_qualified_name = self.qualified_name[:]
2805
+ self.qualified_name.pop()
2806
+ self._set_qualname(node)
2807
+ self._super_visit_FuncDefNode(node)
2808
+ self.qualified_name = orig_qualified_name
2809
+ else:
2810
+ self._set_qualname(node, node.name)
2811
+ self.visit_FuncDefNode(node)
2812
+ return node
2813
+
2814
+ def visit_FuncDefNode(self, node):
2815
+ orig_qualified_name = self.qualified_name[:]
2816
+ if getattr(node, 'name', None) == '<lambda>':
2817
+ self.qualified_name.append('<lambda>')
2818
+ else:
2819
+ self._append_entry(node.entry)
2820
+ self.qualified_name.append('<locals>')
2821
+ self._super_visit_FuncDefNode(node)
2822
+ self.qualified_name = orig_qualified_name
2823
+ return node
2824
+
2825
+ def generate_assignment(self, node, name, value):
2826
+ entry = node.scope.lookup_here(name)
2827
+ lhs = ExprNodes.NameNode(
2828
+ node.pos,
2829
+ name=EncodedString(name),
2830
+ entry=entry,
2831
+ is_target=True)
2832
+ rhs = ExprNodes.UnicodeNode(node.pos, value=value)
2833
+ node.body.stats.insert(0, Nodes.SingleAssignmentNode(
2834
+ node.pos,
2835
+ lhs=lhs,
2836
+ rhs=rhs,
2837
+ ).analyse_expressions(self.current_env()))
2838
+
2839
+ def visit_ClassDefNode(self, node):
2840
+ orig_needs_qualname_assignment = self.needs_qualname_assignment
2841
+ self.needs_qualname_assignment = False
2842
+ orig_needs_module_assignment = self.needs_module_assignment
2843
+ self.needs_module_assignment = False
2844
+ orig_qualified_name = self.qualified_name[:]
2845
+ entry = (getattr(node, 'entry', None) or # PyClass
2846
+ self.current_env().lookup_here(node.target.name)) # CClass
2847
+ self._append_entry(entry)
2848
+ self._super_visit_ClassDefNode(node)
2849
+ if self.needs_qualname_assignment:
2850
+ self.generate_assignment(node, "__qualname__",
2851
+ EncodedString(".".join(self.qualified_name)))
2852
+ if self.needs_module_assignment:
2853
+ self.generate_assignment(node, "__module__",
2854
+ EncodedString(self.module_name))
2855
+ self.qualified_name = orig_qualified_name
2856
+ self.needs_qualname_assignment = orig_needs_qualname_assignment
2857
+ self.needs_module_assignment = orig_needs_module_assignment
2858
+ return node
2859
+
2860
+ def visit_NameNode(self, node):
2861
+ scope = self.current_env()
2862
+ if scope.is_c_class_scope:
2863
+ # unlike for a PyClass scope, these attributes aren't defined in the
2864
+ # dictionary when the class definition is executed, therefore we ask
2865
+ # the compiler to generate an assignment to them at the start of the
2866
+ # body.
2867
+ # NOTE: this doesn't put them in locals()
2868
+ if node.name == "__qualname__":
2869
+ self.needs_qualname_assignment = True
2870
+ elif node.name == "__module__":
2871
+ self.needs_module_assignment = True
2872
+ return node
2873
+
2874
+
2875
+ class AnalyseExpressionsTransform(CythonTransform):
2876
+
2877
+ def visit_ModuleNode(self, node):
2878
+ node.scope.infer_types()
2879
+ node.body = node.body.analyse_expressions(node.scope)
2880
+ self.positions = [{node.pos}]
2881
+ self.visitchildren(node)
2882
+ self._build_positions(node)
2883
+ return node
2884
+
2885
+ def visit_FuncDefNode(self, node):
2886
+ node.local_scope.infer_types()
2887
+ node.body = node.body.analyse_expressions(node.local_scope)
2888
+ self.positions[-1].add(node.pos)
2889
+
2890
+ if node.is_wrapper:
2891
+ # Share positions between function and Python wrapper.
2892
+ local_positions = self.positions[-1]
2893
+ else:
2894
+ local_positions = {node.pos}
2895
+ self.positions.append(local_positions)
2896
+
2897
+ self.visitchildren(node)
2898
+ self._build_positions(node)
2899
+ return node
2900
+
2901
+ def visit_ScopedExprNode(self, node):
2902
+ if node.has_local_scope:
2903
+ node.expr_scope.infer_types()
2904
+ node = node.analyse_scoped_expressions(node.expr_scope)
2905
+ self.visit_ExprNode(node)
2906
+ return node
2907
+
2908
+ def visit_IndexNode(self, node):
2909
+ """
2910
+ Replace index nodes used to specialize cdef functions with fused
2911
+ argument types with the Attribute- or NameNode referring to the
2912
+ function. We then need to copy over the specialization properties to
2913
+ the attribute or name node.
2914
+
2915
+ Because the indexing might be a Python indexing operation on a fused
2916
+ function, or (usually) a Cython indexing operation, we need to
2917
+ re-analyse the types.
2918
+ """
2919
+ self.visit_ExprNode(node)
2920
+ if node.is_fused_index and not node.type.is_error:
2921
+ node = node.base
2922
+ return node
2923
+
2924
+ # Build the line table according to PEP-626.
2925
+ # We mostly just do this here to avoid yet another transform traversal.
2926
+
2927
+ def visit_ExprNode(self, node):
2928
+ self.positions[-1].add(node.pos)
2929
+ self.visitchildren(node)
2930
+ return node
2931
+
2932
+ def visit_StatNode(self, node):
2933
+ self.positions[-1].add(node.pos)
2934
+ self.visitchildren(node)
2935
+ return node
2936
+
2937
+ def _build_positions(self, func_node):
2938
+ """
2939
+ Build the PEP-626 line table and "bytecode-to-position" mapping used for CodeObjects.
2940
+ """
2941
+ # Code can originate from different source files and string code fragments, even within a single function.
2942
+ # Thus, it's not completely correct to just ignore the source files when sorting the line numbers,
2943
+ # but it also doesn't hurt much for the moment. Eventually, we might need different CodeObjects
2944
+ # even within a single function if it uses code from different sources / line number ranges.
2945
+ positions: list = sorted(
2946
+ self.positions.pop(),
2947
+ key=itemgetter(1, 2), # (line, column)
2948
+ # Build ranges backwards to know the end column before we see the start column in the same line.
2949
+ reverse=True,
2950
+ )
2951
+
2952
+ next_line = -1
2953
+ next_column_in_line = 0
2954
+
2955
+ ranges = []
2956
+ for _, line, start_column in positions:
2957
+ ranges.append((line, line, start_column, next_column_in_line if line == next_line else start_column + 1))
2958
+ next_line, next_column_in_line = line, start_column
2959
+
2960
+ ranges.reverse()
2961
+ func_node.node_positions = ranges
2962
+
2963
+ positions.reverse()
2964
+ i: cython.Py_ssize_t
2965
+ func_node.local_scope.node_positions_to_offset = {
2966
+ position: i
2967
+ for i, position in enumerate(positions)
2968
+ }
2969
+
2970
+
2971
+ class FindInvalidUseOfFusedTypes(TreeVisitor):
2972
+
2973
+ def __call__(self, tree):
2974
+ self._in_fused_function = False
2975
+ self.visit(tree)
2976
+ return tree
2977
+
2978
+ def visit_Node(self, node):
2979
+ self.visitchildren(node)
2980
+
2981
+ def visit_FuncDefNode(self, node):
2982
+ outer_status = self._in_fused_function
2983
+ self._in_fused_function = node.has_fused_arguments
2984
+
2985
+ if not self._in_fused_function:
2986
+ # Errors related to use in functions with fused args will already
2987
+ # have been detected.
2988
+ if not node.is_generator_body and node.return_type.is_fused:
2989
+ error(node.pos, "Return type is not specified as argument type")
2990
+
2991
+ self.visitchildren(node)
2992
+ self._in_fused_function = outer_status
2993
+
2994
+ def visit_ExprNode(self, node):
2995
+ if not self._in_fused_function and node.type and node.type.is_fused:
2996
+ error(node.pos, "Invalid use of fused types, type cannot be specialized")
2997
+ # Errors in subtrees are likely related, so do not recurse.
2998
+ else:
2999
+ self.visitchildren(node)
3000
+
3001
+
3002
+ class ExpandInplaceOperators(EnvTransform):
3003
+
3004
+ def visit_InPlaceAssignmentNode(self, node):
3005
+ lhs = node.lhs
3006
+ rhs = node.rhs
3007
+ if lhs.type.is_cpp_class:
3008
+ # No getting around this exact operator here.
3009
+ return node
3010
+ if isinstance(lhs, ExprNodes.BufferIndexNode):
3011
+ # There is code to handle this case in InPlaceAssignmentNode
3012
+ return node
3013
+
3014
+ env = self.current_env()
3015
+ def side_effect_free_reference(node, setting=False):
3016
+ if node.is_name:
3017
+ return node, []
3018
+ elif node.type.is_pyobject and not setting:
3019
+ node = LetRefNode(node)
3020
+ return node, [node]
3021
+ elif node.is_subscript:
3022
+ base, temps = side_effect_free_reference(node.base)
3023
+ index = LetRefNode(node.index)
3024
+ return ExprNodes.IndexNode(node.pos, base=base, index=index), temps + [index]
3025
+ elif node.is_attribute:
3026
+ obj, temps = side_effect_free_reference(node.obj, setting=setting)
3027
+ return ExprNodes.AttributeNode(node.pos, obj=obj, attribute=node.attribute), temps
3028
+ elif isinstance(node, ExprNodes.BufferIndexNode):
3029
+ raise ValueError("Don't allow things like attributes of buffer indexing operations")
3030
+ else:
3031
+ node = LetRefNode(node)
3032
+ return node, [node]
3033
+ try:
3034
+ lhs, let_ref_nodes = side_effect_free_reference(lhs, setting=True)
3035
+ except ValueError:
3036
+ return node
3037
+ dup = lhs.__class__(**lhs.__dict__)
3038
+ binop = ExprNodes.binop_node(node.pos,
3039
+ operator = node.operator,
3040
+ operand1 = dup,
3041
+ operand2 = rhs,
3042
+ inplace=True)
3043
+ # Manually analyse types for new node.
3044
+ lhs.is_target = True
3045
+ lhs = lhs.analyse_target_types(env)
3046
+ dup.analyse_types(env) # FIXME: no need to reanalyse the copy, right?
3047
+ binop.analyse_operation(env)
3048
+ node = Nodes.SingleAssignmentNode(
3049
+ node.pos,
3050
+ lhs = lhs,
3051
+ rhs=binop.coerce_to(lhs.type, env))
3052
+ # Use LetRefNode to avoid side effects.
3053
+ let_ref_nodes.reverse()
3054
+ for t in let_ref_nodes:
3055
+ node = LetNode(t, node)
3056
+ return node
3057
+
3058
+ def visit_ExprNode(self, node):
3059
+ # In-place assignments can't happen within an expression.
3060
+ return node
3061
+
3062
+
3063
+ class AdjustDefByDirectives(CythonTransform, SkipDeclarations):
3064
+ """
3065
+ Adjust function and class definitions by the decorator directives:
3066
+
3067
+ @cython.cfunc
3068
+ @cython.cclass
3069
+ @cython.ccall
3070
+ @cython.inline
3071
+ @cython.nogil
3072
+ @cython.critical_section
3073
+ """
3074
+ # list of directives that cause conversion to cclass
3075
+ converts_to_cclass = ('cclass', 'total_ordering', 'dataclasses.dataclass')
3076
+
3077
+ def visit_ModuleNode(self, node):
3078
+ self.directives = node.directives
3079
+ self.in_py_class = False
3080
+ self.visitchildren(node)
3081
+ return node
3082
+
3083
+ def visit_CompilerDirectivesNode(self, node):
3084
+ old_directives = self.directives
3085
+ self.directives = node.directives
3086
+ self.visitchildren(node)
3087
+ self.directives = old_directives
3088
+ return node
3089
+
3090
+ def visit_DefNode(self, node):
3091
+ modifiers = []
3092
+ if 'inline' in self.directives:
3093
+ modifiers.append('inline')
3094
+ nogil = self.directives.get('nogil')
3095
+ with_gil = self.directives.get('with_gil')
3096
+ except_val = self.directives.get('exceptval')
3097
+ has_explicit_exc_clause = False if except_val is None else True
3098
+ return_type_node = self.directives.get('returns')
3099
+ if return_type_node is None and self.directives['annotation_typing']:
3100
+ return_type_node = node.return_type_annotation
3101
+ # for Python annotations, prefer safe exception handling by default
3102
+ if return_type_node is not None and except_val is None:
3103
+ except_val = (None, True) # except *
3104
+ elif except_val is None:
3105
+ # backward compatible default: no exception check, unless there's also a "@returns" declaration
3106
+ except_val = (None, True if return_type_node else False)
3107
+ if self.directives.get('c_compile_guard') and 'cfunc' not in self.directives:
3108
+ error(node.pos, "c_compile_guard only allowed on C functions")
3109
+ if 'ccall' in self.directives:
3110
+ if 'cfunc' in self.directives:
3111
+ error(node.pos, "cfunc and ccall directives cannot be combined")
3112
+ if with_gil:
3113
+ error(node.pos, "ccall functions cannot be declared 'with_gil'")
3114
+ node = node.as_cfunction(
3115
+ overridable=True, modifiers=modifiers, nogil=nogil,
3116
+ returns=return_type_node, except_val=except_val, has_explicit_exc_clause=has_explicit_exc_clause)
3117
+ return self.visit(node)
3118
+ if 'cfunc' in self.directives:
3119
+ if self.in_py_class:
3120
+ error(node.pos, "cfunc directive is not allowed here")
3121
+ else:
3122
+ node = node.as_cfunction(
3123
+ overridable=False, modifiers=modifiers, nogil=nogil, with_gil=with_gil,
3124
+ returns=return_type_node, except_val=except_val, has_explicit_exc_clause=has_explicit_exc_clause)
3125
+ return self.visit(node)
3126
+ if 'inline' in modifiers:
3127
+ error(node.pos, "Python functions cannot be declared 'inline'")
3128
+ if nogil:
3129
+ # TODO: turn this into a "with gil" declaration.
3130
+ error(node.pos, "Python functions cannot be declared 'nogil'")
3131
+ if with_gil:
3132
+ error(node.pos, "Python functions cannot be declared 'with_gil'")
3133
+ self.visit_FuncDefNode(node)
3134
+ return node
3135
+
3136
+ def visit_FuncDefNode(self, node):
3137
+ if "critical_section" in self.directives:
3138
+ value = self.directives["critical_section"]
3139
+ if value is not None:
3140
+ error(node.pos, "critical_section decorator does not take arguments")
3141
+ new_body = Nodes.CriticalSectionStatNode(
3142
+ node.pos,
3143
+ args=[ExprNodes.FirstArgumentForCriticalSectionNode(node.pos, func_node=node)],
3144
+ body=node.body
3145
+ )
3146
+ node.body = new_body
3147
+ self.visitchildren(node)
3148
+ return node
3149
+
3150
+ def visit_LambdaNode(self, node):
3151
+ # No directives should modify lambdas or generator expressions (and also nothing in them).
3152
+ return node
3153
+
3154
+ def visit_PyClassDefNode(self, node):
3155
+ if any(directive in self.directives for directive in self.converts_to_cclass):
3156
+ node = node.as_cclass()
3157
+ return self.visit(node)
3158
+ else:
3159
+ old_in_pyclass = self.in_py_class
3160
+ self.in_py_class = True
3161
+ self.visitchildren(node)
3162
+ self.in_py_class = old_in_pyclass
3163
+ return node
3164
+
3165
+ def visit_CClassDefNode(self, node):
3166
+ old_in_pyclass = self.in_py_class
3167
+ self.in_py_class = False
3168
+ self.visitchildren(node)
3169
+ self.in_py_class = old_in_pyclass
3170
+ return node
3171
+
3172
+
3173
+ class AlignFunctionDefinitions(CythonTransform):
3174
+ """
3175
+ This class takes the signatures from a .pxd file and applies them to
3176
+ the def methods in a .py file.
3177
+ """
3178
+
3179
+ def visit_ModuleNode(self, node):
3180
+ self.scope = node.scope
3181
+ self.visitchildren(node)
3182
+ return node
3183
+
3184
+ def visit_PyClassDefNode(self, node):
3185
+ pxd_def = self.scope.lookup(node.name)
3186
+ if pxd_def:
3187
+ if pxd_def.is_cclass:
3188
+ return self.visit_CClassDefNode(node.as_cclass(), pxd_def)
3189
+ elif not pxd_def.scope or not pxd_def.scope.is_builtin_scope:
3190
+ error(node.pos, "'%s' redeclared" % node.name)
3191
+ if pxd_def.pos:
3192
+ error(pxd_def.pos, "previous declaration here")
3193
+ return None
3194
+ return node
3195
+
3196
+ def visit_CClassDefNode(self, node, pxd_def=None):
3197
+ if pxd_def is None:
3198
+ pxd_def = self.scope.lookup(node.class_name)
3199
+ if pxd_def:
3200
+ if not pxd_def.defined_in_pxd:
3201
+ return node
3202
+ outer_scope = self.scope
3203
+ self.scope = pxd_def.type.scope
3204
+ self.visitchildren(node)
3205
+ if pxd_def:
3206
+ self.scope = outer_scope
3207
+ return node
3208
+
3209
+ def visit_DefNode(self, node):
3210
+ pxd_def = self.scope.lookup(node.name)
3211
+ if pxd_def and (not pxd_def.scope or not pxd_def.scope.is_builtin_scope):
3212
+ if not pxd_def.is_cfunction:
3213
+ error(node.pos, "'%s' redeclared" % node.name)
3214
+ if pxd_def.pos:
3215
+ error(pxd_def.pos, "previous declaration here")
3216
+ return None
3217
+ node = node.as_cfunction(pxd_def)
3218
+ # Enable this when nested cdef functions are allowed.
3219
+ # self.visitchildren(node)
3220
+ return node
3221
+
3222
+ def visit_ExprNode(self, node):
3223
+ # ignore lambdas and everything else that appears in expressions
3224
+ return node
3225
+
3226
+
3227
+ class AutoCpdefFunctionDefinitions(CythonTransform):
3228
+
3229
+ def visit_ModuleNode(self, node):
3230
+ self.directives = node.directives
3231
+ self.imported_names = set() # hack, see visit_FromImportStatNode()
3232
+ self.scope = node.scope
3233
+ self.visitchildren(node)
3234
+ return node
3235
+
3236
+ def visit_DefNode(self, node):
3237
+ if (self.scope.is_module_scope and self.directives['auto_cpdef']
3238
+ and node.name not in self.imported_names
3239
+ and node.is_cdef_func_compatible()):
3240
+ # FIXME: cpdef-ing should be done in analyse_declarations()
3241
+ node = node.as_cfunction(scope=self.scope)
3242
+ return node
3243
+
3244
+ def visit_CClassDefNode(self, node, pxd_def=None):
3245
+ if pxd_def is None:
3246
+ pxd_def = self.scope.lookup(node.class_name)
3247
+ if pxd_def:
3248
+ if not pxd_def.defined_in_pxd:
3249
+ return node
3250
+ outer_scope = self.scope
3251
+ self.scope = pxd_def.type.scope
3252
+ self.visitchildren(node)
3253
+ if pxd_def:
3254
+ self.scope = outer_scope
3255
+ return node
3256
+
3257
+ def visit_FromImportStatNode(self, node):
3258
+ # hack to prevent conditional import fallback functions from
3259
+ # being cdpef-ed (global Python variables currently conflict
3260
+ # with imports)
3261
+ if self.scope.is_module_scope:
3262
+ for name, _ in node.items:
3263
+ self.imported_names.add(name)
3264
+ return node
3265
+
3266
+ def visit_ExprNode(self, node):
3267
+ # ignore lambdas and everything else that appears in expressions
3268
+ return node
3269
+
3270
+
3271
+ class RemoveUnreachableCode(CythonTransform):
3272
+
3273
+ def visit_StatListNode(self, node):
3274
+ if not self.current_directives['remove_unreachable']:
3275
+ return node
3276
+ self.visitchildren(node)
3277
+ if len(node.stats) == 1 and isinstance(node.stats[0], Nodes.StatListNode) and not node.stats[0].stats:
3278
+ del node.stats[:]
3279
+ for idx, stat in enumerate(node.stats, 1):
3280
+ if stat.is_terminator:
3281
+ if idx < len(node.stats):
3282
+ if self.current_directives['warn.unreachable']:
3283
+ warning(node.stats[idx].pos, "Unreachable code", 2)
3284
+ node.stats = node.stats[:idx]
3285
+ node.is_terminator = True
3286
+ break
3287
+ return node
3288
+
3289
+ def visit_IfClauseNode(self, node):
3290
+ self.visitchildren(node)
3291
+ if node.body.is_terminator:
3292
+ node.is_terminator = True
3293
+ return node
3294
+
3295
+ def visit_IfStatNode(self, node):
3296
+ self.visitchildren(node)
3297
+ if node.else_clause and node.else_clause.is_terminator:
3298
+ for clause in node.if_clauses:
3299
+ if not clause.is_terminator:
3300
+ break
3301
+ else:
3302
+ node.is_terminator = True
3303
+ return node
3304
+
3305
+ def visit_TryExceptStatNode(self, node):
3306
+ self.visitchildren(node)
3307
+ if node.body.is_terminator and node.else_clause:
3308
+ if self.current_directives['warn.unreachable']:
3309
+ warning(node.else_clause.pos, "Unreachable code", 2)
3310
+ node.else_clause = None
3311
+ return node
3312
+
3313
+ def visit_TryFinallyStatNode(self, node):
3314
+ self.visitchildren(node)
3315
+ if node.finally_clause.is_terminator:
3316
+ node.is_terminator = True
3317
+ return node
3318
+
3319
+ def visit_PassStatNode(self, node):
3320
+ """Eliminate useless PassStatNode"""
3321
+ # 'pass' statements often appear in a separate line and must be traced.
3322
+ if not self.current_directives['linetrace']:
3323
+ node = Nodes.StatListNode(pos=node.pos, stats=[])
3324
+ return node
3325
+
3326
+
3327
+ class YieldNodeCollector(TreeVisitor):
3328
+
3329
+ def __init__(self, excludes=[]):
3330
+ super().__init__()
3331
+ self.yields = []
3332
+ self.returns = []
3333
+ self.finallys = []
3334
+ self.excepts = []
3335
+ self.has_return_value = False
3336
+ self.has_yield = False
3337
+ self.has_await = False
3338
+ self.excludes = excludes
3339
+
3340
+ def visit_Node(self, node):
3341
+ if node not in self.excludes:
3342
+ self.visitchildren(node)
3343
+
3344
+ def visit_YieldExprNode(self, node):
3345
+ self.yields.append(node)
3346
+ self.has_yield = True
3347
+ self.visitchildren(node)
3348
+
3349
+ def visit_AwaitExprNode(self, node):
3350
+ self.yields.append(node)
3351
+ self.has_await = True
3352
+ self.visitchildren(node)
3353
+
3354
+ def visit_ReturnStatNode(self, node):
3355
+ self.visitchildren(node)
3356
+ if node.value:
3357
+ self.has_return_value = True
3358
+ self.returns.append(node)
3359
+
3360
+ def visit_TryFinallyStatNode(self, node):
3361
+ self.visitchildren(node)
3362
+ self.finallys.append(node)
3363
+
3364
+ def visit_TryExceptStatNode(self, node):
3365
+ self.visitchildren(node)
3366
+ self.excepts.append(node)
3367
+
3368
+ def visit_ClassDefNode(self, node):
3369
+ pass
3370
+
3371
+ def visit_FuncDefNode(self, node):
3372
+ pass
3373
+
3374
+ def visit_LambdaNode(self, node):
3375
+ pass
3376
+
3377
+ def visit_GeneratorExpressionNode(self, node):
3378
+ # node.loop iterator is evaluated outside the generator expression
3379
+ if isinstance(node.loop, Nodes._ForInStatNode):
3380
+ # Possibly should handle ForFromStatNode
3381
+ # but for now do nothing
3382
+ self.visit(node.loop.iterator)
3383
+
3384
+ def visit_CArgDeclNode(self, node):
3385
+ # do not look into annotations
3386
+ # FIXME: support (yield) in default arguments (currently crashes)
3387
+ pass
3388
+
3389
+
3390
+ class MarkClosureVisitor(CythonTransform):
3391
+ # In addition to marking closures this is also responsible to finding parts of the
3392
+ # generator iterable and marking them
3393
+
3394
+ def visit_ModuleNode(self, node):
3395
+ self.needs_closure = False
3396
+ self.excludes = []
3397
+ self.visitchildren(node)
3398
+ return node
3399
+
3400
+ def visit_FuncDefNode(self, node):
3401
+ self.needs_closure = False
3402
+ self.visitchildren(node)
3403
+ node.needs_closure = self.needs_closure
3404
+ self.needs_closure = True
3405
+
3406
+ collector = YieldNodeCollector(self.excludes)
3407
+ collector.visitchildren(node)
3408
+
3409
+ if node.is_async_def:
3410
+ coroutine_type = Nodes.AsyncDefNode
3411
+ if collector.has_yield:
3412
+ coroutine_type = Nodes.AsyncGenNode
3413
+ for yield_expr in collector.yields + collector.returns:
3414
+ yield_expr.in_async_gen = True
3415
+ elif self.current_directives['iterable_coroutine']:
3416
+ coroutine_type = Nodes.IterableAsyncDefNode
3417
+ elif collector.has_await:
3418
+ found = next(y for y in collector.yields if y.is_await)
3419
+ error(found.pos, "'await' not allowed in generators (use 'yield')")
3420
+ return node
3421
+ elif collector.has_yield:
3422
+ coroutine_type = Nodes.GeneratorDefNode
3423
+ else:
3424
+ return node
3425
+
3426
+ for i, yield_expr in enumerate(collector.yields, 1):
3427
+ yield_expr.label_num = i
3428
+ for retnode in collector.returns + collector.finallys + collector.excepts:
3429
+ retnode.in_generator = True
3430
+
3431
+ gbody = Nodes.GeneratorBodyDefNode(
3432
+ pos=node.pos, name=node.name, body=node.body,
3433
+ is_coroutine_body=node.is_async_def,
3434
+ is_async_gen_body=node.is_async_def and collector.has_yield)
3435
+ coroutine = coroutine_type(
3436
+ pos=node.pos, name=node.name, args=node.args,
3437
+ star_arg=node.star_arg, starstar_arg=node.starstar_arg,
3438
+ doc=node.doc, decorators=node.decorators,
3439
+ gbody=gbody, lambda_name=node.lambda_name,
3440
+ return_type_annotation=node.return_type_annotation,
3441
+ is_generator_expression=node.is_generator_expression)
3442
+ return coroutine
3443
+
3444
+ def visit_CFuncDefNode(self, node):
3445
+ self.needs_closure = False
3446
+ self.visitchildren(node)
3447
+ node.needs_closure = self.needs_closure
3448
+ self.needs_closure = True
3449
+ if node.needs_closure and node.overridable:
3450
+ error(node.pos, "closures inside cpdef functions not yet supported")
3451
+ return node
3452
+
3453
+ def visit_LambdaNode(self, node):
3454
+ self.needs_closure = False
3455
+ self.visitchildren(node)
3456
+ node.needs_closure = self.needs_closure
3457
+ self.needs_closure = True
3458
+ return node
3459
+
3460
+ def visit_ClassDefNode(self, node):
3461
+ self.visitchildren(node)
3462
+ self.needs_closure = True
3463
+ return node
3464
+
3465
+ def visit_GeneratorExpressionNode(self, node):
3466
+ excludes = self.excludes
3467
+ if isinstance(node.loop, Nodes._ForInStatNode):
3468
+ self.excludes = [node.loop.iterator]
3469
+ node = self.visit_LambdaNode(node)
3470
+ self.excludes = excludes
3471
+ if not isinstance(node.loop, Nodes._ForInStatNode):
3472
+ # Possibly should handle ForFromStatNode
3473
+ # but for now do nothing
3474
+ return node
3475
+ itseq = node.loop.iterator.sequence
3476
+ # literals do not need replacing with an argument
3477
+ if itseq.is_literal:
3478
+ return node
3479
+ _GeneratorExpressionArgumentsMarker(node).visit(itseq)
3480
+ return node
3481
+
3482
+
3483
+ class CreateClosureClasses(CythonTransform):
3484
+ # Output closure classes in module scope for all functions
3485
+ # that really need it.
3486
+
3487
+ def __init__(self, context):
3488
+ super().__init__(context)
3489
+ self.path = []
3490
+ self.in_lambda = False
3491
+
3492
+ def visit_ModuleNode(self, node):
3493
+ self.module_scope = node.scope
3494
+ self.visitchildren(node)
3495
+ return node
3496
+
3497
+ def find_entries_used_in_closures(self, node):
3498
+ from_closure = []
3499
+ in_closure = []
3500
+ for scope in node.local_scope.iter_local_scopes():
3501
+ for name, entry in scope.entries.items():
3502
+ if not name:
3503
+ continue
3504
+ if entry.from_closure:
3505
+ from_closure.append((name, entry))
3506
+ elif entry.in_closure:
3507
+ in_closure.append((name, entry))
3508
+ return from_closure, in_closure
3509
+
3510
+ def create_class_from_scope(self, node, target_module_scope, inner_node=None):
3511
+ # move local variables into closure
3512
+ if node.is_generator:
3513
+ for scope in node.local_scope.iter_local_scopes():
3514
+ for entry in scope.entries.values():
3515
+ if not (entry.from_closure or entry.is_pyglobal or entry.is_cglobal):
3516
+ entry.in_closure = True
3517
+
3518
+ from_closure, in_closure = self.find_entries_used_in_closures(node)
3519
+ in_closure.sort()
3520
+
3521
+ # Now from the beginning
3522
+ node.needs_closure = False
3523
+ node.needs_outer_scope = False
3524
+
3525
+ func_scope = node.local_scope
3526
+ cscope = node.entry.scope
3527
+ while cscope.is_py_class_scope or cscope.is_c_class_scope:
3528
+ cscope = cscope.outer_scope
3529
+
3530
+ if not from_closure and (self.path or inner_node):
3531
+ if not inner_node:
3532
+ if not node.py_cfunc_node:
3533
+ raise InternalError("DefNode does not have assignment node")
3534
+ inner_node = node.py_cfunc_node
3535
+ inner_node.needs_closure_code = False
3536
+ node.needs_outer_scope = False
3537
+
3538
+ if node.is_generator:
3539
+ pass
3540
+ elif not in_closure and not from_closure:
3541
+ return
3542
+ elif not in_closure:
3543
+ func_scope.is_passthrough = True
3544
+ func_scope.scope_class = cscope.scope_class
3545
+ node.needs_outer_scope = True
3546
+ return
3547
+
3548
+ # entry.cname can contain periods (eg. a derived C method of a class).
3549
+ # We want to use the cname as part of a C struct name, so we replace
3550
+ # periods with double underscores.
3551
+ as_name = '%s_%s' % (
3552
+ target_module_scope.next_id(Naming.closure_class_prefix),
3553
+ node.entry.cname.replace('.','__'))
3554
+ as_name = EncodedString(as_name)
3555
+
3556
+ entry = target_module_scope.declare_c_class(
3557
+ name=as_name, pos=node.pos, defining=True,
3558
+ implementing=True)
3559
+ entry.type.is_final_type = True
3560
+
3561
+ func_scope.scope_class = entry
3562
+ class_scope = entry.type.scope
3563
+ class_scope.is_internal = True
3564
+ class_scope.is_closure_class_scope = True
3565
+ if node.is_async_def or node.is_generator:
3566
+ # Generators need their closure intact during cleanup as they resume to handle GeneratorExit
3567
+ class_scope.directives['no_gc_clear'] = True
3568
+ if Options.closure_freelist_size:
3569
+ class_scope.directives['freelist'] = Options.closure_freelist_size
3570
+
3571
+ if from_closure:
3572
+ assert cscope.is_closure_scope
3573
+ class_scope.declare_var(pos=node.pos,
3574
+ name=Naming.outer_scope_cname,
3575
+ cname=Naming.outer_scope_cname,
3576
+ type=cscope.scope_class.type,
3577
+ is_cdef=True)
3578
+ node.needs_outer_scope = True
3579
+ for name, entry in in_closure:
3580
+ closure_entry = class_scope.declare_var(
3581
+ pos=entry.pos,
3582
+ name=entry.name if not entry.in_subscope else None,
3583
+ cname=entry.cname,
3584
+ type=entry.type,
3585
+ is_cdef=True)
3586
+ if entry.is_declared_generic:
3587
+ closure_entry.is_declared_generic = 1
3588
+ node.needs_closure = True
3589
+ # Do it here because other classes are already checked
3590
+ target_module_scope.check_c_class(func_scope.scope_class)
3591
+
3592
+ def visit_LambdaNode(self, node):
3593
+ if not isinstance(node.def_node, Nodes.DefNode):
3594
+ # fused function, an error has been previously issued
3595
+ return node
3596
+
3597
+ was_in_lambda = self.in_lambda
3598
+ self.in_lambda = True
3599
+ self.create_class_from_scope(node.def_node, self.module_scope, node)
3600
+ self.visitchildren(node)
3601
+ self.in_lambda = was_in_lambda
3602
+ return node
3603
+
3604
+ def visit_FuncDefNode(self, node):
3605
+ if self.in_lambda:
3606
+ self.visitchildren(node)
3607
+ return node
3608
+ if node.needs_closure or self.path:
3609
+ self.create_class_from_scope(node, self.module_scope)
3610
+ self.path.append(node)
3611
+ self.visitchildren(node)
3612
+ self.path.pop()
3613
+ return node
3614
+
3615
+ def visit_GeneratorBodyDefNode(self, node):
3616
+ self.visitchildren(node)
3617
+ return node
3618
+
3619
+ def visit_CFuncDefNode(self, node):
3620
+ if not node.overridable:
3621
+ return self.visit_FuncDefNode(node)
3622
+ else:
3623
+ self.visitchildren(node)
3624
+ return node
3625
+
3626
+ def visit_GeneratorExpressionNode(self, node):
3627
+ node = _HandleGeneratorArguments()(node)
3628
+ return self.visit_LambdaNode(node)
3629
+
3630
+
3631
+ class InjectGilHandling(VisitorTransform, SkipDeclarations):
3632
+ """
3633
+ Allow certain Python operations inside of nogil blocks by implicitly acquiring the GIL.
3634
+
3635
+ Must run before the AnalyseDeclarationsTransform to make sure the GILStatNodes get
3636
+ set up, parallel sections know that the GIL is acquired inside of them, etc.
3637
+ """
3638
+ nogil = False
3639
+
3640
+ # special node handling
3641
+
3642
+ def _inject_gil_in_nogil(self, node):
3643
+ """Allow the (Python statement) node in nogil sections by wrapping it in a 'with gil' block."""
3644
+ if self.nogil:
3645
+ node = Nodes.GILStatNode(node.pos, state='gil', body=node)
3646
+ return node
3647
+
3648
+ visit_RaiseStatNode = _inject_gil_in_nogil
3649
+ visit_PrintStatNode = _inject_gil_in_nogil # sadly, not the function
3650
+
3651
+ # further candidates:
3652
+ # def visit_ReraiseStatNode(self, node):
3653
+
3654
+ # nogil tracking
3655
+
3656
+ def visit_GILStatNode(self, node):
3657
+ was_nogil = self.nogil
3658
+ self.nogil = (node.state == 'nogil')
3659
+ self.visitchildren(node)
3660
+ self.nogil = was_nogil
3661
+ return node
3662
+
3663
+ def visit_CFuncDefNode(self, node):
3664
+ was_nogil = self.nogil
3665
+ if isinstance(node.declarator, Nodes.CFuncDeclaratorNode):
3666
+ self.nogil = node.declarator.nogil and not node.declarator.with_gil
3667
+ self.visitchildren(node)
3668
+ self.nogil = was_nogil
3669
+ return node
3670
+
3671
+ def visit_ParallelRangeNode(self, node):
3672
+ was_nogil = self.nogil
3673
+ self.nogil = node.nogil
3674
+ self.visitchildren(node)
3675
+ self.nogil = was_nogil
3676
+ return node
3677
+
3678
+ def visit_ExprNode(self, node):
3679
+ # No special GIL handling inside of expressions for now.
3680
+ return node
3681
+
3682
+ visit_Node = VisitorTransform.recurse_to_children
3683
+
3684
+
3685
+ class GilCheck(VisitorTransform):
3686
+ """
3687
+ Call `node.gil_check(env)` on each node to make sure we hold the
3688
+ GIL when we need it. Raise an error when on Python operations
3689
+ inside a `nogil` environment.
3690
+
3691
+ Additionally, raise exceptions for closely nested with gil or with nogil
3692
+ statements. The latter would abort Python.
3693
+ """
3694
+
3695
+ def __call__(self, root):
3696
+ self.env_stack = [root.scope]
3697
+ self.nogil_state = Nodes.NoGilState.HasGil
3698
+
3699
+ self.nogil_state_at_current_gilstatnode = Nodes.NoGilState.HasGil
3700
+ return super().__call__(root)
3701
+
3702
+ def _visit_scoped_children(self, node, nogil_state):
3703
+ was_nogil = self.nogil_state
3704
+ outer_attrs = node.outer_attrs
3705
+ if outer_attrs and len(self.env_stack) > 1:
3706
+ self.nogil_state = (
3707
+ Nodes.NoGilState.NoGil if self.env_stack[-2].nogil else Nodes.NoGilState.HasGil)
3708
+ self.visitchildren(node, outer_attrs)
3709
+
3710
+ self.nogil_state = nogil_state
3711
+ self.visitchildren(node, attrs=None, exclude=outer_attrs)
3712
+ self.nogil_state = was_nogil
3713
+
3714
+ def visit_FuncDefNode(self, node):
3715
+ self.env_stack.append(node.local_scope)
3716
+ inner_nogil = node.local_scope.nogil
3717
+
3718
+ nogil_state = self.nogil_state
3719
+ if inner_nogil:
3720
+ self.nogil_state = Nodes.NoGilState.NoGilScope
3721
+
3722
+ if inner_nogil and node.nogil_check:
3723
+ node.nogil_check(node.local_scope)
3724
+
3725
+ self._visit_scoped_children(node, self.nogil_state)
3726
+
3727
+ # FuncDefNodes can be nested, because a cpdef function contains a def function
3728
+ # inside it. Therefore restore to previous state
3729
+ self.nogil_state = nogil_state
3730
+
3731
+ self.env_stack.pop()
3732
+ return node
3733
+
3734
+ def visit_GILStatNode(self, node):
3735
+ if node.condition is not None:
3736
+ error(node.condition.pos,
3737
+ "Non-constant condition in a "
3738
+ "`with %s(<condition>)` statement" % node.state)
3739
+ return node
3740
+
3741
+ if self.nogil_state and node.nogil_check:
3742
+ node.nogil_check()
3743
+
3744
+ was_nogil = self.nogil_state
3745
+ is_nogil = (node.state == 'nogil')
3746
+
3747
+ if was_nogil == is_nogil and not self.nogil_state == Nodes.NoGilState.NoGilScope:
3748
+ if not was_nogil:
3749
+ error(node.pos, "Trying to acquire the GIL while it is "
3750
+ "already held.")
3751
+ else:
3752
+ error(node.pos, "Trying to release the GIL while it was "
3753
+ "previously released.")
3754
+ if self.nogil_state == Nodes.NoGilState.NoGilScope:
3755
+ node.scope_gil_state_known = False
3756
+
3757
+ if isinstance(node.finally_clause, Nodes.StatListNode):
3758
+ # The finally clause of the GILStatNode is a GILExitNode,
3759
+ # which is wrapped in a StatListNode. Just unpack that.
3760
+ node.finally_clause, = node.finally_clause.stats
3761
+
3762
+ nogil_state_at_current_gilstatnode = self.nogil_state_at_current_gilstatnode
3763
+ self.nogil_state_at_current_gilstatnode = self.nogil_state
3764
+ nogil_state = Nodes.NoGilState.NoGil if is_nogil else Nodes.NoGilState.HasGil
3765
+ self._visit_scoped_children(node, nogil_state)
3766
+ self.nogil_state_at_current_gilstatnode = nogil_state_at_current_gilstatnode
3767
+ return node
3768
+
3769
+ def visit_ParallelRangeNode(self, node):
3770
+ if node.nogil or self.nogil_state == Nodes.NoGilState.NoGilScope:
3771
+ node_was_nogil, node.nogil = node.nogil, False
3772
+ node = Nodes.GILStatNode(node.pos, state='nogil', body=node)
3773
+ if not node_was_nogil and self.nogil_state == Nodes.NoGilState.NoGilScope:
3774
+ # We're in a "nogil" function, but that doesn't prove we
3775
+ # didn't have the gil
3776
+ node.scope_gil_state_known = False
3777
+ return self.visit_GILStatNode(node)
3778
+
3779
+ if not self.nogil_state:
3780
+ error(node.pos, "prange() can only be used without the GIL")
3781
+ # Forget about any GIL-related errors that may occur in the body
3782
+ return None
3783
+
3784
+ node.nogil_check(self.env_stack[-1])
3785
+ self.visitchildren(node)
3786
+ return node
3787
+
3788
+ def visit_ParallelWithBlockNode(self, node):
3789
+ if not self.nogil_state:
3790
+ error(node.pos, "The parallel section may only be used without "
3791
+ "the GIL")
3792
+ return None
3793
+ if self.nogil_state == Nodes.NoGilState.NoGilScope:
3794
+ # We're in a "nogil" function but that doesn't prove we didn't
3795
+ # have the gil, so release it
3796
+ node = Nodes.GILStatNode(node.pos, state='nogil', body=node)
3797
+ node.scope_gil_state_known = False
3798
+ return self.visit_GILStatNode(node)
3799
+
3800
+ if node.nogil_check:
3801
+ # It does not currently implement this, but test for it anyway to
3802
+ # avoid potential future surprises
3803
+ node.nogil_check(self.env_stack[-1])
3804
+
3805
+ self.visitchildren(node)
3806
+ return node
3807
+
3808
+ def visit_TryFinallyStatNode(self, node):
3809
+ """
3810
+ Take care of try/finally statements in nogil code sections.
3811
+ """
3812
+ if not self.nogil_state:
3813
+ return self.visit_Node(node)
3814
+
3815
+ node.nogil_check = None
3816
+ node.is_try_finally_in_nogil = True
3817
+ self.visitchildren(node)
3818
+ return node
3819
+
3820
+ def visit_CriticalSectionStatNode(self, node):
3821
+ # skip normal "try/finally node" handling
3822
+ return self.visit_Node(node)
3823
+
3824
+ def visit_CythonLockStatNode(self, node):
3825
+ # skip normal "try/finally node" handling
3826
+ return self.visit_Node(node)
3827
+
3828
+ def visit_GILExitNode(self, node):
3829
+ if self.nogil_state_at_current_gilstatnode == Nodes.NoGilState.NoGilScope:
3830
+ node.scope_gil_state_known = False
3831
+ self.visitchildren(node)
3832
+ return node
3833
+
3834
+ def visit_Node(self, node):
3835
+ if self.env_stack and self.nogil_state and node.nogil_check:
3836
+ node.nogil_check(self.env_stack[-1])
3837
+ if node.outer_attrs:
3838
+ self._visit_scoped_children(node, self.nogil_state)
3839
+ else:
3840
+ self.visitchildren(node)
3841
+ if self.nogil_state:
3842
+ node.in_nogil_context = self.nogil_state
3843
+ return node
3844
+
3845
+ def visit_SimpleCallNode(self, node):
3846
+ if (node.self and node.self.type.is_cython_lock_type and
3847
+ node.function.is_attribute and node.function.attribute == "acquire" and
3848
+ len(node.args) == 1):
3849
+ # For the cython lock types we can optimize if we know the GIL state.
3850
+ # (Remove this in the distant future when it's all PyMutexes because for these
3851
+ # it doesn't matter)
3852
+ suffix = None
3853
+ if self.nogil_state == Nodes.NoGilState.NoGil:
3854
+ suffix = "Nogil"
3855
+ elif self.nogil_state == Nodes.NoGilState.HasGil:
3856
+ suffix = "Gil"
3857
+ if suffix:
3858
+ node = ExprNodes.PythonCapiCallNode(
3859
+ node.pos,
3860
+ node.function.entry.cname + suffix,
3861
+ node.function.type,
3862
+ args=[node.self],
3863
+ )
3864
+ return self.visit_Node(node)
3865
+
3866
+
3867
+ class CoerceCppTemps(EnvTransform, SkipDeclarations):
3868
+ """
3869
+ For temporary expression that are implemented using std::optional it's necessary the temps are
3870
+ assigned using `__pyx_t_x = value;` but accessed using `something = (*__pyx_t_x)`. This transform
3871
+ inserts a coercion node to take care of this, and runs absolutely last (once nothing else can be
3872
+ inserted into the tree)
3873
+
3874
+ TODO: a possible alternative would be to split ExprNode.result() into ExprNode.rhs_result() and ExprNode.lhs_result()???
3875
+ """
3876
+ def visit_ModuleNode(self, node):
3877
+ if self.current_env().cpp:
3878
+ # skipping this makes it essentially free for C files
3879
+ self.visitchildren(node)
3880
+ return node
3881
+
3882
+ def visit_ExprNode(self, node):
3883
+ self.visitchildren(node)
3884
+ if (self.current_env().directives['cpp_locals'] and
3885
+ node.result_in_temp() and node.type.is_cpp_class and
3886
+ # Fake references are not replaced with "std::optional()".
3887
+ not node.type.is_fake_reference):
3888
+ node = ExprNodes.CppOptionalTempCoercion(node)
3889
+
3890
+ return node
3891
+
3892
+ def visit_CppOptionalTempCoercion(self, node):
3893
+ return node
3894
+
3895
+ def visit_CppIteratorNode(self, node):
3896
+ return node
3897
+
3898
+ def visit_ExprStatNode(self, node):
3899
+ # Deliberately skip `expr` in ExprStatNode - we don't need to access it.
3900
+ self.visitchildren(node.expr)
3901
+ return node
3902
+
3903
+
3904
+ class TransformBuiltinMethods(EnvTransform):
3905
+ """
3906
+ Replace Cython's own cython.* builtins by the corresponding tree nodes.
3907
+ Also handle some Python special builtin functions (e.g. super()/locals())
3908
+ that require introspection by the compiler.
3909
+ """
3910
+ def __init__(self, *args, **kwds):
3911
+ super().__init__(*args, **kwds)
3912
+ self.def_node_body_insertions = {}
3913
+
3914
+ def visit_SingleAssignmentNode(self, node):
3915
+ if node.declaration_only:
3916
+ return None
3917
+ else:
3918
+ self.visitchildren(node)
3919
+ return node
3920
+
3921
+ def visit_AttributeNode(self, node):
3922
+ self.visitchildren(node)
3923
+ return self.visit_cython_attribute(node)
3924
+
3925
+ def visit_NameNode(self, node):
3926
+ if node.name == u'__class__':
3927
+ lenv = self.current_env()
3928
+ entry = lenv.lookup_here(u'__class__')
3929
+ if not entry:
3930
+ node = self._inject_class(node)
3931
+ return self.visit_cython_attribute(node)
3932
+
3933
+ def visit_cython_attribute(self, node):
3934
+ attribute = node.as_cython_attribute()
3935
+ if attribute:
3936
+ if attribute == '__version__':
3937
+ from .. import __version__ as version
3938
+ node = ExprNodes.UnicodeNode(node.pos, value=EncodedString(version))
3939
+ elif attribute == 'NULL':
3940
+ node = ExprNodes.NullNode(node.pos)
3941
+ elif attribute in ('set', 'frozenset', 'staticmethod'):
3942
+ node = ExprNodes.NameNode(node.pos, name=EncodedString(attribute),
3943
+ entry=self.current_env().builtin_scope().lookup_here(attribute))
3944
+ elif PyrexTypes.parse_basic_type(attribute):
3945
+ pass
3946
+ elif self.context.cython_scope.lookup_qualified_name(attribute):
3947
+ pass
3948
+ else:
3949
+ error(node.pos, "'%s' not a valid cython attribute or is being used incorrectly" % attribute)
3950
+ return node
3951
+
3952
+ def visit_ExecStatNode(self, node):
3953
+ lenv = self.current_env()
3954
+ self.visitchildren(node)
3955
+ if len(node.args) == 1:
3956
+ node.args.append(ExprNodes.GlobalsExprNode(node.pos))
3957
+ if not lenv.is_module_scope:
3958
+ node.args.append(
3959
+ ExprNodes.LocalsExprNode(
3960
+ node.pos, self.current_scope_node(), lenv))
3961
+ return node
3962
+
3963
+ def _inject_locals(self, node, func_name):
3964
+ # locals()/dir()/vars() builtins
3965
+ lenv = self.current_env()
3966
+ entry = lenv.lookup_here(func_name)
3967
+ if entry:
3968
+ # not the builtin
3969
+ return node
3970
+ pos = node.pos
3971
+ if func_name in ('locals', 'vars'):
3972
+ if func_name == 'locals' and len(node.args) > 0:
3973
+ error(self.pos, "Builtin 'locals()' called with wrong number of args, expected 0, got %d"
3974
+ % len(node.args))
3975
+ return node
3976
+ elif func_name == 'vars':
3977
+ if len(node.args) > 1:
3978
+ error(self.pos, "Builtin 'vars()' called with wrong number of args, expected 0-1, got %d"
3979
+ % len(node.args))
3980
+ if len(node.args) > 0:
3981
+ return node # nothing to do
3982
+ return ExprNodes.LocalsExprNode(pos, self.current_scope_node(), lenv)
3983
+ else: # dir()
3984
+ if len(node.args) > 1:
3985
+ error(self.pos, "Builtin 'dir()' called with wrong number of args, expected 0-1, got %d"
3986
+ % len(node.args))
3987
+ if len(node.args) > 0:
3988
+ # optimised in Builtin.py
3989
+ return node
3990
+ if lenv.is_py_class_scope or lenv.is_module_scope:
3991
+ if lenv.is_py_class_scope:
3992
+ pyclass = self.current_scope_node()
3993
+ locals_dict = ExprNodes.CloneNode(pyclass.dict)
3994
+ else:
3995
+ locals_dict = ExprNodes.GlobalsExprNode(pos)
3996
+ return ExprNodes.SortedDictKeysNode(locals_dict)
3997
+ local_names = sorted(var.name for var in lenv.entries.values() if var.name)
3998
+ items = [ExprNodes.IdentifierStringNode(pos, value=var)
3999
+ for var in local_names]
4000
+ return ExprNodes.ListNode(pos, args=items)
4001
+
4002
+ def visit_PrimaryCmpNode(self, node):
4003
+ # special case: for in/not-in test, we do not need to sort locals()
4004
+ self.visitchildren(node)
4005
+ if node.operator in 'not_in': # in/not_in
4006
+ if isinstance(node.operand2, ExprNodes.SortedDictKeysNode):
4007
+ arg = node.operand2.arg
4008
+ if isinstance(arg, ExprNodes.NoneCheckNode):
4009
+ arg = arg.arg
4010
+ node.operand2 = arg
4011
+ return node
4012
+
4013
+ def visit_CascadedCmpNode(self, node):
4014
+ return self.visit_PrimaryCmpNode(node)
4015
+
4016
+ def _inject_eval(self, node, func_name):
4017
+ lenv = self.current_env()
4018
+ entry = lenv.lookup(func_name)
4019
+ if len(node.args) != 1 or (entry and not entry.is_builtin):
4020
+ return node
4021
+ # Inject globals and locals
4022
+ node.args.append(ExprNodes.GlobalsExprNode(node.pos))
4023
+ if not lenv.is_module_scope:
4024
+ node.args.append(
4025
+ ExprNodes.LocalsExprNode(
4026
+ node.pos, self.current_scope_node(), lenv))
4027
+ return node
4028
+
4029
+ def _inject_class(self, node):
4030
+ # bare __class__ reference inside function
4031
+ current_def_node = self.current_scope_node()
4032
+
4033
+ if not isinstance(current_def_node, Nodes.FuncDefNode):
4034
+ return node
4035
+
4036
+ # Go up the stack, find the first class node and its direct method (i.e. function).
4037
+ fdef_node = class_node = generator_node = None
4038
+ for stack_node, stack_scope in reversed(self.env_stack):
4039
+ if isinstance(stack_node, Nodes.ClassDefNode):
4040
+ class_node = stack_node
4041
+ class_scope = stack_scope
4042
+ break
4043
+ elif isinstance(stack_node, Nodes.GeneratorDefNode):
4044
+ generator_node = stack_node
4045
+ fdef_node = stack_node.gbody
4046
+ fdef_scope = stack_scope
4047
+ elif isinstance(stack_node, Nodes.FuncDefNode):
4048
+ fdef_node = stack_node
4049
+ fdef_scope = stack_scope
4050
+
4051
+ if not fdef_node or not class_node:
4052
+ # failed to find a class or function
4053
+ return node
4054
+
4055
+ # now we arrange to inject:
4056
+ # __class__ = ... at the start of the def_node body
4057
+ # The advantage of doing it like this is that it automatically appears in locals()
4058
+ # and it can be captured by inner functions
4059
+ if fdef_node not in self.def_node_body_insertions:
4060
+ pos = fdef_node.body.pos
4061
+ if class_scope.is_c_class_scope:
4062
+ # c-classes can be resolved at compile-time, so they have a simpler
4063
+ # implementation
4064
+ rhs = ExprNodes.NameNode(
4065
+ pos, name=class_node.scope.name,
4066
+ entry=class_node.entry)
4067
+ elif class_scope.is_py_class_scope:
4068
+ rhs = ExprNodes.ClassCellNode(pos, is_generator=generator_node is not None)
4069
+ if generator_node:
4070
+ generator_node.requires_classobj = True
4071
+ else:
4072
+ fdef_node.requires_classobj = True
4073
+ class_node.class_cell.is_active = True
4074
+ else:
4075
+ return node # should never happen
4076
+
4077
+ assign_node = Nodes.SingleAssignmentNode(pos,
4078
+ lhs=ExprNodes.NameNode(pos, name=EncodedString("__class__")),
4079
+ rhs=rhs)
4080
+
4081
+ assign_node.analyse_declarations(fdef_scope)
4082
+
4083
+ assert fdef_node not in self.def_node_body_insertions
4084
+ self.def_node_body_insertions[fdef_node] = assign_node
4085
+
4086
+ return node
4087
+
4088
+ def _inject_super(self, node, func_name):
4089
+ lenv = self.current_env()
4090
+ entry = lenv.lookup_here(func_name)
4091
+ if entry or node.args:
4092
+ return node
4093
+ # Inject no-args super
4094
+ def_node = self.current_scope_node()
4095
+ if not isinstance(def_node, Nodes.DefNode) or not def_node.args or len(self.env_stack) < 2:
4096
+ return node
4097
+ class_node, class_scope = self.env_stack[-2]
4098
+ if class_scope.is_py_class_scope:
4099
+ def_node.requires_classobj = True
4100
+ class_node.class_cell.is_active = True
4101
+ node.args = [
4102
+ ExprNodes.ClassCellNode(
4103
+ node.pos, is_generator=def_node.is_generator),
4104
+ ExprNodes.NameNode(node.pos, name=def_node.args[0].name)
4105
+ ]
4106
+ elif class_scope.is_c_class_scope:
4107
+ node.args = [
4108
+ ExprNodes.NameNode(
4109
+ node.pos, name=class_node.scope.name,
4110
+ entry=class_node.entry),
4111
+ ExprNodes.NameNode(node.pos, name=def_node.args[0].name)
4112
+ ]
4113
+ return node
4114
+
4115
+ def _do_body_insertion(self, node):
4116
+ body_insertion = self.def_node_body_insertions.pop(node, None)
4117
+ if body_insertion:
4118
+ if isinstance(node.body, Nodes.StatListNode):
4119
+ node.body.stats.insert(0, body_insertion)
4120
+ else:
4121
+ node.body = Nodes.StatListNode(node.body.pos,
4122
+ stats=[body_insertion, node.body])
4123
+
4124
+ def visit_FuncDefNode(self, node):
4125
+ node = super().visit_FuncDefNode(node)
4126
+ self._do_body_insertion(node)
4127
+ return node
4128
+
4129
+ def visit_GeneratorBodyDefNode(self, node):
4130
+ node = super().visit_GeneratorBodyDefNode(node)
4131
+ self._do_body_insertion(node)
4132
+ return node
4133
+
4134
+ def visit_SimpleCallNode(self, node):
4135
+ # cython.foo
4136
+ function = node.function.as_cython_attribute()
4137
+ if function:
4138
+ if function in InterpretCompilerDirectives.unop_method_nodes:
4139
+ if len(node.args) != 1:
4140
+ error(node.function.pos, "%s() takes exactly one argument" % function)
4141
+ else:
4142
+ node = InterpretCompilerDirectives.unop_method_nodes[function](
4143
+ node.function.pos, operand=node.args[0])
4144
+ elif function in InterpretCompilerDirectives.binop_method_nodes:
4145
+ if len(node.args) != 2:
4146
+ error(node.function.pos, "%s() takes exactly two arguments" % function)
4147
+ else:
4148
+ node = InterpretCompilerDirectives.binop_method_nodes[function](
4149
+ node.function.pos, operand1=node.args[0], operand2=node.args[1])
4150
+ elif function == 'cast':
4151
+ if len(node.args) != 2:
4152
+ error(node.function.pos,
4153
+ "cast() takes exactly two arguments and an optional typecheck keyword")
4154
+ else:
4155
+ type = node.args[0].analyse_as_type(self.current_env())
4156
+ if type:
4157
+ node = ExprNodes.TypecastNode(
4158
+ node.function.pos, type=type, operand=node.args[1], typecheck=False)
4159
+ else:
4160
+ error(node.args[0].pos, "Not a type")
4161
+ elif function == 'sizeof':
4162
+ if len(node.args) != 1:
4163
+ error(node.function.pos, "sizeof() takes exactly one argument")
4164
+ else:
4165
+ type = node.args[0].analyse_as_type(self.current_env())
4166
+ if type:
4167
+ node = ExprNodes.SizeofTypeNode(node.function.pos, arg_type=type)
4168
+ else:
4169
+ node = ExprNodes.SizeofVarNode(node.function.pos, operand=node.args[0])
4170
+ elif function == 'cmod':
4171
+ if len(node.args) != 2:
4172
+ error(node.function.pos, "cmod() takes exactly two arguments")
4173
+ else:
4174
+ node = ExprNodes.binop_node(node.function.pos, '%', node.args[0], node.args[1])
4175
+ node.cdivision = True
4176
+ elif function == 'cdiv':
4177
+ if len(node.args) != 2:
4178
+ error(node.function.pos, "cdiv() takes exactly two arguments")
4179
+ else:
4180
+ node = ExprNodes.binop_node(node.function.pos, '/', node.args[0], node.args[1])
4181
+ node.cdivision = True
4182
+ elif function == 'set':
4183
+ node.function = ExprNodes.NameNode(node.pos, name=EncodedString('set'))
4184
+ elif function == 'staticmethod':
4185
+ node.function = ExprNodes.NameNode(node.pos, name=EncodedString('staticmethod'))
4186
+ elif self.context.cython_scope.lookup_qualified_name(function):
4187
+ pass
4188
+ else:
4189
+ error(node.function.pos,
4190
+ "'%s' not a valid cython language construct" % function)
4191
+
4192
+ self.visitchildren(node)
4193
+
4194
+ if isinstance(node, ExprNodes.SimpleCallNode) and node.function.is_name:
4195
+ func_name = node.function.name
4196
+ if func_name in ('dir', 'locals', 'vars'):
4197
+ return self._inject_locals(node, func_name)
4198
+ if func_name == 'eval':
4199
+ return self._inject_eval(node, func_name)
4200
+ if func_name == 'super':
4201
+ return self._inject_super(node, func_name)
4202
+ return node
4203
+
4204
+ def visit_GeneralCallNode(self, node):
4205
+ function = node.function.as_cython_attribute()
4206
+ if function == 'cast':
4207
+ # NOTE: assuming simple tuple/dict nodes for positional_args and keyword_args
4208
+ args = node.positional_args.args
4209
+ kwargs = node.keyword_args.compile_time_value(None)
4210
+ if (len(args) != 2 or len(kwargs) > 1 or
4211
+ (len(kwargs) == 1 and 'typecheck' not in kwargs)):
4212
+ error(node.function.pos,
4213
+ "cast() takes exactly two arguments and an optional typecheck keyword")
4214
+ else:
4215
+ type = args[0].analyse_as_type(self.current_env())
4216
+ if type:
4217
+ typecheck = kwargs.get('typecheck', False)
4218
+ node = ExprNodes.TypecastNode(
4219
+ node.function.pos, type=type, operand=args[1], typecheck=typecheck)
4220
+ else:
4221
+ error(args[0].pos, "Not a type")
4222
+
4223
+ self.visitchildren(node)
4224
+ return node
4225
+
4226
+
4227
+ class ReplaceFusedTypeChecks(VisitorTransform):
4228
+ """
4229
+ This is not a transform in the pipeline. It is invoked on the specific
4230
+ versions of a cdef function with fused argument types. It filters out any
4231
+ type branches that don't match. e.g.
4232
+
4233
+ if fused_t is mytype:
4234
+ ...
4235
+ elif fused_t in other_fused_type:
4236
+ ...
4237
+ """
4238
+ def __init__(self, local_scope):
4239
+ super().__init__()
4240
+ self.local_scope = local_scope
4241
+ # defer the import until now to avoid circular import time dependencies
4242
+ from .Optimize import ConstantFolding
4243
+ self.transform = ConstantFolding(reevaluate=True)
4244
+
4245
+ def visit_IfStatNode(self, node):
4246
+ """
4247
+ Filters out any if clauses with false compile time type check
4248
+ expression.
4249
+ """
4250
+ self.visitchildren(node)
4251
+ return self.transform(node)
4252
+
4253
+ def visit_GILStatNode(self, node):
4254
+ """
4255
+ Fold constant condition of GILStatNode.
4256
+ """
4257
+ self.visitchildren(node)
4258
+ return self.transform(node)
4259
+
4260
+ def visit_PrimaryCmpNode(self, node):
4261
+ with Errors.local_errors(ignore=True):
4262
+ type1 = node.operand1.analyse_as_type(self.local_scope)
4263
+ type2 = node.operand2.analyse_as_type(self.local_scope)
4264
+
4265
+ if type1 and type2:
4266
+ false_node = ExprNodes.BoolNode(node.pos, value=False)
4267
+ true_node = ExprNodes.BoolNode(node.pos, value=True)
4268
+
4269
+ type1 = self.specialize_type(type1, node.operand1.pos)
4270
+ op = node.operator
4271
+
4272
+ if op in ('is', 'is_not', '==', '!='):
4273
+ type2 = self.specialize_type(type2, node.operand2.pos)
4274
+
4275
+ is_same = type1.same_as(type2)
4276
+ eq = op in ('is', '==')
4277
+
4278
+ if (is_same and eq) or (not is_same and not eq):
4279
+ return true_node
4280
+
4281
+ elif op in ('in', 'not_in'):
4282
+ # We have to do an instance check directly, as operand2
4283
+ # needs to be a fused type and not a type with a subtype
4284
+ # that is fused. First unpack the typedef
4285
+ if isinstance(type2, PyrexTypes.CTypedefType):
4286
+ type2 = type2.typedef_base_type
4287
+
4288
+ if type1.is_fused:
4289
+ error(node.operand1.pos, "Type is fused")
4290
+ elif not type2.is_fused:
4291
+ error(node.operand2.pos,
4292
+ "Can only use 'in' or 'not in' on a fused type")
4293
+ else:
4294
+ types = PyrexTypes.get_specialized_types(type2)
4295
+
4296
+ for specialized_type in types:
4297
+ if type1.same_as(specialized_type):
4298
+ if op == 'in':
4299
+ return true_node
4300
+ else:
4301
+ return false_node
4302
+
4303
+ if op == 'not_in':
4304
+ return true_node
4305
+
4306
+ return false_node
4307
+
4308
+ return node
4309
+
4310
+ def specialize_type(self, type, pos):
4311
+ try:
4312
+ return type.specialize(self.local_scope.fused_to_specific)
4313
+ except KeyError:
4314
+ error(pos, "Type is not specific")
4315
+ return type
4316
+
4317
+ def visit_Node(self, node):
4318
+ self.visitchildren(node)
4319
+ return node
4320
+
4321
+
4322
+ class DebugTransform(CythonTransform):
4323
+ """
4324
+ Write debug information for this Cython module.
4325
+ """
4326
+
4327
+ def __init__(self, context, options, result):
4328
+ super().__init__(context)
4329
+ self.visited = set()
4330
+ # our treebuilder and debug output writer
4331
+ # (see Cython.Debugger.debug_output.CythonDebugWriter)
4332
+ self.tb = self.context.gdb_debug_outputwriter
4333
+ #self.c_output_file = options.output_file
4334
+ self.c_output_file = result.c_file
4335
+
4336
+ # Closure support, basically treat nested functions as if the AST were
4337
+ # never nested
4338
+ self.nested_funcdefs = []
4339
+
4340
+ # tells visit_NameNode whether it should register step-into functions
4341
+ self.register_stepinto = False
4342
+
4343
+ def visit_ModuleNode(self, node):
4344
+ self.tb.module_name = node.full_module_name
4345
+ attrs = dict(
4346
+ module_name=node.full_module_name,
4347
+ filename=node.pos[0].filename,
4348
+ c_filename=self.c_output_file)
4349
+
4350
+ self.tb.start('Module', attrs)
4351
+
4352
+ # serialize functions
4353
+ self.tb.start('Functions')
4354
+ # First, serialize functions normally...
4355
+ self.visitchildren(node)
4356
+
4357
+ # ... then, serialize nested functions
4358
+ for nested_funcdef in self.nested_funcdefs:
4359
+ self.visit_FuncDefNode(nested_funcdef)
4360
+
4361
+ self.register_stepinto = True
4362
+ self.serialize_modulenode_as_function(node)
4363
+ self.register_stepinto = False
4364
+ self.tb.end('Functions')
4365
+
4366
+ # 2.3 compatibility. Serialize global variables
4367
+ self.tb.start('Globals')
4368
+ entries = {}
4369
+
4370
+ for k, v in node.scope.entries.items():
4371
+ if (v.qualified_name not in self.visited and not
4372
+ v.name.startswith('__pyx_') and not
4373
+ v.type.is_cfunction and not
4374
+ v.type.is_extension_type):
4375
+ entries[k]= v
4376
+
4377
+ self.serialize_local_variables(entries)
4378
+ self.tb.end('Globals')
4379
+ # self.tb.end('Module') # end Module after the line number mapping in
4380
+ # Cython.Compiler.ModuleNode.ModuleNode._serialize_lineno_map
4381
+ return node
4382
+
4383
+ def visit_FuncDefNode(self, node):
4384
+ self.visited.add(node.local_scope.qualified_name)
4385
+
4386
+ if getattr(node, 'is_wrapper', False):
4387
+ return node
4388
+
4389
+ if self.register_stepinto:
4390
+ self.nested_funcdefs.append(node)
4391
+ return node
4392
+
4393
+ # node.entry.visibility = 'extern'
4394
+ if node.py_func is None:
4395
+ pf_cname = ''
4396
+ else:
4397
+ pf_cname = node.py_func.entry.func_cname
4398
+
4399
+ # For functions defined using def, cname will be pyfunc_cname=__pyx_pf_*
4400
+ # For functions defined using cpdef or cdef, cname will be func_cname=__pyx_f_*
4401
+ # In all cases, cname will be the name of the function containing the actual code
4402
+ cname = node.entry.pyfunc_cname or node.entry.func_cname
4403
+
4404
+ attrs = dict(
4405
+ name=node.entry.name or getattr(node, 'name', '<unknown>'),
4406
+ cname=cname,
4407
+ pf_cname=pf_cname,
4408
+ qualified_name=node.local_scope.qualified_name,
4409
+ lineno=str(node.pos[1]))
4410
+
4411
+ self.tb.start('Function', attrs=attrs)
4412
+
4413
+ self.tb.start('Locals')
4414
+ self.serialize_local_variables(node.local_scope.entries)
4415
+ self.tb.end('Locals')
4416
+
4417
+ self.tb.start('Arguments')
4418
+ for arg in node.local_scope.arg_entries:
4419
+ self.tb.start(arg.name)
4420
+ self.tb.end(arg.name)
4421
+ self.tb.end('Arguments')
4422
+
4423
+ self.tb.start('StepIntoFunctions')
4424
+ self.register_stepinto = True
4425
+ self.visitchildren(node)
4426
+ self.register_stepinto = False
4427
+ self.tb.end('StepIntoFunctions')
4428
+ self.tb.end('Function')
4429
+
4430
+ return node
4431
+
4432
+ def visit_NameNode(self, node):
4433
+ if (self.register_stepinto and
4434
+ node.type is not None and
4435
+ node.type.is_cfunction and
4436
+ getattr(node, 'is_called', False) and
4437
+ node.entry.func_cname is not None):
4438
+ # don't check node.entry.in_cinclude, as 'cdef extern: ...'
4439
+ # declared functions are not 'in_cinclude'.
4440
+ # This means we will list called 'cdef' functions as
4441
+ # "step into functions", but this is not an issue as they will be
4442
+ # recognized as Cython functions anyway.
4443
+ attrs = dict(name=node.entry.func_cname)
4444
+ self.tb.start('StepIntoFunction', attrs=attrs)
4445
+ self.tb.end('StepIntoFunction')
4446
+
4447
+ self.visitchildren(node)
4448
+ return node
4449
+
4450
+ def serialize_modulenode_as_function(self, node):
4451
+ """
4452
+ Serialize the module-level code as a function so the debugger will know
4453
+ it's a "relevant frame" and it will know where to set the breakpoint
4454
+ for 'break modulename'.
4455
+ """
4456
+ self._serialize_modulenode_as_function(node, dict(
4457
+ name=node.full_module_name.rpartition('.')[-1],
4458
+ cname=node.module_init_func_cname(),
4459
+ pf_cname='',
4460
+ # Ignore the qualified_name, breakpoints should be set using
4461
+ # `cy break modulename:lineno` for module-level breakpoints.
4462
+ qualified_name='',
4463
+ lineno='1',
4464
+ is_initmodule_function="True",
4465
+ ))
4466
+
4467
+ def _serialize_modulenode_as_function(self, node, attrs):
4468
+ self.tb.start('Function', attrs=attrs)
4469
+
4470
+ self.tb.start('Locals')
4471
+ self.serialize_local_variables(node.scope.entries)
4472
+ self.tb.end('Locals')
4473
+
4474
+ self.tb.start('Arguments')
4475
+ self.tb.end('Arguments')
4476
+
4477
+ self.tb.start('StepIntoFunctions')
4478
+ self.register_stepinto = True
4479
+ self.visitchildren(node)
4480
+ self.register_stepinto = False
4481
+ self.tb.end('StepIntoFunctions')
4482
+
4483
+ self.tb.end('Function')
4484
+
4485
+ def serialize_local_variables(self, entries):
4486
+ for entry in entries.values():
4487
+ if not entry.cname:
4488
+ # not a local variable
4489
+ continue
4490
+ if entry.type.is_pyobject:
4491
+ vartype = 'PythonObject'
4492
+ else:
4493
+ vartype = 'CObject'
4494
+
4495
+ if entry.from_closure:
4496
+ # We're dealing with a closure where a variable from an outer
4497
+ # scope is accessed, get it from the scope object.
4498
+ cname = '%s->%s' % (Naming.cur_scope_cname,
4499
+ entry.outer_entry.cname)
4500
+
4501
+ qname = '%s.%s.%s' % (entry.scope.outer_scope.qualified_name,
4502
+ entry.scope.name,
4503
+ entry.name)
4504
+ elif entry.in_closure:
4505
+ cname = '%s->%s' % (Naming.cur_scope_cname,
4506
+ entry.cname)
4507
+ qname = entry.qualified_name
4508
+ else:
4509
+ cname = entry.cname
4510
+ qname = entry.qualified_name
4511
+
4512
+ if not entry.pos:
4513
+ # this happens for variables that are not in the user's code,
4514
+ # e.g. for the global __builtins__, __doc__, etc. We can just
4515
+ # set the lineno to 0 for those.
4516
+ lineno = '0'
4517
+ else:
4518
+ lineno = str(entry.pos[1])
4519
+
4520
+ attrs = dict(
4521
+ name=entry.name,
4522
+ cname=cname,
4523
+ qualified_name=qname,
4524
+ type=vartype,
4525
+ lineno=lineno)
4526
+
4527
+ self.tb.start('LocalVar', attrs)
4528
+ self.tb.end('LocalVar')
4529
+
4530
+
4531
+ class HasNoExceptionHandlingVisitor(TreeVisitor):
4532
+ """
4533
+ Used by finalExceptClauseNode to work out if the body
4534
+ needs to handle exceptions at all. This includes:
4535
+
4536
+ 1. Can raise an exception.
4537
+ 2. May try to access the traceback.
4538
+ """
4539
+ def __init__(self):
4540
+ self.uses_no_exceptions = True
4541
+ self.assignment_lhs = None
4542
+ super().__init__()
4543
+
4544
+ def __call__(self, node) -> bool:
4545
+ self.visit(node)
4546
+ return self.uses_no_exceptions
4547
+
4548
+ def visit_Node(self, node):
4549
+ self.uses_no_exceptions = False # In general, nodes use exceptions
4550
+
4551
+ def visit_ExprStatNode(self, node):
4552
+ self.visitchildren(node)
4553
+
4554
+ def visit_StatListNode(self, node):
4555
+ self.visitchildren(node)
4556
+
4557
+ def visit_ExprNode(self, node):
4558
+ if not node.is_literal:
4559
+ self.uses_no_exceptions = False
4560
+
4561
+ def visit_CallNode(self, node):
4562
+ # Implement this to make the behaviour as explicit as possible.
4563
+ # Even noexcept functions might end up printing a traceback.
4564
+ self.uses_no_exceptions = False
4565
+
4566
+ def visit_PassStatNode(self, node):
4567
+ pass # Does nothing. Good.
4568
+
4569
+ def visit_ReturnStatNode(self, node):
4570
+ if not self.uses_no_exceptions:
4571
+ return # shortcut
4572
+ self.visitchildren(node)
4573
+
4574
+ def visit_SingleAssignmentNode(self, node):
4575
+ if not self.uses_no_exceptions:
4576
+ return # shortcut
4577
+ self.assignment_lhs = node.lhs
4578
+ self.visit(node.lhs)
4579
+ self.assignment_lhs = None
4580
+ rhs_type = node.rhs.type
4581
+ if not (rhs_type.is_numeric or rhs_type.is_pyobject or rhs_type.is_memoryviewslice):
4582
+ # Treat everything we haven't explicitly thought about as potentially dubious.
4583
+ # cpp classes may have non-trivial assignment operators for example.
4584
+ self.uses_no_exceptions = False
4585
+ if not self.uses_no_exceptions:
4586
+ return
4587
+ self.visitchildren(node, exclude=["lhs"])
4588
+
4589
+ def visit_NameNode(self, node):
4590
+ if not self.uses_no_exceptions:
4591
+ return # shortcut
4592
+ entry = node.entry
4593
+ if self.assignment_lhs is node:
4594
+ if not (entry.is_cglobal or entry.is_arg or
4595
+ entry.is_local or entry.in_closure or entry.from_closure):
4596
+ self.uses_no_exceptions = False
4597
+ return
4598
+ else:
4599
+ if entry.is_cglobal:
4600
+ if entry.is_cpp_optional and node.initialized_check:
4601
+ # Otherwise, reading C globals should be safe.
4602
+ self.uses_no_exceptions = False
4603
+ return
4604
+ elif entry.is_arg or entry.is_local or entry.in_closure or entry.from_closure:
4605
+ if (node.cf_is_null or node.cf_maybe_null) and not node.type.is_numeric:
4606
+ # The logic here is slightly simpler than for NameNode error checking.
4607
+ # This gives a few false negatives (which is always the safe thing to do)
4608
+ # for memoryviews and cpp_optionals
4609
+ self.uses_no_exceptions = False
4610
+ return
4611
+ else:
4612
+ # Probably a py_global.
4613
+ self.uses_no_exceptions = False
4614
+ return
4615
+
4616
+ def visit_AttributeNode(self, node):
4617
+ if node.is_py_attr:
4618
+ self.uses_no_exceptions = False
4619
+ elif (node.type.is_memoryviewslice or node.entry.is_cpp_optional) and self.assignment_lhs is not node:
4620
+ # Memoryviewslices and cpp_optional are OK as a target, but reading them involves checks.
4621
+ # (Although cpp optionals are currently banned elsewhere
4622
+ # because C++ classes may have non-trivial assignment).
4623
+ self.uses_no_exceptions = False
4624
+ # Python objects just need an incref and simple C types are fine, too. Others may not be.
4625
+ if not (node.type.is_pyobject or node.type.is_numeric or node.type.is_memoryviewslice):
4626
+ self.uses_no_exceptions = False
4627
+ if self.uses_no_exceptions:
4628
+ self.visitchildren(node)
4629
+
4630
+ def visit_IndexNode(self, node):
4631
+ if not (node.base.type.is_array or node.base.type.is_ptr):
4632
+ self.uses_no_exceptions = False
4633
+ if not self.uses_no_exceptions:
4634
+ return
4635
+ self.visitchildren(node)
4636
+
4637
+ def visit_CoerceToTempNode(self, node):
4638
+ self.visitchildren(node)