brainstate 0.1.8__py2.py3-none-any.whl → 0.1.9__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. brainstate/__init__.py +58 -51
  2. brainstate/_compatible_import.py +148 -148
  3. brainstate/_state.py +1605 -1663
  4. brainstate/_state_test.py +52 -52
  5. brainstate/_utils.py +47 -47
  6. brainstate/augment/__init__.py +30 -30
  7. brainstate/augment/_autograd.py +778 -778
  8. brainstate/augment/_autograd_test.py +1289 -1289
  9. brainstate/augment/_eval_shape.py +99 -99
  10. brainstate/augment/_eval_shape_test.py +38 -38
  11. brainstate/augment/_mapping.py +1060 -1060
  12. brainstate/augment/_mapping_test.py +597 -597
  13. brainstate/augment/_random.py +151 -151
  14. brainstate/compile/__init__.py +38 -38
  15. brainstate/compile/_ad_checkpoint.py +204 -204
  16. brainstate/compile/_ad_checkpoint_test.py +49 -49
  17. brainstate/compile/_conditions.py +256 -256
  18. brainstate/compile/_conditions_test.py +220 -220
  19. brainstate/compile/_error_if.py +92 -92
  20. brainstate/compile/_error_if_test.py +52 -52
  21. brainstate/compile/_jit.py +346 -346
  22. brainstate/compile/_jit_test.py +143 -143
  23. brainstate/compile/_loop_collect_return.py +536 -536
  24. brainstate/compile/_loop_collect_return_test.py +58 -58
  25. brainstate/compile/_loop_no_collection.py +184 -184
  26. brainstate/compile/_loop_no_collection_test.py +50 -50
  27. brainstate/compile/_make_jaxpr.py +888 -888
  28. brainstate/compile/_make_jaxpr_test.py +156 -156
  29. brainstate/compile/_progress_bar.py +202 -202
  30. brainstate/compile/_unvmap.py +159 -159
  31. brainstate/compile/_util.py +147 -147
  32. brainstate/environ.py +563 -563
  33. brainstate/environ_test.py +62 -62
  34. brainstate/functional/__init__.py +27 -26
  35. brainstate/graph/__init__.py +29 -29
  36. brainstate/graph/_graph_node.py +244 -244
  37. brainstate/graph/_graph_node_test.py +73 -73
  38. brainstate/graph/_graph_operation.py +1738 -1738
  39. brainstate/graph/_graph_operation_test.py +563 -563
  40. brainstate/init/__init__.py +26 -26
  41. brainstate/init/_base.py +52 -52
  42. brainstate/init/_generic.py +244 -244
  43. brainstate/init/_random_inits.py +553 -553
  44. brainstate/init/_random_inits_test.py +149 -149
  45. brainstate/init/_regular_inits.py +105 -105
  46. brainstate/init/_regular_inits_test.py +50 -50
  47. brainstate/mixin.py +365 -363
  48. brainstate/mixin_test.py +77 -73
  49. brainstate/nn/__init__.py +135 -131
  50. brainstate/{functional → nn}/_activations.py +808 -813
  51. brainstate/{functional → nn}/_activations_test.py +331 -331
  52. brainstate/nn/_collective_ops.py +514 -514
  53. brainstate/nn/_collective_ops_test.py +43 -43
  54. brainstate/nn/_common.py +178 -178
  55. brainstate/nn/_conv.py +501 -501
  56. brainstate/nn/_conv_test.py +238 -238
  57. brainstate/nn/_delay.py +509 -502
  58. brainstate/nn/_delay_test.py +238 -184
  59. brainstate/nn/_dropout.py +426 -426
  60. brainstate/nn/_dropout_test.py +100 -100
  61. brainstate/nn/_dynamics.py +1343 -1343
  62. brainstate/nn/_dynamics_test.py +78 -78
  63. brainstate/nn/_elementwise.py +1119 -1119
  64. brainstate/nn/_elementwise_test.py +169 -169
  65. brainstate/nn/_embedding.py +58 -58
  66. brainstate/nn/_exp_euler.py +92 -92
  67. brainstate/nn/_exp_euler_test.py +35 -35
  68. brainstate/nn/_fixedprob.py +239 -239
  69. brainstate/nn/_fixedprob_test.py +114 -114
  70. brainstate/nn/_inputs.py +608 -608
  71. brainstate/nn/_linear.py +424 -424
  72. brainstate/nn/_linear_mv.py +83 -83
  73. brainstate/nn/_linear_mv_test.py +120 -120
  74. brainstate/nn/_linear_test.py +107 -107
  75. brainstate/nn/_ltp.py +28 -28
  76. brainstate/nn/_module.py +377 -377
  77. brainstate/nn/_module_test.py +40 -40
  78. brainstate/nn/_neuron.py +705 -705
  79. brainstate/nn/_neuron_test.py +161 -161
  80. brainstate/nn/_normalizations.py +975 -918
  81. brainstate/nn/_normalizations_test.py +73 -73
  82. brainstate/{functional → nn}/_others.py +46 -46
  83. brainstate/nn/_poolings.py +1177 -1177
  84. brainstate/nn/_poolings_test.py +217 -217
  85. brainstate/nn/_projection.py +486 -486
  86. brainstate/nn/_rate_rnns.py +554 -554
  87. brainstate/nn/_rate_rnns_test.py +63 -63
  88. brainstate/nn/_readout.py +209 -209
  89. brainstate/nn/_readout_test.py +53 -53
  90. brainstate/nn/_stp.py +236 -236
  91. brainstate/nn/_synapse.py +505 -505
  92. brainstate/nn/_synapse_test.py +131 -131
  93. brainstate/nn/_synaptic_projection.py +423 -423
  94. brainstate/nn/_synouts.py +162 -162
  95. brainstate/nn/_synouts_test.py +57 -57
  96. brainstate/nn/_utils.py +89 -89
  97. brainstate/nn/metrics.py +388 -388
  98. brainstate/optim/__init__.py +38 -38
  99. brainstate/optim/_base.py +64 -64
  100. brainstate/optim/_lr_scheduler.py +448 -448
  101. brainstate/optim/_lr_scheduler_test.py +50 -50
  102. brainstate/optim/_optax_optimizer.py +152 -152
  103. brainstate/optim/_optax_optimizer_test.py +53 -53
  104. brainstate/optim/_sgd_optimizer.py +1104 -1104
  105. brainstate/random/__init__.py +24 -24
  106. brainstate/random/_rand_funs.py +3616 -3616
  107. brainstate/random/_rand_funs_test.py +567 -567
  108. brainstate/random/_rand_seed.py +210 -210
  109. brainstate/random/_rand_seed_test.py +48 -48
  110. brainstate/random/_rand_state.py +1409 -1409
  111. brainstate/random/_random_for_unit.py +52 -52
  112. brainstate/surrogate.py +1957 -1957
  113. brainstate/transform.py +23 -23
  114. brainstate/typing.py +304 -304
  115. brainstate/util/__init__.py +50 -50
  116. brainstate/util/caller.py +98 -98
  117. brainstate/util/error.py +55 -55
  118. brainstate/util/filter.py +469 -469
  119. brainstate/util/others.py +540 -540
  120. brainstate/util/pretty_pytree.py +945 -945
  121. brainstate/util/pretty_pytree_test.py +159 -159
  122. brainstate/util/pretty_repr.py +328 -328
  123. brainstate/util/pretty_table.py +2954 -2954
  124. brainstate/util/scaling.py +258 -258
  125. brainstate/util/struct.py +523 -523
  126. {brainstate-0.1.8.dist-info → brainstate-0.1.9.dist-info}/METADATA +91 -99
  127. brainstate-0.1.9.dist-info/RECORD +130 -0
  128. {brainstate-0.1.8.dist-info → brainstate-0.1.9.dist-info}/WHEEL +1 -1
  129. {brainstate-0.1.8.dist-info → brainstate-0.1.9.dist-info/licenses}/LICENSE +202 -202
  130. brainstate/functional/_normalization.py +0 -81
  131. brainstate/functional/_spikes.py +0 -204
  132. brainstate-0.1.8.dist-info/RECORD +0 -132
  133. {brainstate-0.1.8.dist-info → brainstate-0.1.9.dist-info}/top_level.txt +0 -0
@@ -1,328 +1,328 @@
1
- # The file is adapted from the Flax library (https://github.com/google/flax).
2
- # The credit should go to the Flax authors.
3
- #
4
- # Copyright 2024 The Flax Authors.
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
-
18
- import dataclasses
19
- import threading
20
- from abc import ABC, abstractmethod
21
- from functools import partial
22
- from typing import Any, Iterator, Mapping, TypeVar, Union, Callable, Optional
23
-
24
- __all__ = [
25
- 'yield_unique_pretty_repr_items',
26
- 'PrettyType',
27
- 'PrettyAttr',
28
- 'PrettyRepr',
29
- 'PrettyMapping',
30
- 'MappingReprMixin',
31
- ]
32
-
33
- A = TypeVar('A')
34
- B = TypeVar('B')
35
-
36
-
37
- @dataclasses.dataclass
38
- class PrettyType:
39
- """
40
- Configuration for pretty representation of objects.
41
- """
42
- type: Union[str, type]
43
- start: str = '('
44
- end: str = ')'
45
- value_sep: str = '='
46
- elem_indent: str = ' '
47
- empty_repr: str = ''
48
-
49
-
50
- @dataclasses.dataclass
51
- class PrettyAttr:
52
- """
53
- Configuration for pretty representation of attributes.
54
- """
55
- key: str
56
- value: Union[str, Any]
57
- start: str = ''
58
- end: str = ''
59
-
60
-
61
- class PrettyRepr(ABC):
62
- """
63
- Interface for pretty representation of objects.
64
-
65
- Example::
66
-
67
- >>> class MyObject(PrettyRepr):
68
- >>> def __pretty_repr__(self):
69
- >>> yield PrettyType(type='MyObject', start='{', end='}')
70
- >>> yield PrettyAttr('key', self.key)
71
- >>> yield PrettyAttr('value', self.value)
72
-
73
- """
74
- __slots__ = ()
75
-
76
- @abstractmethod
77
- def __pretty_repr__(self) -> Iterator[Union[PrettyType, PrettyAttr]]:
78
- raise NotImplementedError
79
-
80
- def __repr__(self) -> str:
81
- # repr the individual object with the pretty representation
82
- return pretty_repr_object(self)
83
-
84
-
85
- def pretty_repr_elem(obj: PrettyType, elem: Any) -> str:
86
- """
87
- Constructs a string representation of a single element within a pretty representation.
88
-
89
- This function takes a `PrettyType` object and an element, which must be an instance
90
- of `PrettyAttr`, and generates a formatted string that represents the element. The
91
- formatting is based on the configuration provided by the `PrettyType` object.
92
-
93
- Parameters
94
- ----------
95
- obj : PrettyType
96
- The configuration object that defines how the element should be formatted.
97
- It includes details such as indentation, separators, and surrounding characters.
98
- elem : Any
99
- The element to be represented. It must be an instance of `PrettyAttr`, which
100
- contains the key and value to be formatted.
101
-
102
- Returns
103
- -------
104
- str
105
- A string that represents the element in a formatted manner, adhering to the
106
- configuration specified by the `PrettyType` object.
107
-
108
- Raises
109
- ------
110
- TypeError
111
- If the provided element is not an instance of `PrettyAttr`.
112
- """
113
- if not isinstance(elem, PrettyAttr):
114
- raise TypeError(f'Item must be Elem, got {type(elem).__name__}')
115
-
116
- value = elem.value if isinstance(elem.value, str) else repr(elem.value)
117
- value = value.replace('\n', '\n' + obj.elem_indent)
118
-
119
- return f'{obj.elem_indent}{elem.start}{elem.key}{obj.value_sep}{value}{elem.end}'
120
-
121
-
122
- def pretty_repr_object(obj: PrettyRepr) -> str:
123
- """
124
- Generates a pretty string representation of an object that implements the PrettyRepr interface.
125
-
126
- This function utilizes the __pretty_repr__ method of the PrettyRepr interface to obtain
127
- a structured representation of the object, which includes both the type and attributes
128
- of the object in a human-readable format.
129
-
130
- Parameters
131
- ----------
132
- obj : PrettyRepr
133
- The object for which the pretty representation is to be generated. The object must
134
- implement the PrettyRepr interface.
135
-
136
- Returns
137
- -------
138
- str
139
- A string that represents the object in a pretty format, including its type and attributes.
140
- The format is determined by the PrettyType and PrettyAttr instances yielded by the
141
- __pretty_repr__ method of the object.
142
-
143
- Raises
144
- ------
145
- TypeError
146
- If the provided object does not implement the PrettyRepr interface or if the first item
147
- yielded by the __pretty_repr__ method is not an instance of PrettyType.
148
- """
149
- if not isinstance(obj, PrettyRepr):
150
- raise TypeError(f'Object {obj!r} is not representable')
151
-
152
- iterator = obj.__pretty_repr__()
153
- obj_repr = next(iterator)
154
-
155
- # repr object
156
- if not isinstance(obj_repr, PrettyType):
157
- raise TypeError(f'First item must be PrettyType, got {type(obj_repr).__name__}')
158
-
159
- # repr attributes
160
- elem_reprs = tuple(map(partial(pretty_repr_elem, obj_repr), iterator))
161
- elems = ',\n'.join(elem_reprs)
162
- if elems:
163
- elems = '\n' + elems + '\n'
164
- else:
165
- elems = obj_repr.empty_repr
166
-
167
- # repr object type
168
- type_repr = obj_repr.type if isinstance(obj_repr.type, str) else obj_repr.type.__name__
169
-
170
- # return repr
171
- return f'{type_repr}{obj_repr.start}{elems}{obj_repr.end}'
172
-
173
-
174
- class MappingReprMixin(Mapping[A, B]):
175
- """
176
- Mapping mixin for pretty representation.
177
- """
178
-
179
- def __pretty_repr__(self):
180
- yield PrettyType(type='', value_sep=': ', start='{', end='}')
181
-
182
- for key, value in self.items():
183
- yield PrettyAttr(repr(key), value)
184
-
185
-
186
- @dataclasses.dataclass(repr=False)
187
- class PrettyMapping(PrettyRepr):
188
- """
189
- Pretty representation of a mapping.
190
- """
191
- mapping: Mapping
192
- type_name: str = ''
193
-
194
- def __pretty_repr__(self):
195
- yield PrettyType(type=self.type_name, value_sep=': ', start='{', end='}')
196
-
197
- for key, value in self.mapping.items():
198
- yield PrettyAttr(repr(key), value)
199
-
200
-
201
- @dataclasses.dataclass
202
- class PrettyReprContext(threading.local):
203
- """
204
- A thread-local context for managing the state of pretty representation.
205
-
206
- This class is used to keep track of objects that have been seen during
207
- the generation of pretty representations, preventing infinite recursion
208
- in cases of circular references.
209
-
210
- Attributes
211
- ----------
212
- seen_modules_repr : dict[int, Any] | None
213
- A dictionary mapping object IDs to objects that have been seen
214
- during the pretty representation process. This is used to avoid
215
- representing the same object multiple times.
216
- """
217
- seen_modules_repr: dict[int, Any] | None = None
218
-
219
-
220
- CONTEXT = PrettyReprContext()
221
-
222
-
223
- def _default_repr_object(node):
224
- """
225
- Generates a default pretty representation for an object.
226
-
227
- This function yields a `PrettyType` instance that represents the type
228
- of the given object. It is used as a default method for representing
229
- objects when no custom representation function is provided.
230
-
231
- Parameters
232
- ----------
233
- node : Any
234
- The object for which the pretty representation is to be generated.
235
-
236
- Yields
237
- ------
238
- PrettyType
239
- An instance of `PrettyType` that contains the type information of
240
- the object.
241
- """
242
- yield PrettyType(type=type(node))
243
-
244
-
245
- def _default_repr_attr(node):
246
- """
247
- Generates a default pretty representation for the attributes of an object.
248
-
249
- This function iterates over the attributes of the given object and yields
250
- a `PrettyAttr` instance for each attribute that does not start with an
251
- underscore. The `PrettyAttr` instances contain the attribute name and its
252
- string representation.
253
-
254
- Parameters
255
- ----------
256
- node : Any
257
- The object whose attributes are to be represented.
258
-
259
- Yields
260
- ------
261
- PrettyAttr
262
- An instance of `PrettyAttr` for each non-private attribute of the object,
263
- containing the attribute name and its string representation.
264
- """
265
- for name, value in vars(node).items():
266
- if name.startswith('_'):
267
- continue
268
- yield PrettyAttr(name, repr(value))
269
-
270
-
271
- def yield_unique_pretty_repr_items(
272
- node,
273
- repr_object: Optional[Callable] = None,
274
- repr_attr: Optional[Callable] = None
275
- ):
276
- """
277
- Generates a pretty representation of an object while avoiding duplicate representations.
278
-
279
- This function is designed to yield a structured representation of an object,
280
- using custom or default methods for representing the object itself and its attributes.
281
- It ensures that each object is only represented once to prevent infinite recursion
282
- in cases of circular references.
283
-
284
- Parameters:
285
- node : Any
286
- The object to be represented.
287
- repr_object : Optional[Callable], optional
288
- A callable that yields the representation of the object itself.
289
- If not provided, a default representation function is used.
290
- repr_attr : Optional[Callable], optional
291
- A callable that yields the representation of the object's attributes.
292
- If not provided, a default attribute representation function is used.
293
-
294
- Yields:
295
- Union[PrettyType, PrettyAttr]
296
- The pretty representation of the object and its attributes,
297
- avoiding duplicates by tracking seen objects.
298
- """
299
- if repr_object is None:
300
- repr_object = _default_repr_object
301
- if repr_attr is None:
302
- repr_attr = _default_repr_attr
303
-
304
- if CONTEXT.seen_modules_repr is None:
305
- # CONTEXT.seen_modules_repr = set()
306
- CONTEXT.seen_modules_repr = dict()
307
- clear_seen = True
308
- else:
309
- clear_seen = False
310
-
311
- # Avoid infinite recursion
312
- if id(node) in CONTEXT.seen_modules_repr:
313
- yield PrettyType(type=type(node), empty_repr='...')
314
- return
315
-
316
- # repr object
317
- yield from repr_object(node)
318
-
319
- # Add to seen modules
320
- # CONTEXT.seen_modules_repr.add(id(node))
321
- CONTEXT.seen_modules_repr[id(node)] = node
322
-
323
- try:
324
- # repr attributes
325
- yield from repr_attr(node)
326
- finally:
327
- if clear_seen:
328
- CONTEXT.seen_modules_repr = None
1
+ # The file is adapted from the Flax library (https://github.com/google/flax).
2
+ # The credit should go to the Flax authors.
3
+ #
4
+ # Copyright 2024 The Flax Authors.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ import dataclasses
19
+ import threading
20
+ from abc import ABC, abstractmethod
21
+ from functools import partial
22
+ from typing import Any, Iterator, Mapping, TypeVar, Union, Callable, Optional
23
+
24
+ __all__ = [
25
+ 'yield_unique_pretty_repr_items',
26
+ 'PrettyType',
27
+ 'PrettyAttr',
28
+ 'PrettyRepr',
29
+ 'PrettyMapping',
30
+ 'MappingReprMixin',
31
+ ]
32
+
33
+ A = TypeVar('A')
34
+ B = TypeVar('B')
35
+
36
+
37
+ @dataclasses.dataclass
38
+ class PrettyType:
39
+ """
40
+ Configuration for pretty representation of objects.
41
+ """
42
+ type: Union[str, type]
43
+ start: str = '('
44
+ end: str = ')'
45
+ value_sep: str = '='
46
+ elem_indent: str = ' '
47
+ empty_repr: str = ''
48
+
49
+
50
+ @dataclasses.dataclass
51
+ class PrettyAttr:
52
+ """
53
+ Configuration for pretty representation of attributes.
54
+ """
55
+ key: str
56
+ value: Union[str, Any]
57
+ start: str = ''
58
+ end: str = ''
59
+
60
+
61
+ class PrettyRepr(ABC):
62
+ """
63
+ Interface for pretty representation of objects.
64
+
65
+ Example::
66
+
67
+ >>> class MyObject(PrettyRepr):
68
+ >>> def __pretty_repr__(self):
69
+ >>> yield PrettyType(type='MyObject', start='{', end='}')
70
+ >>> yield PrettyAttr('key', self.key)
71
+ >>> yield PrettyAttr('value', self.value)
72
+
73
+ """
74
+ __slots__ = ()
75
+
76
+ @abstractmethod
77
+ def __pretty_repr__(self) -> Iterator[Union[PrettyType, PrettyAttr]]:
78
+ raise NotImplementedError
79
+
80
+ def __repr__(self) -> str:
81
+ # repr the individual object with the pretty representation
82
+ return pretty_repr_object(self)
83
+
84
+
85
+ def pretty_repr_elem(obj: PrettyType, elem: Any) -> str:
86
+ """
87
+ Constructs a string representation of a single element within a pretty representation.
88
+
89
+ This function takes a `PrettyType` object and an element, which must be an instance
90
+ of `PrettyAttr`, and generates a formatted string that represents the element. The
91
+ formatting is based on the configuration provided by the `PrettyType` object.
92
+
93
+ Parameters
94
+ ----------
95
+ obj : PrettyType
96
+ The configuration object that defines how the element should be formatted.
97
+ It includes details such as indentation, separators, and surrounding characters.
98
+ elem : Any
99
+ The element to be represented. It must be an instance of `PrettyAttr`, which
100
+ contains the key and value to be formatted.
101
+
102
+ Returns
103
+ -------
104
+ str
105
+ A string that represents the element in a formatted manner, adhering to the
106
+ configuration specified by the `PrettyType` object.
107
+
108
+ Raises
109
+ ------
110
+ TypeError
111
+ If the provided element is not an instance of `PrettyAttr`.
112
+ """
113
+ if not isinstance(elem, PrettyAttr):
114
+ raise TypeError(f'Item must be Elem, got {type(elem).__name__}')
115
+
116
+ value = elem.value if isinstance(elem.value, str) else repr(elem.value)
117
+ value = value.replace('\n', '\n' + obj.elem_indent)
118
+
119
+ return f'{obj.elem_indent}{elem.start}{elem.key}{obj.value_sep}{value}{elem.end}'
120
+
121
+
122
+ def pretty_repr_object(obj: PrettyRepr) -> str:
123
+ """
124
+ Generates a pretty string representation of an object that implements the PrettyRepr interface.
125
+
126
+ This function utilizes the __pretty_repr__ method of the PrettyRepr interface to obtain
127
+ a structured representation of the object, which includes both the type and attributes
128
+ of the object in a human-readable format.
129
+
130
+ Parameters
131
+ ----------
132
+ obj : PrettyRepr
133
+ The object for which the pretty representation is to be generated. The object must
134
+ implement the PrettyRepr interface.
135
+
136
+ Returns
137
+ -------
138
+ str
139
+ A string that represents the object in a pretty format, including its type and attributes.
140
+ The format is determined by the PrettyType and PrettyAttr instances yielded by the
141
+ __pretty_repr__ method of the object.
142
+
143
+ Raises
144
+ ------
145
+ TypeError
146
+ If the provided object does not implement the PrettyRepr interface or if the first item
147
+ yielded by the __pretty_repr__ method is not an instance of PrettyType.
148
+ """
149
+ if not isinstance(obj, PrettyRepr):
150
+ raise TypeError(f'Object {obj!r} is not representable')
151
+
152
+ iterator = obj.__pretty_repr__()
153
+ obj_repr = next(iterator)
154
+
155
+ # repr object
156
+ if not isinstance(obj_repr, PrettyType):
157
+ raise TypeError(f'First item must be PrettyType, got {type(obj_repr).__name__}')
158
+
159
+ # repr attributes
160
+ elem_reprs = tuple(map(partial(pretty_repr_elem, obj_repr), iterator))
161
+ elems = ',\n'.join(elem_reprs)
162
+ if elems:
163
+ elems = '\n' + elems + '\n'
164
+ else:
165
+ elems = obj_repr.empty_repr
166
+
167
+ # repr object type
168
+ type_repr = obj_repr.type if isinstance(obj_repr.type, str) else obj_repr.type.__name__
169
+
170
+ # return repr
171
+ return f'{type_repr}{obj_repr.start}{elems}{obj_repr.end}'
172
+
173
+
174
+ class MappingReprMixin(Mapping[A, B]):
175
+ """
176
+ Mapping mixin for pretty representation.
177
+ """
178
+
179
+ def __pretty_repr__(self):
180
+ yield PrettyType(type='', value_sep=': ', start='{', end='}')
181
+
182
+ for key, value in self.items():
183
+ yield PrettyAttr(repr(key), value)
184
+
185
+
186
+ @dataclasses.dataclass(repr=False)
187
+ class PrettyMapping(PrettyRepr):
188
+ """
189
+ Pretty representation of a mapping.
190
+ """
191
+ mapping: Mapping
192
+ type_name: str = ''
193
+
194
+ def __pretty_repr__(self):
195
+ yield PrettyType(type=self.type_name, value_sep=': ', start='{', end='}')
196
+
197
+ for key, value in self.mapping.items():
198
+ yield PrettyAttr(repr(key), value)
199
+
200
+
201
+ @dataclasses.dataclass
202
+ class PrettyReprContext(threading.local):
203
+ """
204
+ A thread-local context for managing the state of pretty representation.
205
+
206
+ This class is used to keep track of objects that have been seen during
207
+ the generation of pretty representations, preventing infinite recursion
208
+ in cases of circular references.
209
+
210
+ Attributes
211
+ ----------
212
+ seen_modules_repr : dict[int, Any] | None
213
+ A dictionary mapping object IDs to objects that have been seen
214
+ during the pretty representation process. This is used to avoid
215
+ representing the same object multiple times.
216
+ """
217
+ seen_modules_repr: dict[int, Any] | None = None
218
+
219
+
220
+ CONTEXT = PrettyReprContext()
221
+
222
+
223
+ def _default_repr_object(node):
224
+ """
225
+ Generates a default pretty representation for an object.
226
+
227
+ This function yields a `PrettyType` instance that represents the type
228
+ of the given object. It is used as a default method for representing
229
+ objects when no custom representation function is provided.
230
+
231
+ Parameters
232
+ ----------
233
+ node : Any
234
+ The object for which the pretty representation is to be generated.
235
+
236
+ Yields
237
+ ------
238
+ PrettyType
239
+ An instance of `PrettyType` that contains the type information of
240
+ the object.
241
+ """
242
+ yield PrettyType(type=type(node))
243
+
244
+
245
+ def _default_repr_attr(node):
246
+ """
247
+ Generates a default pretty representation for the attributes of an object.
248
+
249
+ This function iterates over the attributes of the given object and yields
250
+ a `PrettyAttr` instance for each attribute that does not start with an
251
+ underscore. The `PrettyAttr` instances contain the attribute name and its
252
+ string representation.
253
+
254
+ Parameters
255
+ ----------
256
+ node : Any
257
+ The object whose attributes are to be represented.
258
+
259
+ Yields
260
+ ------
261
+ PrettyAttr
262
+ An instance of `PrettyAttr` for each non-private attribute of the object,
263
+ containing the attribute name and its string representation.
264
+ """
265
+ for name, value in vars(node).items():
266
+ if name.startswith('_'):
267
+ continue
268
+ yield PrettyAttr(name, repr(value))
269
+
270
+
271
+ def yield_unique_pretty_repr_items(
272
+ node,
273
+ repr_object: Optional[Callable] = None,
274
+ repr_attr: Optional[Callable] = None
275
+ ):
276
+ """
277
+ Generates a pretty representation of an object while avoiding duplicate representations.
278
+
279
+ This function is designed to yield a structured representation of an object,
280
+ using custom or default methods for representing the object itself and its attributes.
281
+ It ensures that each object is only represented once to prevent infinite recursion
282
+ in cases of circular references.
283
+
284
+ Parameters:
285
+ node : Any
286
+ The object to be represented.
287
+ repr_object : Optional[Callable], optional
288
+ A callable that yields the representation of the object itself.
289
+ If not provided, a default representation function is used.
290
+ repr_attr : Optional[Callable], optional
291
+ A callable that yields the representation of the object's attributes.
292
+ If not provided, a default attribute representation function is used.
293
+
294
+ Yields:
295
+ Union[PrettyType, PrettyAttr]
296
+ The pretty representation of the object and its attributes,
297
+ avoiding duplicates by tracking seen objects.
298
+ """
299
+ if repr_object is None:
300
+ repr_object = _default_repr_object
301
+ if repr_attr is None:
302
+ repr_attr = _default_repr_attr
303
+
304
+ if CONTEXT.seen_modules_repr is None:
305
+ # CONTEXT.seen_modules_repr = set()
306
+ CONTEXT.seen_modules_repr = dict()
307
+ clear_seen = True
308
+ else:
309
+ clear_seen = False
310
+
311
+ # Avoid infinite recursion
312
+ if id(node) in CONTEXT.seen_modules_repr:
313
+ yield PrettyType(type=type(node), empty_repr='...')
314
+ return
315
+
316
+ # repr object
317
+ yield from repr_object(node)
318
+
319
+ # Add to seen modules
320
+ # CONTEXT.seen_modules_repr.add(id(node))
321
+ CONTEXT.seen_modules_repr[id(node)] = node
322
+
323
+ try:
324
+ # repr attributes
325
+ yield from repr_attr(node)
326
+ finally:
327
+ if clear_seen:
328
+ CONTEXT.seen_modules_repr = None