Cython 3.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. Cython/Build/BuildExecutable.py +169 -0
  2. Cython/Build/Cache.py +199 -0
  3. Cython/Build/Cythonize.py +323 -0
  4. Cython/Build/Dependencies.py +1306 -0
  5. Cython/Build/Distutils.py +1 -0
  6. Cython/Build/Inline.py +463 -0
  7. Cython/Build/IpythonMagic.py +560 -0
  8. Cython/Build/SharedModule.py +76 -0
  9. Cython/Build/Tests/TestCyCache.py +194 -0
  10. Cython/Build/Tests/TestCythonizeArgsParser.py +481 -0
  11. Cython/Build/Tests/TestDependencies.py +133 -0
  12. Cython/Build/Tests/TestInline.py +177 -0
  13. Cython/Build/Tests/TestIpythonMagic.py +287 -0
  14. Cython/Build/Tests/TestRecythonize.py +212 -0
  15. Cython/Build/Tests/TestStripLiterals.py +155 -0
  16. Cython/Build/Tests/__init__.py +1 -0
  17. Cython/Build/__init__.py +8 -0
  18. Cython/CodeWriter.py +811 -0
  19. Cython/Compiler/AnalysedTreeTransforms.py +97 -0
  20. Cython/Compiler/Annotate.py +326 -0
  21. Cython/Compiler/AutoDocTransforms.py +320 -0
  22. Cython/Compiler/Buffer.py +680 -0
  23. Cython/Compiler/Builtin.py +934 -0
  24. Cython/Compiler/CmdLine.py +259 -0
  25. Cython/Compiler/Code.pxd +148 -0
  26. Cython/Compiler/Code.py +3375 -0
  27. Cython/Compiler/CodeGeneration.py +33 -0
  28. Cython/Compiler/CythonScope.py +187 -0
  29. Cython/Compiler/Dataclass.py +868 -0
  30. Cython/Compiler/DebugFlags.py +24 -0
  31. Cython/Compiler/Errors.py +295 -0
  32. Cython/Compiler/ExprNodes.py +15267 -0
  33. Cython/Compiler/FlowControl.pxd +97 -0
  34. Cython/Compiler/FlowControl.py +1455 -0
  35. Cython/Compiler/FusedNode.py +1002 -0
  36. Cython/Compiler/Future.py +16 -0
  37. Cython/Compiler/Interpreter.py +57 -0
  38. Cython/Compiler/Lexicon.py +340 -0
  39. Cython/Compiler/LineTable.py +114 -0
  40. Cython/Compiler/Main.py +853 -0
  41. Cython/Compiler/MatchCaseNodes.py +259 -0
  42. Cython/Compiler/MemoryView.py +922 -0
  43. Cython/Compiler/ModuleNode.py +4024 -0
  44. Cython/Compiler/Naming.py +374 -0
  45. Cython/Compiler/Nodes.py +10826 -0
  46. Cython/Compiler/Optimize.py +5256 -0
  47. Cython/Compiler/Options.py +835 -0
  48. Cython/Compiler/ParseTreeTransforms.pxd +77 -0
  49. Cython/Compiler/ParseTreeTransforms.py +4509 -0
  50. Cython/Compiler/Parsing.pxd +9 -0
  51. Cython/Compiler/Parsing.py +4789 -0
  52. Cython/Compiler/Pipeline.py +439 -0
  53. Cython/Compiler/PyrexTypes.py +5762 -0
  54. Cython/Compiler/Pythran.py +232 -0
  55. Cython/Compiler/Scanning.pxd +40 -0
  56. Cython/Compiler/Scanning.py +577 -0
  57. Cython/Compiler/StringEncoding.py +347 -0
  58. Cython/Compiler/Symtab.py +3080 -0
  59. Cython/Compiler/Tests/TestBuffer.py +105 -0
  60. Cython/Compiler/Tests/TestBuiltin.py +72 -0
  61. Cython/Compiler/Tests/TestCmdLine.py +586 -0
  62. Cython/Compiler/Tests/TestCode.py +86 -0
  63. Cython/Compiler/Tests/TestFlowControl.py +65 -0
  64. Cython/Compiler/Tests/TestGrammar.py +202 -0
  65. Cython/Compiler/Tests/TestMemView.py +71 -0
  66. Cython/Compiler/Tests/TestParseTreeTransforms.py +285 -0
  67. Cython/Compiler/Tests/TestScanning.py +134 -0
  68. Cython/Compiler/Tests/TestSignatureMatching.py +73 -0
  69. Cython/Compiler/Tests/TestStringEncoding.py +33 -0
  70. Cython/Compiler/Tests/TestTreeFragment.py +63 -0
  71. Cython/Compiler/Tests/TestTreePath.py +103 -0
  72. Cython/Compiler/Tests/TestTypes.py +75 -0
  73. Cython/Compiler/Tests/TestUtilityLoad.py +112 -0
  74. Cython/Compiler/Tests/TestVisitor.py +61 -0
  75. Cython/Compiler/Tests/Utils.py +36 -0
  76. Cython/Compiler/Tests/__init__.py +1 -0
  77. Cython/Compiler/TreeFragment.py +278 -0
  78. Cython/Compiler/TreePath.py +303 -0
  79. Cython/Compiler/TypeInference.py +584 -0
  80. Cython/Compiler/TypeSlots.py +1181 -0
  81. Cython/Compiler/UFuncs.py +311 -0
  82. Cython/Compiler/UtilNodes.py +389 -0
  83. Cython/Compiler/UtilityCode.py +344 -0
  84. Cython/Compiler/Version.py +8 -0
  85. Cython/Compiler/Visitor.pxd +53 -0
  86. Cython/Compiler/Visitor.py +861 -0
  87. Cython/Compiler/__init__.py +1 -0
  88. Cython/Coverage.py +448 -0
  89. Cython/Debugger/Cygdb.py +175 -0
  90. Cython/Debugger/DebugWriter.py +82 -0
  91. Cython/Debugger/Tests/TestLibCython.py +275 -0
  92. Cython/Debugger/Tests/__init__.py +1 -0
  93. Cython/Debugger/Tests/cfuncs.c +8 -0
  94. Cython/Debugger/Tests/codefile +49 -0
  95. Cython/Debugger/Tests/test_libcython_in_gdb.py +578 -0
  96. Cython/Debugger/Tests/test_libpython_in_gdb.py +90 -0
  97. Cython/Debugger/__init__.py +1 -0
  98. Cython/Debugger/libcython.py +1548 -0
  99. Cython/Debugger/libpython.py +2821 -0
  100. Cython/Debugging.py +20 -0
  101. Cython/Distutils/__init__.py +2 -0
  102. Cython/Distutils/build_ext.py +139 -0
  103. Cython/Distutils/extension.py +96 -0
  104. Cython/Distutils/old_build_ext.py +351 -0
  105. Cython/Includes/cpython/__init__.pxd +173 -0
  106. Cython/Includes/cpython/array.pxd +174 -0
  107. Cython/Includes/cpython/bool.pxd +37 -0
  108. Cython/Includes/cpython/buffer.pxd +112 -0
  109. Cython/Includes/cpython/bytearray.pxd +33 -0
  110. Cython/Includes/cpython/bytes.pxd +200 -0
  111. Cython/Includes/cpython/cellobject.pxd +35 -0
  112. Cython/Includes/cpython/ceval.pxd +8 -0
  113. Cython/Includes/cpython/codecs.pxd +121 -0
  114. Cython/Includes/cpython/complex.pxd +60 -0
  115. Cython/Includes/cpython/contextvars.pxd +145 -0
  116. Cython/Includes/cpython/conversion.pxd +36 -0
  117. Cython/Includes/cpython/datetime.pxd +395 -0
  118. Cython/Includes/cpython/descr.pxd +26 -0
  119. Cython/Includes/cpython/dict.pxd +187 -0
  120. Cython/Includes/cpython/exc.pxd +263 -0
  121. Cython/Includes/cpython/fileobject.pxd +57 -0
  122. Cython/Includes/cpython/float.pxd +47 -0
  123. Cython/Includes/cpython/function.pxd +65 -0
  124. Cython/Includes/cpython/genobject.pxd +25 -0
  125. Cython/Includes/cpython/getargs.pxd +12 -0
  126. Cython/Includes/cpython/instance.pxd +25 -0
  127. Cython/Includes/cpython/iterator.pxd +36 -0
  128. Cython/Includes/cpython/iterobject.pxd +24 -0
  129. Cython/Includes/cpython/list.pxd +92 -0
  130. Cython/Includes/cpython/long.pxd +149 -0
  131. Cython/Includes/cpython/longintrepr.pxd +14 -0
  132. Cython/Includes/cpython/mapping.pxd +63 -0
  133. Cython/Includes/cpython/marshal.pxd +66 -0
  134. Cython/Includes/cpython/mem.pxd +120 -0
  135. Cython/Includes/cpython/memoryview.pxd +50 -0
  136. Cython/Includes/cpython/method.pxd +49 -0
  137. Cython/Includes/cpython/module.pxd +208 -0
  138. Cython/Includes/cpython/number.pxd +258 -0
  139. Cython/Includes/cpython/object.pxd +433 -0
  140. Cython/Includes/cpython/pycapsule.pxd +143 -0
  141. Cython/Includes/cpython/pylifecycle.pxd +68 -0
  142. Cython/Includes/cpython/pyport.pxd +8 -0
  143. Cython/Includes/cpython/pystate.pxd +95 -0
  144. Cython/Includes/cpython/pythread.pxd +53 -0
  145. Cython/Includes/cpython/ref.pxd +67 -0
  146. Cython/Includes/cpython/sequence.pxd +134 -0
  147. Cython/Includes/cpython/set.pxd +119 -0
  148. Cython/Includes/cpython/slice.pxd +70 -0
  149. Cython/Includes/cpython/time.pxd +129 -0
  150. Cython/Includes/cpython/tuple.pxd +72 -0
  151. Cython/Includes/cpython/type.pxd +53 -0
  152. Cython/Includes/cpython/unicode.pxd +639 -0
  153. Cython/Includes/cpython/version.pxd +32 -0
  154. Cython/Includes/cpython/weakref.pxd +78 -0
  155. Cython/Includes/libc/__init__.pxd +1 -0
  156. Cython/Includes/libc/complex.pxd +35 -0
  157. Cython/Includes/libc/errno.pxd +127 -0
  158. Cython/Includes/libc/float.pxd +43 -0
  159. Cython/Includes/libc/limits.pxd +28 -0
  160. Cython/Includes/libc/locale.pxd +46 -0
  161. Cython/Includes/libc/math.pxd +209 -0
  162. Cython/Includes/libc/setjmp.pxd +10 -0
  163. Cython/Includes/libc/signal.pxd +64 -0
  164. Cython/Includes/libc/stddef.pxd +9 -0
  165. Cython/Includes/libc/stdint.pxd +105 -0
  166. Cython/Includes/libc/stdio.pxd +80 -0
  167. Cython/Includes/libc/stdlib.pxd +72 -0
  168. Cython/Includes/libc/string.pxd +50 -0
  169. Cython/Includes/libc/threads.pxd +84 -0
  170. Cython/Includes/libc/time.pxd +51 -0
  171. Cython/Includes/libcpp/__init__.pxd +4 -0
  172. Cython/Includes/libcpp/algorithm.pxd +320 -0
  173. Cython/Includes/libcpp/any.pxd +16 -0
  174. Cython/Includes/libcpp/atomic.pxd +59 -0
  175. Cython/Includes/libcpp/barrier.pxd +22 -0
  176. Cython/Includes/libcpp/bit.pxd +29 -0
  177. Cython/Includes/libcpp/cast.pxd +12 -0
  178. Cython/Includes/libcpp/cmath.pxd +518 -0
  179. Cython/Includes/libcpp/complex.pxd +106 -0
  180. Cython/Includes/libcpp/deque.pxd +165 -0
  181. Cython/Includes/libcpp/exception.pxd +86 -0
  182. Cython/Includes/libcpp/execution.pxd +15 -0
  183. Cython/Includes/libcpp/forward_list.pxd +63 -0
  184. Cython/Includes/libcpp/functional.pxd +26 -0
  185. Cython/Includes/libcpp/future.pxd +103 -0
  186. Cython/Includes/libcpp/iterator.pxd +34 -0
  187. Cython/Includes/libcpp/latch.pxd +17 -0
  188. Cython/Includes/libcpp/limits.pxd +61 -0
  189. Cython/Includes/libcpp/list.pxd +117 -0
  190. Cython/Includes/libcpp/map.pxd +252 -0
  191. Cython/Includes/libcpp/memory.pxd +115 -0
  192. Cython/Includes/libcpp/mutex.pxd +130 -0
  193. Cython/Includes/libcpp/numbers.pxd +15 -0
  194. Cython/Includes/libcpp/numeric.pxd +131 -0
  195. Cython/Includes/libcpp/optional.pxd +34 -0
  196. Cython/Includes/libcpp/pair.pxd +1 -0
  197. Cython/Includes/libcpp/queue.pxd +25 -0
  198. Cython/Includes/libcpp/random.pxd +166 -0
  199. Cython/Includes/libcpp/semaphore.pxd +44 -0
  200. Cython/Includes/libcpp/set.pxd +228 -0
  201. Cython/Includes/libcpp/shared_mutex.pxd +72 -0
  202. Cython/Includes/libcpp/span.pxd +87 -0
  203. Cython/Includes/libcpp/stack.pxd +11 -0
  204. Cython/Includes/libcpp/stop_token.pxd +105 -0
  205. Cython/Includes/libcpp/string.pxd +355 -0
  206. Cython/Includes/libcpp/string_view.pxd +181 -0
  207. Cython/Includes/libcpp/typeindex.pxd +15 -0
  208. Cython/Includes/libcpp/typeinfo.pxd +10 -0
  209. Cython/Includes/libcpp/unordered_map.pxd +193 -0
  210. Cython/Includes/libcpp/unordered_set.pxd +152 -0
  211. Cython/Includes/libcpp/utility.pxd +30 -0
  212. Cython/Includes/libcpp/vector.pxd +186 -0
  213. Cython/Includes/openmp.pxd +50 -0
  214. Cython/Includes/posix/__init__.pxd +1 -0
  215. Cython/Includes/posix/dlfcn.pxd +14 -0
  216. Cython/Includes/posix/fcntl.pxd +86 -0
  217. Cython/Includes/posix/ioctl.pxd +4 -0
  218. Cython/Includes/posix/mman.pxd +101 -0
  219. Cython/Includes/posix/resource.pxd +57 -0
  220. Cython/Includes/posix/select.pxd +21 -0
  221. Cython/Includes/posix/signal.pxd +73 -0
  222. Cython/Includes/posix/stat.pxd +98 -0
  223. Cython/Includes/posix/stdio.pxd +37 -0
  224. Cython/Includes/posix/stdlib.pxd +29 -0
  225. Cython/Includes/posix/strings.pxd +9 -0
  226. Cython/Includes/posix/time.pxd +71 -0
  227. Cython/Includes/posix/types.pxd +30 -0
  228. Cython/Includes/posix/uio.pxd +26 -0
  229. Cython/Includes/posix/unistd.pxd +271 -0
  230. Cython/Includes/posix/wait.pxd +38 -0
  231. Cython/Plex/Actions.pxd +24 -0
  232. Cython/Plex/Actions.py +119 -0
  233. Cython/Plex/DFA.pxd +14 -0
  234. Cython/Plex/DFA.py +164 -0
  235. Cython/Plex/Errors.py +48 -0
  236. Cython/Plex/Lexicons.py +178 -0
  237. Cython/Plex/Machines.pxd +36 -0
  238. Cython/Plex/Machines.py +238 -0
  239. Cython/Plex/Regexps.py +539 -0
  240. Cython/Plex/Scanners.pxd +47 -0
  241. Cython/Plex/Scanners.py +360 -0
  242. Cython/Plex/Transitions.pxd +14 -0
  243. Cython/Plex/Transitions.py +239 -0
  244. Cython/Plex/__init__.py +34 -0
  245. Cython/Runtime/__init__.py +1 -0
  246. Cython/Runtime/refnanny.pyx +237 -0
  247. Cython/Shadow.py +690 -0
  248. Cython/Shadow.pyi +521 -0
  249. Cython/StringIOTree.py +170 -0
  250. Cython/Tempita/__init__.py +4 -0
  251. Cython/Tempita/_looper.py +154 -0
  252. Cython/Tempita/_tempita.py +1091 -0
  253. Cython/TestUtils.py +410 -0
  254. Cython/Tests/TestCodeWriter.py +128 -0
  255. Cython/Tests/TestCythonUtils.py +202 -0
  256. Cython/Tests/TestJediTyper.py +223 -0
  257. Cython/Tests/TestShadow.py +114 -0
  258. Cython/Tests/TestStringIOTree.py +67 -0
  259. Cython/Tests/TestTestUtils.py +90 -0
  260. Cython/Tests/__init__.py +1 -0
  261. Cython/Tests/xmlrunner.py +390 -0
  262. Cython/Utility/AsyncGen.c +1002 -0
  263. Cython/Utility/Buffer.c +875 -0
  264. Cython/Utility/BufferFormatFromTypeInfo.pxd +2 -0
  265. Cython/Utility/Builtins.c +776 -0
  266. Cython/Utility/CConvert.pyx +134 -0
  267. Cython/Utility/CMath.c +104 -0
  268. Cython/Utility/CommonStructures.c +118 -0
  269. Cython/Utility/Complex.c +378 -0
  270. Cython/Utility/Coroutine.c +2206 -0
  271. Cython/Utility/CpdefEnums.pyx +103 -0
  272. Cython/Utility/CppConvert.pyx +279 -0
  273. Cython/Utility/CppSupport.cpp +143 -0
  274. Cython/Utility/CythonFunction.c +1794 -0
  275. Cython/Utility/Dataclasses.c +185 -0
  276. Cython/Utility/Dataclasses.py +112 -0
  277. Cython/Utility/Embed.c +125 -0
  278. Cython/Utility/Exceptions.c +1012 -0
  279. Cython/Utility/ExtensionTypes.c +809 -0
  280. Cython/Utility/FunctionArguments.c +965 -0
  281. Cython/Utility/ImportExport.c +987 -0
  282. Cython/Utility/Lock.c +136 -0
  283. Cython/Utility/MemoryView.pxd +187 -0
  284. Cython/Utility/MemoryView.pyx +1481 -0
  285. Cython/Utility/MemoryView_C.c +1046 -0
  286. Cython/Utility/ModuleSetupCode.c +3059 -0
  287. Cython/Utility/NumpyImportArray.c +46 -0
  288. Cython/Utility/ObjectHandling.c +3342 -0
  289. Cython/Utility/Optimize.c +1589 -0
  290. Cython/Utility/Overflow.c +404 -0
  291. Cython/Utility/Printing.c +86 -0
  292. Cython/Utility/Profile.c +709 -0
  293. Cython/Utility/StringTools.c +1259 -0
  294. Cython/Utility/TestCyUtilityLoader.pyx +8 -0
  295. Cython/Utility/TestCythonScope.pyx +75 -0
  296. Cython/Utility/TestUtilityLoader.c +12 -0
  297. Cython/Utility/TypeConversion.c +1284 -0
  298. Cython/Utility/UFuncs.pyx +50 -0
  299. Cython/Utility/UFuncs_C.c +89 -0
  300. Cython/Utility/__init__.py +28 -0
  301. Cython/Utility/arrayarray.h +148 -0
  302. Cython/Utils.py +687 -0
  303. Cython/__init__.py +10 -0
  304. Cython/__init__.pyi +7 -0
  305. Cython/py.typed +0 -0
  306. cython-3.1.0.dist-info/COPYING.txt +19 -0
  307. cython-3.1.0.dist-info/LICENSE.txt +176 -0
  308. cython-3.1.0.dist-info/METADATA +636 -0
  309. cython-3.1.0.dist-info/RECORD +316 -0
  310. cython-3.1.0.dist-info/WHEEL +5 -0
  311. cython-3.1.0.dist-info/entry_points.txt +4 -0
  312. cython-3.1.0.dist-info/top_level.txt +3 -0
  313. cython.py +29 -0
  314. pyximport/__init__.py +4 -0
  315. pyximport/pyxbuild.py +160 -0
  316. pyximport/pyximport.py +482 -0
@@ -0,0 +1,97 @@
1
+ from .Visitor import ScopeTrackingTransform
2
+ from .Nodes import StatListNode, SingleAssignmentNode, CFuncDefNode, DefNode
3
+ from .ExprNodes import DictNode, DictItemNode, NameNode, UnicodeNode
4
+ from .PyrexTypes import py_object_type
5
+ from .StringEncoding import EncodedString
6
+ from . import Symtab
7
+
8
+ class AutoTestDictTransform(ScopeTrackingTransform):
9
+ # Handles autotestdict directive
10
+
11
+ excludelist = ['__cinit__', '__dealloc__', '__richcmp__',
12
+ '__nonzero__', '__bool__',
13
+ '__len__', '__contains__']
14
+
15
+ def visit_ModuleNode(self, node):
16
+ if node.is_pxd:
17
+ return node
18
+ self.scope_type = 'module'
19
+ self.scope_node = node
20
+
21
+ if not self.current_directives['autotestdict']:
22
+ return node
23
+ self.all_docstrings = self.current_directives['autotestdict.all']
24
+ self.cdef_docstrings = self.all_docstrings or self.current_directives['autotestdict.cdef']
25
+
26
+ assert isinstance(node.body, StatListNode)
27
+
28
+ # First see if __test__ is already created
29
+ if '__test__' in node.scope.entries:
30
+ # Do nothing
31
+ return node
32
+
33
+ pos = node.pos
34
+
35
+ self.tests = []
36
+ self.testspos = node.pos
37
+
38
+ test_dict_entry = node.scope.declare_var(EncodedString('__test__'),
39
+ py_object_type,
40
+ pos,
41
+ visibility='public')
42
+ create_test_dict_assignment = SingleAssignmentNode(pos,
43
+ lhs=NameNode(pos, name=EncodedString('__test__'),
44
+ entry=test_dict_entry),
45
+ rhs=DictNode(pos, key_value_pairs=self.tests))
46
+ self.visitchildren(node)
47
+ node.body.stats.append(create_test_dict_assignment)
48
+ return node
49
+
50
+ def add_test(self, testpos, path, doctest):
51
+ pos = self.testspos
52
+ keystr = EncodedString(f'{path} (line {testpos[1]:d})')
53
+ key = UnicodeNode(pos, value=keystr)
54
+ value = UnicodeNode(pos, value=doctest)
55
+ self.tests.append(DictItemNode(pos, key=key, value=value))
56
+
57
+ def visit_ExprNode(self, node):
58
+ # expressions cannot contain functions and lambda expressions
59
+ # do not have a docstring
60
+ return node
61
+
62
+ def visit_FuncDefNode(self, node):
63
+ if not node.doc or (isinstance(node, DefNode) and node.fused_py_func):
64
+ return node
65
+ if not self.cdef_docstrings:
66
+ if isinstance(node, CFuncDefNode) and not node.py_func:
67
+ return node
68
+ if not self.all_docstrings and '>>>' not in node.doc:
69
+ return node
70
+
71
+ pos = self.testspos
72
+ if self.scope_type == 'module':
73
+ path = node.entry.name
74
+ elif self.scope_type in ('pyclass', 'cclass'):
75
+ if isinstance(node, CFuncDefNode):
76
+ if node.py_func is not None:
77
+ name = node.py_func.name
78
+ else:
79
+ name = node.entry.name
80
+ else:
81
+ name = node.name
82
+ if self.scope_type == 'cclass' and name in self.excludelist:
83
+ return node
84
+ if self.scope_type == 'pyclass':
85
+ class_name = self.scope_node.name
86
+ else:
87
+ class_name = self.scope_node.class_name
88
+ if isinstance(node.entry.scope, Symtab.PropertyScope):
89
+ property_method_name = node.entry.scope.name
90
+ path = "%s.%s.%s" % (class_name, node.entry.scope.name,
91
+ node.entry.name)
92
+ else:
93
+ path = "%s.%s" % (class_name, node.entry.name)
94
+ else:
95
+ assert False
96
+ self.add_test(node.pos, path, node.doc)
97
+ return node
@@ -0,0 +1,326 @@
1
+ # Note: Work in progress
2
+
3
+
4
+ import os
5
+ import os.path
6
+ import re
7
+ import codecs
8
+ import textwrap
9
+ from datetime import datetime
10
+ from functools import partial
11
+ from collections import defaultdict
12
+ from xml.sax.saxutils import escape as html_escape
13
+ from io import StringIO
14
+
15
+ from . import Version
16
+ from .Code import CCodeWriter
17
+ from .. import Utils
18
+
19
+
20
+ class AnnotationCCodeWriter(CCodeWriter):
21
+
22
+ # also used as marker for detection of complete code emission in tests
23
+ COMPLETE_CODE_TITLE = "Complete cythonized code"
24
+
25
+ def __init__(self, create_from=None, buffer=None, copy_formatting=True, show_entire_c_code=False, source_desc=None):
26
+ CCodeWriter.__init__(self, create_from, buffer, copy_formatting=copy_formatting)
27
+ self.show_entire_c_code = show_entire_c_code
28
+ if create_from is None:
29
+ self.annotation_buffer = StringIO()
30
+ self.last_annotated_pos = None
31
+ # annotations[filename][line] -> [(column, AnnotationItem)*]
32
+ self.annotations = defaultdict(partial(defaultdict, list))
33
+ # code[filename][line] -> str
34
+ self.code = defaultdict(partial(defaultdict, str))
35
+ # scopes[filename][line] -> set(scopes)
36
+ self.scopes = defaultdict(partial(defaultdict, set))
37
+ else:
38
+ # When creating an insertion point, keep references to the same database
39
+ self.annotation_buffer = create_from.annotation_buffer
40
+ self.annotations = create_from.annotations
41
+ self.code = create_from.code
42
+ self.scopes = create_from.scopes
43
+ self.last_annotated_pos = create_from.last_annotated_pos
44
+
45
+ def create_new(self, create_from, buffer, copy_formatting):
46
+ return AnnotationCCodeWriter(create_from, buffer, copy_formatting)
47
+
48
+ def _write_to_buffer(self, s):
49
+ self.buffer.write(s)
50
+ self.annotation_buffer.write(s)
51
+
52
+ def mark_pos(self, pos, trace=True):
53
+ if pos is not None:
54
+ CCodeWriter.mark_pos(self, pos, trace)
55
+ if self.funcstate and self.funcstate.scope:
56
+ # lambdas and genexprs can result in multiple scopes per line => keep them in a set
57
+ self.scopes[pos[0].filename][pos[1]].add(self.funcstate.scope)
58
+ if self.last_annotated_pos:
59
+ source_desc, line, _ = self.last_annotated_pos
60
+ pos_code = self.code[source_desc.filename]
61
+ pos_code[line] += self.annotation_buffer.getvalue()
62
+ self.annotation_buffer = StringIO()
63
+ self.last_annotated_pos = pos
64
+
65
+ def annotate(self, pos, item):
66
+ self.annotations[pos[0].filename][pos[1]].append((pos[2], item))
67
+
68
+ def _css(self):
69
+ """css template will later allow to choose a colormap"""
70
+ css = [self._css_template]
71
+ for i in range(255):
72
+ color_shade = int(255.0 // (1.0 + i/10.0))
73
+ css.append(f'.cython.score-{i:d} {{background-color: #FFFF{color_shade:02x};}}')
74
+ try:
75
+ from pygments.formatters import HtmlFormatter
76
+ except ImportError:
77
+ pass
78
+ else:
79
+ css.append(HtmlFormatter().get_style_defs('.cython'))
80
+ return '\n'.join(css)
81
+
82
+ _css_template = textwrap.dedent("""
83
+ body.cython { font-family: courier; font-size: 12; }
84
+
85
+ .cython.tag { }
86
+ .cython.line { color: #000000; margin: 0em }
87
+ .cython.code { font-size: 9; color: #444444; display: none; margin: 0px 0px 0px 8px; border-left: 8px none; }
88
+
89
+ .cython.line .run { background-color: #B0FFB0; }
90
+ .cython.line .mis { background-color: #FFB0B0; }
91
+ .cython.code.run { border-left: 8px solid #B0FFB0; }
92
+ .cython.code.mis { border-left: 8px solid #FFB0B0; }
93
+
94
+ .cython.code .py_c_api { color: red; }
95
+ .cython.code .py_macro_api { color: #FF7000; }
96
+ .cython.code .pyx_c_api { color: #FF3000; }
97
+ .cython.code .pyx_macro_api { color: #FF7000; }
98
+ .cython.code .refnanny { color: #FFA000; }
99
+ .cython.code .trace { color: #FFA000; }
100
+ .cython.code .error_goto { color: #FFA000; }
101
+
102
+ .cython.code .coerce { color: #008000; border: 1px dotted #008000 }
103
+ .cython.code .py_attr { color: #FF0000; font-weight: bold; }
104
+ .cython.code .c_attr { color: #0000FF; }
105
+ .cython.code .py_call { color: #FF0000; font-weight: bold; }
106
+ .cython.code .c_call { color: #0000FF; }
107
+ """)
108
+
109
+ # on-click toggle function to show/hide C source code
110
+ _onclick_attr = ' onclick="{}"'.format((
111
+ "(function(s){"
112
+ " s.display = s.display === 'block' ? 'none' : 'block'"
113
+ "})(this.nextElementSibling.style)"
114
+ ).replace(' ', '') # poor dev's JS minification
115
+ )
116
+
117
+ def save_annotation(self, source_filename, target_filename, coverage_xml=None):
118
+ with Utils.open_source_file(source_filename) as f:
119
+ code = f.read()
120
+ generated_code = self.code.get(source_filename, {})
121
+ c_file = Utils.decode_filename(os.path.basename(target_filename))
122
+ html_filename = os.path.splitext(target_filename)[0] + ".html"
123
+
124
+ with codecs.open(html_filename, "w", encoding="UTF-8") as out_buffer:
125
+ out_buffer.write(self._save_annotation(code, generated_code, c_file, source_filename, coverage_xml))
126
+
127
+ def _save_annotation_header(self, c_file, source_filename, coverage_timestamp=None):
128
+ coverage_info = ''
129
+ if coverage_timestamp:
130
+ coverage_info = ' with coverage data from {timestamp}'.format(
131
+ timestamp=datetime.fromtimestamp(int(coverage_timestamp) // 1000))
132
+
133
+ outlist = [
134
+ textwrap.dedent('''\
135
+ <!DOCTYPE html>
136
+ <!-- Generated by Cython {watermark} -->
137
+ <html>
138
+ <head>
139
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
140
+ <title>Cython: {filename}</title>
141
+ <style type="text/css">
142
+ {css}
143
+ </style>
144
+ </head>
145
+ <body class="cython">
146
+ <p><span style="border-bottom: solid 1px grey;">Generated by Cython {watermark}</span>{more_info}</p>
147
+ <p>
148
+ <span style="background-color: #FFFF00">Yellow lines</span> hint at Python interaction.<br />
149
+ Click on a line that starts with a "<code>+</code>" to see the C code that Cython generated for it.
150
+ </p>
151
+ ''').format(css=self._css(), watermark=Version.watermark,
152
+ filename=os.path.basename(source_filename) if source_filename else '',
153
+ more_info=coverage_info)
154
+ ]
155
+ if c_file:
156
+ outlist.append('<p>Raw output: <a href="%s">%s</a></p>\n' % (c_file, c_file))
157
+ return outlist
158
+
159
+ def _save_annotation_footer(self):
160
+ return ('</body></html>\n',)
161
+
162
+ def _save_annotation(self, code, generated_code, c_file=None, source_filename=None, coverage_xml=None):
163
+ """
164
+ lines : original cython source code split by lines
165
+ generated_code : generated c code keyed by line number in original file
166
+ target filename : name of the file in which to store the generated html
167
+ c_file : filename in which the c_code has been written
168
+ """
169
+ if coverage_xml is not None and source_filename:
170
+ coverage_timestamp = coverage_xml.get('timestamp', '').strip()
171
+ covered_lines = self._get_line_coverage(coverage_xml, source_filename)
172
+ else:
173
+ coverage_timestamp = covered_lines = None
174
+ annotation_items = dict(self.annotations[source_filename])
175
+ scopes = dict(self.scopes[source_filename])
176
+
177
+ outlist = []
178
+ outlist.extend(self._save_annotation_header(c_file, source_filename, coverage_timestamp))
179
+ outlist.extend(self._save_annotation_body(code, generated_code, annotation_items, scopes, covered_lines))
180
+ outlist.extend(self._save_annotation_footer())
181
+ return ''.join(outlist)
182
+
183
+ def _get_line_coverage(self, coverage_xml, source_filename):
184
+ coverage_data = None
185
+ for entry in coverage_xml.iterfind('.//class'):
186
+ if not entry.get('filename'):
187
+ continue
188
+ if (entry.get('filename') == source_filename or
189
+ os.path.abspath(entry.get('filename')) == source_filename):
190
+ coverage_data = entry
191
+ break
192
+ elif source_filename.endswith(entry.get('filename')):
193
+ coverage_data = entry # but we might still find a better match...
194
+ if coverage_data is None:
195
+ return None
196
+ return {
197
+ int(line.get('number')): int(line.get('hits'))
198
+ for line in coverage_data.iterfind('lines/line')
199
+ }
200
+
201
+ def _htmlify_code(self, code, language):
202
+ try:
203
+ from pygments import highlight
204
+ from pygments.lexers import CythonLexer, CppLexer
205
+ from pygments.formatters import HtmlFormatter
206
+ except ImportError:
207
+ # no Pygments, just escape the code
208
+ return html_escape(code)
209
+
210
+ if language == "cython":
211
+ lexer = CythonLexer(stripnl=False, stripall=False)
212
+ elif language == "c/cpp":
213
+ lexer = CppLexer(stripnl=False, stripall=False)
214
+ else:
215
+ # unknown language, use fallback
216
+ return html_escape(code)
217
+ html_code = highlight(
218
+ code, lexer,
219
+ HtmlFormatter(nowrap=True))
220
+ return html_code
221
+
222
+ def _save_annotation_body(self, cython_code, generated_code, annotation_items, scopes, covered_lines=None):
223
+ outlist = ['<div class="cython">']
224
+ pos_comment_marker = '/* \N{HORIZONTAL ELLIPSIS} */\n'
225
+ new_calls_map = {
226
+ name: 0 for name in
227
+ 'refnanny trace py_macro_api py_c_api pyx_macro_api pyx_c_api error_goto'.split()
228
+ }.copy
229
+
230
+ self.mark_pos(None)
231
+
232
+ def annotate(match):
233
+ group_name = match.lastgroup
234
+ calls[group_name] += 1
235
+ return f"<span class='{group_name}'>{match.group(group_name)}</span>"
236
+
237
+ lines = self._htmlify_code(cython_code, "cython").splitlines()
238
+ lineno_width = len(str(len(lines)))
239
+ if not covered_lines:
240
+ covered_lines = None
241
+
242
+ for k, line in enumerate(lines, 1):
243
+ try:
244
+ c_code = generated_code[k]
245
+ except KeyError:
246
+ c_code = ''
247
+ else:
248
+ c_code = _replace_pos_comment(pos_comment_marker, c_code)
249
+ if c_code.startswith(pos_comment_marker):
250
+ c_code = c_code[len(pos_comment_marker):]
251
+ c_code = html_escape(c_code)
252
+
253
+ calls = new_calls_map()
254
+ c_code = _parse_code(annotate, c_code)
255
+ score = (5 * calls['py_c_api'] + 2 * calls['pyx_c_api'] +
256
+ calls['py_macro_api'] + calls['pyx_macro_api'])
257
+
258
+ if c_code:
259
+ onclick = self._onclick_attr
260
+ expandsymbol = '+'
261
+ else:
262
+ onclick = ''
263
+ expandsymbol = '&#xA0;'
264
+
265
+ covered = ''
266
+ if covered_lines is not None and k in covered_lines:
267
+ hits = covered_lines[k]
268
+ if hits is not None:
269
+ covered = 'run' if hits else 'mis'
270
+
271
+ outlist.append(
272
+ f'<pre class="cython line score-{score}"{onclick}>'
273
+ # generate line number with expand symbol in front,
274
+ # and the right number of digit
275
+ f'{expandsymbol}<span class="{covered}">{k:0{lineno_width}d}</span>: {line.rstrip()}</pre>\n'
276
+ )
277
+ if c_code:
278
+ outlist.append(f"<pre class='cython code score-{score} {covered}'>{c_code}</pre>")
279
+ outlist.append("</div>")
280
+
281
+ # now the whole c-code if needed:
282
+ if self.show_entire_c_code:
283
+ complete_code_as_html = self._htmlify_code(self.buffer.getvalue(), "c/cpp")
284
+ outlist.append(
285
+ '<p><div class="cython">'
286
+ f"<pre class='cython line'{self._onclick_attr}>+ {AnnotationCCodeWriter.COMPLETE_CODE_TITLE}</pre>\n"
287
+ f"<pre class='cython code'>{complete_code_as_html}</pre>"
288
+ "</div></p>"
289
+ )
290
+
291
+ return outlist
292
+
293
+
294
+ _parse_code = re.compile((
295
+ br'(?P<refnanny>__Pyx_X?(?:GOT|GIVE)REF|__Pyx_RefNanny[A-Za-z]+)|'
296
+ br'(?P<trace>__Pyx_Trace[A-Za-z]+)|'
297
+ br'(?:'
298
+ br'(?P<pyx_macro_api>__Pyx_[A-Z][A-Z_]+)|'
299
+ br'(?P<pyx_c_api>(?:__Pyx_[A-Z][a-z_][A-Za-z_]*)|__pyx_convert_[A-Za-z_]*)|'
300
+ br'(?P<py_macro_api>Py[A-Z][a-z]+_[A-Z][A-Z_]+)|'
301
+ br'(?P<py_c_api>Py[A-Z][a-z]+_[A-Z][a-z][A-Za-z_]*)'
302
+ br')(?=\()|' # look-ahead to exclude subsequent '(' from replacement
303
+ br'(?P<error_goto>(?:(?<=;) *if [^;]* +)?__PYX_ERR\([^)]+\))'
304
+ ).decode('ascii')).sub
305
+
306
+
307
+ _replace_pos_comment = re.compile(
308
+ # this matches what Cython generates as code line marker comment
309
+ br'^\s*/\*(?:(?:[^*]|\*[^/])*\n)+\s*\*/\s*\n'.decode('ascii'),
310
+ re.M
311
+ ).sub
312
+
313
+
314
+ class AnnotationItem:
315
+
316
+ def __init__(self, style, text, tag="", size=0):
317
+ self.style = style
318
+ self.text = text
319
+ self.tag = tag
320
+ self.size = size
321
+
322
+ def start(self):
323
+ return "<span class='cython tag %s' title='%s'>%s" % (self.style, self.text, self.tag)
324
+
325
+ def end(self):
326
+ return self.size, "</span>"