passagemath-objects 10.6.46__cp314-cp314t-macosx_13_0_x86_64.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.

Potentially problematic release.


This version of passagemath-objects might be problematic. Click here for more details.

Files changed (280) hide show
  1. passagemath_objects/.dylibs/libgmp.10.dylib +0 -0
  2. passagemath_objects/__init__.py +3 -0
  3. passagemath_objects-10.6.46.dist-info/METADATA +115 -0
  4. passagemath_objects-10.6.46.dist-info/RECORD +280 -0
  5. passagemath_objects-10.6.46.dist-info/WHEEL +6 -0
  6. passagemath_objects-10.6.46.dist-info/top_level.txt +3 -0
  7. sage/all__sagemath_objects.py +37 -0
  8. sage/arith/all__sagemath_objects.py +5 -0
  9. sage/arith/long.pxd +411 -0
  10. sage/arith/numerical_approx.cpython-314t-darwin.so +0 -0
  11. sage/arith/numerical_approx.pxd +35 -0
  12. sage/arith/numerical_approx.pyx +75 -0
  13. sage/arith/power.cpython-314t-darwin.so +0 -0
  14. sage/arith/power.pxd +31 -0
  15. sage/arith/power.pyx +127 -0
  16. sage/categories/action.cpython-314t-darwin.so +0 -0
  17. sage/categories/action.pxd +29 -0
  18. sage/categories/action.pyx +641 -0
  19. sage/categories/algebra_functor.py +745 -0
  20. sage/categories/all__sagemath_objects.py +33 -0
  21. sage/categories/basic.py +62 -0
  22. sage/categories/cartesian_product.py +295 -0
  23. sage/categories/category.py +3401 -0
  24. sage/categories/category_cy_helper.cpython-314t-darwin.so +0 -0
  25. sage/categories/category_cy_helper.pxd +8 -0
  26. sage/categories/category_cy_helper.pyx +322 -0
  27. sage/categories/category_singleton.cpython-314t-darwin.so +0 -0
  28. sage/categories/category_singleton.pxd +3 -0
  29. sage/categories/category_singleton.pyx +342 -0
  30. sage/categories/category_types.py +637 -0
  31. sage/categories/category_with_axiom.py +2876 -0
  32. sage/categories/covariant_functorial_construction.py +703 -0
  33. sage/categories/facade_sets.py +228 -0
  34. sage/categories/functor.cpython-314t-darwin.so +0 -0
  35. sage/categories/functor.pxd +7 -0
  36. sage/categories/functor.pyx +691 -0
  37. sage/categories/homset.py +1338 -0
  38. sage/categories/homsets.py +364 -0
  39. sage/categories/isomorphic_objects.py +73 -0
  40. sage/categories/map.cpython-314t-darwin.so +0 -0
  41. sage/categories/map.pxd +34 -0
  42. sage/categories/map.pyx +2106 -0
  43. sage/categories/morphism.cpython-314t-darwin.so +0 -0
  44. sage/categories/morphism.pxd +14 -0
  45. sage/categories/morphism.pyx +895 -0
  46. sage/categories/objects.py +167 -0
  47. sage/categories/primer.py +1696 -0
  48. sage/categories/pushout.py +4834 -0
  49. sage/categories/quotients.py +64 -0
  50. sage/categories/realizations.py +200 -0
  51. sage/categories/sets_cat.py +3290 -0
  52. sage/categories/sets_with_partial_maps.py +52 -0
  53. sage/categories/subobjects.py +64 -0
  54. sage/categories/subquotients.py +21 -0
  55. sage/categories/with_realizations.py +311 -0
  56. sage/cpython/__init__.py +19 -0
  57. sage/cpython/_py2_random.py +619 -0
  58. sage/cpython/all.py +3 -0
  59. sage/cpython/atexit.cpython-314t-darwin.so +0 -0
  60. sage/cpython/atexit.pyx +269 -0
  61. sage/cpython/builtin_types.cpython-314t-darwin.so +0 -0
  62. sage/cpython/builtin_types.pyx +7 -0
  63. sage/cpython/cython_metaclass.cpython-314t-darwin.so +0 -0
  64. sage/cpython/cython_metaclass.h +117 -0
  65. sage/cpython/cython_metaclass.pxd +3 -0
  66. sage/cpython/cython_metaclass.pyx +130 -0
  67. sage/cpython/debug.cpython-314t-darwin.so +0 -0
  68. sage/cpython/debug.pyx +302 -0
  69. sage/cpython/dict_del_by_value.cpython-314t-darwin.so +0 -0
  70. sage/cpython/dict_del_by_value.pxd +9 -0
  71. sage/cpython/dict_del_by_value.pyx +191 -0
  72. sage/cpython/dict_internal.h +245 -0
  73. sage/cpython/getattr.cpython-314t-darwin.so +0 -0
  74. sage/cpython/getattr.pxd +9 -0
  75. sage/cpython/getattr.pyx +439 -0
  76. sage/cpython/pycore_long.h +97 -0
  77. sage/cpython/pycore_long.pxd +10 -0
  78. sage/cpython/python_debug.h +44 -0
  79. sage/cpython/python_debug.pxd +47 -0
  80. sage/cpython/pyx_visit.h +13 -0
  81. sage/cpython/string.cpython-314t-darwin.so +0 -0
  82. sage/cpython/string.pxd +76 -0
  83. sage/cpython/string.pyx +34 -0
  84. sage/cpython/string_impl.h +60 -0
  85. sage/cpython/type.cpython-314t-darwin.so +0 -0
  86. sage/cpython/type.pxd +2 -0
  87. sage/cpython/type.pyx +40 -0
  88. sage/cpython/wrapperdescr.pxd +67 -0
  89. sage/ext/all__sagemath_objects.py +3 -0
  90. sage/ext/ccobject.h +64 -0
  91. sage/ext/cplusplus.pxd +17 -0
  92. sage/ext/mod_int.h +30 -0
  93. sage/ext/mod_int.pxd +24 -0
  94. sage/ext/stdsage.pxd +39 -0
  95. sage/groups/all__sagemath_objects.py +1 -0
  96. sage/groups/group.cpython-314t-darwin.so +0 -0
  97. sage/groups/group.pxd +14 -0
  98. sage/groups/group.pyx +322 -0
  99. sage/groups/old.cpython-314t-darwin.so +0 -0
  100. sage/groups/old.pxd +14 -0
  101. sage/groups/old.pyx +219 -0
  102. sage/libs/all__sagemath_objects.py +3 -0
  103. sage/libs/gmp/__init__.py +1 -0
  104. sage/libs/gmp/all.pxd +6 -0
  105. sage/libs/gmp/binop.pxd +23 -0
  106. sage/libs/gmp/misc.pxd +8 -0
  107. sage/libs/gmp/mpf.pxd +88 -0
  108. sage/libs/gmp/mpn.pxd +57 -0
  109. sage/libs/gmp/mpq.pxd +57 -0
  110. sage/libs/gmp/mpz.pxd +202 -0
  111. sage/libs/gmp/pylong.cpython-314t-darwin.so +0 -0
  112. sage/libs/gmp/pylong.pxd +12 -0
  113. sage/libs/gmp/pylong.pyx +150 -0
  114. sage/libs/gmp/random.pxd +25 -0
  115. sage/libs/gmp/randomize.pxd +59 -0
  116. sage/libs/gmp/types.pxd +53 -0
  117. sage/libs/gmpxx.pxd +19 -0
  118. sage/misc/abstract_method.py +276 -0
  119. sage/misc/all__sagemath_objects.py +43 -0
  120. sage/misc/bindable_class.py +253 -0
  121. sage/misc/c3_controlled.cpython-314t-darwin.so +0 -0
  122. sage/misc/c3_controlled.pxd +2 -0
  123. sage/misc/c3_controlled.pyx +1402 -0
  124. sage/misc/cachefunc.cpython-314t-darwin.so +0 -0
  125. sage/misc/cachefunc.pxd +43 -0
  126. sage/misc/cachefunc.pyx +3781 -0
  127. sage/misc/call.py +188 -0
  128. sage/misc/classcall_metaclass.cpython-314t-darwin.so +0 -0
  129. sage/misc/classcall_metaclass.pxd +14 -0
  130. sage/misc/classcall_metaclass.pyx +599 -0
  131. sage/misc/constant_function.cpython-314t-darwin.so +0 -0
  132. sage/misc/constant_function.pyx +130 -0
  133. sage/misc/decorators.py +747 -0
  134. sage/misc/fast_methods.cpython-314t-darwin.so +0 -0
  135. sage/misc/fast_methods.pxd +20 -0
  136. sage/misc/fast_methods.pyx +351 -0
  137. sage/misc/flatten.py +90 -0
  138. sage/misc/fpickle.cpython-314t-darwin.so +0 -0
  139. sage/misc/fpickle.pyx +177 -0
  140. sage/misc/function_mangling.cpython-314t-darwin.so +0 -0
  141. sage/misc/function_mangling.pxd +11 -0
  142. sage/misc/function_mangling.pyx +308 -0
  143. sage/misc/inherit_comparison.cpython-314t-darwin.so +0 -0
  144. sage/misc/inherit_comparison.pxd +5 -0
  145. sage/misc/inherit_comparison.pyx +105 -0
  146. sage/misc/instancedoc.cpython-314t-darwin.so +0 -0
  147. sage/misc/instancedoc.pyx +331 -0
  148. sage/misc/lazy_attribute.cpython-314t-darwin.so +0 -0
  149. sage/misc/lazy_attribute.pyx +607 -0
  150. sage/misc/lazy_format.py +135 -0
  151. sage/misc/lazy_import.cpython-314t-darwin.so +0 -0
  152. sage/misc/lazy_import.pyx +1299 -0
  153. sage/misc/lazy_import_cache.py +36 -0
  154. sage/misc/lazy_list.cpython-314t-darwin.so +0 -0
  155. sage/misc/lazy_list.pxd +19 -0
  156. sage/misc/lazy_list.pyx +1187 -0
  157. sage/misc/lazy_string.cpython-314t-darwin.so +0 -0
  158. sage/misc/lazy_string.pxd +7 -0
  159. sage/misc/lazy_string.pyx +546 -0
  160. sage/misc/misc.py +1066 -0
  161. sage/misc/misc_c.cpython-314t-darwin.so +0 -0
  162. sage/misc/misc_c.pxd +3 -0
  163. sage/misc/misc_c.pyx +766 -0
  164. sage/misc/namespace_package.py +37 -0
  165. sage/misc/nested_class.cpython-314t-darwin.so +0 -0
  166. sage/misc/nested_class.pxd +3 -0
  167. sage/misc/nested_class.pyx +394 -0
  168. sage/misc/persist.cpython-314t-darwin.so +0 -0
  169. sage/misc/persist.pyx +1251 -0
  170. sage/misc/prandom.py +418 -0
  171. sage/misc/randstate.cpython-314t-darwin.so +0 -0
  172. sage/misc/randstate.pxd +30 -0
  173. sage/misc/randstate.pyx +1059 -0
  174. sage/misc/repr.py +203 -0
  175. sage/misc/reset.cpython-314t-darwin.so +0 -0
  176. sage/misc/reset.pyx +196 -0
  177. sage/misc/sage_ostools.cpython-314t-darwin.so +0 -0
  178. sage/misc/sage_ostools.pyx +323 -0
  179. sage/misc/sage_timeit.py +275 -0
  180. sage/misc/sage_timeit_class.cpython-314t-darwin.so +0 -0
  181. sage/misc/sage_timeit_class.pyx +120 -0
  182. sage/misc/sage_unittest.py +637 -0
  183. sage/misc/sageinspect.py +2768 -0
  184. sage/misc/session.cpython-314t-darwin.so +0 -0
  185. sage/misc/session.pyx +392 -0
  186. sage/misc/superseded.py +557 -0
  187. sage/misc/test_nested_class.py +228 -0
  188. sage/misc/timing.py +264 -0
  189. sage/misc/unknown.py +222 -0
  190. sage/misc/verbose.py +253 -0
  191. sage/misc/weak_dict.cpython-314t-darwin.so +0 -0
  192. sage/misc/weak_dict.pxd +15 -0
  193. sage/misc/weak_dict.pyx +1231 -0
  194. sage/modules/all__sagemath_objects.py +1 -0
  195. sage/modules/module.cpython-314t-darwin.so +0 -0
  196. sage/modules/module.pxd +5 -0
  197. sage/modules/module.pyx +329 -0
  198. sage/rings/all__sagemath_objects.py +3 -0
  199. sage/rings/integer_fake.h +22 -0
  200. sage/rings/integer_fake.pxd +55 -0
  201. sage/sets/all__sagemath_objects.py +3 -0
  202. sage/sets/pythonclass.cpython-314t-darwin.so +0 -0
  203. sage/sets/pythonclass.pxd +9 -0
  204. sage/sets/pythonclass.pyx +247 -0
  205. sage/structure/__init__.py +4 -0
  206. sage/structure/all.py +30 -0
  207. sage/structure/category_object.cpython-314t-darwin.so +0 -0
  208. sage/structure/category_object.pxd +28 -0
  209. sage/structure/category_object.pyx +1087 -0
  210. sage/structure/coerce.cpython-314t-darwin.so +0 -0
  211. sage/structure/coerce.pxd +44 -0
  212. sage/structure/coerce.pyx +2107 -0
  213. sage/structure/coerce_actions.cpython-314t-darwin.so +0 -0
  214. sage/structure/coerce_actions.pxd +27 -0
  215. sage/structure/coerce_actions.pyx +988 -0
  216. sage/structure/coerce_dict.cpython-314t-darwin.so +0 -0
  217. sage/structure/coerce_dict.pxd +51 -0
  218. sage/structure/coerce_dict.pyx +1557 -0
  219. sage/structure/coerce_exceptions.py +23 -0
  220. sage/structure/coerce_maps.cpython-314t-darwin.so +0 -0
  221. sage/structure/coerce_maps.pxd +28 -0
  222. sage/structure/coerce_maps.pyx +718 -0
  223. sage/structure/debug_options.cpython-314t-darwin.so +0 -0
  224. sage/structure/debug_options.pxd +6 -0
  225. sage/structure/debug_options.pyx +54 -0
  226. sage/structure/dynamic_class.py +541 -0
  227. sage/structure/element.cpython-314t-darwin.so +0 -0
  228. sage/structure/element.pxd +272 -0
  229. sage/structure/element.pyx +4772 -0
  230. sage/structure/element_wrapper.cpython-314t-darwin.so +0 -0
  231. sage/structure/element_wrapper.pxd +12 -0
  232. sage/structure/element_wrapper.pyx +582 -0
  233. sage/structure/factorization.py +1422 -0
  234. sage/structure/factorization_integer.py +105 -0
  235. sage/structure/factory.cpython-314t-darwin.so +0 -0
  236. sage/structure/factory.pyx +786 -0
  237. sage/structure/formal_sum.py +489 -0
  238. sage/structure/gens_py.py +73 -0
  239. sage/structure/global_options.py +1743 -0
  240. sage/structure/indexed_generators.py +863 -0
  241. sage/structure/list_clone.cpython-314t-darwin.so +0 -0
  242. sage/structure/list_clone.pxd +65 -0
  243. sage/structure/list_clone.pyx +1867 -0
  244. sage/structure/list_clone_demo.cpython-314t-darwin.so +0 -0
  245. sage/structure/list_clone_demo.pyx +248 -0
  246. sage/structure/list_clone_timings.py +179 -0
  247. sage/structure/list_clone_timings_cy.cpython-314t-darwin.so +0 -0
  248. sage/structure/list_clone_timings_cy.pyx +86 -0
  249. sage/structure/mutability.cpython-314t-darwin.so +0 -0
  250. sage/structure/mutability.pxd +21 -0
  251. sage/structure/mutability.pyx +348 -0
  252. sage/structure/nonexact.py +69 -0
  253. sage/structure/parent.cpython-314t-darwin.so +0 -0
  254. sage/structure/parent.pxd +112 -0
  255. sage/structure/parent.pyx +3093 -0
  256. sage/structure/parent_base.cpython-314t-darwin.so +0 -0
  257. sage/structure/parent_base.pxd +13 -0
  258. sage/structure/parent_base.pyx +44 -0
  259. sage/structure/parent_gens.cpython-314t-darwin.so +0 -0
  260. sage/structure/parent_gens.pxd +22 -0
  261. sage/structure/parent_gens.pyx +377 -0
  262. sage/structure/parent_old.cpython-314t-darwin.so +0 -0
  263. sage/structure/parent_old.pxd +25 -0
  264. sage/structure/parent_old.pyx +294 -0
  265. sage/structure/proof/__init__.py +1 -0
  266. sage/structure/proof/all.py +243 -0
  267. sage/structure/proof/proof.py +300 -0
  268. sage/structure/richcmp.cpython-314t-darwin.so +0 -0
  269. sage/structure/richcmp.pxd +213 -0
  270. sage/structure/richcmp.pyx +495 -0
  271. sage/structure/sage_object.cpython-314t-darwin.so +0 -0
  272. sage/structure/sage_object.pxd +3 -0
  273. sage/structure/sage_object.pyx +988 -0
  274. sage/structure/sage_object_test.py +19 -0
  275. sage/structure/sequence.py +937 -0
  276. sage/structure/set_factories.py +1178 -0
  277. sage/structure/set_factories_example.py +527 -0
  278. sage/structure/support_view.py +179 -0
  279. sage/structure/test_factory.py +56 -0
  280. sage/structure/unique_representation.py +1359 -0
@@ -0,0 +1,2768 @@
1
+ # sage_setup: distribution = sagemath-objects
2
+ r"""
3
+ Inspect Python, Sage, and Cython objects
4
+
5
+ This module extends parts of Python's inspect module to Cython objects.
6
+
7
+ EXAMPLES::
8
+
9
+ sage: from sage.misc.sageinspect import *
10
+
11
+ Test introspection of modules defined in Python and Cython files:
12
+
13
+ Cython modules::
14
+
15
+ sage: sage_getfile(sage.rings.rational)
16
+ '.../rational.pyx'
17
+ sage: sage_getdoc(sage.rings.rational).lstrip()
18
+ 'Rational Numbers...'
19
+ sage: sage_getsource(sage.rings.rational)
20
+ '...Rational Numbers...'
21
+
22
+ Python modules::
23
+
24
+ sage: sage_getfile(sage.misc.sageinspect)
25
+ '.../sageinspect.py'
26
+ sage: print(sage_getdoc(sage.misc.sageinspect).lstrip()[:40])
27
+ Inspect Python, Sage, and Cython objects
28
+ sage: sage_getsource(sage.misc.sageinspect).lstrip()[51:-1]
29
+ 'Inspect Python, Sage, and Cython objects...'
30
+
31
+ Test introspection of classes defined in Python and Cython files:
32
+
33
+ Cython classes::
34
+
35
+ sage: sage_getfile(sage.rings.rational.Rational)
36
+ '.../rational.pyx'
37
+ sage: sage_getdoc(sage.rings.rational.Rational).lstrip()
38
+ 'A rational number...'
39
+ sage: sage_getsource(sage.rings.rational.Rational)
40
+ 'cdef class Rational...'
41
+
42
+ Python classes::
43
+
44
+ sage: sage_getfile(BlockFinder)
45
+ '.../sage/misc/sageinspect.py'
46
+ sage: sage_getdoc(BlockFinder).lstrip()[:50] # needs sphinx
47
+ 'Provide a "tokeneater()" method to detect the end '
48
+ sage: sage_getsource(BlockFinder)
49
+ 'class BlockFinder:...'
50
+
51
+ Test introspection of functions defined in Python and Cython files:
52
+
53
+ Cython functions::
54
+
55
+ sage: sage_getdef(sage.rings.rational.make_rational, obj_name='mr')
56
+ 'mr(s)'
57
+ sage: sage_getfile(sage.rings.rational.make_rational)
58
+ '.../rational.pyx'
59
+ sage: sage_getdoc(sage.rings.rational.make_rational).lstrip()
60
+ 'Make a rational number ...'
61
+ sage: sage_getsource(sage.rings.rational.make_rational)
62
+ '@cython.binding(True)\ndef make_rational(s):...'
63
+
64
+ Python functions::
65
+
66
+ sage: sage_getdef(sage.misc.sageinspect.sage_getfile, obj_name='sage_getfile')
67
+ 'sage_getfile(obj)'
68
+ sage: sage_getfile(sage.misc.sageinspect.sage_getfile)
69
+ '.../sageinspect.py'
70
+ sage: sage_getdoc(sage.misc.sageinspect.sage_getfile).lstrip()
71
+ 'Get the full file name associated to "obj" as a string...'
72
+ sage: sage_getsource(sage.misc.sageinspect.sage_getfile)[4:]
73
+ 'sage_getfile(obj):...'
74
+
75
+ Unfortunately, no argspec is extractable from builtins. Hence, we use a
76
+ generic argspec::
77
+
78
+ sage: sage_getdef(''.find, 'find')
79
+ 'find(*args, **kwds)'
80
+ sage: sage_getdef(str.find, 'find')
81
+ 'find(*args, **kwds)'
82
+
83
+ By :issue:`9976` and :issue:`14017`, introspection also works for interactively
84
+ defined Cython code, and with rather tricky argument lines::
85
+
86
+ sage: # needs sage.misc.cython
87
+ sage: cython('def foo(unsigned int x=1, a=\')"\', b={not (2+1==3):\'bar\'}, *args, **kwds): return')
88
+ sage: print(sage_getsource(foo))
89
+ def foo(unsigned int x=1, a=')"', b={not (2+1==3):'bar'}, *args, **kwds): return
90
+ sage: sage_getargspec(foo)
91
+ FullArgSpec(args=['x', 'a', 'b'], varargs='args', varkw='kwds', defaults=(1, ')"', {False: 'bar'}), kwonlyargs=[], kwonlydefaults=None, annotations={})
92
+
93
+ AUTHORS:
94
+
95
+ - Originally taken from Fernando Perez's IPython
96
+ - William Stein: extensive modifications
97
+ - William Stein: in :func:`_sage_getargspec_cython`, a modified version of
98
+ ``inspect.getargspec`` from the Python Standard Library, which was taken from
99
+ IPython for use in Sage
100
+ - Nick Alexander: extensions, testing
101
+ - Simon King: some extension for Cython, generalisation of SageArgSpecVisitor
102
+ - Simon King: in :func:`sage_getsourcelines`, if a class has no docstring then let the
103
+ class definition be found starting from the ``__init__`` method.
104
+ - Simon King: in :func:`sage_getsourcelines`, get source lines for dynamic classes
105
+ - Simon King: in :func:`_sage_getargspec_cython`, return an ``ArgSpec``, fix some bugs
106
+ - Simon King (2011-09): added :func:`_sage_getsourcelines_name_with_dot`
107
+ - Simon King (2013-02): in :func:`_sage_getargspec_cython`, recognise varargs and
108
+ default values in cython code, and return an ``ArgSpec``
109
+ """
110
+
111
+ import ast
112
+ import inspect
113
+ import functools
114
+ import os
115
+ import sys
116
+ import tokenize
117
+ import re
118
+ from inspect import Signature, Parameter
119
+
120
+ try:
121
+ import importlib.machinery as import_machinery
122
+ except ImportError:
123
+ pass
124
+
125
+
126
+ def is_function_or_cython_function(obj):
127
+ """
128
+ Check whether something is a function.
129
+
130
+ This is a variant of :func:`inspect.isfunction`:
131
+ We assume that anything which has a genuine ``__code__``
132
+ attribute (not using ``__getattr__`` overrides) is a function.
133
+ This is meant to support Cython functions.
134
+
135
+ Think twice before using this function (or any function from the
136
+ :mod:`inspect` or :mod:`sage.misc.sageinspect` modules). Most uses of
137
+ :func:`inspect.isfunction` in ordinary library code can be replaced by
138
+ :func:`callable`.
139
+
140
+ EXAMPLES::
141
+
142
+ sage: from sage.misc.sageinspect import is_function_or_cython_function
143
+ sage: def f(): pass
144
+ sage: is_function_or_cython_function(f)
145
+ True
146
+ sage: is_function_or_cython_function(lambda x:x)
147
+ True
148
+ sage: from sage.categories.coercion_methods import _mul_parent
149
+ sage: is_function_or_cython_function(_mul_parent)
150
+ True
151
+ sage: is_function_or_cython_function(Integer.digits) # unbound method
152
+ False
153
+ sage: is_function_or_cython_function(Integer(1).digits) # bound method
154
+ False
155
+
156
+ TESTS:
157
+
158
+ Verify that ipywidgets can correctly determine signatures of Cython
159
+ functions::
160
+
161
+ sage: from ipywidgets.widgets.interaction import signature
162
+ sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import fast_mandelbrot_plot # needs sage.plot sage.symbolic
163
+ sage: signature(fast_mandelbrot_plot) # random # needs sage.plot sage.symbolic
164
+ <IPython.utils._signatures.Signature object at 0x7f3ec8274e10>
165
+ """
166
+ # We use type(obj) instead of just obj to avoid __getattr__().
167
+ # Some types, like methods, will return the __code__ of the
168
+ # underlying function in __getattr__() but we don't want to
169
+ # detect those as functions.
170
+ return hasattr(type(obj), "__code__")
171
+
172
+
173
+ def isclassinstance(obj):
174
+ r"""
175
+ Check if argument is instance of non built-in class.
176
+
177
+ INPUT:
178
+
179
+ - ``obj`` -- object
180
+
181
+ EXAMPLES::
182
+
183
+ sage: from sage.misc.sageinspect import isclassinstance
184
+ sage: isclassinstance(int)
185
+ False
186
+ sage: class myclass: pass
187
+ sage: isclassinstance(myclass())
188
+ True
189
+ sage: isclassinstance(myclass)
190
+ False
191
+ sage: class mymetaclass(type): pass
192
+ sage: class myclass2(metaclass=mymetaclass): pass
193
+ sage: isclassinstance(myclass2)
194
+ False
195
+ """
196
+ builtin_mods = {'__builtin__', 'builtins', 'exceptions'}
197
+
198
+ return (not inspect.isclass(obj) and
199
+ hasattr(obj, '__class__') and
200
+ hasattr(obj.__class__, '__module__') and
201
+ obj.__class__.__module__ not in builtin_mods and
202
+ # Starting with Cython 3, Cython's builtin types have __module__ set
203
+ # to the shared module names like _cython_3_0_0.
204
+ not (isinstance(obj.__class__.__module__, str) and
205
+ obj.__class__.__module__.startswith('_cython_')) and
206
+ # In Cython 3.1, they have 'member_descriptor' type
207
+ 'cython_function_or_method' not in str(obj.__class__.__module__))
208
+
209
+
210
+ # Parse strings of form "File: sage/rings/rational.pyx (starting at line 1080)"
211
+ # "\ " protects a space in re.VERBOSE mode.
212
+ __embedded_position_re = re.compile(r'''
213
+ ^ # anchor to the beginning of the line
214
+ File:\ (?P<FILENAME>.*?) # match File: then filename
215
+ \ \(starting\ at\ line\ (?P<LINENO>\d+)\) # match line number
216
+ \n? # if there is a newline, eat it
217
+ (?P<ORIGINAL>.*) # the original docstring is the end
218
+ \Z # anchor to the end of the string
219
+ ''', re.MULTILINE | re.DOTALL | re.VERBOSE)
220
+
221
+ # Parse Python identifiers
222
+ __identifier_re = re.compile(r"^[^\d\W]\w*")
223
+
224
+
225
+ def _extract_embedded_position(docstring):
226
+ r"""
227
+ If docstring has a Cython embedded position, return a tuple
228
+ (original_docstring, filename, line). If not, return None.
229
+
230
+ INPUT:
231
+
232
+ - ``docstring`` -- string
233
+
234
+ EXAMPLES::
235
+
236
+ sage: from sage.misc.sageinspect import _extract_embedded_position
237
+ sage: import inspect
238
+ sage: _extract_embedded_position(inspect.getdoc(var))[1][-21:] # needs sage.symbolic
239
+ 'sage/calculus/var.pyx'
240
+
241
+ TESTS:
242
+
243
+ The following has been fixed in :issue:`13916`::
244
+
245
+ sage: cython('''cpdef test_funct(x, y): return''') # needs sage.misc.cython
246
+ sage: func_doc = inspect.getdoc(test_funct) # needs sage.misc.cython
247
+ sage: with open(_extract_embedded_position(func_doc)[1]) as f: # needs sage.misc.cython
248
+ ....: print(f.read())
249
+ cpdef test_funct(x, y): return
250
+
251
+ Ensure that the embedded filename of the compiled function is
252
+ correct. In particular it should be relative to ``spyx_tmp()`` in
253
+ order for certain documentation functions to work properly. See
254
+ :issue:`24097`::
255
+
256
+ sage: from sage.env import DOT_SAGE
257
+ sage: from sage.misc.sage_ostools import restore_cwd
258
+ sage: with restore_cwd(DOT_SAGE): # needs sage.misc.cython
259
+ ....: cython('''cpdef test_funct(x, y): return''')
260
+ sage: func_doc = inspect.getdoc(test_funct) # needs sage.misc.cython
261
+ sage: with open(_extract_embedded_position(func_doc)[1]) as f: # needs sage.misc.cython
262
+ ....: print(f.read())
263
+ cpdef test_funct(x, y): return
264
+ """
265
+ try:
266
+ res = __embedded_position_re.search(docstring)
267
+ except TypeError:
268
+ return None
269
+
270
+ if res is None:
271
+ return None
272
+
273
+ raw_filename = res.group('FILENAME')
274
+ filename = raw_filename
275
+
276
+ if not os.path.isabs(filename):
277
+ # Try some common path prefixes for Cython modules built by/for Sage
278
+ # 1) Module in the sage src tree
279
+ # 2) Module compiled by Sage's inline cython() compiler
280
+ from sage.misc.temporary_file import spyx_tmp
281
+ if raw_filename.startswith('sage/'):
282
+ import sage
283
+ from sage.env import SAGE_SRC
284
+ try_filenames = [os.path.join(directory, raw_filename.removeprefix('sage/'))
285
+ for directory in sage.__path__]
286
+ try_filenames.append(os.path.join(SAGE_SRC, raw_filename)) # meson editable install
287
+ else:
288
+ try_filenames = []
289
+ try_filenames.append(
290
+ os.path.join(spyx_tmp(), '_'.join(raw_filename.split('_')[:-1]),
291
+ raw_filename))
292
+ for try_filename in try_filenames:
293
+ if os.path.exists(try_filename):
294
+ filename = try_filename
295
+ break
296
+ # Otherwise we keep the relative path and just hope it's relative to
297
+ # the cwd; otherwise there's no way to be sure.
298
+
299
+ lineno = int(res.group('LINENO'))
300
+ original = res.group('ORIGINAL')
301
+ return (original, filename, lineno)
302
+
303
+
304
+ def _extract_embedded_signature(docstring, name):
305
+ r"""
306
+ If docstring starts with the embedded of a method called ``name``, return
307
+ a tuple (original_docstring, argspec). If not, return (docstring, None).
308
+
309
+ See :issue:`17814`.
310
+
311
+ INPUT:
312
+
313
+ - ``docstring`` -- string
314
+
315
+ EXAMPLES::
316
+
317
+ sage: from sage.misc.sageinspect import _extract_embedded_signature
318
+ sage: from sage.misc.nested_class import MainClass
319
+ sage: print(_extract_embedded_signature(MainClass.NestedClass.NestedSubClass.dummy.__doc__, 'dummy')[0])
320
+ File: ...sage/misc/nested_class.pyx (starting at line ...)
321
+ ...
322
+ sage: _extract_embedded_signature(MainClass.NestedClass.NestedSubClass.dummy.__doc__, 'dummy')[1]
323
+ FullArgSpec(args=['self', 'x', 'r'], varargs='args', varkw='kwds', defaults=((1, 2, 3.4),), kwonlyargs=[], kwonlydefaults=None, annotations={})
324
+ sage: _extract_embedded_signature(range.__call__.__doc__, '__call__')
325
+ ('Call self as a function.', None)
326
+
327
+ """
328
+ # If there is an embedded signature, it is in the first line
329
+ L = docstring.split(os.linesep, 1)
330
+ firstline = L[0]
331
+ # It is possible that the signature is of the form ClassName.method_name,
332
+ # and thus we need to do the following:
333
+ if name not in firstline:
334
+ return docstring, None
335
+ signature = firstline.split(name, 1)[-1]
336
+ if signature.startswith("(") and signature.endswith(")"):
337
+ docstring = L[1] if len(L) > 1 else '' # Remove first line, keep the rest
338
+ def_string = "def " + name + signature + ": pass"
339
+ try:
340
+ return docstring, inspect.FullArgSpec(*_sage_getargspec_cython(def_string))
341
+ except SyntaxError:
342
+ docstring = os.linesep.join(L)
343
+ return docstring, None
344
+
345
+
346
+ class BlockFinder:
347
+ """
348
+ Provide a :meth:`tokeneater` method to detect the end of a code block.
349
+
350
+ This is the Python library's :class:`inspect.BlockFinder` modified
351
+ to recognize Cython definitions.
352
+ """
353
+ def __init__(self):
354
+ self.indent = 0
355
+ self.islambda = False
356
+ self.started = False
357
+ self.passline = False
358
+ self.last = 1
359
+
360
+ def tokeneater(self, type, token, srow_scol, erow_ecol, line):
361
+ srow, scol = srow_scol
362
+ erow, ecol = erow_ecol
363
+ if not self.started:
364
+ # look for the first "(cp)def", "class" or "lambda"
365
+ if token in ("def", "cpdef", "class", "lambda"):
366
+ if token == "lambda":
367
+ self.islambda = True
368
+ self.started = True
369
+ self.passline = True # skip to the end of the line
370
+ elif type == tokenize.NEWLINE:
371
+ self.passline = False # stop skipping when a NEWLINE is seen
372
+ self.last = srow
373
+ if self.islambda: # lambdas always end at the first NEWLINE
374
+ raise inspect.EndOfBlock
375
+ elif self.passline:
376
+ pass
377
+ elif type == tokenize.INDENT:
378
+ self.indent = self.indent + 1
379
+ self.passline = True
380
+ elif type == tokenize.DEDENT:
381
+ self.indent = self.indent - 1
382
+ # the end of matching indent/dedent pairs end a block
383
+ # (note that this only works for "def"/"class" blocks,
384
+ # not e.g. for "if: else:" or "try: finally:" blocks)
385
+ if self.indent <= 0:
386
+ raise inspect.EndOfBlock
387
+ elif self.indent == 0 and type not in (tokenize.COMMENT, tokenize.NL):
388
+ # any other token on the same indentation level end the previous
389
+ # block as well, except the pseudo-tokens COMMENT and NL.
390
+ raise inspect.EndOfBlock
391
+
392
+
393
+ def _getblock(lines):
394
+ """
395
+ Extract the block of code at the top of the given list of lines.
396
+
397
+ This is the Python library's :func:`inspect.getblock`, except that
398
+ it uses an instance of our custom :class:`BlockFinder`.
399
+ """
400
+ blockfinder = BlockFinder()
401
+ iter_lines = iter(lines)
402
+ tokenizer = tokenize.tokenize
403
+
404
+ def readline():
405
+ return next(iter_lines).encode('utf-8')
406
+ try:
407
+ for tok in tokenizer(readline):
408
+ blockfinder.tokeneater(*tok)
409
+ except (inspect.EndOfBlock, IndentationError):
410
+ pass
411
+ return lines[:blockfinder.last]
412
+
413
+
414
+ def _extract_source(lines, lineno):
415
+ r"""
416
+ Given a list of lines or a multiline string and a starting lineno,
417
+ _extract_source returns [source_lines]. [source_lines] is the smallest
418
+ indentation block starting at lineno.
419
+
420
+ INPUT:
421
+
422
+ - ``lines`` -- string or list of strings
423
+ - ``lineno`` -- positive integer
424
+
425
+ EXAMPLES::
426
+
427
+ sage: from sage.misc.sageinspect import _extract_source
428
+ sage: s2 = "#hello\n\n class f():\n pass\n\n#goodbye"
429
+ sage: _extract_source(s2, 3)
430
+ [' class f():\n', ' pass\n']
431
+ """
432
+ if lineno < 1:
433
+ raise ValueError("Line numbering starts at 1! (tried to extract line {})".format(lineno))
434
+ lineno -= 1
435
+
436
+ if isinstance(lines, str):
437
+ lines = lines.splitlines(True) # true keeps the '\n'
438
+ if len(lines):
439
+ # Fixes an issue with getblock
440
+ lines[-1] += '\n'
441
+
442
+ return _getblock(lines[lineno:])
443
+
444
+
445
+ class SageArgSpecVisitor(ast.NodeVisitor):
446
+ """
447
+ A simple visitor class that walks an abstract-syntax tree (AST)
448
+ for a Python function's argspec. It returns the contents of nodes
449
+ representing the basic Python types: None, booleans, numbers,
450
+ strings, lists, tuples, and dictionaries. We use this class in
451
+ :func:`_sage_getargspec_from_ast` to extract an argspec from a
452
+ function's or method's source code.
453
+
454
+ EXAMPLES::
455
+
456
+ sage: import ast, sage.misc.sageinspect as sms
457
+ sage: visitor = sms.SageArgSpecVisitor()
458
+ sage: visitor.visit(ast.parse('[1,2,3]').body[0].value)
459
+ [1, 2, 3]
460
+ sage: v = visitor.visit(ast.parse("{'a':('e',2,[None,({False:True},'pi')]), 37.0:'temp'}").body[0].value)
461
+ sage: sorted(v.items(), key=lambda x: str(x[0]))
462
+ [(37.0, 'temp'), ('a', ('e', 2, [None, ({False: True}, 'pi')]))]
463
+ sage: v = ast.parse("jc = ['veni', 'vidi', 'vici']").body[0]
464
+ sage: isinstance(v, ast.Assign)
465
+ True
466
+ sage: attrs = [x for x in dir(v) if not x.startswith('__')]
467
+ sage: '_attributes' in attrs and '_fields' in attrs and 'col_offset' in attrs
468
+ True
469
+ sage: visitor.visit(v.targets[0])
470
+ 'jc'
471
+ sage: visitor.visit(v.value)
472
+ ['veni', 'vidi', 'vici']
473
+ """
474
+ def visit_Name(self, node):
475
+ """
476
+ Visit a Python AST :class:`ast.Name` node.
477
+
478
+ INPUT:
479
+
480
+ - ``node`` -- the node instance to visit
481
+
482
+ OUTPUT: ``None``, ``True``, ``False``, or the ``node``'s name as a string
483
+
484
+ EXAMPLES::
485
+
486
+ sage: import ast, sage.misc.sageinspect as sms
487
+ sage: visitor = sms.SageArgSpecVisitor()
488
+ sage: vis = lambda x: visitor.visit_Name(ast.parse(x).body[0].value)
489
+ sage: [vis(n) for n in ['foo', 'bar']]
490
+ ['foo', 'bar']
491
+ sage: [type(vis(n)) for n in ['foo', 'bar']]
492
+ [<class 'str'>, <class 'str'>]
493
+ """
494
+ return node.id
495
+
496
+ def visit_arg(self, node):
497
+ r"""
498
+ Visit a Python AST :class:`ast.arg` node.
499
+
500
+ This node type is only on Python 3, where function arguments are
501
+ more complex than just an identifier (e.g. they may also include
502
+ annotations).
503
+
504
+ For now we simply return the argument identifier as a string.
505
+
506
+ INPUT:
507
+
508
+ - ``node`` -- the node instance to visit
509
+
510
+ OUTPUT: the argument name
511
+
512
+ EXAMPLES::
513
+
514
+ sage: import ast, sage.misc.sageinspect as sms
515
+ sage: s = "def f(a, b=2, c={'a': [4, 5.5, False]}, d=(None, True)):\n return"
516
+ sage: visitor = sms.SageArgSpecVisitor()
517
+ sage: args = ast.parse(s).body[0].args.args
518
+ sage: [visitor.visit_arg(n) for n in args]
519
+ ['a', 'b', 'c', 'd']
520
+ """
521
+ return node.arg
522
+
523
+ def visit_List(self, node):
524
+ """
525
+ Visit a Python AST :class:`ast.List` node.
526
+
527
+ INPUT:
528
+
529
+ - ``node`` -- the node instance to visit
530
+
531
+ OUTPUT: the list the ``node`` represents
532
+
533
+ EXAMPLES::
534
+
535
+ sage: import ast, sage.misc.sageinspect as sms
536
+ sage: visitor = sms.SageArgSpecVisitor()
537
+ sage: vis = lambda x: visitor.visit_List(ast.parse(x).body[0].value)
538
+ sage: [vis(l) for l in ['[]', "['s', 't', 'u']", '[[e], [], [pi]]']]
539
+ [[], ['s', 't', 'u'], [['e'], [], ['pi']]]
540
+ """
541
+ return [self.visit(n) for n in node.elts]
542
+
543
+ def visit_Tuple(self, node):
544
+ """
545
+ Visit a Python AST :class:`ast.Tuple` node.
546
+
547
+ INPUT:
548
+
549
+ - ``node`` -- the node instance to visit
550
+
551
+ OUTPUT: the tuple the ``node`` represents
552
+
553
+ EXAMPLES::
554
+
555
+ sage: import ast, sage.misc.sageinspect as sms
556
+ sage: visitor = sms.SageArgSpecVisitor()
557
+ sage: vis = lambda x: visitor.visit_Tuple(ast.parse(x).body[0].value)
558
+ sage: [vis(t) for t in ['()', '(x,y)', '("Au", "Al", "Cu")']]
559
+ [(), ('x', 'y'), ('Au', 'Al', 'Cu')]
560
+ """
561
+ return tuple(self.visit(n) for n in node.elts)
562
+
563
+ def visit_Dict(self, node):
564
+ """
565
+ Visit a Python AST :class:`ast.Dict` node.
566
+
567
+ INPUT:
568
+
569
+ - ``node`` -- the node instance to visit
570
+
571
+ OUTPUT: the dictionary the ``node`` represents
572
+
573
+ EXAMPLES::
574
+
575
+ sage: import ast, sage.misc.sageinspect as sms
576
+ sage: visitor = sms.SageArgSpecVisitor()
577
+ sage: vis = lambda x: visitor.visit_Dict(ast.parse(x).body[0].value)
578
+ sage: v = [vis(d) for d in ['{}', "{1:one, 'two':2, other:bother}"]]
579
+ sage: [sorted(d.items(), key=lambda x: str(x[0])) for d in v]
580
+ [[], [(1, 'one'), ('other', 'bother'), ('two', 2)]]
581
+ """
582
+ d = {}
583
+ for k, v in zip(node.keys, node.values):
584
+ d[self.visit(k)] = self.visit(v)
585
+ return d
586
+
587
+ def visit_BoolOp(self, node):
588
+ """
589
+ Visit a Python AST :class:`ast.BoolOp` node.
590
+
591
+ INPUT:
592
+
593
+ - ``node`` -- the node instance to visit
594
+
595
+ OUTPUT: the result that ``node`` represents
596
+
597
+ EXAMPLES::
598
+
599
+ sage: import ast, sage.misc.sageinspect as sms
600
+ sage: visitor = sms.SageArgSpecVisitor()
601
+ sage: vis = lambda x: visitor.visit(ast.parse(x).body[0].value)
602
+ sage: [vis(d) for d in ['True and 1', 'False or 3 or None', '3 and 4']] #indirect doctest
603
+ [1, 3, 4]
604
+ """
605
+ op = node.op.__class__.__name__
606
+ L = list(node.values)
607
+ out = self.visit(L.pop(0))
608
+ if op == 'And':
609
+ while L:
610
+ next = self.visit(L.pop(0))
611
+ out = out and next
612
+ return out
613
+ if op == 'Or':
614
+ while L:
615
+ next = self.visit(L.pop(0))
616
+ out = out or next
617
+ return out
618
+
619
+ def visit_Compare(self, node):
620
+ """
621
+ Visit a Python AST :class:`ast.Compare` node.
622
+
623
+ INPUT:
624
+
625
+ - ``node`` -- the node instance to visit
626
+
627
+ OUTPUT: the result that ``node`` represents
628
+
629
+ EXAMPLES::
630
+
631
+ sage: import ast, sage.misc.sageinspect as sms
632
+ sage: visitor = sms.SageArgSpecVisitor()
633
+ sage: vis = lambda x: visitor.visit_Compare(ast.parse(x).body[0].value)
634
+ sage: [vis(d) for d in ['1<2==2!=3', '1==1>2', '1<2>1', '1<3<2<4']]
635
+ [True, False, True, False]
636
+ """
637
+ left = self.visit(node.left)
638
+ ops = list(node.ops)
639
+ comparators = list(node.comparators) # the things to be compared with.
640
+ while ops:
641
+ op = ops.pop(0).__class__.__name__
642
+ right = self.visit(comparators.pop(0))
643
+ if op == 'Lt':
644
+ if not left < right:
645
+ return False
646
+ elif op == 'LtE':
647
+ if not left <= right:
648
+ return False
649
+ elif op == 'Gt':
650
+ if not left > right:
651
+ return False
652
+ elif op == 'GtE':
653
+ if not left >= right:
654
+ return False
655
+ elif op == 'Eq':
656
+ if not left == right:
657
+ return False
658
+ elif op == 'NotEq':
659
+ if not left != right:
660
+ return False
661
+ left = right
662
+ return True
663
+
664
+ def visit_BinOp(self, node):
665
+ """
666
+ Visit a Python AST :class:`ast.BinOp` node.
667
+
668
+ INPUT:
669
+
670
+ - ``node`` -- the node instance to visit
671
+
672
+ OUTPUT: the result that ``node`` represents
673
+
674
+ EXAMPLES::
675
+
676
+ sage: import ast, sage.misc.sageinspect as sms
677
+ sage: visitor = sms.SageArgSpecVisitor()
678
+ sage: vis = lambda x: visitor.visit(ast.parse(x).body[0].value)
679
+ sage: [vis(d) for d in ['(3+(2*4))', '7|8', '5^3', '7/3', '7//3', '3<<4']] #indirect doctest
680
+ [11, 15, 6, 2.3333333333333335, 2, 48]
681
+ """
682
+ op = node.op.__class__.__name__
683
+ if op == 'Add':
684
+ return self.visit(node.left) + self.visit(node.right)
685
+ if op == 'Mult':
686
+ return self.visit(node.left) * self.visit(node.right)
687
+ if op == 'BitAnd':
688
+ return self.visit(node.left) & self.visit(node.right)
689
+ if op == 'BitOr':
690
+ return self.visit(node.left) | self.visit(node.right)
691
+ if op == 'BitXor':
692
+ return self.visit(node.left) ^ self.visit(node.right)
693
+ if op == 'Div':
694
+ return self.visit(node.left) / self.visit(node.right)
695
+ if op == 'Eq':
696
+ return self.visit(node.left) == self.visit(node.right)
697
+ if op == 'FloorDiv':
698
+ return self.visit(node.left) // self.visit(node.right)
699
+ if op == 'NotEq':
700
+ return self.visit(node.left) != self.visit(node.right)
701
+ if op == 'NotIn':
702
+ return self.visit(node.left) not in self.visit(node.right)
703
+ if op == 'Pow':
704
+ return self.visit(node.left) ** self.visit(node.right)
705
+ if op == 'RShift':
706
+ return self.visit(node.left) >> self.visit(node.right)
707
+ if op == 'LShift':
708
+ return self.visit(node.left) << self.visit(node.right)
709
+ if op == 'Sub':
710
+ return self.visit(node.left) - self.visit(node.right)
711
+ if op == 'Gt':
712
+ return self.visit(node.left) > self.visit(node.right)
713
+ if op == 'GtE':
714
+ return self.visit(node.left) >= self.visit(node.right)
715
+ if op == 'In':
716
+ return self.visit(node.left) in self.visit(node.right)
717
+ if op == 'Is':
718
+ return self.visit(node.left) is self.visit(node.right)
719
+ if op == 'IsNot':
720
+ return self.visit(node.left) is not self.visit(node.right)
721
+ if op == 'Lt':
722
+ return self.visit(node.left) < self.visit(node.right)
723
+ if op == 'LtE':
724
+ return self.visit(node.left) <= self.visit(node.right)
725
+ if op == 'Mod':
726
+ return self.visit(node.left) % self.visit(node.right)
727
+
728
+ def visit_UnaryOp(self, node):
729
+ """
730
+ Visit a Python AST :class:`ast.BinOp` node.
731
+
732
+ INPUT:
733
+
734
+ - ``node`` -- the node instance to visit
735
+
736
+ OUTPUT: the result that ``node`` represents
737
+
738
+ EXAMPLES::
739
+
740
+ sage: import ast, sage.misc.sageinspect as sms
741
+ sage: visitor = sms.SageArgSpecVisitor()
742
+ sage: vis = lambda x: visitor.visit_UnaryOp(ast.parse(x).body[0].value)
743
+ sage: [vis(d) for d in ['+(3*2)', '-(3*2)']]
744
+ [6, -6]
745
+ """
746
+ op = node.op.__class__.__name__
747
+ if op == 'Not':
748
+ return not self.visit(node.operand)
749
+ if op == 'UAdd':
750
+ return self.visit(node.operand)
751
+ if op == 'USub':
752
+ return -self.visit(node.operand)
753
+
754
+ def visit_Constant(self, node):
755
+ """
756
+ Visit a Python AST :class:`ast.Constant` node.
757
+
758
+ INPUT:
759
+
760
+ - ``node`` -- the node instance to visit
761
+
762
+ OUTPUT: the constant value the ``node`` represents
763
+
764
+ EXAMPLES::
765
+
766
+ sage: import ast, sage.misc.sageinspect as sms
767
+ sage: visitor = sms.SageArgSpecVisitor()
768
+ sage: vis = lambda x: visitor.visit_Constant(ast.parse(x).body[0].value)
769
+ sage: [vis(n) for n in ['123', '0', '3.14', '"hello"', 'True', 'False', 'None']]
770
+ [123, 0, 3.14, 'hello', True, False, None]
771
+ sage: [type(vis(n)) for n in ['123', '0', '3.14', '"hello"', 'True', 'False', 'None']]
772
+ [<class 'int'>, <class 'int'>, <class 'float'>, <class 'str'>, <class 'bool'>, <class 'bool'>, <class 'NoneType'>]
773
+ """
774
+ return node.value
775
+
776
+
777
+ def _grep_first_pair_of_parentheses(s):
778
+ r"""
779
+ Return the first matching pair of parentheses in a code string.
780
+
781
+ INPUT:
782
+
783
+ - ``s`` -- string
784
+
785
+ OUTPUT:
786
+
787
+ A substring of the input, namely the part between the first
788
+ (outmost) matching pair of parentheses (including the
789
+ parentheses).
790
+
791
+ Parentheses between single or double quotation marks do not
792
+ count. If no matching pair of parentheses can be found, a
793
+ :exc:`SyntaxError` is raised.
794
+
795
+ EXAMPLES::
796
+
797
+ sage: from sage.misc.sageinspect import _grep_first_pair_of_parentheses
798
+ sage: code = 'def foo(a="\'):", b=4):\n return'
799
+ sage: _grep_first_pair_of_parentheses(code)
800
+ '(a="\'):", b=4)'
801
+ sage: code = 'def foo(a="%s):", \'b=4):\n return'%("'")
802
+ sage: _grep_first_pair_of_parentheses(code)
803
+ Traceback (most recent call last):
804
+ ...
805
+ SyntaxError: The given string does not contain balanced parentheses
806
+ """
807
+ out = []
808
+ single_quote = False
809
+ double_quote = False
810
+ escaped = False
811
+ level = 0
812
+ for c in s:
813
+ if level > 0:
814
+ out.append(c)
815
+ if c == '(' and not single_quote and not double_quote and not escaped:
816
+ level += 1
817
+ elif c == '"' and not single_quote and not escaped:
818
+ double_quote = not double_quote
819
+ elif c == "'" and not double_quote and not escaped:
820
+ single_quote = not single_quote
821
+ elif c == ')' and not single_quote and not double_quote and not escaped:
822
+ if level == 1:
823
+ return '(' + ''.join(out)
824
+ level -= 1
825
+ elif c == "\\" and (single_quote or double_quote):
826
+ escaped = not escaped
827
+ else:
828
+ escaped = False
829
+ raise SyntaxError("The given string does not contain balanced parentheses")
830
+
831
+
832
+ def _split_syntactical_unit(s):
833
+ """
834
+ Split off a sub-expression from the start of a given string.
835
+
836
+ INPUT:
837
+
838
+ - ``s`` -- string
839
+
840
+ OUTPUT:
841
+
842
+ A pair ``unit, s2``, such that ``unit`` is the string representation of a
843
+ string (single or double quoted) or of a sub-expression surrounded by
844
+ brackets (round, square or curly brackets), or of an identifier, or a
845
+ single character, if none of the above is available. The given string ``s``
846
+ is obtained by appending some whitespace followed by ``s2`` to ``unit``.
847
+
848
+ Blank space between the units is removed.
849
+
850
+ EXAMPLES::
851
+
852
+ sage: from sage.misc.sageinspect import _split_syntactical_unit
853
+ sage: s = "(Hel) lo_1=[)\"!\" ] '''? {world} '''?"
854
+ sage: while s:
855
+ ....: u, s = _split_syntactical_unit(s)
856
+ ....: print(u)
857
+ (Hel)
858
+ lo_1
859
+ =
860
+ [)"!"]
861
+ '''? {world} '''
862
+ ?
863
+
864
+ If the string ends before the unit is completed (mispatching parentheses
865
+ or missing quotation mark), then a syntax error is raised::
866
+
867
+ sage: s = "'''({SAGE}]"
868
+ sage: _split_syntactical_unit(s)
869
+ Traceback (most recent call last):
870
+ ...
871
+ SyntaxError: EOF while scanning string literal
872
+ sage: s = "({SAGE}]"
873
+ sage: _split_syntactical_unit(s)
874
+ Traceback (most recent call last):
875
+ ...
876
+ SyntaxError: Syntactical group starting with '(' did not end with ')'
877
+
878
+ Numbers are not recognised::
879
+
880
+ sage: _split_syntactical_unit('123')
881
+ ('1', '23')
882
+
883
+ TESTS:
884
+
885
+ The following was fixed in :issue:`16309`::
886
+
887
+ sage: _split_syntactical_unit('()): pass')
888
+ ('()', '): pass')
889
+ """
890
+ s = s.strip()
891
+ if not s:
892
+ return s
893
+
894
+ # Split a given string at the next unescaped quotation mark
895
+ def split_string(s, quot):
896
+ escaped = False
897
+ l = len(quot)
898
+ for i in range(len(s)):
899
+ if s[i] == '\\':
900
+ escaped = not escaped
901
+ continue
902
+ if not escaped and s[i:i + l] == quot:
903
+ return s[:i], s[i + l:]
904
+ escaped = False
905
+ raise SyntaxError("EOF while scanning string literal")
906
+ # 1. s is a triple-quoted string
907
+ if s.startswith('"""'):
908
+ a, b = split_string(s[3:], '"""')
909
+ return '"""' + a + '"""', b.strip()
910
+ if s.startswith('r"""'):
911
+ a, b = split_string(s[4:], '"""')
912
+ return 'r"""'+a+'"""', b.strip()
913
+ if s.startswith("'''"):
914
+ a, b = split_string(s[3:], "'''")
915
+ return "'''"+a+"'''", b.strip()
916
+ if s.startswith("r'''"):
917
+ a, b = split_string(s[4:], "'''")
918
+ return "r'''"+a+"'''", b.strip()
919
+
920
+ # 2. s is a single-quoted string
921
+ if s.startswith('"'):
922
+ a, b = split_string(s[1:], '"')
923
+ return '"'+a+'"', b.strip()
924
+ if s.startswith("'"):
925
+ a, b = split_string(s[1:], "'")
926
+ return "'"+a+"'", b.strip()
927
+ if s.startswith('r"'):
928
+ a, b = split_string(s[2:], '"')
929
+ return 'r"'+a+'"', b.strip()
930
+ if s.startswith("r'"):
931
+ a, b = split_string(s[2:], "'")
932
+ return "r'"+a+"'", b.strip()
933
+
934
+ # 3. s is not a string
935
+ start = s[0]
936
+ out = [start]
937
+ if start == '(':
938
+ stop = ')'
939
+ elif start == '[':
940
+ stop = ']'
941
+ elif start == '{':
942
+ stop = '}'
943
+ elif start == '\\':
944
+ # note that python would raise a syntax error
945
+ # if the line contains anything but whitespace
946
+ # after the backslash. But we assume here that
947
+ # the input is syntactically correct.
948
+ return _split_syntactical_unit(s[1:])
949
+ elif start == '#':
950
+ linebreak = s.index(os.linesep)
951
+ if linebreak == -1:
952
+ return '', ''
953
+ return '', s[linebreak:].strip()
954
+ else:
955
+ M = __identifier_re.search(s)
956
+ if M is None:
957
+ return s[0], s[1:].strip()
958
+ return M.group(), s[M.end():].strip()
959
+
960
+ s = s[1:]
961
+ while s:
962
+ tmp_group, s = _split_syntactical_unit(s)
963
+ out.append(tmp_group)
964
+ s = s.strip()
965
+ if tmp_group == stop:
966
+ return ''.join(out), s
967
+ elif s.startswith(stop):
968
+ out.append(stop)
969
+ return ''.join(out), s[1:].strip()
970
+ raise SyntaxError("Syntactical group starting with %s did not end with %s" % (repr(start), repr(stop)))
971
+
972
+
973
+ def _sage_getargspec_from_ast(source):
974
+ r"""
975
+ Return an argspec for a Python function or method by compiling its
976
+ source to an abstract-syntax tree (AST) and walking its ``args``
977
+ subtrees with :class:`SageArgSpecVisitor`. We use this in
978
+ :func:`_sage_getargspec_cython`.
979
+
980
+ INPUT:
981
+
982
+ - ``source`` -- string; the function's (or method's) source code
983
+ definition. The function's body is ignored.
984
+
985
+ OUTPUT: an instance of :obj:`inspect.ArgSpec`, i.e., a named tuple
986
+
987
+ EXAMPLES::
988
+
989
+ sage: import inspect, sage.misc.sageinspect as sms
990
+ sage: from_ast = sms._sage_getargspec_from_ast
991
+ sage: s = "def f(a, b=2, c={'a': [4, 5.5, False]}, d=(None, True)):\n return"
992
+ sage: from_ast(s)
993
+ FullArgSpec(args=['a', 'b', 'c', 'd'], varargs=None, varkw=None, defaults=(2, {'a': [4, 5.5, False]}, (None, True)), kwonlyargs=[], kwonlydefaults=None, annotations={})
994
+ sage: context = {}
995
+ sage: exec(compile(s, '<string>', 'single'), context)
996
+ sage: inspect.getfullargspec(context['f'])
997
+ FullArgSpec(args=['a', 'b', 'c', 'd'], varargs=None, varkw=None, defaults=(2, {'a': [4, 5.5, False]}, (None, True)), kwonlyargs=[], kwonlydefaults=None, annotations={})
998
+ sage: from_ast(s) == inspect.getfullargspec(context['f'])
999
+ True
1000
+ sage: set(from_ast(sms.sage_getsource(x)) == inspect.getfullargspec(x) for x in [factor, identity_matrix, Graph.__init__]) # needs sage.graphs sage.modules
1001
+ {True}
1002
+ """
1003
+ ast_args = ast.parse(source.lstrip()).body[0].args
1004
+
1005
+ visitor = SageArgSpecVisitor()
1006
+ args = [visitor.visit(a) for a in ast_args.args]
1007
+ defaults = [visitor.visit(d) for d in ast_args.defaults]
1008
+
1009
+ # vararg and kwarg may be None
1010
+ vararg = getattr(ast_args.vararg, 'arg', None)
1011
+ kwarg = getattr(ast_args.kwarg, 'arg', None)
1012
+
1013
+ return inspect.FullArgSpec(args, vararg, kwarg,
1014
+ tuple(defaults) if defaults else None,
1015
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1016
+
1017
+
1018
+ def _sage_getargspec_cython(source):
1019
+ r"""
1020
+ inspect.getargspec from source code. That is, get the names and
1021
+ default values of a function's arguments.
1022
+
1023
+ INPUT:
1024
+
1025
+ - ``source`` -- string; the function's (or method's) source code
1026
+ definition. The function's body is ignored. The definition may
1027
+ contain type definitions for the function arguments.
1028
+
1029
+ OUTPUT: an instance of :class:`inspect.FullArgSpec`, i.e., a named tuple
1030
+
1031
+ EXAMPLES::
1032
+
1033
+ sage: from sage.misc.sageinspect import _sage_getargspec_cython as sgc
1034
+ sage: sgc("cpdef double abc(self, Element x=None, Parent base=0):")
1035
+ FullArgSpec(args=['self', 'x', 'base'], varargs=None, varkw=None, defaults=(None, 0), kwonlyargs=[], kwonlydefaults=None, annotations={})
1036
+ sage: sgc("def __init__(self, x=None, unsigned int base=0):")
1037
+ FullArgSpec(args=['self', 'x', 'base'], varargs=None, varkw=None, defaults=(None, 0), kwonlyargs=[], kwonlydefaults=None, annotations={})
1038
+ sage: sgc('def o(p, r={}, *q, **s) except? -1:')
1039
+ FullArgSpec(args=['p', 'r'], varargs='q', varkw='s', defaults=({},), kwonlyargs=[], kwonlydefaults=None, annotations={})
1040
+ sage: sgc('cpdef how(r=(None, "u:doing?")):')
1041
+ FullArgSpec(args=['r'], varargs=None, varkw=None, defaults=((None, 'u:doing?'),), kwonlyargs=[], kwonlydefaults=None, annotations={})
1042
+ sage: sgc('def _(x="):"):')
1043
+ FullArgSpec(args=['x'], varargs=None, varkw=None, defaults=('):',), kwonlyargs=[], kwonlydefaults=None, annotations={})
1044
+ sage: sgc('def f(z = {(1, 2, 3): True}):\n return z')
1045
+ FullArgSpec(args=['z'], varargs=None, varkw=None, defaults=({(1, 2, 3): True},), kwonlyargs=[], kwonlydefaults=None, annotations={})
1046
+ sage: sgc('def f(double x, z = {(1, 2, 3): True}):\n return z')
1047
+ FullArgSpec(args=['x', 'z'], varargs=None, varkw=None, defaults=({(1, 2, 3): True},), kwonlyargs=[], kwonlydefaults=None, annotations={})
1048
+ sage: sgc('def f(*args): pass')
1049
+ FullArgSpec(args=[], varargs='args', varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={})
1050
+ sage: sgc('def f(**args): pass')
1051
+ FullArgSpec(args=[], varargs=None, varkw='args', defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={})
1052
+
1053
+ Some malformed input is detected::
1054
+
1055
+ sage: sgc('def f(x, y')
1056
+ Traceback (most recent call last):
1057
+ ...
1058
+ SyntaxError: Unexpected EOF while parsing argument list
1059
+ sage: sgc('def f(*x = 5, z = {(1,2,3): True}): pass')
1060
+ Traceback (most recent call last):
1061
+ ...
1062
+ SyntaxError: invalid ...
1063
+ sage: sgc('def f(int *x = 5, z = {(1,2,3): True}): pass')
1064
+ Traceback (most recent call last):
1065
+ ...
1066
+ SyntaxError: Pointer types not allowed in def or cpdef functions
1067
+ sage: sgc('def f(x = , z = {(1,2,3): True}): pass')
1068
+ Traceback (most recent call last):
1069
+ ...
1070
+ SyntaxError: Definition of a default argument expected
1071
+ sage: sgc('def f(int x = 5, , z = {(1,2,3): True}): pass')
1072
+ Traceback (most recent call last):
1073
+ ...
1074
+ SyntaxError: invalid ...
1075
+
1076
+ TESTS:
1077
+
1078
+ Some input that is malformed in Python 2 but well formed in Cython or
1079
+ Python 3 is correctly parsed::
1080
+
1081
+ sage: def dummy_python(self, *args, x=1): pass
1082
+ sage: sgc("def dummy_python(self, *args, x=1): pass")
1083
+ FullArgSpec(args=['self', 'x'], varargs='args', varkw=None, defaults=(1,),
1084
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1085
+ sage: cython("def dummy_cython(self, *args, x=1): pass") # needs sage.misc.cython
1086
+ sage: sgc("def dummy_cython(self, *args, x=1): pass")
1087
+ FullArgSpec(args=['self', 'x'], varargs='args', varkw=None, defaults=(1,),
1088
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1089
+
1090
+ In some examples above, a syntax error was raised when a type
1091
+ definition contains a pointer. An exception is made for ``char*``,
1092
+ since C strings are acceptable input in public Cython functions::
1093
+
1094
+ sage: sgc('def f(char *x = "a string", z = {(1,2,3): True}): pass')
1095
+ FullArgSpec(args=['x', 'z'], varargs=None, varkw=None,
1096
+ defaults=('a string', {(1, 2, 3): True}),
1097
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1098
+ """
1099
+ assert isinstance(source, str)
1100
+ # the caller ought to ensure this, but if it forgets (e.g. passing None),
1101
+ # we avoid raising AttributeError to avoid confusing error message
1102
+ # and possible further hard-to-debug errors, see :issue:`39735`
1103
+ defpos = source.find('def ')
1104
+ assert defpos > -1, "The given source does not contain 'def'"
1105
+ s = source[defpos:].strip()
1106
+ while s:
1107
+ if s.startswith('('):
1108
+ break
1109
+ _, s = _split_syntactical_unit(s)
1110
+ s = s[1:].strip()
1111
+ if not s:
1112
+ raise SyntaxError("Function definition must contain an argument list")
1113
+
1114
+ # We remove the type declarations, build a dummy Python function, and
1115
+ # then call _get_argspec_from_ast. This should be
1116
+ # better than creating a complete parser for Cython syntax,
1117
+ # even though _split_syntactical_unit does part of the parsing work anyway.
1118
+
1119
+ cy_units = []
1120
+ while not s.startswith(')'):
1121
+ if not s:
1122
+ raise SyntaxError("Unexpected EOF while parsing argument list")
1123
+ u, s = _split_syntactical_unit(s)
1124
+ cy_units.append(u)
1125
+
1126
+ py_units = []
1127
+ name = None
1128
+ i = 0
1129
+ l = len(cy_units)
1130
+ expect_default = False
1131
+ nb_stars = 0
1132
+ varargs = None
1133
+ keywords = None
1134
+ while (i < l):
1135
+ unit = cy_units[i]
1136
+ if expect_default:
1137
+ if unit in ('=', '*', ','):
1138
+ raise SyntaxError("Definition of a default argument expected")
1139
+ while unit != ',':
1140
+ py_units.append(unit)
1141
+ i += 1
1142
+ if i == l:
1143
+ break
1144
+ unit = cy_units[i]
1145
+ expect_default = False
1146
+ name = None
1147
+ if nb_stars:
1148
+ raise SyntaxError("The %s argument has no default" % ('varargs' if nb_stars == 1 else 'keywords'))
1149
+ continue
1150
+ i += 1
1151
+ if unit == '*':
1152
+ if name:
1153
+ if name != 'char':
1154
+ raise SyntaxError("Pointer types not allowed in def or cpdef functions")
1155
+ else:
1156
+ continue
1157
+ else:
1158
+ nb_stars += 1
1159
+ continue
1160
+ elif unit == ',':
1161
+ if expect_default:
1162
+ raise SyntaxError("Unexpected EOF while parsing argument list")
1163
+ name = None
1164
+ if nb_stars:
1165
+ nb_stars = 0
1166
+ continue
1167
+ elif unit == '=':
1168
+ expect_default = True
1169
+ name = None
1170
+ if nb_stars:
1171
+ raise SyntaxError("The %s argument has no default" % ('varargs' if nb_stars == 1 else 'keywords'))
1172
+ else:
1173
+ name = unit
1174
+ if name is not None:
1175
+ # Is "name" part of a type definition?
1176
+ # If it is the last identifier before '=' or ',',
1177
+ # then it *is* a variable name,
1178
+ if i == l or cy_units[i] in ('=', ','):
1179
+ if nb_stars == 0:
1180
+ py_units.append(name)
1181
+ elif nb_stars == 1:
1182
+ if varargs is None:
1183
+ varargs = name
1184
+ # skip the "=" or ",", since varargs
1185
+ # is treated separately
1186
+ i += 1
1187
+ name = None
1188
+ nb_stars = 0
1189
+ else:
1190
+ raise SyntaxError("varargs cannot be defined twice")
1191
+ elif nb_stars == 2:
1192
+ if keywords is None:
1193
+ keywords = name
1194
+ # skip the "=" or ",", since varargs
1195
+ # is treated separately
1196
+ i += 1
1197
+ name = None
1198
+ nb_stars = 0
1199
+ else:
1200
+ raise SyntaxError("varargs cannot be defined twice")
1201
+ else:
1202
+ raise SyntaxError("variable declaration comprises at most two '*'")
1203
+ else:
1204
+ py_units.append(unit)
1205
+ if varargs is None:
1206
+ varargs = ''
1207
+ elif not py_units or py_units[-1] == ',':
1208
+ varargs = '*' + varargs
1209
+ else:
1210
+ varargs = ',*' + varargs
1211
+ if keywords is None:
1212
+ keywords = ''
1213
+ elif varargs or (py_units and py_units[-1] != ','):
1214
+ keywords = ',**' + keywords
1215
+ else:
1216
+ keywords = '**' + keywords
1217
+ return _sage_getargspec_from_ast('def dummy(' + ''.join(py_units) +
1218
+ varargs + keywords + '): pass')
1219
+
1220
+
1221
+ def sage_getfile(obj):
1222
+ r"""
1223
+ Get the full file name associated to ``obj`` as a string.
1224
+
1225
+ INPUT:
1226
+
1227
+ - ``obj`` -- a Sage object, module, etc.
1228
+
1229
+ EXAMPLES::
1230
+
1231
+ sage: from sage.misc.sageinspect import sage_getfile
1232
+ sage: sage_getfile(sage.rings.rational)
1233
+ '...sage/rings/rational.pyx'
1234
+ sage: from sage.algebras.steenrod.steenrod_algebra import Sq # needs sage.combinat sage.modules
1235
+ sage: sage_getfile(Sq) # needs sage.combinat sage.modules
1236
+ '...sage/algebras/steenrod/steenrod_algebra.py'
1237
+ sage: sage_getfile(x) # needs sage.symbolic
1238
+ '...sage/symbolic/expression.pyx'
1239
+
1240
+ The following tests against some bugs fixed in :issue:`9976`::
1241
+
1242
+ sage: from sage.combinat.partition_algebra import SetPartitionsAk # needs sage.combinat sage.modules
1243
+ sage: obj = SetPartitionsAk # needs sage.combinat sage.modules
1244
+ sage: sage_getfile(obj) # needs sage.combinat sage.modules
1245
+ '...sage/combinat/partition_algebra.py'
1246
+
1247
+ And here is another bug, fixed in :issue:`11298`::
1248
+
1249
+ sage: P.<x,y> = QQ[]
1250
+ sage: sage_getfile(P) # needs sage.libs.singular
1251
+ '...sage/rings/polynomial/multi_polynomial_libsingular...'
1252
+
1253
+ Another bug with editable meson install::
1254
+
1255
+ sage: P.<x,y> = QQ[]
1256
+ sage: I = P * [x,y]
1257
+ sage: path = sage_getfile(I.groebner_basis); path
1258
+ '.../sage/rings/qqbar_decorators.py'
1259
+ sage: path == sage_getfile(sage.rings.qqbar_decorators)
1260
+ True
1261
+
1262
+ A problem fixed in :issue:`16309`::
1263
+
1264
+ sage: cython( # needs sage.misc.cython
1265
+ ....: '''
1266
+ ....: class Bar: pass
1267
+ ....: cdef class Foo: pass
1268
+ ....: ''')
1269
+ sage: sage_getfile(Bar) # needs sage.misc.cython
1270
+ '...pyx'
1271
+ sage: sage_getfile(Foo) # needs sage.misc.cython
1272
+ '...pyx'
1273
+
1274
+ By :issue:`18249`, we return an empty string for Python builtins. In that
1275
+ way, there is no error when the user types, for example, ``range?``::
1276
+
1277
+ sage: sage_getfile(range)
1278
+ ''
1279
+ """
1280
+ # We try to extract from docstrings, but not using Python's inspect
1281
+ # because _sage_getdoc_unformatted is more robust.
1282
+ d = _sage_getdoc_unformatted(obj)
1283
+ pos = _extract_embedded_position(d)
1284
+ if pos is not None:
1285
+ (_, filename, _) = pos
1286
+ return filename
1287
+
1288
+ # The instance case
1289
+ if isclassinstance(obj):
1290
+ if isinstance(obj, functools.partial):
1291
+ return sage_getfile(obj.func)
1292
+ return sage_getfile(obj.__class__) # inspect.getabsfile(obj.__class__)
1293
+ else:
1294
+ if hasattr(obj, '__init__'):
1295
+ pos = _extract_embedded_position(_sage_getdoc_unformatted(obj.__init__))
1296
+ if pos is not None:
1297
+ (_, filename, _) = pos
1298
+ return filename
1299
+
1300
+ # No go? fall back to inspect.
1301
+ try:
1302
+ sourcefile = inspect.getabsfile(obj)
1303
+ except TypeError: # this happens for Python builtins
1304
+ return ''
1305
+ for suffix in import_machinery.EXTENSION_SUFFIXES:
1306
+ if sourcefile.endswith(suffix):
1307
+ # TODO: the following is incorrect in meson editable install
1308
+ # because the build is out-of-tree,
1309
+ # but as long as either the class or its __init__ method has a
1310
+ # docstring, _sage_getdoc_unformatted should return correct result
1311
+ # see https://github.com/mesonbuild/meson-python/issues/723
1312
+ return sourcefile.removesuffix(suffix)+os.path.extsep+'pyx'
1313
+ return sourcefile
1314
+
1315
+
1316
+ def sage_getfile_relative(obj):
1317
+ r"""
1318
+ Get the file name associated to ``obj`` as a string.
1319
+
1320
+ This is the same as :func:`sage_getfile`, but
1321
+ if the source file is part of the ``sage.*`` namespace, it
1322
+ makes the file name relative so that it starts with ``sage/``.
1323
+
1324
+ INPUT:
1325
+
1326
+ - ``obj`` -- a Sage object, module, etc.
1327
+
1328
+ EXAMPLES::
1329
+
1330
+ sage: from sage.misc.sageinspect import sage_getfile_relative
1331
+ sage: sage_getfile_relative(sage.rings.rational)
1332
+ 'sage/rings/rational.pyx'
1333
+ sage: from sage.algebras.steenrod.steenrod_algebra import Sq # needs sage.combinat sage.modules
1334
+ sage: sage_getfile_relative(Sq) # needs sage.combinat sage.modules
1335
+ 'sage/algebras/steenrod/steenrod_algebra.py'
1336
+ sage: sage_getfile_relative(x) # needs sage.symbolic
1337
+ 'sage/symbolic/expression.pyx'
1338
+ sage: sage_getfile_relative(range)
1339
+ ''
1340
+ """
1341
+ filename = sage_getfile(obj)
1342
+ if not filename:
1343
+ return filename
1344
+
1345
+ from os.path import relpath, normpath, commonprefix
1346
+
1347
+ def directories():
1348
+ try:
1349
+ from sage.env import SAGE_SRC
1350
+ except ImportError:
1351
+ pass
1352
+ else:
1353
+ if SAGE_SRC:
1354
+ yield normpath(os.path.join(SAGE_SRC, 'sage'))
1355
+ import sage
1356
+ yield from sage.__path__
1357
+
1358
+ for directory in directories():
1359
+ if commonprefix([filename, directory]) == directory:
1360
+ return os.path.join('sage', relpath(filename, directory))
1361
+
1362
+ return filename
1363
+
1364
+
1365
+ def sage_getargspec(obj):
1366
+ r"""
1367
+ Return the names and default values of a function's arguments.
1368
+
1369
+ INPUT:
1370
+
1371
+ - ``obj`` -- any callable object
1372
+
1373
+ OUTPUT:
1374
+
1375
+ A named tuple :class:`FullArgSpec` is returned, as specified by the
1376
+ Python library function :func:`inspect.getfullargspec`.
1377
+
1378
+ NOTE:
1379
+
1380
+ If the object has a method ``_sage_argspec_``, then the output of
1381
+ that method is transformed into a named tuple and then returned.
1382
+
1383
+ If a class instance has a method ``_sage_src_``, then its output
1384
+ is studied to determine the argspec. This is because currently
1385
+ the :class:`~sage.misc.cachefunc.CachedMethod` decorator has
1386
+ no ``_sage_argspec_`` method.
1387
+
1388
+ EXAMPLES::
1389
+
1390
+ sage: from sage.misc.sageinspect import sage_getargspec
1391
+ sage: def f(x, y, z=1, t=2, *args, **keywords):
1392
+ ....: pass
1393
+ sage: sage_getargspec(f)
1394
+ FullArgSpec(args=['x', 'y', 'z', 't'], varargs='args', varkw='keywords',
1395
+ defaults=(1, 2), kwonlyargs=[], kwonlydefaults=None, annotations={})
1396
+
1397
+ We now run :func:`sage_getargspec` on some functions from the Sage library::
1398
+
1399
+ sage: sage_getargspec(identity_matrix) # needs sage.modules
1400
+ FullArgSpec(args=['ring', 'n', 'sparse'], varargs=None, varkw=None,
1401
+ defaults=(0, False), kwonlyargs=[], kwonlydefaults=None,
1402
+ annotations={})
1403
+ sage: sage_getargspec(factor)
1404
+ FullArgSpec(args=['n', 'proof', 'int_', 'algorithm', 'verbose'],
1405
+ varargs=None, varkw='kwds', defaults=(None, False, 'pari', 0),
1406
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1407
+
1408
+ In the case of a class or a class instance, the :class:`FullArgSpec` of the
1409
+ ``__new__``, ``__init__`` or ``__call__`` method is returned::
1410
+
1411
+ sage: P.<x,y> = QQ[]
1412
+ sage: sage_getargspec(P) # needs sage.libs.singular
1413
+ FullArgSpec(args=['base_ring', 'n', 'names', 'order'],
1414
+ varargs=None, varkw=None, defaults=('degrevlex',),
1415
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1416
+ sage: sage_getargspec(P.__class__) # needs sage.libs.singular
1417
+ FullArgSpec(args=['self', 'x'], varargs='args', varkw='kwds', defaults=(0,),
1418
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1419
+
1420
+ The following tests against various bugs that were fixed in
1421
+ :issue:`9976`::
1422
+
1423
+ sage: from sage.rings.polynomial.real_roots import bernstein_polynomial_factory_ratlist # needs sage.libs.linbox sage.modules
1424
+ sage: sage_getargspec(bernstein_polynomial_factory_ratlist.coeffs_bitsize) # needs sage.libs.linbox sage.modules
1425
+ FullArgSpec(args=['self'], varargs=None, varkw=None, defaults=None,
1426
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1427
+ sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid # needs sage.rings.polynomial.pbori
1428
+ sage: sage_getargspec(BooleanMonomialMonoid.gen) # needs sage.rings.polynomial.pbori
1429
+ FullArgSpec(args=['self', 'i'], varargs=None, varkw=None, defaults=(0,),
1430
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1431
+ sage: I = P*[x,y]
1432
+ sage: sage_getargspec(I.groebner_basis) # needs sage.libs.singular
1433
+ FullArgSpec(args=['self', 'algorithm', 'deg_bound', 'mult_bound', 'prot'],
1434
+ varargs='args', varkw='kwds', defaults=('', None, None, False),
1435
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1436
+ sage: cython("cpdef int foo(x,y) except -1: return 1") # needs sage.misc.cython
1437
+ sage: sage_getargspec(foo) # needs sage.misc.cython
1438
+ FullArgSpec(args=['x', 'y'], varargs=None, varkw=None, defaults=None,
1439
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1440
+
1441
+ If a :func:`functools.partial` instance is involved, we see no other meaningful solution
1442
+ than to return the argspec of the underlying function::
1443
+
1444
+ sage: def f(a, b, c, d=1):
1445
+ ....: return a + b + c + d
1446
+ sage: import functools
1447
+ sage: f1 = functools.partial(f, 1, c=2)
1448
+ sage: sage_getargspec(f1)
1449
+ FullArgSpec(args=['a', 'b', 'c', 'd'], varargs=None, varkw=None, defaults=(1,),
1450
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1451
+
1452
+ TESTS:
1453
+
1454
+ By :issue:`9976`, rather complicated cases work. In the
1455
+ following example, we dynamically create an extension class
1456
+ that returns some source code, and the example shows that
1457
+ the source code is taken for granted, i.e., the argspec of
1458
+ an instance of that class does not coincide with the argspec
1459
+ of its call method. That behaviour is intended, since a
1460
+ decorated method appears to have the generic signature
1461
+ ``*args, **kwds``, but in fact it is only supposed to be called
1462
+ with the arguments requested by the underlying undecorated
1463
+ method. We saw an easy example above, namely ``I.groebner_basis``.
1464
+ Here is a more difficult one::
1465
+
1466
+ sage: # needs sage.misc.cython
1467
+ sage: cython_code = [
1468
+ ....: 'cdef class MyClass:',
1469
+ ....: ' def _sage_src_(self):',
1470
+ ....: ' return "def foo(x, a=\\\')\\\"\\\', b={(2+1):\\\'bar\\\', not 1:3, 3<<4:5}): return\\n"',
1471
+ ....: ' def __call__(self, m, n): return "something"']
1472
+ sage: cython('\n'.join(cython_code))
1473
+ sage: O = MyClass()
1474
+ sage: print(sage.misc.sageinspect.sage_getsource(O))
1475
+ def foo(x, a=')"', b={(2+1):'bar', not 1:3, 3<<4:5}): return
1476
+ sage: spec = sage.misc.sageinspect.sage_getargspec(O)
1477
+ sage: spec.args, spec.varargs, spec.varkw
1478
+ (['x', 'a', 'b'], None, None)
1479
+ sage: spec.defaults[0]
1480
+ ')"'
1481
+ sage: sorted(spec.defaults[1].items(), key=lambda x: str(x))
1482
+ [(3, 'bar'), (48, 5), (False, 3)]
1483
+ sage: sage.misc.sageinspect.sage_getargspec(O.__call__)
1484
+ FullArgSpec(args=['self', 'm', 'n'], varargs=None, varkw=None, defaults=None,
1485
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1486
+
1487
+ ::
1488
+
1489
+ sage: cython('def foo(x, a=\'\\\')"\', b={not (2+1==3):\'bar\'}): return') # needs sage.misc.cython
1490
+ sage: print(sage.misc.sageinspect.sage_getsource(foo)) # needs sage.misc.cython
1491
+ def foo(x, a='\')"', b={not (2+1==3):'bar'}): return
1492
+ <BLANKLINE>
1493
+ sage: sage.misc.sageinspect.sage_getargspec(foo) # needs sage.misc.cython
1494
+ FullArgSpec(args=['x', 'a', 'b'], varargs=None, varkw=None,
1495
+ defaults=('\')"', {False: 'bar'}),
1496
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1497
+
1498
+ The following produced a syntax error before the patch at :issue:`11913`,
1499
+ see also :issue:`26906`::
1500
+
1501
+ sage: sage.misc.sageinspect.sage_getargspec(r.lm) # optional - rpy2
1502
+ FullArgSpec(args=['self'], varargs='args', varkw='kwds', defaults=None,
1503
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1504
+
1505
+ The following was fixed in :issue:`16309`::
1506
+
1507
+ sage: # needs sage.misc.cython
1508
+ sage: cython(
1509
+ ....: '''
1510
+ ....: class Foo:
1511
+ ....: @staticmethod
1512
+ ....: def join(categories, bint as_list=False, tuple ignore_axioms=(), tuple axioms=()): pass
1513
+ ....: cdef class Bar:
1514
+ ....: @staticmethod
1515
+ ....: def join(categories, bint as_list=False, tuple ignore_axioms=(), tuple axioms=()): pass
1516
+ ....: cpdef meet(categories, bint as_list=False, tuple ignore_axioms=(), tuple axioms=()): pass
1517
+ ....: ''')
1518
+ sage: sage_getargspec(Foo.join)
1519
+ FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None,
1520
+ defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={})
1521
+ sage: sage_getargspec(Bar.join)
1522
+ FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None,
1523
+ defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={})
1524
+ sage: sage_getargspec(Bar.meet)
1525
+ FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None,
1526
+ defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={})
1527
+
1528
+ Test that :issue:`17009` is fixed::
1529
+
1530
+ sage: sage_getargspec(gap) # needs sage.libs.gap
1531
+ FullArgSpec(args=['self', 'x', 'name'], varargs=None, varkw=None,
1532
+ defaults=(None,), kwonlyargs=[], kwonlydefaults=None, annotations={})
1533
+
1534
+ By :issue:`17814`, the following gives the correct answer (previously, the
1535
+ defaults would have been found ``None``)::
1536
+
1537
+ sage: from sage.misc.nested_class import MainClass
1538
+ sage: sage_getargspec(MainClass.NestedClass.NestedSubClass.dummy)
1539
+ FullArgSpec(args=['self', 'x', 'r'], varargs='args', varkw='kwds',
1540
+ defaults=((1, 2, 3.4),), kwonlyargs=[], kwonlydefaults=None, annotations={})
1541
+
1542
+ In :issue:`18249` was decided to return a generic signature for Python
1543
+ builtin functions, rather than to raise an error (which is what Python's
1544
+ inspect module does)::
1545
+
1546
+ sage: import inspect
1547
+ sage: sage_getargspec(range)
1548
+ FullArgSpec(args=[], varargs='args', varkw='kwds', defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={})
1549
+
1550
+ Test that :issue:`28524` is fixed::
1551
+
1552
+ sage: from sage.repl.interpreter import get_test_shell
1553
+ sage: shell = get_test_shell()
1554
+ sage: shell.run_cell(
1555
+ ....: 'class Foo:\n'
1556
+ ....: ' def __call__(self):\n'
1557
+ ....: ' return None\n'
1558
+ ....: ' def __module__(self):\n'
1559
+ ....: ' return "sage.misc.sageinspect"\n'
1560
+ ....: ' def _sage_src_(self):\n'
1561
+ ....: ' return "the source code string"')
1562
+ sage: shell.run_cell('f = Foo()')
1563
+ sage: shell.run_cell('f??')
1564
+ ...the source code string...
1565
+ """
1566
+ from sage.misc.lazy_attribute import lazy_attribute
1567
+ from sage.misc.abstract_method import AbstractMethod
1568
+ if inspect.isclass(obj):
1569
+ return sage_getargspec(obj.__call__)
1570
+ if isinstance(obj, (lazy_attribute, AbstractMethod)):
1571
+ source = sage_getsource(obj)
1572
+ return inspect.FullArgSpec(*_sage_getargspec_cython(source))
1573
+ if not callable(obj):
1574
+ raise TypeError("obj is not a code object")
1575
+ try:
1576
+ return inspect.FullArgSpec(*obj._sage_argspec_())
1577
+ except (AttributeError, TypeError):
1578
+ pass
1579
+ # If we are lucky, the function signature is embedded in the docstring.
1580
+ docstring = _sage_getdoc_unformatted(obj)
1581
+ try:
1582
+ name = obj.__name__
1583
+ except AttributeError:
1584
+ name = type(obj).__name__
1585
+ argspec = _extract_embedded_signature(docstring, name)[1]
1586
+ if argspec is not None:
1587
+ return argspec
1588
+ if hasattr(obj, '__code__'):
1589
+ # Note that this may give a wrong result for the constants!
1590
+ try:
1591
+ args, varargs, varkw = inspect.getargs(obj.__code__)
1592
+ return inspect.FullArgSpec(args, varargs, varkw, obj.__defaults__,
1593
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1594
+ except (TypeError, AttributeError):
1595
+ pass
1596
+ if isclassinstance(obj):
1597
+ if hasattr(obj, '_sage_src_'): # it may be a decorator!
1598
+ source = sage_getsource(obj)
1599
+ try:
1600
+ # we try to find the definition and parse it by
1601
+ # _sage_getargspec_ast
1602
+ proxy = 'def dummy' + _grep_first_pair_of_parentheses(source) \
1603
+ + ':\n return'
1604
+ return _sage_getargspec_from_ast(proxy)
1605
+ except SyntaxError:
1606
+ # To fix trac #10860. See #11913 for more information.
1607
+ # See also #26906 and #28524.
1608
+ pass
1609
+ if isinstance(obj, functools.partial):
1610
+ base_spec = sage_getargspec(obj.func)
1611
+ return base_spec
1612
+ return sage_getargspec(obj.__class__.__call__)
1613
+ elif (hasattr(obj, '__objclass__') and hasattr(obj, '__name__') and
1614
+ obj.__name__ == 'next'):
1615
+ # Handle sage.rings.ring.FiniteFieldIterator.next and similar
1616
+ # slot wrappers. This is mainly to suppress Sphinx warnings.
1617
+ return ['self'], None, None, None
1618
+ else:
1619
+ # We try to get the argspec by reading the source, which may be
1620
+ # expensive, but should only be needed for functions defined outside
1621
+ # of the Sage library (since otherwise the signature should be
1622
+ # embedded in the docstring)
1623
+ try:
1624
+ source = sage_getsource(obj)
1625
+ except TypeError: # happens for Python builtins
1626
+ source = ''
1627
+ if source:
1628
+ return inspect.FullArgSpec(*_sage_getargspec_cython(source))
1629
+ else:
1630
+ func_obj = obj
1631
+
1632
+ # Otherwise we're (hopefully!) plain Python, so use inspect
1633
+ try:
1634
+ args, varargs, varkw = inspect.getargs(func_obj.__code__)
1635
+ except AttributeError:
1636
+ try:
1637
+ args, varargs, varkw = inspect.getargs(func_obj)
1638
+ except TypeError: # arg is not a code object
1639
+ # The above "hopefully" was wishful thinking:
1640
+ try:
1641
+ source = sage_getsource(obj)
1642
+ if source is not None:
1643
+ return inspect.FullArgSpec(*_sage_getargspec_cython(source))
1644
+ except TypeError: # This happens for Python builtins
1645
+ pass
1646
+ # The best we can do is to return a generic argspec
1647
+ args = []
1648
+ varargs = 'args'
1649
+ varkw = 'kwds'
1650
+ try:
1651
+ defaults = func_obj.__defaults__
1652
+ except AttributeError:
1653
+ defaults = None
1654
+ return inspect.FullArgSpec(args, varargs, varkw, defaults,
1655
+ kwonlyargs=[], kwonlydefaults=None, annotations={})
1656
+
1657
+
1658
+ def _fullargspec_to_signature(fullargspec):
1659
+ """
1660
+ Converts a :class:`FullArgSpec` instance to a :class:`Signature` instance by best effort.
1661
+ The opposite conversion is implemented in the source code of :func:`inspect.getfullargspec`.
1662
+
1663
+ EXAMPLES::
1664
+
1665
+ sage: from sage.misc.sageinspect import _fullargspec_to_signature
1666
+ sage: from inspect import FullArgSpec
1667
+ sage: fullargspec = FullArgSpec(args=['self', 'x', 'base'], varargs=None, varkw=None, defaults=(None, 0), kwonlyargs=[], kwonlydefaults=None, annotations={})
1668
+ sage: _fullargspec_to_signature(fullargspec)
1669
+ <Signature (self, x=None, base=0)>
1670
+
1671
+ TESTS::
1672
+
1673
+ sage: fullargspec = FullArgSpec(args=['p', 'r'], varargs='q', varkw='s', defaults=({},), kwonlyargs=[], kwonlydefaults=None, annotations={})
1674
+ sage: _fullargspec_to_signature(fullargspec)
1675
+ <Signature (p, r={}, *q, **s)>
1676
+ sage: fullargspec = FullArgSpec(args=['r'], varargs=None, varkw=None, defaults=((None, 'u:doing?'),), kwonlyargs=[], kwonlydefaults=None, annotations={})
1677
+ sage: _fullargspec_to_signature(fullargspec)
1678
+ <Signature (r=(None, 'u:doing?'))>
1679
+ sage: fullargspec = FullArgSpec(args=['x'], varargs=None, varkw=None, defaults=('):',), kwonlyargs=[], kwonlydefaults=None, annotations={})
1680
+ sage: _fullargspec_to_signature(fullargspec)
1681
+ <Signature (x='):')>
1682
+ sage: fullargspec = FullArgSpec(args=['z'], varargs=None, varkw=None, defaults=({(1, 2, 3): True},), kwonlyargs=[], kwonlydefaults=None, annotations={})
1683
+ sage: _fullargspec_to_signature(fullargspec)
1684
+ <Signature (z={(1, 2, 3): True})>
1685
+ sage: fullargspec = FullArgSpec(args=['x', 'z'], varargs=None, varkw=None, defaults=({(1, 2, 3): True},), kwonlyargs=[], kwonlydefaults=None, annotations={})
1686
+ sage: _fullargspec_to_signature(fullargspec)
1687
+ <Signature (x, z={(1, 2, 3): True})>
1688
+ sage: fullargspec = FullArgSpec(args=[], varargs='args', varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={})
1689
+ sage: _fullargspec_to_signature(fullargspec)
1690
+ <Signature (*args)>
1691
+ sage: fullargspec = FullArgSpec(args=[], varargs=None, varkw='args', defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={})
1692
+ sage: _fullargspec_to_signature(fullargspec)
1693
+ <Signature (**args)>
1694
+ sage: fullargspec = FullArgSpec(args=['self', 'x'], varargs='args', varkw=None, defaults=(1,), kwonlyargs=[], kwonlydefaults=None, annotations={})
1695
+ sage: _fullargspec_to_signature(fullargspec)
1696
+ <Signature (self, x=1, *args)>
1697
+ sage: fullargspec = FullArgSpec(args=['self', 'x'], varargs='args', varkw=None, defaults=(1,), kwonlyargs=[], kwonlydefaults=None, annotations={})
1698
+ sage: _fullargspec_to_signature(fullargspec)
1699
+ <Signature (self, x=1, *args)>
1700
+ sage: fullargspec = FullArgSpec(args=['x', 'z'], varargs=None, varkw=None, defaults=('a string', {(1, 2, 3): True}), kwonlyargs=[], kwonlydefaults=None, annotations={})
1701
+ sage: _fullargspec_to_signature(fullargspec)
1702
+ <Signature (x='a string', z={(1, 2, 3): True})>
1703
+ sage: _fullargspec_to_signature(FullArgSpec(args=['a'], varargs=None, varkw=None, defaults=None, kwonlyargs=['b', 'c'], kwonlydefaults={'b': 1}, annotations={}))
1704
+ <Signature (a, *, b=1, c)>
1705
+ sage: _fullargspec_to_signature(FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(1,), kwonlyargs=['c'], kwonlydefaults=None, annotations={}))
1706
+ <Signature (a, b=1, *, c)>
1707
+ """
1708
+ parameters = []
1709
+ defaults_start = len(fullargspec.args) - len(fullargspec.defaults) if fullargspec.defaults else None
1710
+
1711
+ for i, arg in enumerate(fullargspec.args):
1712
+ default = fullargspec.defaults[i - defaults_start] if defaults_start is not None and i >= defaults_start else Parameter.empty
1713
+ param = Parameter(arg, Parameter.POSITIONAL_OR_KEYWORD, default=default)
1714
+ parameters.append(param)
1715
+
1716
+ if fullargspec.varargs:
1717
+ param = Parameter(fullargspec.varargs, Parameter.VAR_POSITIONAL)
1718
+ parameters.append(param)
1719
+
1720
+ if fullargspec.varkw:
1721
+ param = Parameter(fullargspec.varkw, Parameter.VAR_KEYWORD)
1722
+ parameters.append(param)
1723
+
1724
+ for arg in fullargspec.kwonlyargs:
1725
+ param = Parameter(arg, Parameter.KEYWORD_ONLY, default=Parameter.empty if fullargspec.kwonlydefaults is None else
1726
+ fullargspec.kwonlydefaults.get(arg, Parameter.empty))
1727
+ parameters.append(param)
1728
+
1729
+ return Signature(parameters)
1730
+
1731
+
1732
+ def sage_signature(obj):
1733
+ r"""
1734
+ Return the names and default values of a function's arguments.
1735
+
1736
+ INPUT:
1737
+
1738
+ - ``obj`` -- any callable object
1739
+
1740
+ OUTPUT:
1741
+
1742
+ A :class:`Signature` is returned, as specified by the
1743
+ Python library function :func:`inspect.signature`.
1744
+
1745
+
1746
+ .. NOTE::
1747
+
1748
+ Currently the type information is not returned, because the output
1749
+ is converted from the return value of :func:`sage_getargspec`.
1750
+ This should be changed in the future.
1751
+
1752
+ EXAMPLES::
1753
+
1754
+ sage: from sage.misc.sageinspect import sage_signature
1755
+ sage: def f(x, y, z=1, t=2, *args, **keywords):
1756
+ ....: pass
1757
+ sage: sage_signature(f)
1758
+ <Signature (x, y, z=1, t=2, *args, **keywords)>
1759
+
1760
+ We now run :func:`sage_signature` on some functions from the Sage library::
1761
+
1762
+ sage: sage_signature(identity_matrix) # needs sage.modules
1763
+ <Signature (ring, n=0, sparse=False)>
1764
+ sage: sage_signature(factor)
1765
+ <Signature (n, proof=None, int_=False, algorithm='pari', verbose=0, **kwds)>
1766
+
1767
+ In the case of a class or a class instance, the :class:`Signature` of the
1768
+ ``__new__``, ``__init__`` or ``__call__`` method is returned::
1769
+
1770
+ sage: P.<x,y> = QQ[]
1771
+ sage: sage_signature(P) # needs sage.libs.singular
1772
+ <Signature (base_ring, n, names, order='degrevlex')>
1773
+ sage: sage_signature(P.__class__) # needs sage.libs.singular
1774
+ <Signature (self, x=0, *args, **kwds)>
1775
+
1776
+ The following tests against various bugs that were fixed in
1777
+ :issue:`9976`::
1778
+
1779
+ sage: from sage.rings.polynomial.real_roots import bernstein_polynomial_factory_ratlist # needs sage.libs.linbox sage.modules
1780
+ sage: sage_signature(bernstein_polynomial_factory_ratlist.coeffs_bitsize) # needs sage.libs.linbox sage.modules
1781
+ <Signature (self)>
1782
+ sage: from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid # needs sage.rings.polynomial.pbori
1783
+ sage: sage_signature(BooleanMonomialMonoid.gen) # needs sage.rings.polynomial.pbori
1784
+ <Signature (self, i=0)>
1785
+ sage: I = P*[x,y]
1786
+ sage: sage_signature(I.groebner_basis) # needs sage.libs.singular
1787
+ <Signature (self, algorithm='', deg_bound=None, mult_bound=None, prot=False, *args, **kwds)>
1788
+ sage: cython("cpdef int foo(x,y) except -1: return 1") # needs sage.misc.cython
1789
+ sage: sage_signature(foo) # needs sage.misc.cython
1790
+ <Signature (x, y)>
1791
+
1792
+ If a :func:`functools.partial` instance is involved, we see no other meaningful solution
1793
+ than to return the signature of the underlying function::
1794
+
1795
+ sage: def f(a, b, c, d=1):
1796
+ ....: return a + b + c + d
1797
+ sage: import functools
1798
+ sage: f1 = functools.partial(f, 1, c=2)
1799
+ sage: sage_signature(f1)
1800
+ <Signature (a, b, c, d=1)>
1801
+ """
1802
+ return _fullargspec_to_signature(sage_getargspec(obj))
1803
+
1804
+
1805
+ def formatannotation(annotation, base_module=None):
1806
+ """
1807
+ This is taken from Python 3.7's inspect.py; the only change is to
1808
+ add documentation.
1809
+
1810
+ INPUT:
1811
+
1812
+ - ``annotation`` -- annotation for a function
1813
+ - ``base_module`` -- (default: ``None``)
1814
+
1815
+ This is only relevant with Python 3, so the doctests are marked
1816
+ accordingly.
1817
+
1818
+ EXAMPLES::
1819
+
1820
+ sage: from sage.misc.sageinspect import formatannotation
1821
+ sage: import inspect
1822
+ sage: def foo(a, *, b:int, **kwargs):
1823
+ ....: pass
1824
+ sage: s = inspect.signature(foo)
1825
+
1826
+ sage: a = s.parameters['a'].annotation
1827
+ sage: a
1828
+ <class 'inspect._empty'>
1829
+ sage: formatannotation(a)
1830
+ 'inspect._empty'
1831
+
1832
+ sage: b = s.parameters['b'].annotation
1833
+ sage: b
1834
+ <class 'int'>
1835
+ sage: formatannotation(b)
1836
+ 'int'
1837
+ """
1838
+ if getattr(annotation, '__module__', None) == 'typing':
1839
+ return repr(annotation).replace('typing.', '')
1840
+ if isinstance(annotation, type):
1841
+ if annotation.__module__ in ('builtins', base_module):
1842
+ return annotation.__qualname__
1843
+ return annotation.__module__ + '.' + annotation.__qualname__
1844
+ return repr(annotation)
1845
+
1846
+
1847
+ _formatannotation = formatannotation
1848
+
1849
+
1850
+ def sage_formatargspec(args, varargs=None, varkw=None, defaults=None,
1851
+ kwonlyargs=(), kwonlydefaults=None, annotations={},
1852
+ formatarg=str,
1853
+ formatvarargs=None,
1854
+ formatvarkw=None,
1855
+ formatvalue=None,
1856
+ formatreturns=None,
1857
+ formatannotation=None):
1858
+ """
1859
+ Format an argument spec from the values returned by getfullargspec.
1860
+
1861
+ The first seven arguments are (args, varargs, varkw, defaults,
1862
+ kwonlyargs, kwonlydefaults, annotations). The other five arguments
1863
+ are the corresponding optional formatting functions that are called to
1864
+ turn names and values into strings. The last argument is an optional
1865
+ function to format the sequence of arguments.
1866
+
1867
+ This is taken from Python 3.7's inspect.py, where it is
1868
+ deprecated. The only change, aside from documentation (this
1869
+ paragraph and the next, plus doctests), is to remove the
1870
+ deprecation warning.
1871
+
1872
+ Sage uses this function to format arguments, as obtained by
1873
+ :func:`sage_getargspec`. Since :func:`sage_getargspec` works for
1874
+ Cython functions while Python's inspect module does not, it makes
1875
+ sense to keep this function for formatting instances of
1876
+ ``inspect.FullArgSpec``.
1877
+
1878
+ EXAMPLES::
1879
+
1880
+ sage: from sage.misc.sageinspect import sage_formatargspec
1881
+ sage: args = ['a', 'b', 'c']
1882
+ sage: defaults = [3]
1883
+ sage: sage_formatargspec(args, defaults=defaults)
1884
+ '(a, b, c=3)'
1885
+ """
1886
+ if formatvarargs is None:
1887
+ formatvarargs = lambda name: '*' + name
1888
+ if formatvarkw is None:
1889
+ formatvarkw = lambda name: '**' + name
1890
+ if formatvalue is None:
1891
+ formatvalue = lambda value: '=' + repr(value)
1892
+ if formatreturns is None:
1893
+ formatreturns = lambda text: ' -> ' + text
1894
+ if formatannotation is None:
1895
+ formatannotation = _formatannotation
1896
+
1897
+ def formatargandannotation(arg):
1898
+ result = formatarg(arg)
1899
+ if arg in annotations:
1900
+ result += ': ' + formatannotation(annotations[arg])
1901
+ return result
1902
+ specs = []
1903
+ if defaults:
1904
+ firstdefault = len(args) - len(defaults)
1905
+ for i, arg in enumerate(args):
1906
+ spec = formatargandannotation(arg)
1907
+ if defaults and i >= firstdefault:
1908
+ spec = spec + formatvalue(defaults[i - firstdefault])
1909
+ specs.append(spec)
1910
+ if varargs is not None:
1911
+ specs.append(formatvarargs(formatargandannotation(varargs)))
1912
+ else:
1913
+ if kwonlyargs:
1914
+ specs.append('*')
1915
+ if kwonlyargs:
1916
+ for kwonlyarg in kwonlyargs:
1917
+ spec = formatargandannotation(kwonlyarg)
1918
+ if kwonlydefaults and kwonlyarg in kwonlydefaults:
1919
+ spec += formatvalue(kwonlydefaults[kwonlyarg])
1920
+ specs.append(spec)
1921
+ if varkw is not None:
1922
+ specs.append(formatvarkw(formatargandannotation(varkw)))
1923
+ result = '(' + ', '.join(specs) + ')'
1924
+ if 'return' in annotations:
1925
+ result += formatreturns(formatannotation(annotations['return']))
1926
+ return result
1927
+
1928
+
1929
+ def sage_getdef(obj, obj_name=''):
1930
+ r"""
1931
+ Return the definition header for any callable object.
1932
+
1933
+ INPUT:
1934
+
1935
+ - ``obj`` -- function
1936
+ - ``obj_name`` -- string (default: ``''``); prepended to the output
1937
+
1938
+ EXAMPLES::
1939
+
1940
+ sage: from sage.misc.sageinspect import sage_getdef
1941
+ sage: sage_getdef(identity_matrix) # needs sage.modules
1942
+ '(ring, n=0, sparse=False)'
1943
+ sage: sage_getdef(identity_matrix, 'identity_matrix') # needs sage.modules
1944
+ 'identity_matrix(ring, n=0, sparse=False)'
1945
+
1946
+ Check that :issue:`6848` has been fixed::
1947
+
1948
+ sage: sage_getdef(RDF.random_element)
1949
+ '(min=-1, max=1)'
1950
+
1951
+ If an exception is generated, None is returned instead and the
1952
+ exception is suppressed.
1953
+ """
1954
+ try:
1955
+ spec = sage_getargspec(obj)
1956
+ s = str(sage_formatargspec(*spec))
1957
+ s = s.strip('(').strip(')').strip()
1958
+ if s[:4] == 'self':
1959
+ s = s[4:]
1960
+ s = s.lstrip(',').strip()
1961
+ # for use with typesetting the definition with the notebook:
1962
+ # sometimes s contains "*args" or "**keywds", and the
1963
+ # asterisks confuse ReST/sphinx/docutils, so escape them:
1964
+ # change * to \*, and change ** to \**.
1965
+ return obj_name + '(' + s + ')'
1966
+ except (AttributeError, TypeError, ValueError):
1967
+ return '%s( [noargspec] )' % obj_name
1968
+
1969
+
1970
+ def _sage_getdoc_unformatted(obj):
1971
+ r"""
1972
+ Return the unformatted docstring associated to ``obj`` as a
1973
+ string.
1974
+
1975
+ If ``obj`` is a Cython object with an embedded position in its
1976
+ docstring, the embedded position is **not** stripped.
1977
+
1978
+ INPUT:
1979
+
1980
+ - ``obj`` -- a function, module, etc.: something with a docstring
1981
+
1982
+ EXAMPLES::
1983
+
1984
+ sage: from sage.misc.sageinspect import _sage_getdoc_unformatted
1985
+ sage: print(_sage_getdoc_unformatted(sage.rings.integer.Integer))
1986
+ Integer(x=None, base=0)
1987
+ File: ...sage/rings/integer.pyx (starting at line ...)
1988
+ <BLANKLINE>
1989
+ The :class:`Integer` class represents arbitrary precision
1990
+ integers. It derives from the :class:`Element` class, so
1991
+ integers can be used as ring elements anywhere in Sage.
1992
+ ...
1993
+
1994
+ TESTS:
1995
+
1996
+ Test that we suppress useless built-in output (:issue:`3342`)::
1997
+
1998
+ sage: from sage.misc.sageinspect import _sage_getdoc_unformatted
1999
+ sage: _sage_getdoc_unformatted(isinstance.__class__)
2000
+ ''
2001
+
2002
+ """
2003
+ if obj is None:
2004
+ return ''
2005
+ r = obj.__doc__
2006
+
2007
+ # Check if the __doc__ attribute was actually a string, and
2008
+ # not a 'getset_descriptor' or similar.
2009
+ if isinstance(r, str):
2010
+ return r
2011
+ else:
2012
+ # Not a string of any kind
2013
+ return ''
2014
+
2015
+
2016
+ def sage_getdoc_original(obj):
2017
+ r"""
2018
+ Return the unformatted docstring associated to ``obj`` as a
2019
+ string.
2020
+
2021
+ If ``obj`` is a Cython object with an embedded position or signature in
2022
+ its docstring, the embedded information is stripped. If the stripped
2023
+ docstring is empty, then the stripped docstring of ``obj.__init__`` is
2024
+ returned instead.
2025
+
2026
+ Feed the results from this into the function
2027
+ :func:`sage.misc.sagedoc.format` for printing to the screen.
2028
+
2029
+ INPUT:
2030
+
2031
+ - ``obj`` -- a function, module, etc.: something with a docstring
2032
+
2033
+ EXAMPLES::
2034
+
2035
+ sage: from sage.misc.sageinspect import sage_getdoc_original
2036
+
2037
+ Here is a class that has its own docstring::
2038
+
2039
+ sage: print(sage_getdoc_original(sage.rings.integer.Integer))
2040
+ <BLANKLINE>
2041
+ The :class:`Integer` class represents arbitrary precision
2042
+ integers. It derives from the :class:`Element` class, so
2043
+ integers can be used as ring elements anywhere in Sage.
2044
+ ...
2045
+
2046
+ If the class does not have a docstring, the docstring of the
2047
+ ``__init__`` method is used, but not the ``__init__`` method
2048
+ of the base class (this was fixed in :issue:`24936`)::
2049
+
2050
+ sage: from sage.categories.category import Category
2051
+ sage: class A(Category):
2052
+ ....: def __init__(self):
2053
+ ....: '''The __init__ docstring'''
2054
+ sage: sage_getdoc_original(A)
2055
+ 'The __init__ docstring'
2056
+ sage: class B(Category):
2057
+ ....: pass
2058
+ sage: sage_getdoc_original(B)
2059
+ ''
2060
+
2061
+ Old-style classes are supported::
2062
+
2063
+ sage: class OldStyleClass:
2064
+ ....: def __init__(self):
2065
+ ....: '''The __init__ docstring'''
2066
+ ....: pass
2067
+ sage: print(sage_getdoc_original(OldStyleClass))
2068
+ The __init__ docstring
2069
+
2070
+ When there is no ``__init__`` method, we just get an empty string::
2071
+
2072
+ sage: class OldStyleClass:
2073
+ ....: pass
2074
+ sage: sage_getdoc_original(OldStyleClass)
2075
+ ''
2076
+
2077
+ If an instance of a class does not have its own docstring, the docstring
2078
+ of its class results::
2079
+
2080
+ sage: sage_getdoc_original(sage.plot.colors.aliceblue) == sage_getdoc_original(sage.plot.colors.Color) # needs sage.plot
2081
+ True
2082
+ """
2083
+ # typ is the type corresponding to obj, which is obj itself if
2084
+ # that was a type or old-style class
2085
+ if isinstance(obj, type):
2086
+ typ = obj
2087
+ else:
2088
+ typ = type(obj)
2089
+
2090
+ s, argspec = _extract_embedded_signature(_sage_getdoc_unformatted(obj), typ.__name__)
2091
+ if s:
2092
+ pos = _extract_embedded_position(s)
2093
+ if pos is not None:
2094
+ s = pos[0]
2095
+ if not s:
2096
+ # The docstring of obj is empty. To get something, we want to use
2097
+ # the documentation of the __init__ method, but only if it belongs
2098
+ # to (the type of) obj.
2099
+ init = typ.__dict__.get("__init__")
2100
+ if init:
2101
+ return sage_getdoc_original(init)
2102
+ return s
2103
+
2104
+
2105
+ def sage_getdoc(obj, obj_name='', embedded=False):
2106
+ r"""
2107
+ Return the docstring associated to ``obj`` as a string.
2108
+
2109
+ If ``obj`` is a Cython object with an embedded position in its
2110
+ docstring, the embedded position is stripped.
2111
+
2112
+ The optional boolean argument ``embedded`` controls the
2113
+ string formatting. It is False by default.
2114
+
2115
+ INPUT:
2116
+
2117
+ - ``obj`` -- a function, module, etc.: something with a docstring
2118
+
2119
+ EXAMPLES::
2120
+
2121
+ sage: from sage.misc.sageinspect import sage_getdoc
2122
+ sage: sage_getdoc(identity_matrix)[87:124] # needs sage.modules
2123
+ '...the n x n identity matrix...'
2124
+ sage: def f(a, b, c, d=1): return a+b+c+d
2125
+ ...
2126
+ sage: import functools
2127
+ sage: f1 = functools.partial(f, 1,c=2)
2128
+ sage: f.__doc__ = "original documentation"
2129
+ sage: f1.__doc__ = "specialised documentation"
2130
+ sage: sage_getdoc(f)
2131
+ 'original documentation\n'
2132
+ sage: sage_getdoc(f1)
2133
+ 'specialised documentation\n'
2134
+ """
2135
+ import sage.misc.sagedoc
2136
+ if obj is None:
2137
+ return ''
2138
+ r = sage_getdoc_original(obj)
2139
+ s = sage.misc.sagedoc.format(r, embedded=embedded)
2140
+ f = sage_getfile(obj)
2141
+ if f and os.path.exists(f):
2142
+ from sage.doctest.control import skipfile
2143
+ skip = skipfile(f)
2144
+ if isinstance(skip, str):
2145
+ warn = """WARNING: the enclosing module is marked '{}',
2146
+ so doctests may not pass.""".format(skip)
2147
+ s = warn + "\n\n" + s
2148
+
2149
+ # Fix object naming
2150
+ if obj_name != '':
2151
+ i = obj_name.find('.')
2152
+ if i != -1:
2153
+ obj_name = obj_name[:i]
2154
+ s = s.replace('self.', '%s.' % obj_name)
2155
+
2156
+ return s
2157
+
2158
+
2159
+ def sage_getsource(obj):
2160
+ r"""
2161
+ Return the source code associated to obj as a string, or None.
2162
+
2163
+ INPUT:
2164
+
2165
+ - ``obj`` -- function, etc.
2166
+
2167
+ EXAMPLES::
2168
+
2169
+ sage: from sage.misc.sageinspect import sage_getsource
2170
+ sage: sage_getsource(identity_matrix)[19:60] # needs sage.modules
2171
+ 'identity_matrix(ring, n=0, sparse=False):'
2172
+ sage: sage_getsource(identity_matrix)[19:60] # needs sage.modules
2173
+ 'identity_matrix(ring, n=0, sparse=False):'
2174
+ """
2175
+ # First we should check if the object has a _sage_src_
2176
+ # method. If it does, we just return the output from
2177
+ # that. This is useful for getting pexpect interface
2178
+ # elements to behave similar to regular Python objects
2179
+ # with respect to introspection.
2180
+ try:
2181
+ return obj._sage_src_()
2182
+ except (AttributeError, TypeError):
2183
+ pass
2184
+
2185
+ t = sage_getsourcelines(obj)
2186
+ if not t:
2187
+ return None
2188
+ (source_lines, lineno) = t
2189
+ return ''.join(source_lines)
2190
+
2191
+
2192
+ def _sage_getsourcelines_name_with_dot(obj):
2193
+ r"""
2194
+ Get the source lines of an object whose name
2195
+ contains a dot and whose source lines can not
2196
+ be obtained by different methods.
2197
+
2198
+ EXAMPLES::
2199
+
2200
+ sage: C = Rings()
2201
+ sage: from sage.misc.sageinspect import sage_getsource
2202
+ sage: print(sage_getsource(C.parent_class)) #indirect doctest
2203
+ class ParentMethods:
2204
+ ...
2205
+ Return the Lie bracket `[x, y] = x y - y x` of `x` and `y`.
2206
+ ...
2207
+
2208
+ TESTS:
2209
+
2210
+ The following was fixed in :issue:`16309`::
2211
+
2212
+ sage: # needs sage.misc.cython
2213
+ sage: cython(
2214
+ ....: '''
2215
+ ....: class A:
2216
+ ....: def __init__(self):
2217
+ ....: "some init doc"
2218
+ ....: pass
2219
+ ....: class B:
2220
+ ....: "some class doc"
2221
+ ....: class A(A):
2222
+ ....: pass
2223
+ ....: ''')
2224
+ sage: B.A.__name__
2225
+ 'A'
2226
+ sage: B.A.__qualname__
2227
+ 'B.A'
2228
+ sage: sage_getsource(B.A)
2229
+ ' class A(A):\n pass\n\n'
2230
+
2231
+ Note that for this example to work, it is essential that the class ``B``
2232
+ has a docstring. Otherwise, the code of ``B`` could not be found (Cython
2233
+ inserts embedding information into the docstring) and thus the code of
2234
+ ``B.A`` couldn't be found either.
2235
+ """
2236
+ # First, split the name:
2237
+ if '.' in obj.__name__:
2238
+ splitted_name = obj.__name__.split('.')
2239
+ elif hasattr(obj, '__qualname__'):
2240
+ splitted_name = obj.__qualname__.split('.')
2241
+ else:
2242
+ splitted_name = obj.__name__
2243
+ path = obj.__module__.split('.')+splitted_name[:-1]
2244
+ name = splitted_name[-1]
2245
+ try:
2246
+ M = __import__(path.pop(0))
2247
+ except ImportError:
2248
+ try:
2249
+ B = obj.__base__
2250
+ if B is None:
2251
+ raise AttributeError
2252
+ except AttributeError:
2253
+ raise OSError("could not get source code")
2254
+ return sage_getsourcelines(B)
2255
+ # M should just be the top-most module.
2256
+ # Hence, normally it is just 'sage'
2257
+ try:
2258
+ while path:
2259
+ M = getattr(M, path.pop(0))
2260
+ except AttributeError:
2261
+ try:
2262
+ B = obj.__base__
2263
+ if B is None:
2264
+ raise AttributeError
2265
+ except AttributeError:
2266
+ raise OSError("could not get source code")
2267
+ return sage_getsourcelines(B)
2268
+
2269
+ lines, base_lineno = sage_getsourcelines(M)
2270
+ # the rest of the function is copied from
2271
+ # inspect.findsource
2272
+ if not lines:
2273
+ raise OSError('could not get source code')
2274
+
2275
+ if inspect.ismodule(obj):
2276
+ return lines, base_lineno
2277
+
2278
+ if inspect.isclass(obj):
2279
+ pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
2280
+ # make some effort to find the best matching class definition:
2281
+ # use the one with the least indentation, which is the one
2282
+ # that's most probably not inside a function definition.
2283
+ candidates = []
2284
+ for i in range(len(lines)):
2285
+ match = pat.match(lines[i])
2286
+ if match:
2287
+ # if it's at toplevel, it's already the best one
2288
+ if lines[i][0] == 'c':
2289
+ return inspect.getblock(lines[i:]), i+base_lineno
2290
+ # else add whitespace to candidate list
2291
+ candidates.append((match.group(1), i))
2292
+ if candidates:
2293
+ # this will sort by whitespace, and by line number,
2294
+ # less whitespace first
2295
+ candidates.sort()
2296
+ return inspect.getblock(lines[candidates[0][1]:]), candidates[0][1]+base_lineno
2297
+ else:
2298
+ raise OSError('could not find class definition')
2299
+
2300
+ if inspect.ismethod(obj):
2301
+ obj = obj.__func__
2302
+ if is_function_or_cython_function(obj):
2303
+ obj = obj.__code__
2304
+ if inspect.istraceback(obj):
2305
+ obj = obj.tb_frame
2306
+ if inspect.isframe(obj):
2307
+ obj = obj.f_code
2308
+ if inspect.iscode(obj):
2309
+ if not hasattr(obj, 'co_firstlineno'):
2310
+ raise OSError('could not find function definition')
2311
+ pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
2312
+ pmatch = pat.match
2313
+ # fperez - fix: sometimes, co_firstlineno can give a number larger than
2314
+ # the length of lines, which causes an error. Safeguard against that.
2315
+ lnum = min(obj.co_firstlineno, len(lines)) - 1
2316
+ while lnum > 0:
2317
+ if pmatch(lines[lnum]):
2318
+ break
2319
+ lnum -= 1
2320
+
2321
+ return inspect.getblock(lines[lnum:]), lnum+base_lineno
2322
+ raise OSError('could not find code object')
2323
+
2324
+
2325
+ def sage_getsourcelines(obj):
2326
+ r"""
2327
+ Return a pair ([source_lines], starting line number) of the source
2328
+ code associated to obj, or None.
2329
+
2330
+ INPUT:
2331
+
2332
+ - ``obj`` -- function, etc.
2333
+
2334
+ OUTPUT:
2335
+
2336
+ (source_lines, lineno) or None: ``source_lines`` is a list of
2337
+ strings, and ``lineno`` is an integer.
2338
+
2339
+ EXAMPLES::
2340
+
2341
+ sage: from sage.misc.sageinspect import sage_getsourcelines
2342
+
2343
+ sage: # needs sage.modules
2344
+ sage: from sage.matrix.constructor import matrix
2345
+ sage: sage_getsourcelines(matrix)[1]
2346
+ 22
2347
+ sage: sage_getsourcelines(matrix)[0][0]
2348
+ 'def matrix(*args, **kwds):\n'
2349
+
2350
+ Some classes customize this using a ``_sage_src_lines_`` method,
2351
+ which gives the source lines of a class instance, but not the class
2352
+ itself. We demonstrate this for :class:`CachedFunction`::
2353
+
2354
+ sage: # needs sage.combinat
2355
+ sage: cachedfib = cached_function(fibonacci)
2356
+ sage: sage_getsourcelines(cachedfib)[0][0]
2357
+ "def fibonacci(n, algorithm='pari') -> Integer:\n"
2358
+ sage: sage_getsourcelines(type(cachedfib))[0][0]
2359
+ 'cdef class CachedFunction():\n'
2360
+
2361
+ TESTS::
2362
+
2363
+ sage: # needs sage.misc.cython
2364
+ sage: cython('''cpdef test_funct(x, y): return''')
2365
+ sage: sage_getsourcelines(test_funct)
2366
+ (['cpdef test_funct(x, y): return\n'], 1)
2367
+
2368
+ The following tests that an instance of ``functools.partial`` is correctly
2369
+ dealt with (see :issue:`9976`)::
2370
+
2371
+ sage: from sage.tests.functools_partial_src import test_func
2372
+ sage: sage_getsourcelines(test_func)
2373
+ (['def base(x):\n',
2374
+ ...
2375
+ ' return x\n'], 9)
2376
+
2377
+ Here are some cases that were covered in :issue:`11298`;
2378
+ note that line numbers may easily change, and therefore we do
2379
+ not test them::
2380
+
2381
+ sage: P.<x,y> = QQ[]
2382
+ sage: I = P*[x,y]
2383
+ sage: sage_getsourcelines(P) # needs sage.libs.singular
2384
+ (['cdef class MPolynomialRing_libsingular(MPolynomialRing_base):\n',
2385
+ '\n',
2386
+ ' def __cinit__(self):\n',
2387
+ ...)
2388
+ sage: sage_getsourcelines(I) # needs sage.libs.singular
2389
+ ([...'class MPolynomialIdeal(MPolynomialIdeal_singular_repr,\n',
2390
+ ...)
2391
+ sage: x = var('x') # needs sage.symbolic
2392
+ sage: lines, lineno = sage_getsourcelines(x); lines[0:5] # needs sage.symbolic
2393
+ ['cdef class Expression(...):\n',
2394
+ '\n',
2395
+ ' cdef GEx _gobj\n',
2396
+ '\n',
2397
+ ' cpdef object pyobject(self):\n']
2398
+ sage: lines[-1] # last line # needs sage.symbolic
2399
+ ' return S\n'
2400
+
2401
+ We show some enhancements provided by :issue:`11768`. First, we
2402
+ use a dummy parent class that has defined an element class by a
2403
+ nested class definition::
2404
+
2405
+ sage: from sage.misc.test_nested_class import TestNestedParent
2406
+ sage: from sage.misc.sageinspect import sage_getsource
2407
+ sage: P = TestNestedParent()
2408
+ sage: E = P.element_class
2409
+ sage: E.__bases__
2410
+ (<class 'sage.misc.test_nested_class.TestNestedParent.Element'>,
2411
+ <class 'sage.categories.sets_cat.Sets.element_class'>)
2412
+ sage: print(sage_getsource(E))
2413
+ class Element:
2414
+ "This is a dummy element class"
2415
+ pass
2416
+ sage: print(sage_getsource(P))
2417
+ class TestNestedParent(UniqueRepresentation, Parent):
2418
+ ...
2419
+ class Element:
2420
+ "This is a dummy element class"
2421
+ pass
2422
+
2423
+ Here is another example that relies on a nested class definition
2424
+ in the background::
2425
+
2426
+ sage: C = AdditiveMagmas()
2427
+ sage: HC = C.Homsets()
2428
+ sage: sage_getsourcelines(HC)
2429
+ ([' class Homsets(HomsetsCategory):\n', ...], ...)
2430
+
2431
+ Testing against a bug that has occurred during work on :issue:`11768`::
2432
+
2433
+ sage: P.<x,y> = QQ[]
2434
+ sage: I = P*[x,y]
2435
+ sage: sage_getsourcelines(I) # needs sage.libs.singular
2436
+ ([...'class MPolynomialIdeal(MPolynomialIdeal_singular_repr,\n',
2437
+ ' MPolynomialIdeal_macaulay2_repr,\n',
2438
+ ' MPolynomialIdeal_magma_repr,\n',
2439
+ ' Ideal_generic):\n',
2440
+ ' def __init__(self, ring, gens, coerce=True...):\n',
2441
+ ...)
2442
+ """
2443
+ # First try the method _sage_src_lines_(), which is meant to give
2444
+ # the source lines of an object (not of its type!).
2445
+ try:
2446
+ sage_src_lines = obj._sage_src_lines_
2447
+ except AttributeError:
2448
+ pass
2449
+ else:
2450
+ try:
2451
+ return sage_src_lines()
2452
+ except (NotImplementedError, TypeError):
2453
+ # NotImplementedError can be raised by _sage_src_lines_()
2454
+ # to indicate that it didn't find the source lines.
2455
+ #
2456
+ # TypeError can happen when obj is a type and
2457
+ # obj._sage_src_lines_ is an unbound method. In this case,
2458
+ # we don't want to use _sage_src_lines_(), we just want to
2459
+ # get the source of the type itself.
2460
+ pass
2461
+
2462
+ # Check if we deal with an instance
2463
+ if isclassinstance(obj):
2464
+ if isinstance(obj, functools.partial):
2465
+ return sage_getsourcelines(obj.func)
2466
+ else:
2467
+ return sage_getsourcelines(obj.__class__)
2468
+
2469
+ # First, we deal with nested classes. Their name contains a dot, and we
2470
+ # have a special function for that purpose.
2471
+ # This is the case for ParentMethods of categories, for example.
2472
+ if (inspect.isclass(obj) and
2473
+ ('.' in obj.__name__ or '.' in getattr(obj, '__qualname__', ''))):
2474
+ return _sage_getsourcelines_name_with_dot(obj)
2475
+
2476
+ # Next, we try _sage_getdoc_unformatted()
2477
+ d = _sage_getdoc_unformatted(obj)
2478
+ pos = _extract_embedded_position(d)
2479
+ if pos is None:
2480
+ try:
2481
+ return inspect.getsourcelines(obj)
2482
+ except (OSError, TypeError) as err:
2483
+ if hasattr(obj, '__init__'):
2484
+ d = _sage_getdoc_unformatted(obj.__init__)
2485
+ pos = _extract_embedded_position(d)
2486
+ if pos is None:
2487
+ if inspect.isclass(obj):
2488
+ try:
2489
+ B = obj.__base__
2490
+ except AttributeError:
2491
+ B = None
2492
+ if B is not None and B is not obj:
2493
+ return sage_getsourcelines(B)
2494
+ if obj.__class__ != type:
2495
+ return sage_getsourcelines(obj.__class__)
2496
+ raise err
2497
+
2498
+ (orig, filename, lineno) = pos
2499
+ try:
2500
+ with open(filename) as f:
2501
+ source_lines = f.readlines()
2502
+ except OSError:
2503
+ try:
2504
+ from sage.misc.temporary_file import spyx_tmp
2505
+ raw_name = filename.split('/')[-1]
2506
+ newname = os.path.join(spyx_tmp(), '_'.join(raw_name.split('_')[:-1]), raw_name)
2507
+ with open(newname) as f:
2508
+ source_lines = f.readlines()
2509
+ except OSError:
2510
+ return None
2511
+
2512
+ # It is possible that the source lines belong to the __init__ method,
2513
+ # rather than to the class. So, we try to look back and find the class
2514
+ # definition.
2515
+ first_line = source_lines[lineno-1]
2516
+ leading_blanks = len(first_line)-len(first_line.lstrip())
2517
+ if first_line.lstrip().startswith('def ') and "__init__" in first_line and obj.__name__ != '__init__':
2518
+ ignore = False
2519
+ double_quote = None
2520
+ for lnb in range(lineno, 0, -1):
2521
+ new_first_line = source_lines[lnb-1]
2522
+ nfl_strip = new_first_line.lstrip()
2523
+ if nfl_strip.startswith('"""'):
2524
+ if double_quote is None:
2525
+ double_quote = True
2526
+ if double_quote:
2527
+ ignore = not ignore
2528
+ elif nfl_strip.startswith("'''"):
2529
+ if double_quote is None:
2530
+ double_quote = False
2531
+ if double_quote is False:
2532
+ ignore = not ignore
2533
+ if ignore:
2534
+ continue
2535
+ if len(new_first_line)-len(nfl_strip) < leading_blanks and nfl_strip:
2536
+ # We are not inside a doc string. So, if the indentation
2537
+ # is less than the indentation of the __init__ method
2538
+ # then we must be at the class definition!
2539
+ lineno = lnb
2540
+ break
2541
+ return _extract_source(source_lines, lineno), lineno
2542
+
2543
+
2544
+ def sage_getvariablename(self, omit_underscore_names=True):
2545
+ """
2546
+ Attempt to get the name of a Sage object.
2547
+
2548
+ INPUT:
2549
+
2550
+ - ``self`` -- any object
2551
+
2552
+ - ``omit_underscore_names`` -- boolean (default: ``True``)
2553
+
2554
+ OUTPUT:
2555
+
2556
+ If the user has assigned an object ``obj`` to a variable name,
2557
+ then return that variable name. If several variables point to
2558
+ ``obj``, return a sorted list of those names. If
2559
+ ``omit_underscore_names`` is ``True`` (the default) then omit names
2560
+ starting with an underscore "_".
2561
+
2562
+ EXAMPLES::
2563
+
2564
+ sage: # needs sage.modules
2565
+ sage: from sage.misc.sageinspect import sage_getvariablename
2566
+ sage: A = random_matrix(ZZ, 100)
2567
+ sage: sage_getvariablename(A)
2568
+ 'A'
2569
+ sage: B = A
2570
+ sage: sage_getvariablename(A)
2571
+ ['A', 'B']
2572
+
2573
+ If an object is not assigned to a variable, an empty list is returned::
2574
+
2575
+ sage: sage_getvariablename(random_matrix(ZZ, 60)) # needs sage.modules
2576
+ []
2577
+ """
2578
+ # This is a modified version of code taken from
2579
+ # https://web.archive.org/web/20100416095847/http://pythonic.pocoo.org/2009/5/30/finding-objects-names
2580
+ # written by Georg Brandl.
2581
+ result = []
2582
+ for frame in inspect.stack():
2583
+ for name, obj in frame[0].f_globals.items():
2584
+ if obj is self:
2585
+ result.append(name)
2586
+ if len(result) == 1:
2587
+ return result[0]
2588
+ else:
2589
+ return sorted(result)
2590
+
2591
+
2592
+ __internal_teststring = '''
2593
+ import os # 1
2594
+ # preceding comment not include # 2
2595
+ def test1(a, b=2): # 3
2596
+ if a: # 4
2597
+ return 1 # 5
2598
+ return b # 6
2599
+ # intervening comment not included # 7
2600
+ class test2(): # 8
2601
+ pass # 9
2602
+ # indented comment not included # 10
2603
+ # trailing comment not included # 11
2604
+ def test3(b, # 12
2605
+ a=2): # 13
2606
+ pass # EOF # 14'''
2607
+
2608
+
2609
+ def __internal_tests():
2610
+ r"""
2611
+ Test internals of the sageinspect module.
2612
+
2613
+ EXAMPLES::
2614
+
2615
+ sage: from sage.misc.sageinspect import *
2616
+ sage: from sage.misc.sageinspect import _extract_source, _extract_embedded_position, _sage_getargspec_cython, __internal_teststring
2617
+
2618
+ If docstring is None, nothing bad happens::
2619
+
2620
+ sage: sage_getdoc(None)
2621
+ ''
2622
+
2623
+ sage: import sage.all__sagemath_objects
2624
+ sage: sage_getsource(sage.all__sagemath_objects)
2625
+ "...all..."
2626
+
2627
+ A cython function with default arguments (one of which is a string)::
2628
+
2629
+ sage: sage_getdef(sage.rings.integer.Integer.factor, obj_name='factor')
2630
+ "factor(algorithm='pari', proof=None, limit=None, int_=False, verbose=0)"
2631
+
2632
+ This used to be problematic, but was fixed in :issue:`10094`::
2633
+
2634
+ sage: sage_getsource(sage.rings.integer.Integer.__init__)
2635
+ ' def __init__(self, x=None, base=0):\n...'
2636
+ sage: sage_getdef(sage.rings.integer.Integer.__init__, obj_name='__init__')
2637
+ '__init__(x=None, base=0)'
2638
+
2639
+ Test _extract_source with some likely configurations, including no trailing
2640
+ newline at the end of the file::
2641
+
2642
+ sage: s = __internal_teststring.strip()
2643
+ sage: es = lambda ls, l: ''.join(_extract_source(ls, l)).rstrip()
2644
+
2645
+ sage: print(es(s, 3))
2646
+ def test1(a, b=2): # 3
2647
+ if a: # 4
2648
+ return 1 # 5
2649
+ return b # 6
2650
+
2651
+ sage: print(es(s, 8))
2652
+ class test2(): # 8
2653
+ pass # 9
2654
+
2655
+ sage: print(es(s, 12))
2656
+ def test3(b, # 12
2657
+ a=2): # 13
2658
+ pass # EOF # 14
2659
+
2660
+ Test _sage_getargspec_cython with multiple default arguments and a type::
2661
+
2662
+ sage: _sage_getargspec_cython("def init(self, x=None, base=0):")
2663
+ FullArgSpec(args=['self', 'x', 'base'], varargs=None, varkw=None, defaults=(None, 0), kwonlyargs=[], kwonlydefaults=None, annotations={})
2664
+ sage: _sage_getargspec_cython("def __init__(self, x=None, base=0):")
2665
+ FullArgSpec(args=['self', 'x', 'base'], varargs=None, varkw=None, defaults=(None, 0), kwonlyargs=[], kwonlydefaults=None, annotations={})
2666
+ sage: _sage_getargspec_cython("def __init__(self, x=None, unsigned int base=0, **keys):")
2667
+ FullArgSpec(args=['self', 'x', 'base'], varargs=None, varkw='keys', defaults=(None, 0), kwonlyargs=[], kwonlydefaults=None, annotations={})
2668
+
2669
+ Test _extract_embedded_position:
2670
+
2671
+ We cannot test the filename since it depends on the installation location.
2672
+
2673
+ Make sure things work with no trailing newline::
2674
+
2675
+ sage: _extract_embedded_position('File: sage/rings/rational.pyx (starting at line 1080)')
2676
+ ('', '.../rational.pyx', 1080)
2677
+
2678
+ And with a trailing newline::
2679
+
2680
+ sage: s = 'File: sage/rings/rational.pyx (starting at line 1080)\n'
2681
+ sage: _extract_embedded_position(s)
2682
+ ('', '.../rational.pyx', 1080)
2683
+
2684
+ And with an original docstring::
2685
+
2686
+ sage: s = 'File: sage/rings/rational.pyx (starting at line 1080)\noriginal'
2687
+ sage: _extract_embedded_position(s)
2688
+ ('original', '.../rational.pyx', 1080)
2689
+
2690
+ And with a complicated original docstring::
2691
+
2692
+ sage: s = 'File: sage/rings/rational.pyx (starting at line 1080)\n\n\noriginal test\noriginal'
2693
+ sage: _extract_embedded_position(s)
2694
+ ('\n\noriginal test\noriginal', ..., 1080)
2695
+
2696
+ sage: s = 'no embedded position'
2697
+ sage: _extract_embedded_position(s) is None
2698
+ True
2699
+ """
2700
+
2701
+
2702
+ def find_object_modules(obj):
2703
+ r"""
2704
+ Return a dictionary whose keys are the names of the modules where ``obj``
2705
+ appear and the value at a given module name is the list of names that
2706
+ ``obj`` have in that module.
2707
+
2708
+ It is very unlikely that the output dictionary has several keys except when
2709
+ ``obj`` is an instance of a class.
2710
+
2711
+ EXAMPLES::
2712
+
2713
+ sage: from sage.misc.sageinspect import find_object_modules
2714
+ sage: find_object_modules(RR) # needs sage.rings.real_mpfr
2715
+ {'sage.rings.real_mpfr': ['RR']}
2716
+ sage: find_object_modules(ZZ)
2717
+ {'sage.rings.integer_ring': ['Z', 'ZZ']}
2718
+ """
2719
+ # see if the object is defined in its own module
2720
+ # might be wrong for class instances as the instantiation might appear
2721
+ # outside of the module !!
2722
+ module_name = None
2723
+ if isclassinstance(obj):
2724
+ module_name = obj.__class__.__module__
2725
+ elif hasattr(obj, '__module__') and obj.__module__:
2726
+ module_name = obj.__module__
2727
+
2728
+ if module_name:
2729
+ if module_name not in sys.modules:
2730
+ raise ValueError("this should never happen")
2731
+ d = sys.modules[module_name].__dict__
2732
+ matching = sorted(key for key in d if d[key] is obj)
2733
+ if matching:
2734
+ return {module_name: matching}
2735
+
2736
+ # otherwise, we parse all (already loaded) modules and hope to find
2737
+ # something
2738
+ module_to_obj = {}
2739
+ for module_name, module in sys.modules.items():
2740
+ if module_name != '__main__' and hasattr(module, '__dict__'):
2741
+ d = module.__dict__
2742
+ names = [key for key in d if d[key] is obj]
2743
+ if names:
2744
+ module_to_obj[module_name] = names
2745
+
2746
+ # if the object is an instance, we try to guess where it is defined
2747
+ if isclassinstance(obj):
2748
+ dec_pattern = re.compile(r"^(\w[\w0-9\_]*)\s*=", re.MULTILINE)
2749
+ module_to_obj2 = {}
2750
+ for module_name, obj_names in module_to_obj.items():
2751
+ module_to_obj2[module_name] = []
2752
+ try:
2753
+ src = sage_getsource(sys.modules[module_name])
2754
+ except TypeError:
2755
+ pass
2756
+ else:
2757
+ m = dec_pattern.search(src)
2758
+ while m:
2759
+ if m.group(1) in obj_names:
2760
+ module_to_obj2[module_name].append(m.group(1))
2761
+ m = dec_pattern.search(src, m.end())
2762
+ if not module_to_obj2[module_name]:
2763
+ del module_to_obj2[module_name]
2764
+
2765
+ if module_to_obj2:
2766
+ return module_to_obj2
2767
+
2768
+ return module_to_obj