matelab-python-sdk 0.2.0a8__py3-none-any.whl → 0.2.0a9__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.
- matelab/_generated/models.py +1 -1
- matelab/domains/records.py +49 -22
- {matelab_python_sdk-0.2.0a8.dist-info → matelab_python_sdk-0.2.0a9.dist-info}/METADATA +1 -1
- {matelab_python_sdk-0.2.0a8.dist-info → matelab_python_sdk-0.2.0a9.dist-info}/RECORD +7 -7
- {matelab_python_sdk-0.2.0a8.dist-info → matelab_python_sdk-0.2.0a9.dist-info}/WHEEL +0 -0
- {matelab_python_sdk-0.2.0a8.dist-info → matelab_python_sdk-0.2.0a9.dist-info}/licenses/LICENSE +0 -0
- {matelab_python_sdk-0.2.0a8.dist-info → matelab_python_sdk-0.2.0a9.dist-info}/licenses/NOTICE +0 -0
matelab/_generated/models.py
CHANGED
matelab/domains/records.py
CHANGED
|
@@ -16,6 +16,7 @@ from matelab._hashes import LOWERCASE_SHA256_PATTERN
|
|
|
16
16
|
from matelab._transport import Encoding, Operation, SessionTransport, StreamOperation
|
|
17
17
|
from matelab._wire_validation import WireValue, encode_json_field, validate_multipart_request, validate_request
|
|
18
18
|
from matelab.domains._attachment_content import prepare_attachment_content
|
|
19
|
+
from matelab.domains.cloud_drive import CloudFile
|
|
19
20
|
from matelab.domains.notebooks import NotebookPermissions, NotebookRef, validate_notebook_scope
|
|
20
21
|
from matelab.domains.uploads import UploadBindingRef
|
|
21
22
|
from matelab.domains.users import UserRef
|
|
@@ -432,6 +433,7 @@ RecordImportValue: TypeAlias = (
|
|
|
432
433
|
| bool
|
|
433
434
|
| None
|
|
434
435
|
| StagedAttachment
|
|
436
|
+
| CloudFile
|
|
435
437
|
| Mapping[str, "RecordImportValue"]
|
|
436
438
|
| list["RecordImportValue"]
|
|
437
439
|
| tuple["RecordImportValue", ...]
|
|
@@ -488,11 +490,11 @@ class RecordValueAddition:
|
|
|
488
490
|
|
|
489
491
|
@dataclass(frozen=True, slots=True)
|
|
490
492
|
class RecordFormAttachmentFieldAddition:
|
|
491
|
-
"""Add one
|
|
493
|
+
"""Add one file field from a staged upload or an existing cloud file."""
|
|
492
494
|
|
|
493
495
|
module: str
|
|
494
496
|
name: str
|
|
495
|
-
attachment: StagedAttachment
|
|
497
|
+
attachment: StagedAttachment | CloudFile
|
|
496
498
|
|
|
497
499
|
|
|
498
500
|
@dataclass(frozen=True, slots=True)
|
|
@@ -929,8 +931,8 @@ def _current_attachment(source: RecordLocator, current: Record, requested: Recor
|
|
|
929
931
|
return attachment
|
|
930
932
|
|
|
931
933
|
|
|
932
|
-
def _iter_import_attachments(value: RecordImportValue) -> Iterator[StagedAttachment]:
|
|
933
|
-
if isinstance(value, StagedAttachment):
|
|
934
|
+
def _iter_import_attachments(value: RecordImportValue) -> Iterator[StagedAttachment | CloudFile]:
|
|
935
|
+
if isinstance(value, StagedAttachment | CloudFile):
|
|
934
936
|
yield value
|
|
935
937
|
elif isinstance(value, Mapping):
|
|
936
938
|
for item in value.values():
|
|
@@ -940,13 +942,13 @@ def _iter_import_attachments(value: RecordImportValue) -> Iterator[StagedAttachm
|
|
|
940
942
|
yield from _iter_import_attachments(item)
|
|
941
943
|
|
|
942
944
|
|
|
943
|
-
def _iter_dataset_attachments(items: Sequence[RecordImportItem]) -> Iterator[StagedAttachment]:
|
|
945
|
+
def _iter_dataset_attachments(items: Sequence[RecordImportItem]) -> Iterator[StagedAttachment | CloudFile]:
|
|
944
946
|
for item in items:
|
|
945
947
|
for value in item.data.values():
|
|
946
948
|
yield from _iter_import_attachments(value)
|
|
947
949
|
|
|
948
950
|
|
|
949
|
-
def _iter_update_attachments(changes: tuple[RecordChange, ...]) -> Iterator[StagedAttachment]:
|
|
951
|
+
def _iter_update_attachments(changes: tuple[RecordChange, ...]) -> Iterator[StagedAttachment | CloudFile]:
|
|
950
952
|
for change in changes:
|
|
951
953
|
if isinstance(
|
|
952
954
|
change,
|
|
@@ -965,6 +967,7 @@ def _iter_update_attachments(changes: tuple[RecordChange, ...]) -> Iterator[Stag
|
|
|
965
967
|
def _validate_staged_name_update_shape(changes: tuple[RecordChange, ...]) -> None:
|
|
966
968
|
has_finalizer = any(
|
|
967
969
|
isinstance(change, RecordFormAttachmentFieldAddition | RecordTableFileCellSet | RecordTableRowAttachmentAppend)
|
|
970
|
+
and isinstance(change.attachment, StagedAttachment)
|
|
968
971
|
or isinstance(change, RecordFilesAttachmentAppend)
|
|
969
972
|
and bool(change.folder_path)
|
|
970
973
|
for change in changes
|
|
@@ -994,7 +997,7 @@ def _record_update_requires_current_state(changes: tuple[RecordChange, ...]) ->
|
|
|
994
997
|
)
|
|
995
998
|
|
|
996
999
|
|
|
997
|
-
def _encode_record_attachment_reference(attachment: StagedAttachment) -> str:
|
|
1000
|
+
def _encode_record_attachment_reference(attachment: StagedAttachment | CloudFile) -> str:
|
|
998
1001
|
return f"#hash{{{attachment.filename}, {attachment.sha256}}}"
|
|
999
1002
|
|
|
1000
1003
|
|
|
@@ -1925,7 +1928,7 @@ class Records:
|
|
|
1925
1928
|
| RecordTableRowAttachmentAppend
|
|
1926
1929
|
| RecordFilesAttachmentAppend,
|
|
1927
1930
|
):
|
|
1928
|
-
self.
|
|
1931
|
+
self._compile_record_attachment_addition(change, patch)
|
|
1929
1932
|
else:
|
|
1930
1933
|
self._compile_observed_record_attachment_change(
|
|
1931
1934
|
source, current, change, removals, value_deletions, patch
|
|
@@ -1939,7 +1942,7 @@ class Records:
|
|
|
1939
1942
|
patch.claim(path)
|
|
1940
1943
|
patch.modify.append({"path": list(path), "data": _encode_record_rich_text(change)})
|
|
1941
1944
|
|
|
1942
|
-
def
|
|
1945
|
+
def _compile_record_attachment_addition(
|
|
1943
1946
|
self,
|
|
1944
1947
|
change: (
|
|
1945
1948
|
RecordFormAttachmentFieldAddition
|
|
@@ -1953,19 +1956,31 @@ class Records:
|
|
|
1953
1956
|
patch.reject_deleted_module(change.module)
|
|
1954
1957
|
module = patch.modules.get(change.module)
|
|
1955
1958
|
if module is None or module.get("type") != "form":
|
|
1956
|
-
raise MatelabUsageError("A record attachment
|
|
1957
|
-
if
|
|
1958
|
-
|
|
1959
|
+
raise MatelabUsageError("A record attachment requires an existing form module.")
|
|
1960
|
+
if (
|
|
1961
|
+
not change.name
|
|
1962
|
+
or _module_field(module, change.name) is not None
|
|
1963
|
+
or any(
|
|
1964
|
+
isinstance(addition, dict)
|
|
1965
|
+
and addition.get("module") == change.module
|
|
1966
|
+
and addition.get("name") == change.name
|
|
1967
|
+
for addition in patch.add_values
|
|
1968
|
+
)
|
|
1969
|
+
):
|
|
1970
|
+
raise MatelabUsageError("A record attachment requires a new, unique non-empty form field name.")
|
|
1971
|
+
patch.claim((change.module, change.name))
|
|
1972
|
+
attachment = change.attachment
|
|
1973
|
+
if isinstance(attachment, CloudFile):
|
|
1974
|
+
model = wire_models.AddValueOperation
|
|
1975
|
+
reference = _encode_record_attachment_reference(attachment)
|
|
1976
|
+
else:
|
|
1977
|
+
model = wire_models.AddNewFormFileFieldWithStagedAttachmentOperation
|
|
1978
|
+
reference = f"#file{{{attachment.binding.uid}}}"
|
|
1959
1979
|
patch.add_values.append(
|
|
1960
1980
|
_operation_payload(
|
|
1961
|
-
|
|
1962
|
-
{
|
|
1963
|
-
|
|
1964
|
-
"name": change.name,
|
|
1965
|
-
"type": "file",
|
|
1966
|
-
"data": [f"#file{{{change.attachment.binding.uid}}}"],
|
|
1967
|
-
},
|
|
1968
|
-
"New form-file attachment finalizer does not satisfy the contract.",
|
|
1981
|
+
model,
|
|
1982
|
+
{"module": change.module, "name": change.name, "type": "file", "data": [reference]},
|
|
1983
|
+
"New form-file attachment does not satisfy the contract.",
|
|
1969
1984
|
)
|
|
1970
1985
|
)
|
|
1971
1986
|
return
|
|
@@ -2235,11 +2250,21 @@ class Records:
|
|
|
2235
2250
|
)
|
|
2236
2251
|
|
|
2237
2252
|
def _staged_attachment_keys(
|
|
2238
|
-
self, notebook: NotebookRef, attachments: Iterable[StagedAttachment]
|
|
2253
|
+
self, notebook: NotebookRef, attachments: Iterable[StagedAttachment | CloudFile]
|
|
2239
2254
|
) -> set[_StagedAttachmentKey]:
|
|
2240
2255
|
uploader = UserRef(userid=self._transport.require_session().identity.userid)
|
|
2241
2256
|
keys: set[_StagedAttachmentKey] = set()
|
|
2242
2257
|
for attachment in attachments:
|
|
2258
|
+
if isinstance(attachment, CloudFile):
|
|
2259
|
+
if (
|
|
2260
|
+
not attachment.filename
|
|
2261
|
+
or len(attachment.filename) > 255
|
|
2262
|
+
or "\r" in attachment.filename
|
|
2263
|
+
or "\n" in attachment.filename
|
|
2264
|
+
or LOWERCASE_SHA256_PATTERN.fullmatch(attachment.sha256) is None
|
|
2265
|
+
):
|
|
2266
|
+
raise MatelabUsageError("Cloud-file metadata does not satisfy the Integration Contract.")
|
|
2267
|
+
continue
|
|
2243
2268
|
if attachment.notebook != notebook:
|
|
2244
2269
|
raise MatelabUsageError("Staged attachment belongs to a different notebook.")
|
|
2245
2270
|
if attachment.uploader != uploader:
|
|
@@ -2262,9 +2287,11 @@ class Records:
|
|
|
2262
2287
|
"""Encode one domain import value."""
|
|
2263
2288
|
if isinstance(value, StagedAttachment):
|
|
2264
2289
|
return f"#file{{{value.binding.uid}}}"
|
|
2290
|
+
if isinstance(value, CloudFile):
|
|
2291
|
+
return _encode_record_attachment_reference(value)
|
|
2265
2292
|
if isinstance(value, str):
|
|
2266
2293
|
if _contains_native_attachment_reference(value):
|
|
2267
|
-
raise MatelabUsageError("Provider-native attachment references require a
|
|
2294
|
+
raise MatelabUsageError("Provider-native attachment references require a typed attachment object.")
|
|
2268
2295
|
return value
|
|
2269
2296
|
if isinstance(value, Mapping):
|
|
2270
2297
|
return {name: self._encode_import_value(item) for name, item in value.items()}
|
|
@@ -8,19 +8,19 @@ matelab/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
|
8
8
|
matelab/session.py,sha256=D8i9uyAt32-bADotS-Lrb7RpmWRNYNIkIpZUijTYvy4,899
|
|
9
9
|
matelab/streaming.py,sha256=36epH2josWk87Ors_JY0YsjNmtiq3NZa2iB9djLmlnY,3792
|
|
10
10
|
matelab/_generated/__init__.py,sha256=jmGbXLLe3JorsGU7OUdF8iKGewFMRqkCzgyFAw7C15w,79
|
|
11
|
-
matelab/_generated/models.py,sha256=
|
|
11
|
+
matelab/_generated/models.py,sha256=VhE1ixBb-qflSTEO8VEbixnB6jtHshXqCd9gAgJBGQ8,179704
|
|
12
12
|
matelab/domains/__init__.py,sha256=C34IWsGoCAwP7r6uYKJ79DJkToZyrZ4LWA87ufc1i0Q,65
|
|
13
13
|
matelab/domains/_attachment_content.py,sha256=z26hwFpdgAk2x1w8EGe00XBLtJ3QFDX8BPS35Zc5SoE,1461
|
|
14
14
|
matelab/domains/cloud_drive.py,sha256=EoqAYWvLVUfVOsic3g-TxsQEtAI3wTp5h0_vB79KpwY,10406
|
|
15
15
|
matelab/domains/groups.py,sha256=H8-Da2WTm2Ui4m3Nr1bhMNMn7stbzbRa3EmpkTRmkBU,1300
|
|
16
16
|
matelab/domains/literature.py,sha256=xpdHuFCR1RpEPB_Lk8VOaddlIpGQ2rnp4kxl5a8Bk4M,27473
|
|
17
17
|
matelab/domains/notebooks.py,sha256=_SvffaViECSEOkN5iwXtSv6El3fK2OYaR4RJvUCXfGk,13477
|
|
18
|
-
matelab/domains/records.py,sha256=
|
|
18
|
+
matelab/domains/records.py,sha256=U3zOLwTjJ1YbnuayGXKroEKhUZLhsmjRCYtnFaDyJzQ,95614
|
|
19
19
|
matelab/domains/templates.py,sha256=-x7RqRLhrRnAK3HGK3c4I3C-SsAk7zpicIcvzoVd414,17698
|
|
20
20
|
matelab/domains/uploads.py,sha256=6y4i85UWGxumasWw9HAubcgtK_fqgZLnBUbX0oghxQU,7699
|
|
21
21
|
matelab/domains/users.py,sha256=ZhlLzC_AadwYqNqxQrqkUBgIUqL7MJ6FgMyxwq-az8s,1445
|
|
22
|
-
matelab_python_sdk-0.2.
|
|
23
|
-
matelab_python_sdk-0.2.
|
|
24
|
-
matelab_python_sdk-0.2.
|
|
25
|
-
matelab_python_sdk-0.2.
|
|
26
|
-
matelab_python_sdk-0.2.
|
|
22
|
+
matelab_python_sdk-0.2.0a9.dist-info/METADATA,sha256=l5luHe5D2BRV0uAUoy1EusPL6A_bpCGQSlqHEp08_Ro,2436
|
|
23
|
+
matelab_python_sdk-0.2.0a9.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
24
|
+
matelab_python_sdk-0.2.0a9.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
25
|
+
matelab_python_sdk-0.2.0a9.dist-info/licenses/NOTICE,sha256=Hc4scCJTof5VL3JCPI2aaewnnzCejIHUGuBVYBf-klc,98
|
|
26
|
+
matelab_python_sdk-0.2.0a9.dist-info/RECORD,,
|
|
File without changes
|
{matelab_python_sdk-0.2.0a8.dist-info → matelab_python_sdk-0.2.0a9.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{matelab_python_sdk-0.2.0a8.dist-info → matelab_python_sdk-0.2.0a9.dist-info}/licenses/NOTICE
RENAMED
|
File without changes
|