atdata 0.1.3b4__py3-none-any.whl → 0.2.2b1__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 +44 -8
- atdata/_cid.py +150 -0
- atdata/_hf_api.py +692 -0
- atdata/_protocols.py +519 -0
- atdata/_schema_codec.py +442 -0
- atdata/_sources.py +515 -0
- atdata/_stub_manager.py +529 -0
- atdata/_type_utils.py +90 -0
- atdata/atmosphere/__init__.py +332 -0
- atdata/atmosphere/_types.py +331 -0
- atdata/atmosphere/client.py +533 -0
- atdata/atmosphere/lens.py +284 -0
- atdata/atmosphere/records.py +509 -0
- atdata/atmosphere/schema.py +239 -0
- atdata/atmosphere/store.py +208 -0
- atdata/cli/__init__.py +213 -0
- atdata/cli/diagnose.py +165 -0
- atdata/cli/local.py +280 -0
- atdata/dataset.py +510 -324
- atdata/lens.py +63 -112
- atdata/local.py +1707 -0
- atdata/promote.py +199 -0
- atdata-0.2.2b1.dist-info/METADATA +272 -0
- atdata-0.2.2b1.dist-info/RECORD +28 -0
- {atdata-0.1.3b4.dist-info → atdata-0.2.2b1.dist-info}/WHEEL +1 -1
- atdata-0.1.3b4.dist-info/METADATA +0 -172
- atdata-0.1.3b4.dist-info/RECORD +0 -9
- {atdata-0.1.3b4.dist-info → atdata-0.2.2b1.dist-info}/entry_points.txt +0 -0
- {atdata-0.1.3b4.dist-info → atdata-0.2.2b1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"""Lens transformation publishing for ATProto.
|
|
2
|
+
|
|
3
|
+
This module provides classes for publishing Lens transformation records to
|
|
4
|
+
ATProto. Lenses are published as ``ac.foundation.dataset.lens`` records.
|
|
5
|
+
|
|
6
|
+
Note:
|
|
7
|
+
For security reasons, lens code is stored as references to git repositories
|
|
8
|
+
rather than inline code. Users must manually install and import lens
|
|
9
|
+
implementations.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
from .client import AtmosphereClient
|
|
15
|
+
from ._types import (
|
|
16
|
+
AtUri,
|
|
17
|
+
LensRecord,
|
|
18
|
+
CodeReference,
|
|
19
|
+
LEXICON_NAMESPACE,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
# Import for type checking only
|
|
23
|
+
from typing import TYPE_CHECKING
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from ..lens import Lens
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class LensPublisher:
|
|
29
|
+
"""Publishes Lens transformation records to ATProto.
|
|
30
|
+
|
|
31
|
+
This class creates lens records that reference source and target schemas
|
|
32
|
+
and point to the transformation code in a git repository.
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
::
|
|
36
|
+
|
|
37
|
+
>>> @atdata.lens
|
|
38
|
+
... def my_lens(source: SourceType) -> TargetType:
|
|
39
|
+
... return TargetType(field=source.other_field)
|
|
40
|
+
>>>
|
|
41
|
+
>>> client = AtmosphereClient()
|
|
42
|
+
>>> client.login("handle", "password")
|
|
43
|
+
>>>
|
|
44
|
+
>>> publisher = LensPublisher(client)
|
|
45
|
+
>>> uri = publisher.publish(
|
|
46
|
+
... name="my_lens",
|
|
47
|
+
... source_schema_uri="at://did:plc:abc/ac.foundation.dataset.sampleSchema/source",
|
|
48
|
+
... target_schema_uri="at://did:plc:abc/ac.foundation.dataset.sampleSchema/target",
|
|
49
|
+
... code_repository="https://github.com/user/repo",
|
|
50
|
+
... code_commit="abc123def456",
|
|
51
|
+
... getter_path="mymodule.lenses:my_lens",
|
|
52
|
+
... putter_path="mymodule.lenses:my_lens_putter",
|
|
53
|
+
... )
|
|
54
|
+
|
|
55
|
+
Security Note:
|
|
56
|
+
Lens code is stored as references to git repositories rather than
|
|
57
|
+
inline code. This prevents arbitrary code execution from ATProto
|
|
58
|
+
records. Users must manually install and trust lens implementations.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(self, client: AtmosphereClient):
|
|
62
|
+
"""Initialize the lens publisher.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
client: Authenticated AtmosphereClient instance.
|
|
66
|
+
"""
|
|
67
|
+
self.client = client
|
|
68
|
+
|
|
69
|
+
def publish(
|
|
70
|
+
self,
|
|
71
|
+
*,
|
|
72
|
+
name: str,
|
|
73
|
+
source_schema_uri: str,
|
|
74
|
+
target_schema_uri: str,
|
|
75
|
+
description: Optional[str] = None,
|
|
76
|
+
code_repository: Optional[str] = None,
|
|
77
|
+
code_commit: Optional[str] = None,
|
|
78
|
+
getter_path: Optional[str] = None,
|
|
79
|
+
putter_path: Optional[str] = None,
|
|
80
|
+
rkey: Optional[str] = None,
|
|
81
|
+
) -> AtUri:
|
|
82
|
+
"""Publish a lens transformation record to ATProto.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
name: Human-readable lens name.
|
|
86
|
+
source_schema_uri: AT URI of the source schema.
|
|
87
|
+
target_schema_uri: AT URI of the target schema.
|
|
88
|
+
description: What this transformation does.
|
|
89
|
+
code_repository: Git repository URL containing the lens code.
|
|
90
|
+
code_commit: Git commit hash for reproducibility.
|
|
91
|
+
getter_path: Module path to the getter function
|
|
92
|
+
(e.g., 'mymodule.lenses:my_getter').
|
|
93
|
+
putter_path: Module path to the putter function
|
|
94
|
+
(e.g., 'mymodule.lenses:my_putter').
|
|
95
|
+
rkey: Optional explicit record key.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
The AT URI of the created lens record.
|
|
99
|
+
|
|
100
|
+
Raises:
|
|
101
|
+
ValueError: If code references are incomplete.
|
|
102
|
+
"""
|
|
103
|
+
# Build code references if provided
|
|
104
|
+
getter_code: Optional[CodeReference] = None
|
|
105
|
+
putter_code: Optional[CodeReference] = None
|
|
106
|
+
|
|
107
|
+
if code_repository and code_commit:
|
|
108
|
+
if getter_path:
|
|
109
|
+
getter_code = CodeReference(
|
|
110
|
+
repository=code_repository,
|
|
111
|
+
commit=code_commit,
|
|
112
|
+
path=getter_path,
|
|
113
|
+
)
|
|
114
|
+
if putter_path:
|
|
115
|
+
putter_code = CodeReference(
|
|
116
|
+
repository=code_repository,
|
|
117
|
+
commit=code_commit,
|
|
118
|
+
path=putter_path,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
lens_record = LensRecord(
|
|
122
|
+
name=name,
|
|
123
|
+
source_schema=source_schema_uri,
|
|
124
|
+
target_schema=target_schema_uri,
|
|
125
|
+
description=description,
|
|
126
|
+
getter_code=getter_code,
|
|
127
|
+
putter_code=putter_code,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
return self.client.create_record(
|
|
131
|
+
collection=f"{LEXICON_NAMESPACE}.lens",
|
|
132
|
+
record=lens_record.to_record(),
|
|
133
|
+
rkey=rkey,
|
|
134
|
+
validate=False,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
def publish_from_lens(
|
|
138
|
+
self,
|
|
139
|
+
lens_obj: "Lens",
|
|
140
|
+
*,
|
|
141
|
+
name: str,
|
|
142
|
+
source_schema_uri: str,
|
|
143
|
+
target_schema_uri: str,
|
|
144
|
+
code_repository: str,
|
|
145
|
+
code_commit: str,
|
|
146
|
+
description: Optional[str] = None,
|
|
147
|
+
rkey: Optional[str] = None,
|
|
148
|
+
) -> AtUri:
|
|
149
|
+
"""Publish a lens record from an existing Lens object.
|
|
150
|
+
|
|
151
|
+
This method extracts the getter and putter function names from
|
|
152
|
+
the Lens object and publishes a record referencing them.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
lens_obj: The Lens object to publish.
|
|
156
|
+
name: Human-readable lens name.
|
|
157
|
+
source_schema_uri: AT URI of the source schema.
|
|
158
|
+
target_schema_uri: AT URI of the target schema.
|
|
159
|
+
code_repository: Git repository URL.
|
|
160
|
+
code_commit: Git commit hash.
|
|
161
|
+
description: What this transformation does.
|
|
162
|
+
rkey: Optional explicit record key.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
The AT URI of the created lens record.
|
|
166
|
+
"""
|
|
167
|
+
# Extract function names from the lens
|
|
168
|
+
getter_name = lens_obj._getter.__name__
|
|
169
|
+
putter_name = lens_obj._putter.__name__
|
|
170
|
+
|
|
171
|
+
# Get module info if available
|
|
172
|
+
getter_module = getattr(lens_obj._getter, "__module__", "")
|
|
173
|
+
putter_module = getattr(lens_obj._putter, "__module__", "")
|
|
174
|
+
|
|
175
|
+
getter_path = f"{getter_module}:{getter_name}" if getter_module else getter_name
|
|
176
|
+
putter_path = f"{putter_module}:{putter_name}" if putter_module else putter_name
|
|
177
|
+
|
|
178
|
+
return self.publish(
|
|
179
|
+
name=name,
|
|
180
|
+
source_schema_uri=source_schema_uri,
|
|
181
|
+
target_schema_uri=target_schema_uri,
|
|
182
|
+
description=description,
|
|
183
|
+
code_repository=code_repository,
|
|
184
|
+
code_commit=code_commit,
|
|
185
|
+
getter_path=getter_path,
|
|
186
|
+
putter_path=putter_path,
|
|
187
|
+
rkey=rkey,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class LensLoader:
|
|
192
|
+
"""Loads lens records from ATProto.
|
|
193
|
+
|
|
194
|
+
This class fetches lens transformation records. Note that actually
|
|
195
|
+
using a lens requires installing the referenced code and importing
|
|
196
|
+
it manually.
|
|
197
|
+
|
|
198
|
+
Example:
|
|
199
|
+
::
|
|
200
|
+
|
|
201
|
+
>>> client = AtmosphereClient()
|
|
202
|
+
>>> loader = LensLoader(client)
|
|
203
|
+
>>>
|
|
204
|
+
>>> record = loader.get("at://did:plc:abc/ac.foundation.dataset.lens/xyz")
|
|
205
|
+
>>> print(record["name"])
|
|
206
|
+
>>> print(record["sourceSchema"])
|
|
207
|
+
>>> print(record.get("getterCode", {}).get("repository"))
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
def __init__(self, client: AtmosphereClient):
|
|
211
|
+
"""Initialize the lens loader.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
client: AtmosphereClient instance.
|
|
215
|
+
"""
|
|
216
|
+
self.client = client
|
|
217
|
+
|
|
218
|
+
def get(self, uri: str | AtUri) -> dict:
|
|
219
|
+
"""Fetch a lens record by AT URI.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
uri: The AT URI of the lens record.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
The lens record as a dictionary.
|
|
226
|
+
|
|
227
|
+
Raises:
|
|
228
|
+
ValueError: If the record is not a lens record.
|
|
229
|
+
"""
|
|
230
|
+
record = self.client.get_record(uri)
|
|
231
|
+
|
|
232
|
+
expected_type = f"{LEXICON_NAMESPACE}.lens"
|
|
233
|
+
if record.get("$type") != expected_type:
|
|
234
|
+
raise ValueError(
|
|
235
|
+
f"Record at {uri} is not a lens record. "
|
|
236
|
+
f"Expected $type='{expected_type}', got '{record.get('$type')}'"
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
return record
|
|
240
|
+
|
|
241
|
+
def list_all(
|
|
242
|
+
self,
|
|
243
|
+
repo: Optional[str] = None,
|
|
244
|
+
limit: int = 100,
|
|
245
|
+
) -> list[dict]:
|
|
246
|
+
"""List lens records from a repository.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
repo: The DID of the repository. Defaults to authenticated user.
|
|
250
|
+
limit: Maximum number of records to return.
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
List of lens records.
|
|
254
|
+
"""
|
|
255
|
+
return self.client.list_lenses(repo=repo, limit=limit)
|
|
256
|
+
|
|
257
|
+
def find_by_schemas(
|
|
258
|
+
self,
|
|
259
|
+
source_schema_uri: str,
|
|
260
|
+
target_schema_uri: Optional[str] = None,
|
|
261
|
+
repo: Optional[str] = None,
|
|
262
|
+
) -> list[dict]:
|
|
263
|
+
"""Find lenses that transform between specific schemas.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
source_schema_uri: AT URI of the source schema.
|
|
267
|
+
target_schema_uri: Optional AT URI of the target schema.
|
|
268
|
+
If not provided, returns all lenses from the source.
|
|
269
|
+
repo: The DID of the repository to search.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
List of matching lens records.
|
|
273
|
+
"""
|
|
274
|
+
all_lenses = self.list_all(repo=repo, limit=1000)
|
|
275
|
+
|
|
276
|
+
matches = []
|
|
277
|
+
for lens_record in all_lenses:
|
|
278
|
+
if lens_record.get("sourceSchema") == source_schema_uri:
|
|
279
|
+
if target_schema_uri is None:
|
|
280
|
+
matches.append(lens_record)
|
|
281
|
+
elif lens_record.get("targetSchema") == target_schema_uri:
|
|
282
|
+
matches.append(lens_record)
|
|
283
|
+
|
|
284
|
+
return matches
|