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.
Files changed (73) hide show
  1. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/.agents/skills/matelab-sdk-release/SKILL.md +7 -7
  2. matelab_python_sdk-0.3.0a1/.python-version +1 -0
  3. matelab_python_sdk-0.3.0a1/.workflow/release.yml +89 -0
  4. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/PKG-INFO +8 -8
  5. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/README.md +4 -4
  6. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/docs/agent-workflows.md +9 -9
  7. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/docs/development.md +9 -2
  8. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/pyproject.toml +8 -8
  9. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/scripts/generate_models.py +1 -1
  10. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/_generated/models.py +1 -1
  11. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/_transport.py +6 -8
  12. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/_wire_validation.py +4 -5
  13. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/records.py +6 -7
  14. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_release.py +2 -2
  15. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_streaming.py +1 -1
  16. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_transport.py +1 -2
  17. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/uv.lock +157 -243
  18. matelab_python_sdk-0.2.0a10/.github/workflows/release.yml +0 -119
  19. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/.agents/skills/matelab-sdk-release/agents/openai.yaml +0 -0
  20. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/.agents/skills/matelab-sdk-release/scripts/verify_pypi.py +0 -0
  21. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/.env.example +0 -0
  22. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/.gitignore +0 -0
  23. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/AGENTS.md +0 -0
  24. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/CONTEXT.md +0 -0
  25. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/LICENSE +0 -0
  26. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/NOTICE +0 -0
  27. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/contracts/matelab-integration-v1.lock.json +0 -0
  28. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/contracts/matelab-integration-v1.openapi.yaml +0 -0
  29. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/docs/agent-architecture.md +0 -0
  30. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/docs/api.md +0 -0
  31. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/docs/operation-coverage.yaml +0 -0
  32. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/scripts/check_installed_package.py +0 -0
  33. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/scripts/check_release.py +0 -0
  34. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/__init__.py +0 -0
  35. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/_generated/__init__.py +0 -0
  36. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/_hashes.py +0 -0
  37. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/client.py +0 -0
  38. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/__init__.py +0 -0
  39. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/_attachment_content.py +0 -0
  40. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/cloud_drive.py +0 -0
  41. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/groups.py +0 -0
  42. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/literature.py +0 -0
  43. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/notebooks.py +0 -0
  44. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/templates.py +0 -0
  45. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/uploads.py +0 -0
  46. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/users.py +0 -0
  47. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/errors.py +0 -0
  48. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/py.typed +0 -0
  49. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/session.py +0 -0
  50. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/src/matelab/streaming.py +0 -0
  51. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/conftest.py +0 -0
  52. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/provider/test_provider_smoke.py +0 -0
  53. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_async_attachment_upload.py +0 -0
  54. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_client.py +0 -0
  55. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_cloud_drive.py +0 -0
  56. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_comments_and_downloads.py +0 -0
  57. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_consumer_smoke.py +0 -0
  58. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_errors.py +0 -0
  59. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_extended_records.py +0 -0
  60. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_generation.py +0 -0
  61. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_groups.py +0 -0
  62. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_notebook_lifecycle.py +0 -0
  63. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_notebooks.py +0 -0
  64. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_operation_coverage.py +0 -0
  65. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_record_lifecycle.py +0 -0
  66. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_record_mutations.py +0 -0
  67. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_records.py +0 -0
  68. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_template_lifecycle.py +0 -0
  69. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_templates.py +0 -0
  70. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_uploads_and_literature.py +0 -0
  71. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/test_users.py +0 -0
  72. {matelab_python_sdk-0.2.0a10 → matelab_python_sdk-0.3.0a1}/tests/typing/consumer.py +0 -0
  73. {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 或 Actions。文档在提交前完成,仅维护受影响的当前态,不新建
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
- 同一 branch/tag 到两个远端,并验证远端 refs。跟踪该 commit/tag 的发布 workflow,逐项确认测试、可复现构建、
71
- 制品审计、安装后消费和 PyPI 上传成功;tag 推送成功不等于发布完成。
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
- 使用可用的 GitHub 工具读取 jobs 和构建日志;能力不足时用 `gh`。从本次 CI 构建审计输出或下载的 CI 制品取得
74
- wheel/sdist 的 SHA-256,不能仅用 PyPI 自报哈希证明 CI/PyPI 一致。在 SDK 锁定开发环境中运行本技能的只读核验器:
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
- 候选前核对现有授权,已发布版本不可覆盖。最后报告版本/链接、单次提交、两个远端 refs、CI/PyPI 核验和工作区状态;
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.2.0a10
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.11
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.1
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.11 or newer.
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://github.com/tiannianzhu/matelab-python-sdk/blob/main/docs/api.md): Session persistence, errors, examples, and Provider limits.
68
- - [Operation coverage](https://github.com/tiannianzhu/matelab-python-sdk/blob/main/docs/operation-coverage.yaml): supported operations.
69
- - [Development and releases](https://github.com/tiannianzhu/matelab-python-sdk/blob/main/docs/development.md): checks, Provider smoke, versioning, and builds.
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.11 or newer.
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://github.com/tiannianzhu/matelab-python-sdk/blob/main/docs/api.md): Session persistence, errors, examples, and Provider limits.
40
- - [Operation coverage](https://github.com/tiannianzhu/matelab-python-sdk/blob/main/docs/operation-coverage.yaml): supported operations.
41
- - [Development and releases](https://github.com/tiannianzhu/matelab-python-sdk/blob/main/docs/development.md): checks, Provider smoke, versioning, and builds.
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 和 dev 依赖仅声明已验证的 `>=` 下限;`nodejs-wheel-binaries` 保留系统所需的精确版本。
35
- 更新声明时同步 lock 和安装后消费检查使用的 CI requirement。
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 版本与固定的 PyPI 发布 Action 是否兼容;需要本地制品证据时使用
95
+ metadata 与流水线固定版本的 uv 是否兼容;需要本地制品证据时使用
95
96
  [手工构建命令](development.md#reproducible-release-build)。创建已审查、已授权的 release commit。
96
- 3. 创建指向 release commit 的 annotated `v{version}` tag。先推送 `origin` 的 main/tag,再推送 GitHub mirror 的
97
- main/tag;只有与项目版本完全匹配的 tag 触发 Trusted Publishing workflow,第三方 Action 固定 immutable
98
- commit SHA。
99
- 4. 跟踪 [发布 CI](../.github/workflows/release.yml) 的测试、可复现构建、制品审计、安装后消费和 PyPI publish。
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
- A `v*` release tag pushed to GitHub triggers CI to build twice, compare wheel/sdist bytes, audit artifacts, verify an
55
- installed-wheel consumer, and publish to PyPI. Preparation follows the
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.2.0a10"
7
+ version = "0.3.0a1"
8
8
  description = "Reusable async Python client for the Matelab Integration Contract"
9
- requires-python = ">=3.11"
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.1",
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.87.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.3",
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.11"
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.11",
454
+ "3.12",
455
455
  "--disable-future-imports",
456
456
  "--use-union-operator",
457
457
  "--use-standard-collections",
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # @generated by datamodel-codegen:
4
4
  # filename: openapi.yaml
5
- # version: 0.87.0
5
+ # version: 0.90.0
6
6
 
7
7
  from enum import StrEnum
8
8
  from typing import Annotated, Any, Literal
@@ -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, Generic, TypeAlias, TypeVar, cast
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: TypeAlias = str | int | float | bool | None
29
- _Files: TypeAlias = Mapping[str, tuple[str, IO[bytes] | bytes, str]]
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(Generic[WireResponse]):
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 TypeAlias, TypeVar, cast
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: TypeAlias = JsonValue | bytes | Mapping[str, "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, TypeAlias, cast
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: TypeAlias = (
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, "RecordImportValue"]
438
- | list["RecordImportValue"]
439
- | tuple["RecordImportValue", ...]
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: TypeAlias = (
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 / ".github" / "workflows" / "release.yml").read_text()
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"] == ">=3.11"
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