matelab-python-sdk 0.1.0a7__tar.gz → 0.1.0a8__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.0a7 → matelab_python_sdk-0.1.0a8}/AGENTS.md +2 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/CHANGELOG.md +5 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/PKG-INFO +23 -19
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/README.md +22 -18
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/pyproject.toml +1 -1
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/scripts/check_installed_package.py +3 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/__init__.py +2 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/errors.py +22 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_client.py +14 -7
- matelab_python_sdk-0.1.0a8/tests/test_errors.py +65 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/typing/consumer.py +8 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/uv.lock +1 -1
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/.env.example +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/.github/workflows/release.yml +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/.gitignore +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/LICENSE +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/NOTICE +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/contracts/matelab-integration-v1.lock.json +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/contracts/matelab-integration-v1.openapi.yaml +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/docs/operation-coverage.yaml +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/scripts/check_release.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/scripts/generate_models.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/_generated/__init__.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/_generated/models.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/_transport.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/_wire_validation.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/client.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/domains/__init__.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/domains/cloud_drive.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/domains/groups.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/domains/literature.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/domains/notebooks.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/domains/records.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/domains/templates.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/domains/uploads.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/domains/users.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/py.typed +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/session.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/streaming.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/conftest.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/provider/test_provider_smoke.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_cloud_drive.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_comments_and_downloads.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_consumer_smoke.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_extended_records.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_generation.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_groups.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_notebook_lifecycle.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_notebooks.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_operation_coverage.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_record_lifecycle.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_record_mutations.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_records.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_release.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_streaming.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_template_lifecycle.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_templates.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_transport.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_uploads_and_literature.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_users.py +0 -0
- {matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/typing/pyproject.toml +0 -0
|
@@ -62,6 +62,8 @@
|
|
|
62
62
|
- mutation 默认不自动重试。multipart 或 stream 只有在确认可重放时才能重试;download stream 不自动重放,
|
|
63
63
|
必须支持显式关闭。
|
|
64
64
|
- HTTP/transport、Provider business/authentication、Wire Contract/schema 和 client usage 错误必须稳定区分。
|
|
65
|
+
- 每个公开 `MatelabError` 必须直接提供稳定的集成级 `category` 和保守的 `retryable`;Provider kind 和 HTTP
|
|
66
|
+
status 只用于诊断,下游不得重新解释 Provider 数字 code 才能决定恢复类别。
|
|
65
67
|
- password、token、SSO code/key、临时附件凭证、原始文件内容和敏感 payload 不得进入日志、异常或提交内容。
|
|
66
68
|
|
|
67
69
|
## 测试与质量门槛
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.0a8 - 2026-08-04
|
|
4
|
+
|
|
5
|
+
- Adds a unified `MatelabErrorCategory` and conservative `retryable` flag to every public SDK error, centralizing
|
|
6
|
+
Provider error semantics so integrations no longer reinterpret `MatelabProviderErrorKind`.
|
|
7
|
+
|
|
3
8
|
## 0.1.0a7 - 2026-08-04
|
|
4
9
|
|
|
5
10
|
- Replaces the hand-maintained wire-validation rule tables with deterministic generated JSON Schema 2020-12 rules.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: matelab-python-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.0a8
|
|
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
|
|
@@ -29,7 +29,7 @@ Description-Content-Type: text/markdown
|
|
|
29
29
|
|
|
30
30
|
Reusable async Python client for the Matelab Integration Contract.
|
|
31
31
|
|
|
32
|
-
The current alpha is `0.1.
|
|
32
|
+
The current alpha is `0.1.0a8`. `[project].version` in `pyproject.toml` is the sole SDK version source;
|
|
33
33
|
`uv.lock` only mirrors that source.
|
|
34
34
|
|
|
35
35
|
The SDK is pinned to the immutable `matelab-spec v0.3.0` Contract Release. The sole release pin is
|
|
@@ -58,7 +58,7 @@ To test the same artifact a downstream Consumer will install, build and install
|
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
60
|
uv build --no-build-isolation --out-dir dist/release
|
|
61
|
-
python -m pip install dist/release/matelab_python_sdk-0.1.
|
|
61
|
+
python -m pip install dist/release/matelab_python_sdk-0.1.0a8-py3-none-any.whl
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
Do not infer Provider compatibility from the SDK version alone. A release is also bound to the Contract
|
|
@@ -85,25 +85,29 @@ explicitly when needed, for example `AsyncMatelab("https://custom.example/api")`
|
|
|
85
85
|
|
|
86
86
|
### Error handling
|
|
87
87
|
|
|
88
|
-
Catch `MatelabError`
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
|
93
|
-
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
|
|
98
|
-
`
|
|
99
|
-
|
|
100
|
-
`
|
|
101
|
-
|
|
88
|
+
Catch `MatelabError` once at an integration seam. Every instance exposes a stable `category: MatelabErrorCategory` and
|
|
89
|
+
`retryable: bool`, so an integration can copy the message, category, and retry flag without inspecting subclasses or
|
|
90
|
+
Provider details:
|
|
91
|
+
|
|
92
|
+
| Error | Category | Retryable | Meaning and normal response |
|
|
93
|
+
|---|---|---:|---|
|
|
94
|
+
| `MatelabAuthenticationError` | `AUTHENTICATION` | No | The Session or credentials cannot authenticate; obtain valid authentication before making a new call. |
|
|
95
|
+
| `MatelabUsageError` | `VALIDATION` | No | The call cannot be represented safely; correct its arguments. |
|
|
96
|
+
| `MatelabProviderError` | `BUSINESS`, `VALIDATION`, or `UPSTREAM` | No | The Provider rejected the request; route by `category`, with `kind` available only for diagnosis. |
|
|
97
|
+
| `MatelabTransportError` | `UPSTREAM` | No | The HTTP exchange failed; `status_code` is present for HTTP failures, and a mutation outcome may be unknown. |
|
|
98
|
+
| `MatelabProtocolError` | `UPSTREAM` | No | The response violates the pinned Contract; treat it as Provider drift or an SDK defect. |
|
|
99
|
+
|
|
100
|
+
For `MatelabProviderError`, the SDK maps `REJECTED` to `BUSINESS`, `VALIDATION` to `VALIDATION`, and `SERVER` or
|
|
101
|
+
`UNKNOWN` to `UPSTREAM`. `MatelabProviderError.kind` remains a `MatelabProviderErrorKind` diagnostic, but integrations
|
|
102
|
+
do not need it for recovery routing. Authentication codes are hidden behind `MatelabAuthenticationError`; raw Provider
|
|
103
|
+
codes are not part of the public error interface.
|
|
102
104
|
Codes `0` and `10` remain operation-specific successes selected by the pinned Contract; an operation that receives a
|
|
103
105
|
success code it does not allow raises `MatelabProtocolError` rather than assigning an error kind.
|
|
104
106
|
`MatelabTransportError.status_code` remains available for HTTP failures. Provider response bodies and caller inputs are
|
|
105
|
-
never attached to exceptions.
|
|
106
|
-
error
|
|
107
|
+
never attached to exceptions. `retryable` means the identical SDK call is safe to replay without further
|
|
108
|
+
interpretation. It is conservatively `False` for every current SDK error because the Contract does not guarantee
|
|
109
|
+
mutation idempotency or outcome; an integration may add a narrower operation-specific retry policy only when it owns
|
|
110
|
+
that evidence.
|
|
107
111
|
|
|
108
112
|
### Session ownership
|
|
109
113
|
|
|
@@ -2,7 +2,7 @@
|
|
|
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.0a8`. `[project].version` in `pyproject.toml` is the sole SDK version source;
|
|
6
6
|
`uv.lock` only mirrors that source.
|
|
7
7
|
|
|
8
8
|
The SDK is pinned to the immutable `matelab-spec v0.3.0` Contract Release. The sole release pin is
|
|
@@ -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.0a8-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
|
|
@@ -58,25 +58,29 @@ explicitly when needed, for example `AsyncMatelab("https://custom.example/api")`
|
|
|
58
58
|
|
|
59
59
|
### Error handling
|
|
60
60
|
|
|
61
|
-
Catch `MatelabError`
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
|
66
|
-
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
|
|
71
|
-
`
|
|
72
|
-
|
|
73
|
-
`
|
|
74
|
-
|
|
61
|
+
Catch `MatelabError` once at an integration seam. Every instance exposes a stable `category: MatelabErrorCategory` and
|
|
62
|
+
`retryable: bool`, so an integration can copy the message, category, and retry flag without inspecting subclasses or
|
|
63
|
+
Provider details:
|
|
64
|
+
|
|
65
|
+
| Error | Category | Retryable | Meaning and normal response |
|
|
66
|
+
|---|---|---:|---|
|
|
67
|
+
| `MatelabAuthenticationError` | `AUTHENTICATION` | No | The Session or credentials cannot authenticate; obtain valid authentication before making a new call. |
|
|
68
|
+
| `MatelabUsageError` | `VALIDATION` | No | The call cannot be represented safely; correct its arguments. |
|
|
69
|
+
| `MatelabProviderError` | `BUSINESS`, `VALIDATION`, or `UPSTREAM` | No | The Provider rejected the request; route by `category`, with `kind` available only for diagnosis. |
|
|
70
|
+
| `MatelabTransportError` | `UPSTREAM` | No | The HTTP exchange failed; `status_code` is present for HTTP failures, and a mutation outcome may be unknown. |
|
|
71
|
+
| `MatelabProtocolError` | `UPSTREAM` | No | The response violates the pinned Contract; treat it as Provider drift or an SDK defect. |
|
|
72
|
+
|
|
73
|
+
For `MatelabProviderError`, the SDK maps `REJECTED` to `BUSINESS`, `VALIDATION` to `VALIDATION`, and `SERVER` or
|
|
74
|
+
`UNKNOWN` to `UPSTREAM`. `MatelabProviderError.kind` remains a `MatelabProviderErrorKind` diagnostic, but integrations
|
|
75
|
+
do not need it for recovery routing. Authentication codes are hidden behind `MatelabAuthenticationError`; raw Provider
|
|
76
|
+
codes are not part of the public error interface.
|
|
75
77
|
Codes `0` and `10` remain operation-specific successes selected by the pinned Contract; an operation that receives a
|
|
76
78
|
success code it does not allow raises `MatelabProtocolError` rather than assigning an error kind.
|
|
77
79
|
`MatelabTransportError.status_code` remains available for HTTP failures. Provider response bodies and caller inputs are
|
|
78
|
-
never attached to exceptions.
|
|
79
|
-
error
|
|
80
|
+
never attached to exceptions. `retryable` means the identical SDK call is safe to replay without further
|
|
81
|
+
interpretation. It is conservatively `False` for every current SDK error because the Contract does not guarantee
|
|
82
|
+
mutation idempotency or outcome; an integration may add a narrower operation-specific retry policy only when it owns
|
|
83
|
+
that evidence.
|
|
80
84
|
|
|
81
85
|
### Session ownership
|
|
82
86
|
|
{matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/scripts/check_installed_package.py
RENAMED
|
@@ -17,6 +17,9 @@ def main() -> None:
|
|
|
17
17
|
raise RuntimeError(f"Public symbol {name} leaks a generated wire type.")
|
|
18
18
|
if not files(matelab).joinpath("py.typed").is_file():
|
|
19
19
|
raise RuntimeError("The installed wheel does not contain matelab/py.typed.")
|
|
20
|
+
error = matelab.MatelabUsageError("invalid input")
|
|
21
|
+
if error.category is not matelab.MatelabErrorCategory.VALIDATION or error.retryable:
|
|
22
|
+
raise RuntimeError("The installed package does not expose stable integration error semantics.")
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
if __name__ == "__main__":
|
|
@@ -115,6 +115,7 @@ from matelab.domains.users import UserRef, UserSummary
|
|
|
115
115
|
from matelab.errors import (
|
|
116
116
|
MatelabAuthenticationError,
|
|
117
117
|
MatelabError,
|
|
118
|
+
MatelabErrorCategory,
|
|
118
119
|
MatelabProtocolError,
|
|
119
120
|
MatelabProviderError,
|
|
120
121
|
MatelabProviderErrorKind,
|
|
@@ -165,6 +166,7 @@ __all__ = [
|
|
|
165
166
|
"LiteratureStoredMetadata",
|
|
166
167
|
"MatelabAuthenticationError",
|
|
167
168
|
"MatelabError",
|
|
169
|
+
"MatelabErrorCategory",
|
|
168
170
|
"MatelabProtocolError",
|
|
169
171
|
"MatelabProviderError",
|
|
170
172
|
"MatelabProviderErrorKind",
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
from enum import StrEnum
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
class MatelabErrorCategory(StrEnum):
|
|
5
|
+
"""Stable integration-level recovery category for an SDK error."""
|
|
6
|
+
|
|
7
|
+
AUTHENTICATION = "authentication"
|
|
8
|
+
BUSINESS = "business"
|
|
9
|
+
VALIDATION = "validation"
|
|
10
|
+
UPSTREAM = "upstream"
|
|
11
|
+
|
|
12
|
+
|
|
4
13
|
class MatelabError(Exception):
|
|
5
14
|
"""Base class for stable Matelab SDK errors."""
|
|
6
15
|
|
|
16
|
+
category: MatelabErrorCategory = MatelabErrorCategory.UPSTREAM
|
|
17
|
+
retryable: bool = False
|
|
18
|
+
|
|
7
19
|
|
|
8
20
|
class MatelabProviderErrorKind(StrEnum):
|
|
9
21
|
"""Stable interpretation of a Provider error envelope."""
|
|
@@ -20,15 +32,25 @@ class MatelabProviderError(MatelabError):
|
|
|
20
32
|
def __init__(self, message: str, *, kind: MatelabProviderErrorKind) -> None:
|
|
21
33
|
super().__init__(message)
|
|
22
34
|
self.kind = kind
|
|
35
|
+
self.category = {
|
|
36
|
+
MatelabProviderErrorKind.REJECTED: MatelabErrorCategory.BUSINESS,
|
|
37
|
+
MatelabProviderErrorKind.SERVER: MatelabErrorCategory.UPSTREAM,
|
|
38
|
+
MatelabProviderErrorKind.VALIDATION: MatelabErrorCategory.VALIDATION,
|
|
39
|
+
MatelabProviderErrorKind.UNKNOWN: MatelabErrorCategory.UPSTREAM,
|
|
40
|
+
}[kind]
|
|
23
41
|
|
|
24
42
|
|
|
25
43
|
class MatelabAuthenticationError(MatelabError):
|
|
26
44
|
"""The Matelab session cannot authenticate Provider requests."""
|
|
27
45
|
|
|
46
|
+
category = MatelabErrorCategory.AUTHENTICATION
|
|
47
|
+
|
|
28
48
|
|
|
29
49
|
class MatelabUsageError(MatelabError):
|
|
30
50
|
"""The requested SDK operation cannot be represented safely."""
|
|
31
51
|
|
|
52
|
+
category = MatelabErrorCategory.VALIDATION
|
|
53
|
+
|
|
32
54
|
|
|
33
55
|
class MatelabTransportError(MatelabError):
|
|
34
56
|
"""The HTTP exchange with the Provider failed."""
|
|
@@ -10,6 +10,7 @@ from matelab import (
|
|
|
10
10
|
Identity,
|
|
11
11
|
MatelabAuthenticationError,
|
|
12
12
|
MatelabError,
|
|
13
|
+
MatelabErrorCategory,
|
|
13
14
|
MatelabProtocolError,
|
|
14
15
|
MatelabProviderError,
|
|
15
16
|
MatelabProviderErrorKind,
|
|
@@ -365,17 +366,19 @@ def test_session_models_reject_invalid_values_and_extra_fields() -> None:
|
|
|
365
366
|
|
|
366
367
|
|
|
367
368
|
@pytest.mark.parametrize(
|
|
368
|
-
("code", "kind"),
|
|
369
|
+
("code", "kind", "category"),
|
|
369
370
|
[
|
|
370
|
-
(2, MatelabProviderErrorKind.REJECTED),
|
|
371
|
-
(3, MatelabProviderErrorKind.SERVER),
|
|
372
|
-
(4, MatelabProviderErrorKind.VALIDATION),
|
|
373
|
-
(11, MatelabProviderErrorKind.UNKNOWN),
|
|
374
|
-
(99, MatelabProviderErrorKind.UNKNOWN),
|
|
371
|
+
(2, MatelabProviderErrorKind.REJECTED, MatelabErrorCategory.BUSINESS),
|
|
372
|
+
(3, MatelabProviderErrorKind.SERVER, MatelabErrorCategory.UPSTREAM),
|
|
373
|
+
(4, MatelabProviderErrorKind.VALIDATION, MatelabErrorCategory.VALIDATION),
|
|
374
|
+
(11, MatelabProviderErrorKind.UNKNOWN, MatelabErrorCategory.UPSTREAM),
|
|
375
|
+
(99, MatelabProviderErrorKind.UNKNOWN, MatelabErrorCategory.UPSTREAM),
|
|
375
376
|
],
|
|
376
377
|
)
|
|
377
378
|
@pytest.mark.asyncio
|
|
378
|
-
async def
|
|
379
|
+
async def test_provider_error_codes_are_hidden_behind_stable_semantics(
|
|
380
|
+
code: int, kind: MatelabProviderErrorKind, category: MatelabErrorCategory
|
|
381
|
+
) -> None:
|
|
379
382
|
async def handler(_: Request) -> Response:
|
|
380
383
|
return Response(200, json={"code": code, "msg": "rejected", "errcode": code, "errmsg": "rejected"})
|
|
381
384
|
|
|
@@ -386,6 +389,8 @@ async def test_provider_error_codes_are_hidden_behind_stable_kinds(code: int, ki
|
|
|
386
389
|
|
|
387
390
|
assert isinstance(caught.value, MatelabError)
|
|
388
391
|
assert caught.value.kind is kind
|
|
392
|
+
assert caught.value.category is category
|
|
393
|
+
assert caught.value.retryable is False
|
|
389
394
|
assert not hasattr(caught.value, "code")
|
|
390
395
|
|
|
391
396
|
|
|
@@ -400,6 +405,8 @@ async def test_provider_authentication_codes_are_hidden_behind_authentication_er
|
|
|
400
405
|
with pytest.raises(MatelabAuthenticationError) as caught:
|
|
401
406
|
_ = await client.authenticate("alice@example.org", "bad-password")
|
|
402
407
|
|
|
408
|
+
assert caught.value.category is MatelabErrorCategory.AUTHENTICATION
|
|
409
|
+
assert caught.value.retryable is False
|
|
403
410
|
assert not hasattr(caught.value, "code")
|
|
404
411
|
|
|
405
412
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from matelab import (
|
|
4
|
+
MatelabAuthenticationError,
|
|
5
|
+
MatelabError,
|
|
6
|
+
MatelabErrorCategory,
|
|
7
|
+
MatelabProtocolError,
|
|
8
|
+
MatelabProviderError,
|
|
9
|
+
MatelabProviderErrorKind,
|
|
10
|
+
MatelabTransportError,
|
|
11
|
+
MatelabUsageError,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_error_category_values_are_stable_for_integrations() -> None:
|
|
16
|
+
assert {category.name: category.value for category in MatelabErrorCategory} == {
|
|
17
|
+
"AUTHENTICATION": "authentication",
|
|
18
|
+
"BUSINESS": "business",
|
|
19
|
+
"VALIDATION": "validation",
|
|
20
|
+
"UPSTREAM": "upstream",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.mark.parametrize(
|
|
25
|
+
("error", "category"),
|
|
26
|
+
[
|
|
27
|
+
pytest.param(MatelabError("failure"), MatelabErrorCategory.UPSTREAM, id="base"),
|
|
28
|
+
pytest.param(MatelabAuthenticationError("failure"), MatelabErrorCategory.AUTHENTICATION, id="authentication"),
|
|
29
|
+
pytest.param(MatelabUsageError("failure"), MatelabErrorCategory.VALIDATION, id="usage"),
|
|
30
|
+
pytest.param(MatelabTransportError("failure"), MatelabErrorCategory.UPSTREAM, id="transport"),
|
|
31
|
+
pytest.param(MatelabProtocolError("failure"), MatelabErrorCategory.UPSTREAM, id="protocol"),
|
|
32
|
+
],
|
|
33
|
+
)
|
|
34
|
+
def test_public_errors_expose_integration_category_and_conservative_retryability(
|
|
35
|
+
error: MatelabError, category: MatelabErrorCategory
|
|
36
|
+
) -> None:
|
|
37
|
+
assert error.category is category
|
|
38
|
+
assert error.retryable is False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@pytest.mark.parametrize(
|
|
42
|
+
("kind", "category"),
|
|
43
|
+
[
|
|
44
|
+
(MatelabProviderErrorKind.REJECTED, MatelabErrorCategory.BUSINESS),
|
|
45
|
+
(MatelabProviderErrorKind.SERVER, MatelabErrorCategory.UPSTREAM),
|
|
46
|
+
(MatelabProviderErrorKind.VALIDATION, MatelabErrorCategory.VALIDATION),
|
|
47
|
+
(MatelabProviderErrorKind.UNKNOWN, MatelabErrorCategory.UPSTREAM),
|
|
48
|
+
],
|
|
49
|
+
)
|
|
50
|
+
def test_provider_error_kind_is_diagnostic_and_category_is_integration_semantics(
|
|
51
|
+
kind: MatelabProviderErrorKind, category: MatelabErrorCategory
|
|
52
|
+
) -> None:
|
|
53
|
+
error = MatelabProviderError("failure", kind=kind)
|
|
54
|
+
|
|
55
|
+
assert error.kind is kind
|
|
56
|
+
assert error.category is category
|
|
57
|
+
assert error.retryable is False
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_transport_error_preserves_http_status_without_changing_integration_semantics() -> None:
|
|
61
|
+
error = MatelabTransportError("failure", status_code=503)
|
|
62
|
+
|
|
63
|
+
assert error.status_code == 503
|
|
64
|
+
assert error.category is MatelabErrorCategory.UPSTREAM
|
|
65
|
+
assert error.retryable is False
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from matelab import (
|
|
2
2
|
AsyncMatelab,
|
|
3
|
+
MatelabError,
|
|
4
|
+
MatelabErrorCategory,
|
|
3
5
|
NotebookCollection,
|
|
4
6
|
NotebookRef,
|
|
5
7
|
RecordAttachmentChange,
|
|
@@ -21,6 +23,12 @@ from matelab import (
|
|
|
21
23
|
)
|
|
22
24
|
|
|
23
25
|
|
|
26
|
+
def consume_error(error: MatelabError) -> tuple[MatelabErrorCategory, bool]:
|
|
27
|
+
category: MatelabErrorCategory = error.category
|
|
28
|
+
retryable: bool = error.retryable
|
|
29
|
+
return category, retryable
|
|
30
|
+
|
|
31
|
+
|
|
24
32
|
async def consume_public_types(client: AsyncMatelab) -> tuple[Session | None, NotebookCollection]:
|
|
25
33
|
notebooks: NotebookCollection = await client.notebooks.list()
|
|
26
34
|
session: Session | None = client.session
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/contracts/matelab-integration-v1.lock.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/_generated/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/src/matelab/domains/cloud_drive.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/provider/test_provider_smoke.py
RENAMED
|
File without changes
|
|
File without changes
|
{matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_comments_and_downloads.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{matelab_python_sdk-0.1.0a7 → matelab_python_sdk-0.1.0a8}/tests/test_uploads_and_literature.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|