passagemath-objects 10.6.45__cp313-cp313-musllinux_1_2_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/__init__.py +3 -0
  2. passagemath_objects-10.6.45.dist-info/METADATA +115 -0
  3. passagemath_objects-10.6.45.dist-info/RECORD +280 -0
  4. passagemath_objects-10.6.45.dist-info/WHEEL +5 -0
  5. passagemath_objects-10.6.45.dist-info/top_level.txt +3 -0
  6. passagemath_objects.libs/libgmp-0e7fc84e.so.10.5.0 +0 -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-313-x86_64-linux-musl.so +0 -0
  11. sage/arith/numerical_approx.pxd +35 -0
  12. sage/arith/numerical_approx.pyx +75 -0
  13. sage/arith/power.cpython-313-x86_64-linux-musl.so +0 -0
  14. sage/arith/power.pxd +31 -0
  15. sage/arith/power.pyx +127 -0
  16. sage/categories/action.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  41. sage/categories/map.pxd +34 -0
  42. sage/categories/map.pyx +2106 -0
  43. sage/categories/morphism.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  60. sage/cpython/atexit.pyx +269 -0
  61. sage/cpython/builtin_types.cpython-313-x86_64-linux-musl.so +0 -0
  62. sage/cpython/builtin_types.pyx +7 -0
  63. sage/cpython/cython_metaclass.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  68. sage/cpython/debug.pyx +302 -0
  69. sage/cpython/dict_del_by_value.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  97. sage/groups/group.pxd +14 -0
  98. sage/groups/group.pyx +322 -0
  99. sage/groups/old.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  122. sage/misc/c3_controlled.pxd +2 -0
  123. sage/misc/c3_controlled.pyx +1402 -0
  124. sage/misc/cachefunc.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  132. sage/misc/constant_function.pyx +130 -0
  133. sage/misc/decorators.py +747 -0
  134. sage/misc/fast_methods.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  139. sage/misc/fpickle.pyx +177 -0
  140. sage/misc/function_mangling.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  144. sage/misc/inherit_comparison.pxd +5 -0
  145. sage/misc/inherit_comparison.pyx +105 -0
  146. sage/misc/instancedoc.cpython-313-x86_64-linux-musl.so +0 -0
  147. sage/misc/instancedoc.pyx +331 -0
  148. sage/misc/lazy_attribute.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  166. sage/misc/nested_class.pxd +3 -0
  167. sage/misc/nested_class.pyx +394 -0
  168. sage/misc/persist.cpython-313-x86_64-linux-musl.so +0 -0
  169. sage/misc/persist.pyx +1251 -0
  170. sage/misc/prandom.py +418 -0
  171. sage/misc/randstate.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  176. sage/misc/reset.pyx +196 -0
  177. sage/misc/sage_ostools.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  208. sage/structure/category_object.pxd +28 -0
  209. sage/structure/category_object.pyx +1087 -0
  210. sage/structure/coerce.cpython-313-x86_64-linux-musl.so +0 -0
  211. sage/structure/coerce.pxd +44 -0
  212. sage/structure/coerce.pyx +2107 -0
  213. sage/structure/coerce_actions.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  228. sage/structure/element.pxd +272 -0
  229. sage/structure/element.pyx +4772 -0
  230. sage/structure/element_wrapper.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  248. sage/structure/list_clone_timings_cy.pyx +86 -0
  249. sage/structure/mutability.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  254. sage/structure/parent.pxd +112 -0
  255. sage/structure/parent.pyx +3093 -0
  256. sage/structure/parent_base.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  269. sage/structure/richcmp.pxd +213 -0
  270. sage/structure/richcmp.pyx +495 -0
  271. sage/structure/sage_object.cpython-313-x86_64-linux-musl.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,607 @@
1
+ # sage_setup: distribution = sagemath-objects
2
+ """
3
+ Lazy attributes
4
+
5
+ AUTHORS:
6
+
7
+ - Nicolas Thiery (2008): Initial version
8
+ - Nils Bruin (2013-05): Cython version
9
+ """
10
+
11
+ # ****************************************************************************
12
+ # Copyright (C) 2008 Nicolas M. Thiery <nthiery at users.sf.net>
13
+ #
14
+ # Distributed under the terms of the GNU General Public License (GPL)
15
+ #
16
+ # This code is distributed in the hope that it will be useful,
17
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19
+ # General Public License for more details.
20
+ #
21
+ # The full text of the GPL is available at:
22
+ #
23
+ # https://www.gnu.org/licenses/
24
+ # ****************************************************************************
25
+
26
+ cdef class _lazy_attribute():
27
+ """
28
+ Cython base class for lazy attributes.
29
+
30
+ EXAMPLES:
31
+
32
+ Only Python subclasses of this class are supposed to be instantiated::
33
+
34
+ sage: from sage.misc.lazy_attribute import _lazy_attribute
35
+ sage: _lazy_attribute(lambda x:1)
36
+ Traceback (most recent call last):
37
+ ...
38
+ NotImplementedError: Only instantiate wrapper python class
39
+ """
40
+
41
+ cdef public f
42
+ cdef public str __name__
43
+
44
+ def __init__(self, f):
45
+ r"""
46
+ Constructor for lazy attributes.
47
+
48
+ EXAMPLES::
49
+
50
+ sage: def f(x):
51
+ ....: "doc of f"
52
+ ....: return 1
53
+ ....:
54
+ sage: x = lazy_attribute(f); x
55
+ <sage.misc.lazy_attribute.lazy_attribute object at ...>
56
+ sage: x.__doc__
57
+ 'doc of f'
58
+ sage: x.__name__
59
+ 'f'
60
+ sage: x.__module__
61
+ '__main__'
62
+
63
+ TESTS:
64
+
65
+ We check that :issue:`9251` is solved::
66
+
67
+ sage: Parent.element_class
68
+ <sage.misc.lazy_attribute.lazy_attribute object at 0x...>
69
+ sage: "The (default) class for the elements of this parent" in Parent.element_class.__doc__
70
+ True
71
+ sage: Parent.element_class.__name__
72
+ 'element_class'
73
+ sage: Parent.element_class.__module__
74
+ 'sage.misc.lazy_attribute'
75
+ """
76
+ raise NotImplementedError("Only instantiate wrapper python class")
77
+
78
+ def _sage_src_lines_(self):
79
+ r"""
80
+ Return the source code location for the wrapped function.
81
+
82
+ EXAMPLES::
83
+
84
+ sage: from sage.misc.sageinspect import sage_getsourcelines
85
+ sage: g = lazy_attribute(sage.misc.banner.banner)
86
+ sage: (src, lines) = sage_getsourcelines(g)
87
+ sage: src[0]
88
+ 'def banner():\n'
89
+ sage: lines
90
+ 95
91
+ """
92
+ from sage.misc.sageinspect import sage_getsourcelines
93
+ return sage_getsourcelines(self.f)
94
+
95
+ def __get__(self, a, cls):
96
+ """
97
+ Implement the attribute access protocol.
98
+
99
+ EXAMPLES::
100
+
101
+ sage: class A: pass
102
+ sage: def f(x): return 1
103
+ ...
104
+ sage: f = lazy_attribute(f)
105
+ sage: f.__get__(A(), A)
106
+ 1
107
+ """
108
+ cdef CM
109
+ cdef result
110
+ if a is None: # when doing cls.x for cls a class and x a lazy attribute
111
+ return self
112
+ try:
113
+ # _cached_methods is supposed to be a public Cython attribute.
114
+ # Apparently, these are *not* subject to name mangling.
115
+ CM = getattr(a, '_cached_methods')
116
+ if CM is None:
117
+ CM = {}
118
+ setattr(a, '_cached_methods', CM)
119
+ except AttributeError as msg:
120
+ CM = None
121
+ if CM is not None:
122
+ try:
123
+ return CM[self.__name__]
124
+ except KeyError:
125
+ pass
126
+ result = self.f(a)
127
+ if result is NotImplemented:
128
+ # Workaround: we make sure that cls is the class
129
+ # where the lazy attribute self is actually defined.
130
+ # This avoids running into an infinite loop
131
+ # See About descriptor specifications
132
+ for supercls in cls.__mro__:
133
+ if self.__name__ in supercls.__dict__ and self is supercls.__dict__[self.__name__]:
134
+ cls = supercls
135
+ return getattr(super(cls, a), self.__name__)
136
+ try:
137
+ setattr(a, self.__name__, result)
138
+ except AttributeError:
139
+ if CM is not None:
140
+ CM[self.__name__] = result
141
+ return result
142
+ raise
143
+ return result
144
+
145
+
146
+ class lazy_attribute(_lazy_attribute):
147
+ r"""
148
+ A lazy attribute for an object is like a usual attribute, except
149
+ that, instead of being computed when the object is constructed
150
+ (i.e. in ``__init__``), it is computed on the fly the first time it
151
+ is accessed.
152
+
153
+ For constant values attached to an object, lazy attributes provide
154
+ a shorter syntax and automatic caching (unlike methods), while
155
+ playing well with inheritance (like methods): a subclass can
156
+ easily override a given attribute; you don't need to call the
157
+ super class constructor, etc.
158
+
159
+ Technically, a :class:`lazy_attribute` is a non-data descriptor (see
160
+ Invoking Descriptors in the Python reference manual).
161
+
162
+ EXAMPLES:
163
+
164
+ We create a class whose instances have a lazy attribute ``x``::
165
+
166
+ sage: class A():
167
+ ....: def __init__(self):
168
+ ....: self.a=2 # just to have some data to calculate from
169
+ ....:
170
+ ....: @lazy_attribute
171
+ ....: def x(self):
172
+ ....: print("calculating x in A")
173
+ ....: return self.a + 1
174
+ ....:
175
+
176
+ For an instance ``a`` of ``A``, ``a.x`` is calculated the first time it
177
+ is accessed, and then stored as a usual attribute::
178
+
179
+ sage: a = A()
180
+ sage: a.x
181
+ calculating x in A
182
+ 3
183
+ sage: a.x
184
+ 3
185
+
186
+ .. rubric:: Implementation details
187
+
188
+ We redo the same example, but opening the hood to see what happens to
189
+ the internal dictionary of the object::
190
+
191
+ sage: a = A()
192
+ sage: a.__dict__
193
+ {'a': 2}
194
+ sage: a.x
195
+ calculating x in A
196
+ 3
197
+ sage: a.__dict__
198
+ {'a': 2, 'x': 3}
199
+ sage: a.x
200
+ 3
201
+ sage: timeit('a.x') # random
202
+ 625 loops, best of 3: 89.6 ns per loop
203
+
204
+ This shows that, after the first calculation, the attribute ``x``
205
+ becomes a usual attribute; in particular, there is no time penalty
206
+ to access it.
207
+
208
+ A lazy attribute may be set as usual, even before its first access,
209
+ in which case the lazy calculation is completely ignored::
210
+
211
+ sage: a = A()
212
+ sage: a.x = 4
213
+ sage: a.x
214
+ 4
215
+ sage: a.__dict__
216
+ {'a': 2, 'x': 4}
217
+
218
+ Class binding results in the lazy attribute itself::
219
+
220
+ sage: A.x
221
+ <sage.misc.lazy_attribute.lazy_attribute object at ...>
222
+
223
+ .. rubric:: Conditional definitions
224
+
225
+ The function calculating the attribute may return NotImplemented
226
+ to declare that, after all, it is not able to do it. In that case,
227
+ the attribute lookup proceeds in the super class hierarchy::
228
+
229
+ sage: class B(A):
230
+ ....: @lazy_attribute
231
+ ....: def x(self):
232
+ ....: if hasattr(self, "y"):
233
+ ....: print("calculating x from y in B")
234
+ ....: return self.y
235
+ ....: else:
236
+ ....: print("y not there; B does not define x")
237
+ ....: return NotImplemented
238
+ ....:
239
+ sage: b = B()
240
+ sage: b.x
241
+ y not there; B does not define x
242
+ calculating x in A
243
+ 3
244
+ sage: b = B()
245
+ sage: b.y = 1
246
+ sage: b.x
247
+ calculating x from y in B
248
+ 1
249
+
250
+ .. rubric:: Attribute existence testing
251
+
252
+ Testing for the existence of an attribute with hasattr currently
253
+ always triggers its full calculation, which may not be desirable
254
+ when the calculation is expensive::
255
+
256
+ sage: a = A()
257
+ sage: hasattr(a, "x")
258
+ calculating x in A
259
+ True
260
+
261
+ It would be great if we could take over the control somehow, if at
262
+ all possible without a special implementation of hasattr, so as to
263
+ allow for something like::
264
+
265
+ sage: class A ():
266
+ ....: @lazy_attribute
267
+ ....: def x(self, existence_only=False):
268
+ ....: if existence_only:
269
+ ....: print("testing for x existence")
270
+ ....: return True
271
+ ....: else:
272
+ ....: print("calculating x in A")
273
+ ....: return 3
274
+ ....:
275
+ sage: a = A()
276
+ sage: hasattr(a, "x") # todo: not implemented
277
+ testing for x existence
278
+ sage: a.x
279
+ calculating x in A
280
+ 3
281
+ sage: a.x
282
+ 3
283
+
284
+ Here is a full featured example, with both conditional definition
285
+ and existence testing::
286
+
287
+ sage: class B(A):
288
+ ....: @lazy_attribute
289
+ ....: def x(self, existence_only=False):
290
+ ....: if hasattr(self, "y"):
291
+ ....: if existence_only:
292
+ ....: print("testing for x existence in B")
293
+ ....: return True
294
+ ....: else:
295
+ ....: print("calculating x from y in B")
296
+ ....: return self.y
297
+ ....: else:
298
+ ....: print("y not there; B does not define x")
299
+ ....: return NotImplemented
300
+ ....:
301
+ sage: b = B()
302
+ sage: hasattr(b, "x") # todo: not implemented
303
+ y not there; B does not define x
304
+ testing for x existence
305
+ True
306
+ sage: b.x
307
+ y not there; B does not define x
308
+ calculating x in A
309
+ 3
310
+ sage: b = B()
311
+ sage: b.y = 1
312
+ sage: hasattr(b, "x") # todo: not implemented
313
+ testing for x existence in B
314
+ True
315
+ sage: b.x
316
+ calculating x from y in B
317
+ 1
318
+
319
+
320
+ .. rubric:: lazy attributes and introspection
321
+
322
+ .. TODO::
323
+
324
+ Make the following work nicely::
325
+
326
+ sage: b.x? # todo: not implemented
327
+ sage: b.x?? # todo: not implemented
328
+
329
+ Right now, the first one includes the doc of this class, and the
330
+ second one brings up the code of this class, both being not very
331
+ useful.
332
+
333
+ TESTS:
334
+
335
+ .. rubric:: Lazy attributes and Cython
336
+
337
+ This attempts to check that lazy attributes work with built-in
338
+ functions like cpdef methods::
339
+
340
+ sage: class A:
341
+ ....: def __len__(x):
342
+ ....: return int(5)
343
+ ....: len = lazy_attribute(len)
344
+ ....:
345
+ sage: A().len
346
+ 5
347
+
348
+ Since :issue:`11115`, extension classes derived from
349
+ :class:`~sage.structure.parent.Parent` can inherit a lazy attribute,
350
+ such as ``element_class``::
351
+
352
+ sage: cython_code = ["from sage.structure.parent cimport Parent",
353
+ ....: "from sage.structure.element cimport Element",
354
+ ....: "cdef class MyElement(Element): pass",
355
+ ....: "cdef class MyParent(Parent):",
356
+ ....: " Element = MyElement"]
357
+ sage: cython('\n'.join(cython_code)) # needs sage.misc.cython
358
+ sage: P = MyParent(category=Rings()) # needs sage.misc.cython
359
+ sage: P.element_class # indirect doctest # needs sage.misc.cython
360
+ <class '...MyElement'>
361
+
362
+ .. rubric:: About descriptor specifications
363
+
364
+ The specifications of descriptors (see 3.4.2.3 Invoking
365
+ Descriptors in the Python reference manual) are incomplete
366
+ w.r.t. inheritance, and maybe even ill-implemented. We illustrate
367
+ this on a simple class hierarchy, with an instrumented descriptor::
368
+
369
+ sage: class descriptor():
370
+ ....: def __get__(self, obj, cls):
371
+ ....: print(cls)
372
+ ....: return 1
373
+ sage: class A():
374
+ ....: x = descriptor()
375
+ sage: class B(A):
376
+ ....: pass
377
+ ....:
378
+
379
+ This is fine::
380
+
381
+ sage: A.x
382
+ <class '__main__.A'>
383
+ 1
384
+
385
+ The behaviour for the following case is not specified (see Instance Binding)
386
+ when ``x`` is not in the dictionary of ``B`` but in that of some super
387
+ category::
388
+
389
+ sage: B().x
390
+ <class '__main__.B'>
391
+ 1
392
+
393
+ It would seem more natural (and practical!) to get ``A`` rather than ``B``.
394
+
395
+ From the specifications for Super Binding, it would be expected to
396
+ get ``A`` and not ``B`` as cls parameter::
397
+
398
+ sage: super(B, B()).x
399
+ <class '__main__.B'>
400
+ 1
401
+
402
+ Due to this, the natural implementation runs into an infinite loop
403
+ in the following example::
404
+
405
+ sage: class A():
406
+ ....: @lazy_attribute
407
+ ....: def unimplemented_A(self):
408
+ ....: return NotImplemented
409
+ ....: @lazy_attribute
410
+ ....: def unimplemented_AB(self):
411
+ ....: return NotImplemented
412
+ ....: @lazy_attribute
413
+ ....: def unimplemented_B_implemented_A(self):
414
+ ....: return 1
415
+ ....:
416
+ sage: class B(A):
417
+ ....: @lazy_attribute
418
+ ....: def unimplemented_B(self):
419
+ ....: return NotImplemented
420
+ ....: @lazy_attribute
421
+ ....: def unimplemented_AB(self):
422
+ ....: return NotImplemented
423
+ ....: @lazy_attribute
424
+ ....: def unimplemented_B_implemented_A(self):
425
+ ....: return NotImplemented
426
+ ....:
427
+ sage: class C(B):
428
+ ....: pass
429
+ ....:
430
+
431
+ This is the simplest case where, without workaround, we get an
432
+ infinite loop::
433
+
434
+ sage: hasattr(B(), "unimplemented_A") # todo: not implemented
435
+ False
436
+
437
+ .. TODO::
438
+
439
+ Improve the error message::
440
+
441
+ sage: B().unimplemented_A # todo: not implemented
442
+ Traceback (most recent call last):
443
+ ...
444
+ AttributeError: 'super' object has no attribute 'unimplemented_A'...
445
+
446
+ We now make some systematic checks::
447
+
448
+ sage: B().unimplemented_A
449
+ Traceback (most recent call last):
450
+ ...
451
+ AttributeError: '...' object has no attribute 'unimplemented_A'...
452
+ sage: B().unimplemented_B
453
+ Traceback (most recent call last):
454
+ ...
455
+ AttributeError: '...' object has no attribute 'unimplemented_B'...
456
+ sage: B().unimplemented_AB
457
+ Traceback (most recent call last):
458
+ ...
459
+ AttributeError: '...' object has no attribute 'unimplemented_AB'...
460
+ sage: B().unimplemented_B_implemented_A
461
+ 1
462
+
463
+ sage: C().unimplemented_A()
464
+ Traceback (most recent call last):
465
+ ...
466
+ AttributeError: '...' object has no attribute 'unimplemented_A'...
467
+ sage: C().unimplemented_B()
468
+ Traceback (most recent call last):
469
+ ...
470
+ AttributeError: '...' object has no attribute 'unimplemented_B'...
471
+ sage: C().unimplemented_AB()
472
+ Traceback (most recent call last):
473
+ ...
474
+ AttributeError: '...' object has no attribute 'unimplemented_AB'...
475
+ sage: C().unimplemented_B_implemented_A # todo: not implemented
476
+ 1
477
+ """
478
+ def __init__(self, f):
479
+ """
480
+ Initialize ``self``.
481
+
482
+ EXAMPLES::
483
+
484
+ sage: def f(x):
485
+ ....: "doc of f"
486
+ ....: return 1
487
+ ....:
488
+ sage: x = lazy_attribute(f); x
489
+ <sage.misc.lazy_attribute.lazy_attribute object at ...>
490
+ sage: x.__doc__
491
+ 'doc of f'
492
+ sage: x.__name__
493
+ 'f'
494
+ sage: x.__module__
495
+ '__main__'
496
+ """
497
+ self.f = f
498
+ if hasattr(f, "__doc__"):
499
+ self.__doc__ = f.__doc__
500
+ elif hasattr(f, "__doc__"): # Needed to handle Cython methods
501
+ self.__doc__ = f.__doc__
502
+ if hasattr(f, "__name__"):
503
+ self.__name__ = f.__name__
504
+ elif hasattr(f, "__name__"): # Needed to handle Cython methods
505
+ self.__name__ = f.__name__
506
+ if hasattr(f, "__module__"):
507
+ self.__module__ = f.__module__
508
+
509
+
510
+ class lazy_class_attribute(lazy_attribute):
511
+ """
512
+ A lazy class attribute for a class is like a usual class attribute,
513
+ except that, instead of being computed when the class is constructed, it
514
+ is computed on the fly the first time it is accessed, either through the
515
+ class itself or through one of its objects.
516
+
517
+ This is very similar to :class:`lazy_attribute` except that the attribute
518
+ is a class attribute. More precisely, once computed, the lazy class
519
+ attribute is stored in the class rather than in the object. The lazy class
520
+ attribute is only computed once for all the objects::
521
+
522
+ sage: class Cl():
523
+ ....: @lazy_class_attribute
524
+ ....: def x(cls):
525
+ ....: print("computing x")
526
+ ....: return 1
527
+ sage: Cl.x
528
+ computing x
529
+ 1
530
+ sage: Cl.x
531
+ 1
532
+
533
+ As for a any usual class attribute it is also possible to access it from
534
+ an object::
535
+
536
+ sage: b = Cl()
537
+ sage: b.x
538
+ 1
539
+
540
+ First access from an object also properly triggers the computation::
541
+
542
+ sage: class Cl1():
543
+ ....: @lazy_class_attribute
544
+ ....: def x(cls):
545
+ ....: print("computing x")
546
+ ....: return 1
547
+ sage: Cl1().x
548
+ computing x
549
+ 1
550
+ sage: Cl1().x
551
+ 1
552
+
553
+ .. WARNING::
554
+
555
+ The behavior of lazy class attributes with respect to inheritance is
556
+ not specified. It currently depends on the evaluation order::
557
+
558
+ sage: class A():
559
+ ....: @lazy_class_attribute
560
+ ....: def x(cls):
561
+ ....: print("computing x")
562
+ ....: return str(cls)
563
+ ....: @lazy_class_attribute
564
+ ....: def y(cls):
565
+ ....: print("computing y")
566
+ ....: return str(cls)
567
+ sage: class B(A):
568
+ ....: pass
569
+
570
+ sage: A.x
571
+ computing x
572
+ "<class '__main__.A'>"
573
+ sage: B.x
574
+ "<class '__main__.A'>"
575
+
576
+ sage: B.y
577
+ computing y
578
+ "<class '__main__.B'>"
579
+ sage: A.y
580
+ computing y
581
+ "<class '__main__.A'>"
582
+ sage: B.y
583
+ "<class '__main__.B'>"
584
+
585
+ TESTS::
586
+
587
+ sage: "x" in b.__dict__
588
+ False
589
+ """
590
+ def __get__(self, _, cls):
591
+ """
592
+ Implement the attribute access protocol.
593
+
594
+ EXAMPLES::
595
+
596
+ sage: class A: pass
597
+ sage: def f(x): return 1
598
+ ....:
599
+ sage: f = lazy_class_attribute(f)
600
+ sage: f.__get__(A(), A)
601
+ 1
602
+ """
603
+ result = self.f(cls)
604
+ if result is NotImplemented:
605
+ return getattr(super(cls, cls), self.__name__)
606
+ setattr(cls, self.__name__, result)
607
+ return result