atdata 0.1.3b3__py3-none-any.whl → 0.2.0a1__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.
- atdata/__init__.py +39 -1
- atdata/_helpers.py +39 -3
- atdata/atmosphere/__init__.py +61 -0
- atdata/atmosphere/_types.py +329 -0
- atdata/atmosphere/client.py +393 -0
- atdata/atmosphere/lens.py +280 -0
- atdata/atmosphere/records.py +342 -0
- atdata/atmosphere/schema.py +296 -0
- atdata/dataset.py +336 -203
- atdata/lens.py +177 -77
- atdata/local.py +492 -0
- atdata-0.2.0a1.dist-info/METADATA +181 -0
- atdata-0.2.0a1.dist-info/RECORD +16 -0
- {atdata-0.1.3b3.dist-info → atdata-0.2.0a1.dist-info}/WHEEL +1 -1
- atdata-0.1.3b3.dist-info/METADATA +0 -18
- atdata-0.1.3b3.dist-info/RECORD +0 -9
- {atdata-0.1.3b3.dist-info → atdata-0.2.0a1.dist-info}/entry_points.txt +0 -0
- {atdata-0.1.3b3.dist-info → atdata-0.2.0a1.dist-info}/licenses/LICENSE +0 -0
atdata/lens.py
CHANGED
|
@@ -1,4 +1,42 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Lens-based type transformations for datasets.
|
|
2
|
+
|
|
3
|
+
This module implements a lens system for bidirectional transformations between
|
|
4
|
+
different sample types. Lenses enable viewing a dataset through different type
|
|
5
|
+
schemas without duplicating the underlying data.
|
|
6
|
+
|
|
7
|
+
Key components:
|
|
8
|
+
|
|
9
|
+
- ``Lens``: Bidirectional transformation with getter (S -> V) and optional
|
|
10
|
+
putter (V, S -> S)
|
|
11
|
+
- ``LensNetwork``: Global singleton registry for lens transformations
|
|
12
|
+
- ``@lens``: Decorator to create and register lens transformations
|
|
13
|
+
|
|
14
|
+
Lenses support the functional programming concept of composable, well-behaved
|
|
15
|
+
transformations that satisfy lens laws (GetPut and PutGet).
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
>>> @packable
|
|
19
|
+
... class FullData:
|
|
20
|
+
... name: str
|
|
21
|
+
... age: int
|
|
22
|
+
... embedding: NDArray
|
|
23
|
+
...
|
|
24
|
+
>>> @packable
|
|
25
|
+
... class NameOnly:
|
|
26
|
+
... name: str
|
|
27
|
+
...
|
|
28
|
+
>>> @lens
|
|
29
|
+
... def name_view(full: FullData) -> NameOnly:
|
|
30
|
+
... return NameOnly(name=full.name)
|
|
31
|
+
...
|
|
32
|
+
>>> @name_view.putter
|
|
33
|
+
... def name_view_put(view: NameOnly, source: FullData) -> FullData:
|
|
34
|
+
... return FullData(name=view.name, age=source.age,
|
|
35
|
+
... embedding=source.embedding)
|
|
36
|
+
...
|
|
37
|
+
>>> ds = Dataset[FullData]("data.tar")
|
|
38
|
+
>>> ds_names = ds.as_type(NameOnly) # Uses registered lens
|
|
39
|
+
"""
|
|
2
40
|
|
|
3
41
|
##
|
|
4
42
|
# Imports
|
|
@@ -39,24 +77,45 @@ type LensPutter[S, V] = Callable[[V, S], S]
|
|
|
39
77
|
# Shortcut decorators
|
|
40
78
|
|
|
41
79
|
class Lens( Generic[S, V] ):
|
|
42
|
-
"""
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
80
|
+
"""A bidirectional transformation between two sample types.
|
|
81
|
+
|
|
82
|
+
A lens provides a way to view and update data of type ``S`` (source) as if
|
|
83
|
+
it were type ``V`` (view). It consists of a getter that transforms ``S -> V``
|
|
84
|
+
and an optional putter that transforms ``(V, S) -> S``, enabling updates to
|
|
85
|
+
the view to be reflected back in the source.
|
|
86
|
+
|
|
87
|
+
Type Parameters:
|
|
88
|
+
S: The source type, must derive from ``PackableSample``.
|
|
89
|
+
V: The view type, must derive from ``PackableSample``.
|
|
90
|
+
|
|
91
|
+
Example:
|
|
92
|
+
>>> @lens
|
|
93
|
+
... def name_lens(full: FullData) -> NameOnly:
|
|
94
|
+
... return NameOnly(name=full.name)
|
|
95
|
+
...
|
|
96
|
+
>>> @name_lens.putter
|
|
97
|
+
... def name_lens_put(view: NameOnly, source: FullData) -> FullData:
|
|
98
|
+
... return FullData(name=view.name, age=source.age)
|
|
99
|
+
"""
|
|
55
100
|
|
|
56
101
|
def __init__( self, get: LensGetter[S, V],
|
|
57
102
|
put: Optional[LensPutter[S, V]] = None
|
|
58
103
|
) -> None:
|
|
59
|
-
"""
|
|
104
|
+
"""Initialize a lens with a getter and optional putter function.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
get: A function that transforms from source type ``S`` to view type
|
|
108
|
+
``V``. Must accept exactly one parameter annotated with the
|
|
109
|
+
source type.
|
|
110
|
+
put: An optional function that updates the source based on a modified
|
|
111
|
+
view. Takes a view of type ``V`` and original source of type ``S``,
|
|
112
|
+
and returns an updated source of type ``S``. If not provided, a
|
|
113
|
+
trivial putter is used that ignores updates to the view.
|
|
114
|
+
|
|
115
|
+
Raises:
|
|
116
|
+
AssertionError: If the getter function doesn't have exactly one
|
|
117
|
+
parameter.
|
|
118
|
+
"""
|
|
60
119
|
##
|
|
61
120
|
|
|
62
121
|
# Check argument validity
|
|
@@ -70,11 +129,11 @@ class Lens( Generic[S, V] ):
|
|
|
70
129
|
functools.update_wrapper( self, get )
|
|
71
130
|
|
|
72
131
|
self.source_type: Type[PackableSample] = input_types[0].annotation
|
|
73
|
-
self.view_type = sig.return_annotation
|
|
132
|
+
self.view_type: Type[PackableSample] = sig.return_annotation
|
|
74
133
|
|
|
75
134
|
# Store the getter
|
|
76
135
|
self._getter = get
|
|
77
|
-
|
|
136
|
+
|
|
78
137
|
# Determine and store the putter
|
|
79
138
|
if put is None:
|
|
80
139
|
# Trivial putter does not update the source
|
|
@@ -86,7 +145,20 @@ class Lens( Generic[S, V] ):
|
|
|
86
145
|
#
|
|
87
146
|
|
|
88
147
|
def putter( self, put: LensPutter[S, V] ) -> LensPutter[S, V]:
|
|
89
|
-
"""
|
|
148
|
+
"""Decorator to register a putter function for this lens.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
put: A function that takes a view of type ``V`` and source of type
|
|
152
|
+
``S``, and returns an updated source of type ``S``.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
The putter function, allowing this to be used as a decorator.
|
|
156
|
+
|
|
157
|
+
Example:
|
|
158
|
+
>>> @my_lens.putter
|
|
159
|
+
... def my_lens_put(view: ViewType, source: SourceType) -> SourceType:
|
|
160
|
+
... return SourceType(...)
|
|
161
|
+
"""
|
|
90
162
|
##
|
|
91
163
|
self._putter = put
|
|
92
164
|
return put
|
|
@@ -94,107 +166,135 @@ class Lens( Generic[S, V] ):
|
|
|
94
166
|
# Methods to actually execute transformations
|
|
95
167
|
|
|
96
168
|
def put( self, v: V, s: S ) -> S:
|
|
97
|
-
"""
|
|
169
|
+
"""Update the source based on a modified view.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
v: The modified view of type ``V``.
|
|
173
|
+
s: The original source of type ``S``.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
An updated source of type ``S`` that reflects changes from the view.
|
|
177
|
+
"""
|
|
98
178
|
return self._putter( v, s )
|
|
99
179
|
|
|
100
180
|
def get( self, s: S ) -> V:
|
|
101
|
-
"""
|
|
181
|
+
"""Transform the source into the view type.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
s: The source sample of type ``S``.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
A view of the source as type ``V``.
|
|
188
|
+
"""
|
|
102
189
|
return self( s )
|
|
103
190
|
|
|
104
191
|
# Convenience to enable calling the lens as its getter
|
|
105
|
-
|
|
192
|
+
|
|
106
193
|
def __call__( self, s: S ) -> V:
|
|
107
|
-
|
|
194
|
+
"""Apply the lens transformation (same as ``get()``).
|
|
108
195
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
# """Register the annotated function `f` as the getter of a sample lens"""
|
|
196
|
+
Args:
|
|
197
|
+
s: The source sample of type ``S``.
|
|
112
198
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
# _network.register( ret )
|
|
118
|
-
# return ret
|
|
119
|
-
|
|
120
|
-
# # Return the lens decorator
|
|
121
|
-
# return _decorator
|
|
199
|
+
Returns:
|
|
200
|
+
A view of the source as type ``V``.
|
|
201
|
+
"""
|
|
202
|
+
return self._getter( s )
|
|
122
203
|
|
|
123
|
-
# # For convenience
|
|
124
|
-
# lens = _lens_factory
|
|
125
204
|
|
|
126
205
|
def lens( f: LensGetter[S, V] ) -> Lens[S, V]:
|
|
206
|
+
"""Decorator to create and register a lens transformation.
|
|
207
|
+
|
|
208
|
+
This decorator converts a getter function into a ``Lens`` object and
|
|
209
|
+
automatically registers it in the global ``LensNetwork`` registry.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
f: A getter function that transforms from source type ``S`` to view
|
|
213
|
+
type ``V``. Must have exactly one parameter with a type annotation.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
A ``Lens[S, V]`` object that can be called to apply the transformation
|
|
217
|
+
or decorated with ``@lens_name.putter`` to add a putter function.
|
|
218
|
+
|
|
219
|
+
Example:
|
|
220
|
+
>>> @lens
|
|
221
|
+
... def extract_name(full: FullData) -> NameOnly:
|
|
222
|
+
... return NameOnly(name=full.name)
|
|
223
|
+
...
|
|
224
|
+
>>> @extract_name.putter
|
|
225
|
+
... def extract_name_put(view: NameOnly, source: FullData) -> FullData:
|
|
226
|
+
... return FullData(name=view.name, age=source.age)
|
|
227
|
+
"""
|
|
127
228
|
ret = Lens[S, V]( f )
|
|
128
229
|
_network.register( ret )
|
|
129
230
|
return ret
|
|
130
231
|
|
|
131
232
|
|
|
132
|
-
|
|
133
|
-
|
|
233
|
+
class LensNetwork:
|
|
234
|
+
"""Global registry for lens transformations between sample types.
|
|
134
235
|
|
|
135
|
-
|
|
136
|
-
|
|
236
|
+
This class implements a singleton pattern to maintain a global registry of
|
|
237
|
+
all lenses decorated with ``@lens``. It enables looking up transformations
|
|
238
|
+
between different ``PackableSample`` types.
|
|
137
239
|
|
|
138
|
-
|
|
139
|
-
|
|
240
|
+
Attributes:
|
|
241
|
+
_instance: The singleton instance of this class.
|
|
242
|
+
_registry: Dictionary mapping ``(source_type, view_type)`` tuples to
|
|
243
|
+
their corresponding ``Lens`` objects.
|
|
244
|
+
"""
|
|
140
245
|
|
|
141
246
|
_instance = None
|
|
142
247
|
"""The singleton instance"""
|
|
143
248
|
|
|
144
249
|
def __new__(cls, *args, **kwargs):
|
|
250
|
+
"""Ensure only one instance of LensNetwork exists (singleton pattern)."""
|
|
145
251
|
if cls._instance is None:
|
|
146
252
|
# If no instance exists, create a new one
|
|
147
253
|
cls._instance = super().__new__(cls)
|
|
148
254
|
return cls._instance # Return the existing (or newly created) instance
|
|
149
255
|
|
|
150
256
|
def __init__(self):
|
|
257
|
+
"""Initialize the lens registry (only on first instantiation)."""
|
|
151
258
|
if not hasattr(self, '_initialized'): # Check if already initialized
|
|
152
259
|
self._registry: Dict[LensSignature, Lens] = dict()
|
|
153
260
|
self._initialized = True
|
|
154
261
|
|
|
155
262
|
def register( self, _lens: Lens ):
|
|
156
|
-
"""
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
# self._registry[input_type, output_type] = _lens
|
|
168
|
-
# print( _lens.source_type )
|
|
263
|
+
"""Register a lens as the canonical transformation between two types.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
_lens: The lens to register. Will be stored in the registry under
|
|
267
|
+
the key ``(_lens.source_type, _lens.view_type)``.
|
|
268
|
+
|
|
269
|
+
Note:
|
|
270
|
+
If a lens already exists for the same type pair, it will be
|
|
271
|
+
overwritten.
|
|
272
|
+
"""
|
|
169
273
|
self._registry[_lens.source_type, _lens.view_type] = _lens
|
|
170
274
|
|
|
171
275
|
def transform( self, source: DatasetType, view: DatasetType ) -> Lens:
|
|
172
|
-
"""
|
|
173
|
-
|
|
174
|
-
# TODO Handle compositional closure
|
|
175
|
-
ret = self._registry.get( (source, view), None )
|
|
176
|
-
if ret is None:
|
|
177
|
-
raise ValueError( f'No registered lens from source {source} to view {view}' )
|
|
178
|
-
|
|
179
|
-
return ret
|
|
276
|
+
"""Look up the lens transformation between two sample types.
|
|
180
277
|
|
|
278
|
+
Args:
|
|
279
|
+
source: The source sample type (must derive from ``PackableSample``).
|
|
280
|
+
view: The target view type (must derive from ``PackableSample``).
|
|
181
281
|
|
|
182
|
-
|
|
183
|
-
|
|
282
|
+
Returns:
|
|
283
|
+
The registered ``Lens`` that transforms from ``source`` to ``view``.
|
|
184
284
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
# ##
|
|
188
|
-
|
|
189
|
-
# sig = inspect.signature( f )
|
|
285
|
+
Raises:
|
|
286
|
+
ValueError: If no lens has been registered for the given type pair.
|
|
190
287
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
288
|
+
Note:
|
|
289
|
+
Currently only supports direct transformations. Compositional
|
|
290
|
+
transformations (chaining multiple lenses) are not yet implemented.
|
|
291
|
+
"""
|
|
292
|
+
ret = self._registry.get( (source, view), None )
|
|
293
|
+
if ret is None:
|
|
294
|
+
raise ValueError( f'No registered lens from source {source} to view {view}' )
|
|
195
295
|
|
|
196
|
-
|
|
296
|
+
return ret
|
|
197
297
|
|
|
198
|
-
# )
|
|
199
298
|
|
|
200
|
-
#
|
|
299
|
+
# Global singleton registry instance
|
|
300
|
+
_network = LensNetwork()
|