notex-python 0.1.0__tar.gz → 0.2.0__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.
- {notex_python-0.1.0/src/notex_python.egg-info → notex_python-0.2.0}/PKG-INFO +371 -323
- {notex_python-0.1.0 → notex_python-0.2.0}/README.md +56 -10
- {notex_python-0.1.0 → notex_python-0.2.0}/pyproject.toml +3 -1
- {notex_python-0.1.0 → notex_python-0.2.0}/setup.cfg +4 -4
- {notex_python-0.1.0 → notex_python-0.2.0}/src/notex/__init__.py +7 -1
- {notex_python-0.1.0 → notex_python-0.2.0/src/notex_python.egg-info}/PKG-INFO +371 -323
- {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_python.egg-info/SOURCES.txt +2 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_sdk/__init__.py +5 -1
- notex_python-0.2.0/src/notex_sdk/facade.py +210 -0
- notex_python-0.2.0/tests/test_smoke_api.py +37 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/tests/test_staging_integration.py +15 -1
- {notex_python-0.1.0 → notex_python-0.2.0}/LICENSE +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/src/notex/py.typed +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_python.egg-info/dependency_links.txt +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_python.egg-info/requires.txt +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_python.egg-info/top_level.txt +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_sdk/async_client.py +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_sdk/client.py +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_sdk/config.py +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_sdk/errors.py +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_sdk/models.py +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_sdk/py.typed +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_sdk/transport.py +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/tests/test_client.py +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/tests/test_content_api.py +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/tests/test_external_consumer.py +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/tests/test_models.py +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/tests/test_public_api.py +0 -0
- {notex_python-0.1.0 → notex_python-0.2.0}/tests/test_reliability.py +0 -0
|
@@ -1,323 +1,371 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: notex-python
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Typed, dependency-free Python SDK for the NoteX API
|
|
5
|
-
Author: NoteX
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Project-URL: Homepage, https://notexapp.com
|
|
8
|
-
Project-URL: Documentation, https://be-docs.notexapp.com
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
Classifier:
|
|
13
|
-
Classifier:
|
|
14
|
-
Classifier:
|
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.
|
|
20
|
-
Classifier:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
Provides-Extra:
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Requires-Dist:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
)
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: notex-python
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Typed, dependency-free Python SDK for the NoteX API
|
|
5
|
+
Author: NoteX
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://notexapp.com
|
|
8
|
+
Project-URL: Documentation, https://be-docs.notexapp.com
|
|
9
|
+
Project-URL: Repository, https://github.com/sota-labs/notex-python-sdk
|
|
10
|
+
Project-URL: Issues, https://github.com/sota-labs/notex-python-sdk/issues
|
|
11
|
+
Keywords: notex,ai,notes,sdk,api-client
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Provides-Extra: test
|
|
27
|
+
Requires-Dist: pytest>=7; extra == "test"
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
31
|
+
Requires-Dist: pyright>=1.1.400; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.12; extra == "dev"
|
|
33
|
+
Requires-Dist: twine>=6; extra == "dev"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# NoteX Python SDK
|
|
37
|
+
|
|
38
|
+
[](https://pypi.org/project/notex-python/)
|
|
39
|
+
[](https://pypi.org/project/notex-python/)
|
|
40
|
+
|
|
41
|
+
Typed, dependency-free Python SDK for API-key-enabled NoteX endpoints.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install notex-python
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from notex import Notex
|
|
49
|
+
|
|
50
|
+
client = Notex(api_key="ntx_live_xxx")
|
|
51
|
+
task = client.notes.create(text="Summarize this content.")
|
|
52
|
+
print(task.id)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Requires Python 3.9 or newer.
|
|
56
|
+
|
|
57
|
+
`text` is uploaded internally as a temporary UTF-8 text file because the
|
|
58
|
+
NoteX create endpoint consumes URL or file sources. The temporary local file
|
|
59
|
+
is removed immediately after NoteX accepts the asynchronous task.
|
|
60
|
+
|
|
61
|
+
Use `task.result()` for one status check or `task.wait()` from a worker:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
result = task.wait(timeout=120, poll_interval=5)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`NotexClient` remains the typed low-level client for explicit endpoint access.
|
|
68
|
+
|
|
69
|
+
## Naming convention
|
|
70
|
+
|
|
71
|
+
The public SDK has one flat, explicit naming convention:
|
|
72
|
+
|
|
73
|
+
| Operation | Convention | Examples |
|
|
74
|
+
| --- | --- | --- |
|
|
75
|
+
| Read one value | `get_<resource>()` | `get_profile()`, `get_task_result()` |
|
|
76
|
+
| Read a collection | `list_<resources>()` | `list_notes()` |
|
|
77
|
+
| Validate input | `validate_<resource>()` | `validate_source()` |
|
|
78
|
+
| Create content | `create_<resource>()` | `create_note()`, `create_quiz()` |
|
|
79
|
+
| Wait for an async operation | `wait_for_<resource>()` | `wait_for_task()` |
|
|
80
|
+
|
|
81
|
+
Sync and async clients expose the same method names. The async variants are
|
|
82
|
+
awaited.
|
|
83
|
+
|
|
84
|
+
## Multi-user integration
|
|
85
|
+
|
|
86
|
+
Each `Notex` or `NotexClient` instance represents one user API key for one
|
|
87
|
+
request or background job. Do not mutate a singleton client's key between
|
|
88
|
+
users.
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from notex import NotexClient
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def notex_for_connection(connection_id: str) -> NotexClient:
|
|
95
|
+
api_key = credential_store.decrypt(connection_id)
|
|
96
|
+
return NotexClient(api_key=api_key)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The SDK does not create, list, delete, persist, or automatically load API
|
|
100
|
+
keys. The integrating backend owns credential storage and rotation.
|
|
101
|
+
|
|
102
|
+
## Supported methods
|
|
103
|
+
|
|
104
|
+
### Account and notes
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
credits = client.get_credits()
|
|
108
|
+
quota = client.get_quota()
|
|
109
|
+
profile = client.get_profile()
|
|
110
|
+
notes = client.list_notes(limit=20, sort_field="createdAt", sort_order=-1)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Credit fields vary by account/plan. `Credits` exposes optional
|
|
114
|
+
`total_credits`, `reward_credits`, `purchased_credits`, `user_type`, and
|
|
115
|
+
`has_paid`, while `raw` preserves the complete backend payload. `balance` is
|
|
116
|
+
an optional compatibility alias for responses that provide `balance` or
|
|
117
|
+
`total_credits`.
|
|
118
|
+
|
|
119
|
+
### Validate and create notes
|
|
120
|
+
|
|
121
|
+
Create a note from a web URL:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
submission = client.create_note(
|
|
125
|
+
web_url="https://youtu.be/example",
|
|
126
|
+
language_hints=["en"],
|
|
127
|
+
)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Convenience equivalent:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
submission = client.create_note_from_url(
|
|
134
|
+
"https://youtu.be/example",
|
|
135
|
+
language_hints=["en"],
|
|
136
|
+
)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Create a note from an existing NoteX file URL:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
submission = client.create_note_from_file_url(
|
|
143
|
+
"audio/user-1/lecture.mp3",
|
|
144
|
+
language_hints=["en"],
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Create a note from a local file:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
submission = client.create_note_from_file(
|
|
152
|
+
"./lecture.mp3",
|
|
153
|
+
upload_file_name="lecture.mp3",
|
|
154
|
+
language_hints=["en"],
|
|
155
|
+
content_type="audio/mpeg",
|
|
156
|
+
)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The local-file method performs the complete internal flow:
|
|
160
|
+
|
|
161
|
+
1. Request a presigned upload contract.
|
|
162
|
+
2. Upload the file directly without the NoteX API key.
|
|
163
|
+
3. Submit note creation using the returned `file_url`.
|
|
164
|
+
|
|
165
|
+
Presign and direct-upload helpers are intentionally private. Use
|
|
166
|
+
`upload_file_name` when the local filename contains characters rejected by the
|
|
167
|
+
target gateway or object storage.
|
|
168
|
+
|
|
169
|
+
Validate a source before spending credits:
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
validation = client.validate_source(web_url="https://youtu.be/example")
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Exactly one of `web_url` or `file_url` is accepted by `create_note()` and
|
|
176
|
+
`validate_source()`.
|
|
177
|
+
|
|
178
|
+
### Create content from a note
|
|
179
|
+
|
|
180
|
+
All create methods return `TaskSubmission` with a `task_id`.
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
flashcards = client.create_flashcards(
|
|
184
|
+
"note-id",
|
|
185
|
+
num_cards=10,
|
|
186
|
+
difficulty="medium",
|
|
187
|
+
)
|
|
188
|
+
quiz = client.create_quiz("note-id", num_questions=10)
|
|
189
|
+
mindmap = client.create_mindmap("note-id")
|
|
190
|
+
podcast = client.create_podcast("note-id", duration=180)
|
|
191
|
+
shorts = client.create_shorts("note-id", voice_id="en-US-Standard-A")
|
|
192
|
+
quiz_video = client.create_quiz_video("note-id", voice_id="en-US-Standard-A")
|
|
193
|
+
slides = client.create_slide("note-id", template_id="default", language="en")
|
|
194
|
+
translation = client.create_translation("note-id", language="vi")
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Poll task results
|
|
198
|
+
|
|
199
|
+
Use `get_task_result()` when the integrating backend manages scheduling:
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
result = client.get_task_result(submission.task_id)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Use `wait_for_task()` in a worker when a blocking helper is appropriate:
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
result = client.wait_for_task(
|
|
209
|
+
submission.task_id,
|
|
210
|
+
poll_interval=5,
|
|
211
|
+
timeout=120,
|
|
212
|
+
)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Do not run file uploads or blocking task polling directly in a web request.
|
|
216
|
+
Use a durable queue or background worker.
|
|
217
|
+
|
|
218
|
+
Completed results can be parsed into feature-specific typed models:
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
from notex import FlashcardSet
|
|
222
|
+
|
|
223
|
+
result = client.wait_for_task(flashcards.task_id)
|
|
224
|
+
flashcard_set = FlashcardSet.from_task_result(result)
|
|
225
|
+
print(flashcard_set.cards[0].front)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Available result models include `GeneratedNote`, `FlashcardSet`, `QuizSet`,
|
|
229
|
+
`Mindmap`, `Podcast`, `Video`, and `SlideDeck`.
|
|
230
|
+
|
|
231
|
+
## Async client
|
|
232
|
+
|
|
233
|
+
`AsyncNotexClient` exposes the same names and runs blocking standard-library
|
|
234
|
+
I/O in worker threads so the event loop remains responsive.
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
from notex import AsyncNotexClient
|
|
238
|
+
|
|
239
|
+
client = AsyncNotexClient(api_key="ntx_live_xxx")
|
|
240
|
+
|
|
241
|
+
submission = await client.create_quiz("note-id", num_questions=10)
|
|
242
|
+
result = await client.wait_for_task(submission.task_id, timeout=120)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Errors and retry behavior
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
from notex import NotexAuthenticationError, NotexRateLimitError
|
|
249
|
+
|
|
250
|
+
try:
|
|
251
|
+
client.get_credits()
|
|
252
|
+
except NotexAuthenticationError:
|
|
253
|
+
reconnect_notex_account()
|
|
254
|
+
except NotexRateLimitError as error:
|
|
255
|
+
reschedule_job(error.retry_after)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Public errors:
|
|
259
|
+
|
|
260
|
+
- `NotexAPIError`
|
|
261
|
+
- `NotexAuthenticationError`
|
|
262
|
+
- `NotexPermissionError`
|
|
263
|
+
- `NotexRateLimitError`
|
|
264
|
+
- `NotexUploadError`
|
|
265
|
+
- `NotexTaskError`
|
|
266
|
+
|
|
267
|
+
GET and HEAD requests retry transient network failures and HTTP 429/5xx
|
|
268
|
+
responses, honoring `Retry-After`. Content-creating POST requests are not
|
|
269
|
+
retried by default because repeating them can create duplicate work.
|
|
270
|
+
|
|
271
|
+
## API compatibility
|
|
272
|
+
|
|
273
|
+
Every high-level method accepts `endpoint=` and `base_url=` overrides. This
|
|
274
|
+
allows an integration to adopt a new backend version before the SDK releases
|
|
275
|
+
an update.
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
client.get_credits(endpoint="/v3/credits/me")
|
|
279
|
+
client.create_flashcards("note-id", endpoint="/v7/create/flashcards")
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
For a documented endpoint that is not wrapped yet, use the low-level escape
|
|
283
|
+
hatch:
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
response = client.request(
|
|
287
|
+
"POST",
|
|
288
|
+
"/v10/feature",
|
|
289
|
+
form={"note_id": "note-id"},
|
|
290
|
+
headers={"Idempotency-Key": "internal-job-id"},
|
|
291
|
+
)
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Real API integration tests
|
|
295
|
+
|
|
296
|
+
Copy the safe template and put the real key/base URL in the local file:
|
|
297
|
+
|
|
298
|
+
```powershell
|
|
299
|
+
Copy-Item .notex-test.env.example .notex-test.env
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Edit `.notex-test.env`:
|
|
303
|
+
|
|
304
|
+
```dotenv
|
|
305
|
+
NOTEX_TEST_API_KEY=ntx_test_your_real_key
|
|
306
|
+
NOTEX_TEST_BASE_URL=https://api.notexapp.com
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
The file is ignored by Git. Read-only tests cover profile, credits, quota, and
|
|
310
|
+
note listing:
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
python -m pytest tests/test_staging_integration.py -v
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Creation tests can upload files and spend credits, so they require explicit
|
|
317
|
+
opt-in:
|
|
318
|
+
|
|
319
|
+
```dotenv
|
|
320
|
+
NOTEX_RUN_WRITE_TESTS=1
|
|
321
|
+
NOTEX_TEST_WEB_URL=https://youtu.be/example
|
|
322
|
+
NOTEX_TEST_FILE=C:\path\to\lecture.mp3
|
|
323
|
+
NOTEX_TEST_NOTE_ID=existing-note-id
|
|
324
|
+
NOTEX_TEST_FEATURES=flashcards,quiz,mindmap,translation
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Optional feature configuration is documented in
|
|
328
|
+
`.notex-test.env.example`. Test output is not persisted by the suite; local
|
|
329
|
+
credentials and `.notex-test-results/` are both excluded from Git.
|
|
330
|
+
|
|
331
|
+
The exact README smoke flow is also covered by an opt-in real integration
|
|
332
|
+
test:
|
|
333
|
+
|
|
334
|
+
```powershell
|
|
335
|
+
python -m pytest tests/test_staging_integration.py::test_real_readme_smoke_create_note_from_text -v
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Development and release checks
|
|
339
|
+
|
|
340
|
+
Install development tools once:
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
python -m pip install -e ".[dev]"
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Run the complete local release gate on Windows:
|
|
347
|
+
|
|
348
|
+
```powershell
|
|
349
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\check-local.ps1
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
This runs linting, type checking, non-network unit tests, package build,
|
|
353
|
+
`twine check`, clean-wheel installation, and a public import/version smoke
|
|
354
|
+
test. It never reads `PYPI_API_KEY` and never publishes.
|
|
355
|
+
|
|
356
|
+
On GitHub, changing `project.version` in `pyproject.toml` on `main`
|
|
357
|
+
automatically triggers the publish workflow. It checks PyPI first and skips
|
|
358
|
+
the upload when that exact version already exists. Pull requests never
|
|
359
|
+
receive the PyPI secret.
|
|
360
|
+
|
|
361
|
+
Verify the package published on PyPI in a clean Windows environment:
|
|
362
|
+
|
|
363
|
+
```powershell
|
|
364
|
+
py -m venv .verify
|
|
365
|
+
.\.verify\Scripts\python.exe -m pip install --upgrade notex-python
|
|
366
|
+
.\.verify\Scripts\python.exe -c "from notex import Notex; print(Notex)"
|
|
367
|
+
.\.verify\Scripts\python.exe -c "import importlib.metadata; print(importlib.metadata.version('notex-python'))"
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
See [RELEASING.md](RELEASING.md) for the repository-secret release flow.
|
|
371
|
+
Never log, commit, or return API keys to frontend clients.
|