matelab-python-sdk 0.2.0a10__tar.gz → 0.3.0a1__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.2.0a10 → matelab_python_sdk-0.3.0a1}/.agents/skills/matelab-sdk-release/SKILL.md +7 -7
- matelab_python_sdk-0.3.0a1/.python-version +1 -0
- matelab_python_sdk-0.3.0a1/.workflow/release.yml +89 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/PKG-INFO +8 -8
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/README.md +4 -4
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/docs/agent-workflows.md +9 -9
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/docs/development.md +9 -2
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/pyproject.toml +8 -8
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/scripts/generate_models.py +1 -1
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/_generated/models.py +1 -1
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/_transport.py +6 -8
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/_wire_validation.py +4 -5
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/records.py +6 -7
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_release.py +2 -2
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_streaming.py +1 -1
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_transport.py +1 -2
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/uv.lock +157 -243
- matelab_python_sdk-0.2.0a10/.github/workflows/release.yml +0 -119
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/.agents/skills/matelab-sdk-release/agents/openai.yaml +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/.agents/skills/matelab-sdk-release/scripts/verify_pypi.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/.env.example +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/.gitignore +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/AGENTS.md +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/CONTEXT.md +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/LICENSE +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/NOTICE +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/contracts/matelab-integration-v1.lock.json +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/contracts/matelab-integration-v1.openapi.yaml +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/docs/agent-architecture.md +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/docs/api.md +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/docs/operation-coverage.yaml +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/scripts/check_installed_package.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/scripts/check_release.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/__init__.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/_generated/__init__.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/_hashes.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/client.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/__init__.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/_attachment_content.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/cloud_drive.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/groups.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/literature.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/notebooks.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/templates.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/uploads.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/users.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/errors.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/py.typed +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/session.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/streaming.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/conftest.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/provider/test_provider_smoke.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_async_attachment_upload.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_client.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_cloud_drive.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_comments_and_downloads.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_consumer_smoke.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_errors.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_extended_records.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_generation.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_groups.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_notebook_lifecycle.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_notebooks.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_operation_coverage.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_record_lifecycle.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_record_mutations.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_records.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_template_lifecycle.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_templates.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_uploads_and_literature.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_users.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/typing/consumer.py +0 -0
- {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/typing/pyproject.toml +0 -0
|
@@ -57,7 +57,7 @@ commit/diff;只改版本后补版本一致性检查,不重跑未受影响的
|
|
|
57
57
|
SDK 版本与 spec 日历版本独立,不跳过未占用的预发布序号。`pyproject.toml` 是唯一版本来源,更新 `uv.lock` 的
|
|
58
58
|
本项目版本。仅改版本时保持其余 lock 内容,使用发布 workflow 固定的 uv 检查兼容性。
|
|
59
59
|
|
|
60
|
-
按需读配置/依赖章节;不因发布顺手升级依赖、Node
|
|
60
|
+
按需读配置/依赖章节;不因发布顺手升级依赖、Node、uv 或流水线插件。文档在提交前完成,仅维护受影响的当前态,不新建
|
|
61
61
|
CHANGELOG。正常发布由 CI 完成可复现构建、制品审计和安装后消费;只有打包故障或打包配置变化需要时才本地构建,
|
|
62
62
|
使用临时目录,不积累 `dist/`。
|
|
63
63
|
|
|
@@ -66,12 +66,12 @@ release commit。已有提交的历史仅在另有授权时改写。
|
|
|
66
66
|
|
|
67
67
|
## 4. 推送、发布并核验
|
|
68
68
|
|
|
69
|
-
执行 `docs/agent-workflows.md#release`:创建指向 release commit 的 annotated `v{version}` tag
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
执行 `docs/agent-workflows.md#release`:创建指向 release commit 的 annotated `v{version}` tag,推送
|
|
70
|
+
`origin` 的 main/tag,并验证远端 refs;tag 推送自动触发发布 CI。跟踪该 commit/tag 的 CI,确认测试、
|
|
71
|
+
可复现构建、制品审计、安装后消费和 PyPI 上传成功;tag 推送成功不等于发布完成。
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
从本次 CI 构建审计输出或下载的制品取得 wheel/sdist 的 SHA-256,不能仅用 PyPI 自报哈希证明 CI/PyPI 一致。
|
|
74
|
+
在 SDK 锁定开发环境中运行本技能的只读核验器:
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
77
|
uv run --frozen --no-sync python -B .agents/skills/matelab-sdk-release/scripts/verify_pypi.py \
|
|
@@ -85,5 +85,5 @@ uv run --frozen --no-sync python -B .agents/skills/matelab-sdk-release/scripts/v
|
|
|
85
85
|
历史制品。PyPI 暂未可读时允许短暂重试,等待期间保持进度更新。
|
|
86
86
|
|
|
87
87
|
失败时定位具体阶段,优先重试失败环节;不通过删测试、跳检查或自动另选版本制造成功。改变已推送历史或处理失败
|
|
88
|
-
|
|
88
|
+
候选前核对现有授权,已发布版本不可覆盖。最后报告版本/链接、单次提交、`origin` 的 main/tag refs、CI/PyPI 核验和工作区状态;
|
|
89
89
|
明确剩余失败或未验证项。
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.15
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
version: "1.0"
|
|
2
|
+
name: sdk-release
|
|
3
|
+
displayName: Publish SDK to PyPI
|
|
4
|
+
ref_variable:
|
|
5
|
+
- UV_PUBLISH_TOKEN
|
|
6
|
+
triggers:
|
|
7
|
+
push:
|
|
8
|
+
tags:
|
|
9
|
+
prefix:
|
|
10
|
+
- v
|
|
11
|
+
stages:
|
|
12
|
+
- name: release
|
|
13
|
+
displayName: Test, build and publish
|
|
14
|
+
strategy: naturally
|
|
15
|
+
trigger: auto
|
|
16
|
+
steps:
|
|
17
|
+
- step: build@python
|
|
18
|
+
name: build-and-publish
|
|
19
|
+
displayName: Validate and publish release
|
|
20
|
+
# Gitee's Python bootstraps uv; the SDK runs on uv-managed Python 3.15.
|
|
21
|
+
pythonVersion: "3.14"
|
|
22
|
+
commands: |
|
|
23
|
+
set +x
|
|
24
|
+
set -eu
|
|
25
|
+
# Gitee injects its pytest tooling into Python's import path.
|
|
26
|
+
unset PYTHONPATH PYTHONHOME
|
|
27
|
+
python -m pip install --user --index-url https://pypi.org/simple "uv==0.13.7"
|
|
28
|
+
export PATH="$(python -m site --user-base)/bin:$PATH"
|
|
29
|
+
uv --version
|
|
30
|
+
release_tag="v$(uv version --short)"
|
|
31
|
+
build_ref="${GITEE_BRANCH:?Gitee build ref is required}"
|
|
32
|
+
if [ "$build_ref" != main ] && [ "$build_ref" != "$release_tag" ]; then
|
|
33
|
+
echo "Release ref must be $release_tag; received $build_ref" >&2
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
if [ "$(git rev-parse HEAD)" != "${GITEE_COMMIT:?Gitee trigger commit is required}" ]; then
|
|
37
|
+
echo "Checkout does not match the Gitee build commit" >&2
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
export SOURCE_DATE_EPOCH="$(git show -s --format=%ct HEAD)"
|
|
41
|
+
unset MATELAB_PROVIDER_BASE_URL MATELAB_PROVIDER_USERNAME MATELAB_PROVIDER_PASSWORD
|
|
42
|
+
|
|
43
|
+
test_status=0
|
|
44
|
+
for python_version in 3.12 3.13 3.14 3.15; do
|
|
45
|
+
uv run --frozen --exact --python "$python_version" pytest -rs || test_status=1
|
|
46
|
+
done
|
|
47
|
+
test "$test_status" -eq 0
|
|
48
|
+
|
|
49
|
+
export UV_PYTHON="$(cat .python-version)"
|
|
50
|
+
uv run --frozen python scripts/generate_models.py --check
|
|
51
|
+
uv run --frozen ruff check .
|
|
52
|
+
uv run --frozen ruff format --check .
|
|
53
|
+
uv run --frozen basedpyright
|
|
54
|
+
|
|
55
|
+
mkdir -p dist/release dist/reproducible
|
|
56
|
+
uv build --no-build-isolation --out-dir dist/release
|
|
57
|
+
uv build --no-build-isolation --out-dir dist/reproducible
|
|
58
|
+
cmp dist/release/*.whl dist/reproducible/*.whl
|
|
59
|
+
cmp dist/release/*.tar.gz dist/reproducible/*.tar.gz
|
|
60
|
+
uv run --frozen python scripts/check_release.py dist/release/*.whl dist/release/*.tar.gz
|
|
61
|
+
(cd dist/release && sha256sum *.whl *.tar.gz > SHA256SUMS)
|
|
62
|
+
cat dist/release/SHA256SUMS
|
|
63
|
+
|
|
64
|
+
sdk_consumer_dir="$(mktemp -d)"
|
|
65
|
+
trap 'rm -rf -- "$sdk_consumer_dir"' EXIT
|
|
66
|
+
uv venv "$sdk_consumer_dir/venv"
|
|
67
|
+
uv pip install \
|
|
68
|
+
--python "$sdk_consumer_dir/venv/bin/python" \
|
|
69
|
+
dist/release/*.whl \
|
|
70
|
+
"basedpyright>=1.40.2" \
|
|
71
|
+
"nodejs-wheel-binaries==22.20.0"
|
|
72
|
+
"$sdk_consumer_dir/venv/bin/python" scripts/check_installed_package.py
|
|
73
|
+
"$sdk_consumer_dir/venv/bin/basedpyright" \
|
|
74
|
+
--pythonpath "$sdk_consumer_dir/venv/bin/python" \
|
|
75
|
+
--project tests/typing/pyproject.toml
|
|
76
|
+
|
|
77
|
+
if [ "$build_ref" = main ]; then
|
|
78
|
+
echo "Validation passed on main; publishing requires tag $release_tag"
|
|
79
|
+
exit 0
|
|
80
|
+
fi
|
|
81
|
+
: "${UV_PUBLISH_TOKEN:?Configure UV_PUBLISH_TOKEN as a protected pipeline secret}"
|
|
82
|
+
uv publish --trusted-publishing never \
|
|
83
|
+
--publish-url https://upload.pypi.org/legacy/ \
|
|
84
|
+
dist/release/*.whl dist/release/*.tar.gz
|
|
85
|
+
artifacts:
|
|
86
|
+
- name: RELEASE_ARTIFACT
|
|
87
|
+
path:
|
|
88
|
+
- ./dist/release
|
|
89
|
+
type: .tar.gz
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: matelab-python-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0a1
|
|
4
4
|
Summary: Reusable async Python client for the Matelab Integration Contract
|
|
5
5
|
Author: CMPDC, Institute of Physics, Chinese Academy of Sciences
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -12,24 +12,24 @@ Classifier: Framework :: AsyncIO
|
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
14
14
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
19
19
|
Classifier: Topic :: Scientific/Engineering
|
|
20
20
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
21
|
Classifier: Typing :: Typed
|
|
22
|
-
Requires-Python: >=3.
|
|
22
|
+
Requires-Python: >=3.12
|
|
23
23
|
Requires-Dist: httpx2>=2.16.0
|
|
24
24
|
Requires-Dist: jsonschema>=4.26.0
|
|
25
|
-
Requires-Dist: pydantic>=2.14.
|
|
25
|
+
Requires-Dist: pydantic>=2.14.3
|
|
26
26
|
Requires-Dist: typing-extensions>=4.16.0
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
28
28
|
|
|
29
29
|
# matelab-python-sdk
|
|
30
30
|
|
|
31
31
|
Async Python client for the Matelab Integration Contract, with typed interfaces for authentication,
|
|
32
|
-
notebooks, records, attachments, cloud files, templates, and literature. Requires Python 3.
|
|
32
|
+
notebooks, records, attachments, cloud files, templates, and literature. Requires Python 3.12 or newer.
|
|
33
33
|
|
|
34
34
|
## Installation
|
|
35
35
|
|
|
@@ -64,6 +64,6 @@ asyncio.run(main())
|
|
|
64
64
|
|
|
65
65
|
## Documentation
|
|
66
66
|
|
|
67
|
-
- [API reference](https://
|
|
68
|
-
- [Operation coverage](https://
|
|
69
|
-
- [Development and releases](https://
|
|
67
|
+
- [API reference](https://gitee.com/mat-code/matelab-python-sdk/blob/main/docs/api.md): Session persistence, errors, examples, and Provider limits.
|
|
68
|
+
- [Operation coverage](https://gitee.com/mat-code/matelab-python-sdk/blob/main/docs/operation-coverage.yaml): supported operations.
|
|
69
|
+
- [Development and releases](https://gitee.com/mat-code/matelab-python-sdk/blob/main/docs/development.md): checks, Provider smoke, versioning, and builds.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# matelab-python-sdk
|
|
2
2
|
|
|
3
3
|
Async Python client for the Matelab Integration Contract, with typed interfaces for authentication,
|
|
4
|
-
notebooks, records, attachments, cloud files, templates, and literature. Requires Python 3.
|
|
4
|
+
notebooks, records, attachments, cloud files, templates, and literature. Requires Python 3.12 or newer.
|
|
5
5
|
|
|
6
6
|
## Installation
|
|
7
7
|
|
|
@@ -36,6 +36,6 @@ asyncio.run(main())
|
|
|
36
36
|
|
|
37
37
|
## Documentation
|
|
38
38
|
|
|
39
|
-
- [API reference](https://
|
|
40
|
-
- [Operation coverage](https://
|
|
41
|
-
- [Development and releases](https://
|
|
39
|
+
- [API reference](https://gitee.com/mat-code/matelab-python-sdk/blob/main/docs/api.md): Session persistence, errors, examples, and Provider limits.
|
|
40
|
+
- [Operation coverage](https://gitee.com/mat-code/matelab-python-sdk/blob/main/docs/operation-coverage.yaml): supported operations.
|
|
41
|
+
- [Development and releases](https://gitee.com/mat-code/matelab-python-sdk/blob/main/docs/development.md): checks, Provider smoke, versioning, and builds.
|
|
@@ -31,8 +31,9 @@
|
|
|
31
31
|
- `[tool.basedpyright]` 字段依次为 `pythonVersion`、`typeCheckingMode`、`include`、`exclude`、
|
|
32
32
|
`reportUnannotatedClassAttribute`、`reportIncompatibleUnannotatedOverride`,其他设置放在其后。沿用 SDK 的目标
|
|
33
33
|
Python 版本与检查路径,不照搬其他项目的值。
|
|
34
|
-
- runtime、build
|
|
35
|
-
|
|
34
|
+
- 升级 runtime、build 或 dev 依赖时,必须将验证通过的新版本同步为 `pyproject.toml` 中对应依赖的 `>=` 下限,
|
|
35
|
+
同时更新对应 CI 约束(包括安装后消费检查)。`uv.lock` 必须通过 `uv lock` 等 uv 命令自动更新,不手工编辑。
|
|
36
|
+
例外:`nodejs-wheel-binaries` 保留系统所需的精确版本,并同步 lock 和对应 CI 约束。
|
|
36
37
|
- 构建后端依赖和 wheel package 显式配置;metadata 与制品检查从这些声明推导预期值。
|
|
37
38
|
|
|
38
39
|
完成标准:配置可解析、声明与 lock 一致、受影响 CI 使用同一约束;字段重排不改变配置含义。
|
|
@@ -85,16 +86,15 @@ Ruff/Basedpyright 诊断通过修复代码或精确 ignore 解决,不缩小检
|
|
|
85
86
|
## Release
|
|
86
87
|
|
|
87
88
|
普通 commit/push 只处理已授权的 diff,不自动改版本、建 tag 或发布。发布准备完成但缺少外部操作授权时,
|
|
88
|
-
|
|
89
|
+
先交付可审查结果,再请求执行授权。发布与普通推送只同步 `origin`。以下步骤适用于实际发布:
|
|
89
90
|
|
|
90
91
|
1. 按 [版本规则](development.md#versioning) 选择 SDK 版本,读取 `[project].version`,确认待发布版本未发布
|
|
91
92
|
且 Contract pin 完整。公开接口删除、重命名、类型收紧或不兼容行为变化按 breaking change 处理。
|
|
92
93
|
完成标准:版本、当前文档和 tag 名 `v{version}` 一致。
|
|
93
94
|
2. 提交前同步受影响的当前文档,完成 [delivery gate](#delivery-gate)。构建元数据或发布流程变化时,核对
|
|
94
|
-
metadata
|
|
95
|
+
metadata 与流水线固定版本的 uv 是否兼容;需要本地制品证据时使用
|
|
95
96
|
[手工构建命令](development.md#reproducible-release-build)。创建已审查、已授权的 release commit。
|
|
96
|
-
3. 创建指向 release commit 的 annotated `v{version}` tag
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
完成标准:两个远端 main/tag 对应 release commit,CI 全部通过,PyPI 对应版本可读取。
|
|
97
|
+
3. 创建指向 release commit 的 annotated `v{version}` tag,推送 `origin` 的 main/tag;tag 推送自动触发
|
|
98
|
+
[发布 CI](../.workflow/release.yml)。
|
|
99
|
+
4. 跟踪发布 CI 的测试、可复现构建、制品审计、安装后消费和 PyPI publish。
|
|
100
|
+
完成标准:`origin` 的 main/tag 对应 release commit,CI 全部通过,PyPI 对应版本与 CI 制品哈希一致。
|
|
@@ -15,6 +15,11 @@ according to its public impact.
|
|
|
15
15
|
|
|
16
16
|
## Development
|
|
17
17
|
|
|
18
|
+
Use Python 3.15 for development; `.python-version` selects it for `uv sync` and `uv run`.
|
|
19
|
+
The SDK supports Python 3.12 and newer. Source syntax, type checking, and generated models target Python 3.12.
|
|
20
|
+
Release CI tests Python 3.12, 3.13, 3.14, and 3.15. Generated-model, lint, formatting, and type checks run only on
|
|
21
|
+
Python 3.15, which also builds the distributions and checks the installed-wheel consumer.
|
|
22
|
+
|
|
18
23
|
```bash
|
|
19
24
|
uv sync --frozen
|
|
20
25
|
uv run python scripts/generate_models.py --check
|
|
@@ -28,6 +33,7 @@ Choose checks by change type using the
|
|
|
28
33
|
[maintenance workflows](agent-workflows.md#delivery-gate).
|
|
29
34
|
Contract or generator changes regenerate private models with `uv run python scripts/generate_models.py` before checking.
|
|
30
35
|
`pyproject.toml` declares dependency minimums; `uv.lock` fixes the development and release environment.
|
|
36
|
+
The exact Node.js wheel pin preserves compatibility with glibc 2.17 on the development host.
|
|
31
37
|
|
|
32
38
|
## Opt-in Provider consumer smoke
|
|
33
39
|
|
|
@@ -51,9 +57,10 @@ for target authorization and keep credentials out of Git.
|
|
|
51
57
|
|
|
52
58
|
## Reproducible release build
|
|
53
59
|
|
|
54
|
-
|
|
55
|
-
installed-wheel
|
|
60
|
+
Pushing a `v{version}` release tag to `origin` automatically triggers [CI](../.workflow/release.yml): checks, reproducible
|
|
61
|
+
builds, artifact audits, installed-wheel validation, and PyPI publishing. Preparation follows the
|
|
56
62
|
[release workflow](agent-workflows.md#release).
|
|
63
|
+
A manual run of `main` performs validation without publishing.
|
|
57
64
|
|
|
58
65
|
Local builds are only needed for packaging changes or artifact diagnostics. To reproduce CI artifacts, use the release
|
|
59
66
|
tag's checkout and committer timestamp, the locked environment, and a temporary output directory:
|
|
@@ -4,9 +4,9 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "matelab-python-sdk"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0a1"
|
|
8
8
|
description = "Reusable async Python client for the Matelab Integration Contract"
|
|
9
|
-
requires-python = ">=3.
|
|
9
|
+
requires-python = ">=3.12"
|
|
10
10
|
keywords = ["matelab", "electronic-lab-notebook", "eln", "asyncio", "sdk"]
|
|
11
11
|
authors = [
|
|
12
12
|
{ name = "CMPDC, Institute of Physics, Chinese Academy of Sciences" },
|
|
@@ -14,7 +14,7 @@ authors = [
|
|
|
14
14
|
dependencies = [
|
|
15
15
|
"httpx2>=2.16.0",
|
|
16
16
|
"jsonschema>=4.26.0",
|
|
17
|
-
"pydantic>=2.14.
|
|
17
|
+
"pydantic>=2.14.3",
|
|
18
18
|
"typing-extensions>=4.16.0",
|
|
19
19
|
]
|
|
20
20
|
readme = "README.md"
|
|
@@ -26,10 +26,10 @@ classifiers = [
|
|
|
26
26
|
"Operating System :: OS Independent",
|
|
27
27
|
"Programming Language :: Python :: 3",
|
|
28
28
|
"Programming Language :: Python :: 3 :: Only",
|
|
29
|
-
"Programming Language :: Python :: 3.11",
|
|
30
29
|
"Programming Language :: Python :: 3.12",
|
|
31
30
|
"Programming Language :: Python :: 3.13",
|
|
32
31
|
"Programming Language :: Python :: 3.14",
|
|
32
|
+
"Programming Language :: Python :: 3.15",
|
|
33
33
|
"Topic :: Scientific/Engineering",
|
|
34
34
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
35
35
|
"Typing :: Typed",
|
|
@@ -39,12 +39,12 @@ classifiers = [
|
|
|
39
39
|
dev = [
|
|
40
40
|
"basedpyright>=1.40.2",
|
|
41
41
|
"nodejs-wheel-binaries==22.20.0",
|
|
42
|
-
"datamodel-code-generator>=0.
|
|
42
|
+
"datamodel-code-generator>=0.90.0",
|
|
43
43
|
"hatchling>=1.33.0",
|
|
44
44
|
"pytest>=9.2.0",
|
|
45
|
-
"pytest-asyncio>=1.4.1",
|
|
45
|
+
"pytest-asyncio>=1.4.1.post1",
|
|
46
46
|
"pyyaml>=6.0.3",
|
|
47
|
-
"ruff>=0.17.
|
|
47
|
+
"ruff>=0.17.4",
|
|
48
48
|
]
|
|
49
49
|
|
|
50
50
|
[tool.ruff]
|
|
@@ -54,7 +54,7 @@ lint.isort.split-on-trailing-comma = false
|
|
|
54
54
|
format.skip-magic-trailing-comma = true
|
|
55
55
|
|
|
56
56
|
[tool.basedpyright]
|
|
57
|
-
pythonVersion = "3.
|
|
57
|
+
pythonVersion = "3.12"
|
|
58
58
|
typeCheckingMode = "recommended"
|
|
59
59
|
include = ["src", "tests", "scripts"]
|
|
60
60
|
exclude = ["src/matelab/_generated"]
|
|
@@ -451,7 +451,7 @@ def _render_models(document: dict[str, Any]) -> bytes:
|
|
|
451
451
|
"--base-class",
|
|
452
452
|
"matelab._wire_validation.WireModel",
|
|
453
453
|
"--target-python-version",
|
|
454
|
-
"3.
|
|
454
|
+
"3.12",
|
|
455
455
|
"--disable-future-imports",
|
|
456
456
|
"--use-union-operator",
|
|
457
457
|
"--use-standard-collections",
|
|
@@ -5,11 +5,10 @@ from collections.abc import AsyncIterator, Mapping
|
|
|
5
5
|
from contextlib import suppress
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from enum import StrEnum
|
|
8
|
-
from typing import IO,
|
|
8
|
+
from typing import IO, cast, override
|
|
9
9
|
|
|
10
10
|
from httpx2 import AsyncByteStream, AsyncClient, Request, RequestError, Response, SyncByteStream
|
|
11
11
|
from pydantic import BaseModel, JsonValue, SecretStr, ValidationError
|
|
12
|
-
from typing_extensions import override
|
|
13
12
|
|
|
14
13
|
from matelab._generated import models as wire_models
|
|
15
14
|
from matelab.errors import (
|
|
@@ -23,10 +22,9 @@ from matelab.errors import (
|
|
|
23
22
|
from matelab.session import Session, Token
|
|
24
23
|
from matelab.streaming import ByteRange, DownloadStream
|
|
25
24
|
|
|
26
|
-
WireResponse = TypeVar("WireResponse", bound=BaseModel)
|
|
27
25
|
_ACCESS_EXPIRY_SKEW_MS = 30_000
|
|
28
|
-
_ParameterValue
|
|
29
|
-
_Files
|
|
26
|
+
type _ParameterValue = str | int | float | bool | None
|
|
27
|
+
type _Files = Mapping[str, tuple[str, IO[bytes] | bytes, str]]
|
|
30
28
|
_CONTENT_RANGE_PATTERN = re.compile(r"^bytes [0-9]+-[0-9]+/[0-9]+$")
|
|
31
29
|
|
|
32
30
|
|
|
@@ -100,7 +98,7 @@ class Encoding(StrEnum):
|
|
|
100
98
|
|
|
101
99
|
|
|
102
100
|
@dataclass(frozen=True, slots=True)
|
|
103
|
-
class Operation
|
|
101
|
+
class Operation[WireResponse: BaseModel]:
|
|
104
102
|
method: str
|
|
105
103
|
path: str
|
|
106
104
|
encoding: Encoding
|
|
@@ -177,7 +175,7 @@ class SessionTransport:
|
|
|
177
175
|
)
|
|
178
176
|
return self.session
|
|
179
177
|
|
|
180
|
-
async def _send_multipart(
|
|
178
|
+
async def _send_multipart[WireResponse: BaseModel](
|
|
181
179
|
self,
|
|
182
180
|
operation: Operation[WireResponse],
|
|
183
181
|
*,
|
|
@@ -205,7 +203,7 @@ class SessionTransport:
|
|
|
205
203
|
request.stream = _ThreadedMultipartStream(request.stream)
|
|
206
204
|
return await self._http.send(request)
|
|
207
205
|
|
|
208
|
-
async def request(
|
|
206
|
+
async def request[WireResponse: BaseModel](
|
|
209
207
|
self, operation: Operation[WireResponse], *, payload: BaseModel | None = None, files: _Files | None = None
|
|
210
208
|
) -> WireResponse:
|
|
211
209
|
if (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from collections.abc import Callable, Collection, Iterator, Mapping
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import cast
|
|
4
4
|
|
|
5
5
|
from jsonschema import Draft202012Validator
|
|
6
6
|
from jsonschema.exceptions import ValidationError as JsonSchemaValidationError
|
|
@@ -15,8 +15,7 @@ from matelab.errors import MatelabUsageError
|
|
|
15
15
|
|
|
16
16
|
_RUNTIME_RULES_KEY = "matelab-runtime-rules"
|
|
17
17
|
JsonObject = dict[str, JsonValue]
|
|
18
|
-
WireValue
|
|
19
|
-
ModelT = TypeVar("ModelT", bound=BaseModel)
|
|
18
|
+
type WireValue = JsonValue | bytes | Mapping[str, WireValue]
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
def _reject_json_constant(_value: str) -> None:
|
|
@@ -44,7 +43,7 @@ _ContractValidator = cast(
|
|
|
44
43
|
)
|
|
45
44
|
|
|
46
45
|
|
|
47
|
-
def validate_request(model: type[ModelT], data: Mapping[str, WireValue], message: str) -> ModelT:
|
|
46
|
+
def validate_request[ModelT: BaseModel](model: type[ModelT], data: Mapping[str, WireValue], message: str) -> ModelT:
|
|
48
47
|
"""Validate caller input without exposing it in the public exception."""
|
|
49
48
|
try:
|
|
50
49
|
return model.model_validate(data)
|
|
@@ -52,7 +51,7 @@ def validate_request(model: type[ModelT], data: Mapping[str, WireValue], message
|
|
|
52
51
|
raise MatelabUsageError(message) from None
|
|
53
52
|
|
|
54
53
|
|
|
55
|
-
def validate_multipart_request(
|
|
54
|
+
def validate_multipart_request[ModelT: BaseModel](
|
|
56
55
|
model: type[ModelT], data: Mapping[str, WireValue], file_fields: Collection[str], message: str
|
|
57
56
|
) -> ModelT:
|
|
58
57
|
"""Validate multipart fields whose binary content is transported separately."""
|
|
@@ -5,11 +5,10 @@ from copy import deepcopy
|
|
|
5
5
|
from dataclasses import dataclass, field
|
|
6
6
|
from html.parser import HTMLParser
|
|
7
7
|
from types import MappingProxyType
|
|
8
|
-
from typing import IO, Literal, Self,
|
|
8
|
+
from typing import IO, Literal, Self, cast, override
|
|
9
9
|
from urllib.parse import unquote
|
|
10
10
|
|
|
11
11
|
from pydantic import BaseModel, JsonValue, model_validator
|
|
12
|
-
from typing_extensions import override
|
|
13
12
|
|
|
14
13
|
from matelab._generated import models as wire_models
|
|
15
14
|
from matelab._hashes import LOWERCASE_SHA256_PATTERN
|
|
@@ -426,7 +425,7 @@ class StagedAttachment:
|
|
|
426
425
|
raise ValueError("Staged attachment metadata is invalid.")
|
|
427
426
|
|
|
428
427
|
|
|
429
|
-
RecordImportValue
|
|
428
|
+
type RecordImportValue = (
|
|
430
429
|
str
|
|
431
430
|
| int
|
|
432
431
|
| float
|
|
@@ -434,9 +433,9 @@ RecordImportValue: TypeAlias = (
|
|
|
434
433
|
| None
|
|
435
434
|
| StagedAttachment
|
|
436
435
|
| CloudFile
|
|
437
|
-
| Mapping[str,
|
|
438
|
-
| list[
|
|
439
|
-
| tuple[
|
|
436
|
+
| Mapping[str, RecordImportValue]
|
|
437
|
+
| list[RecordImportValue]
|
|
438
|
+
| tuple[RecordImportValue, ...]
|
|
440
439
|
)
|
|
441
440
|
|
|
442
441
|
|
|
@@ -563,7 +562,7 @@ class RecordTableFileColumnDeletion:
|
|
|
563
562
|
name: str
|
|
564
563
|
|
|
565
564
|
|
|
566
|
-
RecordChange
|
|
565
|
+
type RecordChange = (
|
|
567
566
|
RecordValueUpdate
|
|
568
567
|
| RecordValueDeletion
|
|
569
568
|
| RecordModuleCreation
|
|
@@ -129,7 +129,7 @@ def test_release_dependencies_publish_minimum_versions() -> None:
|
|
|
129
129
|
assert build == [dev["hatchling"]]
|
|
130
130
|
assert project["license"] == "Apache-2.0"
|
|
131
131
|
assert project["license-files"] == ["LICENSE", "NOTICE"]
|
|
132
|
-
workflow = (_ROOT / ".
|
|
132
|
+
workflow = (_ROOT / ".workflow" / "release.yml").read_text()
|
|
133
133
|
assert str(dev["basedpyright"]) in workflow
|
|
134
134
|
assert str(dev["nodejs-wheel-binaries"]) in workflow
|
|
135
135
|
|
|
@@ -141,7 +141,7 @@ def test_built_metadata_contains_public_dependency_and_python_contract(built_whe
|
|
|
141
141
|
|
|
142
142
|
assert metadata["Name"] == "matelab-python-sdk"
|
|
143
143
|
assert metadata["Version"] == _RELEASE_VERSION
|
|
144
|
-
assert metadata["Requires-Python"] == "
|
|
144
|
+
assert metadata["Requires-Python"] == _PROJECT["requires-python"]
|
|
145
145
|
assert set(cast(list[str], metadata.get_all("Requires-Dist", []))) == _EXPECTED_REQUIRES_DIST
|
|
146
146
|
assert "Development Status :: 3 - Alpha" in metadata.get_all("Classifier", [])
|
|
147
147
|
assert "Typing :: Typed" in metadata.get_all("Classifier", [])
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from collections.abc import AsyncIterator, Callable
|
|
2
|
+
from typing import override
|
|
2
3
|
|
|
3
4
|
import pytest
|
|
4
5
|
from httpx2 import AsyncByteStream, AsyncClient, MockTransport, ReadError, Request, Response
|
|
5
6
|
from pydantic import BaseModel
|
|
6
|
-
from typing_extensions import override
|
|
7
7
|
|
|
8
8
|
from matelab import (
|
|
9
9
|
ByteRange,
|
|
@@ -6,12 +6,11 @@ import traceback
|
|
|
6
6
|
from collections.abc import AsyncIterator
|
|
7
7
|
from io import BytesIO
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from typing import Literal
|
|
9
|
+
from typing import Literal, override
|
|
10
10
|
|
|
11
11
|
import pytest
|
|
12
12
|
from httpx2 import AsyncByteStream, AsyncClient, MockTransport, Request, Response
|
|
13
13
|
from pydantic import BaseModel, ConfigDict, Field, SecretStr
|
|
14
|
-
from typing_extensions import override
|
|
15
14
|
|
|
16
15
|
from matelab._generated.models import BasicSuccessResponse, RecordListResponse, ResponseEnvelope, TokenRefreshResponse
|
|
17
16
|
from matelab._transport import Encoding, Operation, SessionTransport
|