brainstate 0.1.8__py2.py3-none-any.whl → 0.1.10__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.
- brainstate/__init__.py +58 -51
- brainstate/_compatible_import.py +148 -148
- brainstate/_state.py +1605 -1663
- brainstate/_state_test.py +52 -52
- brainstate/_utils.py +47 -47
- brainstate/augment/__init__.py +30 -30
- brainstate/augment/_autograd.py +778 -778
- brainstate/augment/_autograd_test.py +1289 -1289
- brainstate/augment/_eval_shape.py +99 -99
- brainstate/augment/_eval_shape_test.py +38 -38
- brainstate/augment/_mapping.py +1060 -1060
- brainstate/augment/_mapping_test.py +597 -597
- brainstate/augment/_random.py +151 -151
- brainstate/compile/__init__.py +38 -38
- brainstate/compile/_ad_checkpoint.py +204 -204
- brainstate/compile/_ad_checkpoint_test.py +49 -49
- brainstate/compile/_conditions.py +256 -256
- brainstate/compile/_conditions_test.py +220 -220
- brainstate/compile/_error_if.py +92 -92
- brainstate/compile/_error_if_test.py +52 -52
- brainstate/compile/_jit.py +346 -346
- brainstate/compile/_jit_test.py +143 -143
- brainstate/compile/_loop_collect_return.py +536 -536
- brainstate/compile/_loop_collect_return_test.py +58 -58
- brainstate/compile/_loop_no_collection.py +184 -184
- brainstate/compile/_loop_no_collection_test.py +50 -50
- brainstate/compile/_make_jaxpr.py +888 -888
- brainstate/compile/_make_jaxpr_test.py +156 -156
- brainstate/compile/_progress_bar.py +202 -202
- brainstate/compile/_unvmap.py +159 -159
- brainstate/compile/_util.py +147 -147
- brainstate/environ.py +563 -563
- brainstate/environ_test.py +62 -62
- brainstate/functional/__init__.py +27 -26
- brainstate/graph/__init__.py +29 -29
- brainstate/graph/_graph_node.py +244 -244
- brainstate/graph/_graph_node_test.py +73 -73
- brainstate/graph/_graph_operation.py +1738 -1738
- brainstate/graph/_graph_operation_test.py +563 -563
- brainstate/init/__init__.py +26 -26
- brainstate/init/_base.py +52 -52
- brainstate/init/_generic.py +244 -244
- brainstate/init/_random_inits.py +553 -553
- brainstate/init/_random_inits_test.py +149 -149
- brainstate/init/_regular_inits.py +105 -105
- brainstate/init/_regular_inits_test.py +50 -50
- brainstate/mixin.py +365 -363
- brainstate/mixin_test.py +77 -73
- brainstate/nn/__init__.py +135 -131
- brainstate/{functional → nn}/_activations.py +808 -813
- brainstate/{functional → nn}/_activations_test.py +331 -331
- brainstate/nn/_collective_ops.py +514 -514
- brainstate/nn/_collective_ops_test.py +43 -43
- brainstate/nn/_common.py +178 -178
- brainstate/nn/_conv.py +501 -501
- brainstate/nn/_conv_test.py +238 -238
- brainstate/nn/_delay.py +588 -502
- brainstate/nn/_delay_test.py +238 -184
- brainstate/nn/_dropout.py +426 -426
- brainstate/nn/_dropout_test.py +100 -100
- brainstate/nn/_dynamics.py +1343 -1343
- brainstate/nn/_dynamics_test.py +78 -78
- brainstate/nn/_elementwise.py +1119 -1119
- brainstate/nn/_elementwise_test.py +169 -169
- brainstate/nn/_embedding.py +58 -58
- brainstate/nn/_exp_euler.py +92 -92
- brainstate/nn/_exp_euler_test.py +35 -35
- brainstate/nn/_fixedprob.py +239 -239
- brainstate/nn/_fixedprob_test.py +114 -114
- brainstate/nn/_inputs.py +608 -608
- brainstate/nn/_linear.py +424 -424
- brainstate/nn/_linear_mv.py +83 -83
- brainstate/nn/_linear_mv_test.py +120 -120
- brainstate/nn/_linear_test.py +107 -107
- brainstate/nn/_ltp.py +28 -28
- brainstate/nn/_module.py +377 -377
- brainstate/nn/_module_test.py +40 -40
- brainstate/nn/_neuron.py +705 -705
- brainstate/nn/_neuron_test.py +161 -161
- brainstate/nn/_normalizations.py +975 -918
- brainstate/nn/_normalizations_test.py +73 -73
- brainstate/{functional → nn}/_others.py +46 -46
- brainstate/nn/_poolings.py +1177 -1177
- brainstate/nn/_poolings_test.py +217 -217
- brainstate/nn/_projection.py +486 -486
- brainstate/nn/_rate_rnns.py +554 -554
- brainstate/nn/_rate_rnns_test.py +63 -63
- brainstate/nn/_readout.py +209 -209
- brainstate/nn/_readout_test.py +53 -53
- brainstate/nn/_stp.py +236 -236
- brainstate/nn/_synapse.py +505 -505
- brainstate/nn/_synapse_test.py +131 -131
- brainstate/nn/_synaptic_projection.py +423 -423
- brainstate/nn/_synouts.py +162 -162
- brainstate/nn/_synouts_test.py +57 -57
- brainstate/nn/_utils.py +89 -89
- brainstate/nn/metrics.py +388 -388
- brainstate/optim/__init__.py +38 -38
- brainstate/optim/_base.py +64 -64
- brainstate/optim/_lr_scheduler.py +448 -448
- brainstate/optim/_lr_scheduler_test.py +50 -50
- brainstate/optim/_optax_optimizer.py +152 -152
- brainstate/optim/_optax_optimizer_test.py +53 -53
- brainstate/optim/_sgd_optimizer.py +1104 -1104
- brainstate/random/__init__.py +24 -24
- brainstate/random/_rand_funs.py +3616 -3616
- brainstate/random/_rand_funs_test.py +567 -567
- brainstate/random/_rand_seed.py +210 -210
- brainstate/random/_rand_seed_test.py +48 -48
- brainstate/random/_rand_state.py +1409 -1409
- brainstate/random/_random_for_unit.py +52 -52
- brainstate/surrogate.py +1957 -1957
- brainstate/transform.py +23 -23
- brainstate/typing.py +304 -304
- brainstate/util/__init__.py +50 -50
- brainstate/util/caller.py +98 -98
- brainstate/util/error.py +55 -55
- brainstate/util/filter.py +469 -469
- brainstate/util/others.py +540 -540
- brainstate/util/pretty_pytree.py +945 -945
- brainstate/util/pretty_pytree_test.py +159 -159
- brainstate/util/pretty_repr.py +328 -328
- brainstate/util/pretty_table.py +2954 -2954
- brainstate/util/scaling.py +258 -258
- brainstate/util/struct.py +523 -523
- {brainstate-0.1.8.dist-info → brainstate-0.1.10.dist-info}/METADATA +91 -99
- brainstate-0.1.10.dist-info/RECORD +130 -0
- {brainstate-0.1.8.dist-info → brainstate-0.1.10.dist-info}/WHEEL +1 -1
- {brainstate-0.1.8.dist-info → brainstate-0.1.10.dist-info/licenses}/LICENSE +202 -202
- brainstate/functional/_normalization.py +0 -81
- brainstate/functional/_spikes.py +0 -204
- brainstate-0.1.8.dist-info/RECORD +0 -132
- {brainstate-0.1.8.dist-info → brainstate-0.1.10.dist-info}/top_level.txt +0 -0
brainstate/util/pretty_repr.py
CHANGED
@@ -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
|