matelab-python-sdk 0.1.0a4__tar.gz → 0.1.0a5__tar.gz
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_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/CHANGELOG.md +16 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/PKG-INFO +154 -18
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/README.md +153 -17
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/contracts/matelab-integration-v1.lock.json +3 -3
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/contracts/matelab-integration-v1.openapi.yaml +299 -16
- matelab_python_sdk-0.1.0a5/docs/attachment-write-contract-gaps.md +103 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/docs/operation-coverage.yaml +1 -1
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/docs/roadmap.md +23 -7
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/pyproject.toml +1 -1
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/__init__.py +20 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/_generated/models.py +104 -1
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/domains/records.py +764 -44
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_generation.py +55 -1
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_record_mutations.py +832 -80
- matelab_python_sdk-0.1.0a5/tests/typing/consumer.py +83 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/uv.lock +1 -1
- matelab_python_sdk-0.1.0a4/tests/typing/consumer.py +0 -44
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/.env.example +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/.github/workflows/release.yml +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/.gitignore +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/AGENTS.md +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/LICENSE +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/NOTICE +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/scripts/check_installed_package.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/scripts/check_release.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/scripts/generate_models.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/_generated/__init__.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/_transport.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/_wire_validation.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/client.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/domains/__init__.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/domains/cloud_drive.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/domains/groups.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/domains/literature.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/domains/notebooks.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/domains/templates.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/domains/uploads.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/domains/users.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/errors.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/py.typed +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/session.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/src/matelab/streaming.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/conftest.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/provider/test_provider_smoke.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_client.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_cloud_drive.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_comments_and_downloads.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_consumer_smoke.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_extended_records.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_groups.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_notebook_lifecycle.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_notebooks.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_operation_coverage.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_record_lifecycle.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_records.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_release.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_streaming.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_template_lifecycle.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_templates.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_transport.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_uploads_and_literature.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/test_users.py +0 -0
- {matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/tests/typing/pyproject.toml +0 -0
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.0a5 - 2026-07-29
|
|
4
|
+
|
|
5
|
+
- Adopts immutable `matelab-spec v0.1.2` at commit `6dd674ac001ae61197495ace5eba489d585cb825` and OpenAPI
|
|
6
|
+
SHA-256 `7ccaf3116c8dcec7a6e49ff51fb1a5ecc83a140e013ec600516f5c2ffb3b5378`.
|
|
7
|
+
- Adds notebook-scoped staged finalizers for a null table file cell, an attachment-bearing appended table row,
|
|
8
|
+
files-module root append, and table/files occurrence replacement, with exact provenance, one-shot transport, and
|
|
9
|
+
canonical readback checks.
|
|
10
|
+
- Adds typed intents for an empty file table column and complete form file-field/table file-column deletion.
|
|
11
|
+
- Allows `records.update(..., module_deletions=...)` to delete a canonical module even when the current record read
|
|
12
|
+
observes attachments in it, verifies absence on persisted readback, and does not claim attachment-quote or byte
|
|
13
|
+
cleanup beyond the pinned Contract.
|
|
14
|
+
- Rejects cross-scope staged attachment handles before the record read, preserving the public static types while
|
|
15
|
+
avoiding the Provider's read-audit side effect for invalid runtime input.
|
|
16
|
+
- Keeps notebook-staged rich-text binding and attachment-bearing multi-record import explicitly unsupported because
|
|
17
|
+
v0.1.2 Provider Verification does not provide safe one-shot semantics for either.
|
|
18
|
+
|
|
3
19
|
## 0.1.0a4 - 2026-07-28
|
|
4
20
|
|
|
5
21
|
- Further reduces the alpha public surface from 150 to 126 symbols by accepting direct keyword arguments and existing
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: matelab-python-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.0a5
|
|
4
4
|
Summary: Reusable async Python client for the Matelab Integration Contract
|
|
5
5
|
Author-email: 朱天念 <zhutiannian@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -28,10 +28,10 @@ Description-Content-Type: text/markdown
|
|
|
28
28
|
|
|
29
29
|
Reusable async Python client for the Matelab Integration Contract.
|
|
30
30
|
|
|
31
|
-
The current alpha is `0.1.
|
|
31
|
+
The current alpha is `0.1.0a5`. `[project].version` in `pyproject.toml` is the sole SDK version source;
|
|
32
32
|
`uv.lock` only mirrors that source.
|
|
33
33
|
|
|
34
|
-
The SDK is pinned to the immutable `matelab-spec v0.1.
|
|
34
|
+
The SDK is pinned to the immutable `matelab-spec v0.1.2` Contract Release. The sole release pin is
|
|
35
35
|
`contracts/matelab-integration-v1.lock.json`, which records
|
|
36
36
|
the source tag, commit, OpenAPI path, local snapshot path, and SHA-256.
|
|
37
37
|
|
|
@@ -57,7 +57,7 @@ To test the same artifact a downstream Consumer will install, build and install
|
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
59
|
uv build --no-build-isolation --out-dir dist/release
|
|
60
|
-
python -m pip install dist/release/matelab_python_sdk-0.1.
|
|
60
|
+
python -m pip install dist/release/matelab_python_sdk-0.1.0a5-py3-none-any.whl
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
Do not infer Provider compatibility from the SDK version alone. A release is also bound to the Contract
|
|
@@ -218,8 +218,8 @@ result = await client.records.import_dataset(
|
|
|
218
218
|
)
|
|
219
219
|
```
|
|
220
220
|
|
|
221
|
-
The same staged handle may instead be consumed by one safe update
|
|
222
|
-
module:
|
|
221
|
+
The same staged handle may instead be consumed by one safe update finalizer. For example, add a new file field to an
|
|
222
|
+
existing form module:
|
|
223
223
|
|
|
224
224
|
```python
|
|
225
225
|
from matelab import RecordFormAttachmentFieldAddition
|
|
@@ -237,17 +237,144 @@ result = await client.records.update(
|
|
|
237
237
|
)
|
|
238
238
|
```
|
|
239
239
|
|
|
240
|
-
Choose exactly one finalizer. A staged name may occur once in either a single-record import or one update
|
|
241
|
-
|
|
242
|
-
attachment references, cross-user or cross-notebook
|
|
243
|
-
and a second finalization attempt through the same
|
|
244
|
-
|
|
245
|
-
|
|
240
|
+
Choose exactly one finalizer. A staged name may occur once in either a single-record import or one update operation;
|
|
241
|
+
do not reuse it after transport starts, even when the Provider outcome is unknown. A locally rejected target or
|
|
242
|
+
argument does not spend the handle. The SDK rejects raw Provider attachment references, cross-user or cross-notebook
|
|
243
|
+
handles, unsafe combinations, duplicate use in one request, and a second finalization attempt through the same
|
|
244
|
+
client. A Session imported without identity must first call `await client.resolve_identity()`.
|
|
245
|
+
|
|
246
|
+
The Provider supplies no staging status, abort, TTL, atomicity, idempotency, or retry guarantee. Integrations that
|
|
247
|
+
persist handles must durably claim their own `uploaded -> finalizing/indeterminate` transition before calling a
|
|
248
|
+
finalizer.
|
|
249
|
+
|
|
250
|
+
### Attachment-bearing record update boundary
|
|
251
|
+
|
|
252
|
+
The v0.1.2 Contract adds dedicated, verified notebook-staged update shapes. They remain public intent objects; callers
|
|
253
|
+
never construct Provider paths or attachment strings:
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
from matelab import (
|
|
257
|
+
RecordFilesAttachmentRootAppend,
|
|
258
|
+
RecordTableFileCellSet,
|
|
259
|
+
RecordTableRowAttachmentAppend,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
# Each example is a separate finalizer; never run several with the same handle.
|
|
263
|
+
await client.records.update(
|
|
264
|
+
source,
|
|
265
|
+
attachment_changes=(
|
|
266
|
+
RecordTableFileCellSet(
|
|
267
|
+
table="Measurements",
|
|
268
|
+
column="Evidence",
|
|
269
|
+
row=0,
|
|
270
|
+
attachment=staged,
|
|
271
|
+
),
|
|
272
|
+
),
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
await client.records.update(
|
|
276
|
+
source,
|
|
277
|
+
attachment_changes=(
|
|
278
|
+
RecordTableRowAttachmentAppend(
|
|
279
|
+
table="Measurements",
|
|
280
|
+
file_column="Evidence",
|
|
281
|
+
values={"Label": "Sample C"},
|
|
282
|
+
attachment=another_staged,
|
|
283
|
+
),
|
|
284
|
+
),
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
await client.records.update(
|
|
288
|
+
source,
|
|
289
|
+
attachment_changes=(
|
|
290
|
+
RecordFilesAttachmentRootAppend(
|
|
291
|
+
module="Files",
|
|
292
|
+
caption="Evidence",
|
|
293
|
+
attachment=third_staged,
|
|
294
|
+
),
|
|
295
|
+
),
|
|
296
|
+
)
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
`RecordTableFileCellSet` requires an existing file column and a cell whose immediate canonical value is exactly
|
|
300
|
+
`null`. The row-append intent addresses the new row by the row count from the SDK's immediate read and supports one
|
|
301
|
+
staged file column. Files append is root-only and requires a string caption.
|
|
302
|
+
|
|
303
|
+
Replacement starts with an occurrence returned by `records.read()`; applications must not fabricate a
|
|
304
|
+
`RecordAttachmentRef`:
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
from matelab import RecordFilesAttachmentReplacement
|
|
308
|
+
|
|
309
|
+
record = await client.records.read(notebook=source.notebook, record=source.record)
|
|
310
|
+
existing = next(
|
|
311
|
+
attachment
|
|
312
|
+
for attachment in record.attachments
|
|
313
|
+
if attachment.location is not None and attachment.location.kind == "files_module"
|
|
314
|
+
)
|
|
315
|
+
await client.records.update(
|
|
316
|
+
source,
|
|
317
|
+
attachment_changes=(RecordFilesAttachmentReplacement(existing=existing, replacement=staged, caption=None),),
|
|
318
|
+
)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
For a files occurrence, `caption=None` preserves the observed string caption (an observed null caption normalizes to
|
|
322
|
+
the required empty string). Canonical readback must preserve the Provider `uid` and folder. Table replacement requires
|
|
323
|
+
exactly one current attachment in the selected cell and uses `RecordTableFileAttachmentReplacement`. Both replacement
|
|
324
|
+
forms require the new hash to differ.
|
|
325
|
+
|
|
326
|
+
Row/index-based finalizers use an immediate SDK read and must not be called while a concurrent editor is known to be
|
|
327
|
+
active. The Provider offers no expected hash or revision. `expected_content_sha256` is only a client-side prewrite
|
|
328
|
+
check, not Provider compare-and-swap.
|
|
329
|
+
|
|
330
|
+
File-bearing structure changes do not consume staging:
|
|
331
|
+
|
|
332
|
+
```python
|
|
333
|
+
from matelab import (
|
|
334
|
+
RecordFormFileFieldDeletion,
|
|
335
|
+
RecordTableFileColumnAddition,
|
|
336
|
+
RecordTableFileColumnDeletion,
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
await client.records.update(
|
|
340
|
+
source,
|
|
341
|
+
file_structure_changes=(
|
|
342
|
+
RecordTableFileColumnAddition(table="Measurements", name="Additional evidence"),
|
|
343
|
+
RecordFormFileFieldDeletion(form="Attachments", name="Obsolete evidence"),
|
|
344
|
+
RecordTableFileColumnDeletion(table="Measurements", name="Old evidence"),
|
|
345
|
+
),
|
|
346
|
+
)
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
The SDK emits the v0.1.2 dedicated operations: a new file column omits wire `data` and must read back as `null` for
|
|
350
|
+
every existing row; whole form-field/table-column deletion uses a strict two-segment target. It does not expose a
|
|
351
|
+
three-segment delete as table-cell clearing.
|
|
352
|
+
|
|
353
|
+
Deleting a complete canonical module is also supported even when the current read observes attachments in it:
|
|
354
|
+
|
|
355
|
+
```python
|
|
356
|
+
result = await client.records.update(
|
|
357
|
+
source,
|
|
358
|
+
module_deletions=("Raw files",),
|
|
359
|
+
expected_content_sha256=record.content_sha256,
|
|
360
|
+
)
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
This requests only a canonical record-content mutation. Readback proves the module is absent; it does not prove
|
|
364
|
+
attachment-quote cleanup or byte deletion. Collaboration-pending has no readback and is not reported as persisted.
|
|
365
|
+
|
|
366
|
+
Notebook-staged rich-text binding remains unsupported. Provider Verification shows that `#file{name}` is stored as
|
|
367
|
+
plain text and that a hash-based URI can fall back to an existing quote, so neither is a one-shot staged finalizer.
|
|
368
|
+
Existing `RecordRichTextUpdate` remains legal only with record-scoped `StagedRecordAttachment`. Attachment-bearing
|
|
369
|
+
multi-record import is also forbidden; callers must split it into single-record finalizers.
|
|
370
|
+
|
|
371
|
+
See `docs/attachment-write-contract-gaps.md` in the source repository for the exact support matrix, integration
|
|
372
|
+
lifecycle requirements, and minimum Contract changes needed to expose the remaining operations.
|
|
246
373
|
|
|
247
374
|
## Implementation roadmap
|
|
248
375
|
|
|
249
376
|
`docs/roadmap.md` is the complete SDK-only execution plan. It assigns all 71
|
|
250
|
-
`matelab-spec v0.1.
|
|
377
|
+
`matelab-spec v0.1.2` operations to ordered work packages, defines the machine-readable coverage that
|
|
251
378
|
must be added, records Provider-risk gates, and specifies the final completion checks.
|
|
252
379
|
|
|
253
380
|
The SDK exposes all 71 Contract operations through public domain interfaces. It deliberately excludes MCP
|
|
@@ -580,8 +707,17 @@ agree. With the environment prepared:
|
|
|
580
707
|
uv run --env-file .env.test pytest -m provider tests/provider/test_provider_smoke.py
|
|
581
708
|
```
|
|
582
709
|
|
|
583
|
-
|
|
584
|
-
|
|
710
|
+
This command runs only the SDK public-interface smoke; it is not the 71-operation Provider Verification. To reuse the
|
|
711
|
+
same `.env.test` for the complete Contract suite, also populate the optional share user, secondary account, record
|
|
712
|
+
staging opt-in, and Chat SSO settings documented in `.env.example`, then run from sibling checkouts:
|
|
713
|
+
|
|
714
|
+
```bash
|
|
715
|
+
cd ../matelab-spec
|
|
716
|
+
uv run --env-file ../matelab-python-sdk/.env.test pytest
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
The files are never loaded implicitly, so normal test runs remain safely skipped. Do not use either flow against
|
|
720
|
+
production, and never commit Provider credentials.
|
|
585
721
|
|
|
586
722
|
## Reproducible release build
|
|
587
723
|
|
|
@@ -599,6 +735,6 @@ uv run python scripts/check_release.py dist/release/*.whl dist/release/*.tar.gz
|
|
|
599
735
|
```
|
|
600
736
|
|
|
601
737
|
Rebuilding the same commit with the same locked environment and `SOURCE_DATE_EPOCH` must produce
|
|
602
|
-
byte-identical wheel and source distribution hashes. The release is bound to `matelab-spec v0.1.
|
|
603
|
-
commit `
|
|
604
|
-
`
|
|
738
|
+
byte-identical wheel and source distribution hashes. The release is bound to `matelab-spec v0.1.2`,
|
|
739
|
+
commit `6dd674ac001ae61197495ace5eba489d585cb825`, and OpenAPI SHA-256
|
|
740
|
+
`7ccaf3116c8dcec7a6e49ff51fb1a5ecc83a140e013ec600516f5c2ffb3b5378`.
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Reusable async Python client for the Matelab Integration Contract.
|
|
4
4
|
|
|
5
|
-
The current alpha is `0.1.
|
|
5
|
+
The current alpha is `0.1.0a5`. `[project].version` in `pyproject.toml` is the sole SDK version source;
|
|
6
6
|
`uv.lock` only mirrors that source.
|
|
7
7
|
|
|
8
|
-
The SDK is pinned to the immutable `matelab-spec v0.1.
|
|
8
|
+
The SDK is pinned to the immutable `matelab-spec v0.1.2` Contract Release. The sole release pin is
|
|
9
9
|
`contracts/matelab-integration-v1.lock.json`, which records
|
|
10
10
|
the source tag, commit, OpenAPI path, local snapshot path, and SHA-256.
|
|
11
11
|
|
|
@@ -31,7 +31,7 @@ To test the same artifact a downstream Consumer will install, build and install
|
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
33
|
uv build --no-build-isolation --out-dir dist/release
|
|
34
|
-
python -m pip install dist/release/matelab_python_sdk-0.1.
|
|
34
|
+
python -m pip install dist/release/matelab_python_sdk-0.1.0a5-py3-none-any.whl
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
Do not infer Provider compatibility from the SDK version alone. A release is also bound to the Contract
|
|
@@ -192,8 +192,8 @@ result = await client.records.import_dataset(
|
|
|
192
192
|
)
|
|
193
193
|
```
|
|
194
194
|
|
|
195
|
-
The same staged handle may instead be consumed by one safe update
|
|
196
|
-
module:
|
|
195
|
+
The same staged handle may instead be consumed by one safe update finalizer. For example, add a new file field to an
|
|
196
|
+
existing form module:
|
|
197
197
|
|
|
198
198
|
```python
|
|
199
199
|
from matelab import RecordFormAttachmentFieldAddition
|
|
@@ -211,17 +211,144 @@ result = await client.records.update(
|
|
|
211
211
|
)
|
|
212
212
|
```
|
|
213
213
|
|
|
214
|
-
Choose exactly one finalizer. A staged name may occur once in either a single-record import or one update
|
|
215
|
-
|
|
216
|
-
attachment references, cross-user or cross-notebook
|
|
217
|
-
and a second finalization attempt through the same
|
|
218
|
-
|
|
219
|
-
|
|
214
|
+
Choose exactly one finalizer. A staged name may occur once in either a single-record import or one update operation;
|
|
215
|
+
do not reuse it after transport starts, even when the Provider outcome is unknown. A locally rejected target or
|
|
216
|
+
argument does not spend the handle. The SDK rejects raw Provider attachment references, cross-user or cross-notebook
|
|
217
|
+
handles, unsafe combinations, duplicate use in one request, and a second finalization attempt through the same
|
|
218
|
+
client. A Session imported without identity must first call `await client.resolve_identity()`.
|
|
219
|
+
|
|
220
|
+
The Provider supplies no staging status, abort, TTL, atomicity, idempotency, or retry guarantee. Integrations that
|
|
221
|
+
persist handles must durably claim their own `uploaded -> finalizing/indeterminate` transition before calling a
|
|
222
|
+
finalizer.
|
|
223
|
+
|
|
224
|
+
### Attachment-bearing record update boundary
|
|
225
|
+
|
|
226
|
+
The v0.1.2 Contract adds dedicated, verified notebook-staged update shapes. They remain public intent objects; callers
|
|
227
|
+
never construct Provider paths or attachment strings:
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
from matelab import (
|
|
231
|
+
RecordFilesAttachmentRootAppend,
|
|
232
|
+
RecordTableFileCellSet,
|
|
233
|
+
RecordTableRowAttachmentAppend,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
# Each example is a separate finalizer; never run several with the same handle.
|
|
237
|
+
await client.records.update(
|
|
238
|
+
source,
|
|
239
|
+
attachment_changes=(
|
|
240
|
+
RecordTableFileCellSet(
|
|
241
|
+
table="Measurements",
|
|
242
|
+
column="Evidence",
|
|
243
|
+
row=0,
|
|
244
|
+
attachment=staged,
|
|
245
|
+
),
|
|
246
|
+
),
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
await client.records.update(
|
|
250
|
+
source,
|
|
251
|
+
attachment_changes=(
|
|
252
|
+
RecordTableRowAttachmentAppend(
|
|
253
|
+
table="Measurements",
|
|
254
|
+
file_column="Evidence",
|
|
255
|
+
values={"Label": "Sample C"},
|
|
256
|
+
attachment=another_staged,
|
|
257
|
+
),
|
|
258
|
+
),
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
await client.records.update(
|
|
262
|
+
source,
|
|
263
|
+
attachment_changes=(
|
|
264
|
+
RecordFilesAttachmentRootAppend(
|
|
265
|
+
module="Files",
|
|
266
|
+
caption="Evidence",
|
|
267
|
+
attachment=third_staged,
|
|
268
|
+
),
|
|
269
|
+
),
|
|
270
|
+
)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
`RecordTableFileCellSet` requires an existing file column and a cell whose immediate canonical value is exactly
|
|
274
|
+
`null`. The row-append intent addresses the new row by the row count from the SDK's immediate read and supports one
|
|
275
|
+
staged file column. Files append is root-only and requires a string caption.
|
|
276
|
+
|
|
277
|
+
Replacement starts with an occurrence returned by `records.read()`; applications must not fabricate a
|
|
278
|
+
`RecordAttachmentRef`:
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
from matelab import RecordFilesAttachmentReplacement
|
|
282
|
+
|
|
283
|
+
record = await client.records.read(notebook=source.notebook, record=source.record)
|
|
284
|
+
existing = next(
|
|
285
|
+
attachment
|
|
286
|
+
for attachment in record.attachments
|
|
287
|
+
if attachment.location is not None and attachment.location.kind == "files_module"
|
|
288
|
+
)
|
|
289
|
+
await client.records.update(
|
|
290
|
+
source,
|
|
291
|
+
attachment_changes=(RecordFilesAttachmentReplacement(existing=existing, replacement=staged, caption=None),),
|
|
292
|
+
)
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
For a files occurrence, `caption=None` preserves the observed string caption (an observed null caption normalizes to
|
|
296
|
+
the required empty string). Canonical readback must preserve the Provider `uid` and folder. Table replacement requires
|
|
297
|
+
exactly one current attachment in the selected cell and uses `RecordTableFileAttachmentReplacement`. Both replacement
|
|
298
|
+
forms require the new hash to differ.
|
|
299
|
+
|
|
300
|
+
Row/index-based finalizers use an immediate SDK read and must not be called while a concurrent editor is known to be
|
|
301
|
+
active. The Provider offers no expected hash or revision. `expected_content_sha256` is only a client-side prewrite
|
|
302
|
+
check, not Provider compare-and-swap.
|
|
303
|
+
|
|
304
|
+
File-bearing structure changes do not consume staging:
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
from matelab import (
|
|
308
|
+
RecordFormFileFieldDeletion,
|
|
309
|
+
RecordTableFileColumnAddition,
|
|
310
|
+
RecordTableFileColumnDeletion,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
await client.records.update(
|
|
314
|
+
source,
|
|
315
|
+
file_structure_changes=(
|
|
316
|
+
RecordTableFileColumnAddition(table="Measurements", name="Additional evidence"),
|
|
317
|
+
RecordFormFileFieldDeletion(form="Attachments", name="Obsolete evidence"),
|
|
318
|
+
RecordTableFileColumnDeletion(table="Measurements", name="Old evidence"),
|
|
319
|
+
),
|
|
320
|
+
)
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
The SDK emits the v0.1.2 dedicated operations: a new file column omits wire `data` and must read back as `null` for
|
|
324
|
+
every existing row; whole form-field/table-column deletion uses a strict two-segment target. It does not expose a
|
|
325
|
+
three-segment delete as table-cell clearing.
|
|
326
|
+
|
|
327
|
+
Deleting a complete canonical module is also supported even when the current read observes attachments in it:
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
result = await client.records.update(
|
|
331
|
+
source,
|
|
332
|
+
module_deletions=("Raw files",),
|
|
333
|
+
expected_content_sha256=record.content_sha256,
|
|
334
|
+
)
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
This requests only a canonical record-content mutation. Readback proves the module is absent; it does not prove
|
|
338
|
+
attachment-quote cleanup or byte deletion. Collaboration-pending has no readback and is not reported as persisted.
|
|
339
|
+
|
|
340
|
+
Notebook-staged rich-text binding remains unsupported. Provider Verification shows that `#file{name}` is stored as
|
|
341
|
+
plain text and that a hash-based URI can fall back to an existing quote, so neither is a one-shot staged finalizer.
|
|
342
|
+
Existing `RecordRichTextUpdate` remains legal only with record-scoped `StagedRecordAttachment`. Attachment-bearing
|
|
343
|
+
multi-record import is also forbidden; callers must split it into single-record finalizers.
|
|
344
|
+
|
|
345
|
+
See `docs/attachment-write-contract-gaps.md` in the source repository for the exact support matrix, integration
|
|
346
|
+
lifecycle requirements, and minimum Contract changes needed to expose the remaining operations.
|
|
220
347
|
|
|
221
348
|
## Implementation roadmap
|
|
222
349
|
|
|
223
350
|
`docs/roadmap.md` is the complete SDK-only execution plan. It assigns all 71
|
|
224
|
-
`matelab-spec v0.1.
|
|
351
|
+
`matelab-spec v0.1.2` operations to ordered work packages, defines the machine-readable coverage that
|
|
225
352
|
must be added, records Provider-risk gates, and specifies the final completion checks.
|
|
226
353
|
|
|
227
354
|
The SDK exposes all 71 Contract operations through public domain interfaces. It deliberately excludes MCP
|
|
@@ -554,8 +681,17 @@ agree. With the environment prepared:
|
|
|
554
681
|
uv run --env-file .env.test pytest -m provider tests/provider/test_provider_smoke.py
|
|
555
682
|
```
|
|
556
683
|
|
|
557
|
-
|
|
558
|
-
|
|
684
|
+
This command runs only the SDK public-interface smoke; it is not the 71-operation Provider Verification. To reuse the
|
|
685
|
+
same `.env.test` for the complete Contract suite, also populate the optional share user, secondary account, record
|
|
686
|
+
staging opt-in, and Chat SSO settings documented in `.env.example`, then run from sibling checkouts:
|
|
687
|
+
|
|
688
|
+
```bash
|
|
689
|
+
cd ../matelab-spec
|
|
690
|
+
uv run --env-file ../matelab-python-sdk/.env.test pytest
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
The files are never loaded implicitly, so normal test runs remain safely skipped. Do not use either flow against
|
|
694
|
+
production, and never commit Provider credentials.
|
|
559
695
|
|
|
560
696
|
## Reproducible release build
|
|
561
697
|
|
|
@@ -573,6 +709,6 @@ uv run python scripts/check_release.py dist/release/*.whl dist/release/*.tar.gz
|
|
|
573
709
|
```
|
|
574
710
|
|
|
575
711
|
Rebuilding the same commit with the same locked environment and `SOURCE_DATE_EPOCH` must produce
|
|
576
|
-
byte-identical wheel and source distribution hashes. The release is bound to `matelab-spec v0.1.
|
|
577
|
-
commit `
|
|
578
|
-
`
|
|
712
|
+
byte-identical wheel and source distribution hashes. The release is bound to `matelab-spec v0.1.2`,
|
|
713
|
+
commit `6dd674ac001ae61197495ace5eba489d585cb825`, and OpenAPI SHA-256
|
|
714
|
+
`7ccaf3116c8dcec7a6e49ff51fb1a5ecc83a140e013ec600516f5c2ffb3b5378`.
|
{matelab_python_sdk-0.1.0a4 → matelab_python_sdk-0.1.0a5}/contracts/matelab-integration-v1.lock.json
RENAMED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
"schema_version": 1,
|
|
3
3
|
"contract_line": "integration-v1",
|
|
4
4
|
"release": {
|
|
5
|
-
"tag": "v0.1.
|
|
6
|
-
"commit": "
|
|
5
|
+
"tag": "v0.1.2",
|
|
6
|
+
"commit": "6dd674ac001ae61197495ace5eba489d585cb825"
|
|
7
7
|
},
|
|
8
8
|
"openapi": {
|
|
9
9
|
"source_path": "openapi/matelab-integration-v1.openapi.yaml",
|
|
10
10
|
"snapshot": "matelab-integration-v1.openapi.yaml",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "7ccaf3116c8dcec7a6e49ff51fb1a5ecc83a140e013ec600516f5c2ffb3b5378"
|
|
12
12
|
}
|
|
13
13
|
}
|