waveium 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.
Files changed (59) hide show
  1. waveium-0.2.0/.gitignore +247 -0
  2. waveium-0.2.0/.python-version +1 -0
  3. waveium-0.2.0/LICENSE +21 -0
  4. waveium-0.2.0/PKG-INFO +426 -0
  5. waveium-0.2.0/README.md +392 -0
  6. waveium-0.2.0/examples/.env.example +18 -0
  7. waveium-0.2.0/examples/async_streaming_usage.py +109 -0
  8. waveium-0.2.0/examples/async_usage.py +44 -0
  9. waveium-0.2.0/examples/basic_usage.py +86 -0
  10. waveium-0.2.0/examples/load_and_plot.py +60 -0
  11. waveium-0.2.0/examples/streaming_usage.py +158 -0
  12. waveium-0.2.0/examples/underground_workflow.py +328 -0
  13. waveium-0.2.0/examples/underground_workflow_sse.py +287 -0
  14. waveium-0.2.0/examples/upload_download.py +109 -0
  15. waveium-0.2.0/examples/verify_sdk.py +79 -0
  16. waveium-0.2.0/pyproject.toml +76 -0
  17. waveium-0.2.0/setup.cfg +2 -0
  18. waveium-0.2.0/src/waveium/__init__.py +204 -0
  19. waveium-0.2.0/src/waveium/_config.py +88 -0
  20. waveium-0.2.0/src/waveium/_http.py +362 -0
  21. waveium-0.2.0/src/waveium/_sse.py +497 -0
  22. waveium-0.2.0/src/waveium/async_client.py +92 -0
  23. waveium-0.2.0/src/waveium/client.py +90 -0
  24. waveium-0.2.0/src/waveium/exceptions.py +131 -0
  25. waveium-0.2.0/src/waveium/models/__init__.py +224 -0
  26. waveium-0.2.0/src/waveium/models/auth.py +58 -0
  27. waveium-0.2.0/src/waveium/models/common.py +130 -0
  28. waveium-0.2.0/src/waveium/models/environments.py +247 -0
  29. waveium-0.2.0/src/waveium/models/events.py +132 -0
  30. waveium-0.2.0/src/waveium/models/executions.py +145 -0
  31. waveium-0.2.0/src/waveium/models/files.py +158 -0
  32. waveium-0.2.0/src/waveium/models/organizations.py +51 -0
  33. waveium-0.2.0/src/waveium/models/projects.py +177 -0
  34. waveium-0.2.0/src/waveium/models/roles.py +48 -0
  35. waveium-0.2.0/src/waveium/models/scenes.py +222 -0
  36. waveium-0.2.0/src/waveium/models/shares.py +51 -0
  37. waveium-0.2.0/src/waveium/models/teams.py +90 -0
  38. waveium-0.2.0/src/waveium/models/users.py +88 -0
  39. waveium-0.2.0/src/waveium/resources/__init__.py +41 -0
  40. waveium-0.2.0/src/waveium/resources/_base.py +30 -0
  41. waveium-0.2.0/src/waveium/resources/_file_ops.py +371 -0
  42. waveium-0.2.0/src/waveium/resources/_share_ops.py +175 -0
  43. waveium-0.2.0/src/waveium/resources/_stream_ops.py +318 -0
  44. waveium-0.2.0/src/waveium/resources/_upload_ops.py +286 -0
  45. waveium-0.2.0/src/waveium/resources/auth.py +157 -0
  46. waveium-0.2.0/src/waveium/resources/environments.py +1047 -0
  47. waveium-0.2.0/src/waveium/resources/executions.py +1142 -0
  48. waveium-0.2.0/src/waveium/resources/organizations.py +281 -0
  49. waveium-0.2.0/src/waveium/resources/projects.py +559 -0
  50. waveium-0.2.0/src/waveium/resources/roles.py +104 -0
  51. waveium-0.2.0/src/waveium/resources/scenes.py +1314 -0
  52. waveium-0.2.0/src/waveium/resources/teams.py +351 -0
  53. waveium-0.2.0/src/waveium/resources/users.py +448 -0
  54. waveium-0.2.0/tests/__init__.py +0 -0
  55. waveium-0.2.0/tests/test_events.py +150 -0
  56. waveium-0.2.0/tests/test_exceptions.py +76 -0
  57. waveium-0.2.0/tests/test_repr.py +268 -0
  58. waveium-0.2.0/tests/test_sse_connection.py +271 -0
  59. waveium-0.2.0/tests/test_sse_parser.py +110 -0
@@ -0,0 +1,247 @@
1
+ #
2
+ #
3
+ .DS_Store
4
+ Thumbs.db
5
+
6
+
7
+ # ### Matlab
8
+ # Windows default autosave extension
9
+ *.asv
10
+
11
+ # OSX / *nix default autosave extension
12
+ *.m~
13
+
14
+ # Compiled MEX binaries (all platforms)
15
+ *.mex*
16
+
17
+ # Packaged app and toolbox files
18
+ *.mlappinstall
19
+ *.mltbx
20
+
21
+ # Generated helpsearch folders
22
+ helpsearch*/
23
+
24
+ # Simulink code generation folders
25
+ slprj/
26
+ sccprj/
27
+
28
+ # Matlab code generation folders
29
+ codegen/
30
+
31
+ # Simulink autosave extension
32
+ *.autosave
33
+
34
+ # Octave session info
35
+ octave-workspace
36
+
37
+
38
+
39
+
40
+ # ### Python
41
+ # Byte-compiled / optimized / DLL files
42
+ __pycache__/
43
+ *.py[codz]
44
+ *$py.class
45
+
46
+ # C extensions
47
+ *.so
48
+
49
+ # Distribution / packaging
50
+ .Python
51
+ build/
52
+ develop-eggs/
53
+ dist/
54
+ downloads/
55
+ eggs/
56
+ .eggs/
57
+ lib/
58
+ lib64/
59
+ parts/
60
+ sdist/
61
+ var/
62
+ wheels/
63
+ share/python-wheels/
64
+ *.egg-info/
65
+ .installed.cfg
66
+ *.egg
67
+ MANIFEST
68
+
69
+ # PyInstaller
70
+ # Usually these files are written by a python script from a template
71
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
72
+ *.manifest
73
+ *.spec
74
+
75
+ # Installer logs
76
+ pip-log.txt
77
+ pip-delete-this-directory.txt
78
+
79
+ # Unit test / coverage reports
80
+ htmlcov/
81
+ .tox/
82
+ .nox/
83
+ .coverage
84
+ .coverage.*
85
+ .cache
86
+ nosetests.xml
87
+ coverage.xml
88
+ *.cover
89
+ *.py.cover
90
+ .hypothesis/
91
+ .pytest_cache/
92
+ cover/
93
+
94
+ # Translations
95
+ *.mo
96
+ *.pot
97
+
98
+ # Django stuff:
99
+ *.log
100
+ local_settings.py
101
+ db.sqlite3
102
+ db.sqlite3-journal
103
+
104
+ # Flask stuff:
105
+ instance/
106
+ .webassets-cache
107
+
108
+ # Scrapy stuff:
109
+ .scrapy
110
+
111
+ # Sphinx documentation
112
+ docs/_build/
113
+
114
+ # PyBuilder
115
+ .pybuilder/
116
+ target/
117
+
118
+ # Jupyter Notebook
119
+ .ipynb_checkpoints
120
+
121
+ # IPython
122
+ profile_default/
123
+ ipython_config.py
124
+
125
+ # pyenv
126
+ # For a library or package, you might want to ignore these files since the code is
127
+ # intended to run in multiple environments; otherwise, check them in:
128
+ # .python-version
129
+
130
+ # pipenv
131
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
132
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
133
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
134
+ # install all needed dependencies.
135
+ #Pipfile.lock
136
+
137
+ # UV
138
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
139
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
140
+ # commonly ignored for libraries.
141
+ #uv.lock
142
+
143
+ # poetry
144
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
145
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
146
+ # commonly ignored for libraries.
147
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
148
+ #poetry.lock
149
+ #poetry.toml
150
+
151
+ # pdm
152
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
153
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
154
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
155
+ #pdm.lock
156
+ #pdm.toml
157
+ .pdm-python
158
+ .pdm-build/
159
+
160
+ # pixi
161
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
162
+ #pixi.lock
163
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
164
+ # in the .venv directory. It is recommended not to include this directory in version control.
165
+ .pixi
166
+
167
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
168
+ __pypackages__/
169
+
170
+ # Celery stuff
171
+ celerybeat-schedule
172
+ celerybeat.pid
173
+
174
+ # SageMath parsed files
175
+ *.sage.py
176
+
177
+ # Environments
178
+ .env
179
+ .envrc
180
+ .venv
181
+ env/
182
+ venv/
183
+ ENV/
184
+ env.bak/
185
+ venv.bak/
186
+
187
+ # Spyder project settings
188
+ .spyderproject
189
+ .spyproject
190
+
191
+ # Rope project settings
192
+ .ropeproject
193
+
194
+ # mkdocs documentation
195
+ /site
196
+
197
+ # mypy
198
+ .mypy_cache/
199
+ .dmypy.json
200
+ dmypy.json
201
+
202
+ # Pyre type checker
203
+ .pyre/
204
+
205
+ # pytype static type analyzer
206
+ .pytype/
207
+
208
+ # Cython debug symbols
209
+ cython_debug/
210
+
211
+ # PyCharm
212
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
213
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
214
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
215
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
216
+ #.idea/
217
+
218
+ # Abstra
219
+ # Abstra is an AI-powered process automation framework.
220
+ # Ignore directories containing user credentials, local state, and settings.
221
+ # Learn more at https://abstra.io/docs
222
+ .abstra/
223
+
224
+ # Visual Studio Code
225
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
226
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
227
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
228
+ # you could uncomment the following to ignore the entire vscode folder
229
+ # .vscode/
230
+
231
+ # Ruff stuff:
232
+ .ruff_cache/
233
+
234
+ # PyPI configuration file
235
+ .pypirc
236
+
237
+ # Cursor
238
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
239
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
240
+ # refer to https://docs.cursor.com/context/ignore-files
241
+ .cursorignore
242
+ .cursorindexingignore
243
+
244
+ # Marimo
245
+ marimo/_static/
246
+ marimo/_lsp/
247
+ __marimo__/
@@ -0,0 +1 @@
1
+ 3.13
waveium-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Waveium Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
waveium-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,426 @@
1
+ Metadata-Version: 2.4
2
+ Name: waveium
3
+ Version: 0.2.0
4
+ Summary: Official Python SDK for the Waveium API
5
+ Project-URL: Homepage, https://waveium.io
6
+ Project-URL: Documentation, https://docs.waveium.io
7
+ Project-URL: Repository, https://github.com/waveium/waveium-sdk
8
+ Author: Waveium Team
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: api,environment,execution,rf-simulation,scene,sdk,waveium
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.9
23
+ Requires-Dist: httpx>=0.27.0
24
+ Requires-Dist: pydantic>=2.0.0
25
+ Requires-Dist: python-dotenv>=1.0.0
26
+ Requires-Dist: typing-extensions>=4.9.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: black>=24.0.0; extra == 'dev'
29
+ Requires-Dist: flake8>=7.0.0; extra == 'dev'
30
+ Requires-Dist: mypy>=1.8.0; extra == 'dev'
31
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
32
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # Waveium Python SDK
36
+
37
+ Official Python client library for the Waveium API.
38
+
39
+ ## Current Status
40
+
41
+ The SDK is fully functional for simulation management. File upload/download endpoints are implemented in the SDK but not yet available on the server (they return 404/405 errors). The SDK is production-ready and will work automatically once these endpoints are deployed.
42
+
43
+ ## Features
44
+
45
+ - Full support for Waveium API simulation endpoints
46
+ - File upload/download implementation (ready for when endpoints are deployed)
47
+ - Both synchronous and asynchronous clients
48
+ - Type-safe with Pydantic models
49
+ - Automatic 3-step file upload workflow
50
+ - Batch file downloads
51
+ - Environment variable configuration (supports WAVEIUM_API_KEY and WAVEIUM_SDK_TOKEN)
52
+ - Comprehensive error handling
53
+
54
+ ## Installation
55
+
56
+ ```bash
57
+ pip install waveium
58
+ ```
59
+
60
+ For development:
61
+
62
+ ```bash
63
+ pip install waveium[dev]
64
+ ```
65
+
66
+ ## Quick Start
67
+
68
+ ### Using an API Key
69
+
70
+ ```python
71
+ import waveium
72
+
73
+ # Initialize with API key
74
+ client = waveium.init(api_key="wvx_your_api_key_here")
75
+
76
+ # Create a simulation
77
+ simulation = client.simulations.create(
78
+ name="My First Simulation",
79
+ parameters={"param1": "value1"}
80
+ )
81
+
82
+ print(f"Created simulation: {simulation.simulation_id}")
83
+
84
+ # Upload a file
85
+ client.files.upload(
86
+ simulation_id=simulation.simulation_id,
87
+ file_path="./input_data.json",
88
+ subdir="Assets"
89
+ )
90
+
91
+ # List files
92
+ files = client.files.list(simulation.simulation_id)
93
+ for file in files.files:
94
+ print(f"File: {file.filename} ({file.file_size_bytes} bytes)")
95
+
96
+ # Download a file
97
+ client.files.download(
98
+ simulation_id=simulation.simulation_id,
99
+ file_id=files.files[0].file_id,
100
+ destination="./downloaded_file.json"
101
+ )
102
+ ```
103
+
104
+ ### Using Firebase Authentication
105
+
106
+ ```python
107
+ import waveium
108
+
109
+ # Exchange Firebase JWT for API key
110
+ client = waveium.auth(
111
+ firebase_token="your_firebase_jwt_token",
112
+ token_name="My Python App"
113
+ )
114
+
115
+ # Use client as normal
116
+ simulation = client.simulations.create(name="Test Simulation")
117
+ ```
118
+
119
+ ### Using Environment Variables
120
+
121
+ Create a `.env` file:
122
+
123
+ ```env
124
+ WAVEIUM_API_KEY=wvx_your_api_key_here
125
+ ```
126
+
127
+ Then use without arguments:
128
+
129
+ ```python
130
+ import waveium
131
+
132
+ client = waveium.init() # Automatically loads from environment
133
+ ```
134
+
135
+ ## Async Support
136
+
137
+ The SDK provides full async support:
138
+
139
+ ```python
140
+ import asyncio
141
+ import waveium
142
+
143
+ async def main():
144
+ async with waveium.AsyncClient(api_key="wvx_...") as client:
145
+ # Create simulation
146
+ simulation = await client.simulations.create(
147
+ name="Async Simulation"
148
+ )
149
+
150
+ # Upload file
151
+ await client.files.upload(
152
+ simulation_id=simulation.simulation_id,
153
+ file_path="./data.json"
154
+ )
155
+
156
+ # List and download files
157
+ files = await client.files.list(simulation.simulation_id)
158
+ for file in files.files:
159
+ await client.files.download(
160
+ simulation_id=simulation.simulation_id,
161
+ file_id=file.file_id,
162
+ destination=f"./{file.filename}"
163
+ )
164
+
165
+ asyncio.run(main())
166
+ ```
167
+
168
+ ## API Reference
169
+
170
+ ### Client Initialization
171
+
172
+ #### `waveium.init(api_key=None, base_url=None, timeout=None, max_retries=None)`
173
+
174
+ Initialize a synchronous client with an API key.
175
+
176
+ **Parameters:**
177
+ - `api_key` (str, optional): API key. Falls back to `WAVEIUM_API_KEY` env var
178
+ - `base_url` (str, optional): Custom API base URL
179
+ - `timeout` (float, optional): Request timeout in seconds (default: 60)
180
+ - `max_retries` (int, optional): Max retry attempts (default: 3)
181
+
182
+ **Returns:** `Client` instance
183
+
184
+ #### `waveium.auth(firebase_token=None, token_name="Python SDK Token", expires_days=30, ...)`
185
+
186
+ Initialize a client by exchanging Firebase JWT for an API key.
187
+
188
+ **Parameters:**
189
+ - `firebase_token` (str, optional): Firebase JWT. Falls back to `FIREBASE_TOKEN` env var
190
+ - `token_name` (str): Name for the created token
191
+ - `expires_days` (int): Token expiration in days (1-365, default: 30)
192
+
193
+ **Returns:** `Client` instance with API key
194
+
195
+ ### Simulations
196
+
197
+ #### `client.simulations.create(name=None, parameters=None, template_id=None)`
198
+
199
+ Create a new GSCM simulation.
200
+
201
+ **Parameters:**
202
+ - `name` (str, optional): Simulation name
203
+ - `parameters` (dict, optional): Simulation parameters
204
+ - `template_id` (str, optional): Template ID to use
205
+
206
+ **Returns:** `SimulationResponse` object
207
+
208
+ ```python
209
+ simulation = client.simulations.create(
210
+ name="Production Run",
211
+ parameters={
212
+ "duration": 365,
213
+ "model_type": "deterministic"
214
+ }
215
+ )
216
+ ```
217
+
218
+ ### File Operations
219
+
220
+ #### `client.files.upload(simulation_id, file_path, subdir="Assets", content_type=None)`
221
+
222
+ Upload a file to a simulation. Handles the complete 3-step workflow automatically.
223
+
224
+ **Parameters:**
225
+ - `simulation_id` (str): Simulation ID
226
+ - `file_path` (str | Path): Path to file to upload
227
+ - `subdir` (str): Target subdirectory ("Assets" or "Results")
228
+ - `content_type` (str, optional): MIME type (auto-detected if not provided)
229
+
230
+ **Returns:** `CompleteUploadResponse` object
231
+
232
+ ```python
233
+ result = client.files.upload(
234
+ simulation_id="sim_123",
235
+ file_path="./model_config.json",
236
+ subdir="Assets"
237
+ )
238
+ print(f"Uploaded: {result.file_id}")
239
+ ```
240
+
241
+ #### `client.files.list(simulation_id, subdir=None)`
242
+
243
+ List all files in a simulation.
244
+
245
+ **Parameters:**
246
+ - `simulation_id` (str): Simulation ID
247
+ - `subdir` (str, optional): Filter by subdirectory ("Assets" or "Results")
248
+
249
+ **Returns:** `SimulationFileListResponse` object
250
+
251
+ ```python
252
+ files = client.files.list("sim_123", subdir="Results")
253
+ print(f"Total files: {files.total_files}")
254
+ for file in files.files:
255
+ print(f" {file.filename}: {file.file_size_bytes} bytes")
256
+ ```
257
+
258
+ #### `client.files.download(simulation_id, file_id, destination)`
259
+
260
+ Download a single file from a simulation.
261
+
262
+ **Parameters:**
263
+ - `simulation_id` (str): Simulation ID
264
+ - `file_id` (str): File ID
265
+ - `destination` (str | Path): Path where to save the file
266
+
267
+ **Returns:** `Path` object pointing to downloaded file
268
+
269
+ ```python
270
+ path = client.files.download(
271
+ simulation_id="sim_123",
272
+ file_id="file_456",
273
+ destination="./results/output.json"
274
+ )
275
+ print(f"Downloaded to: {path}")
276
+ ```
277
+
278
+ #### `client.files.download_batch(simulation_id, destination_dir, subdir=None)`
279
+
280
+ Download all files from a simulation.
281
+
282
+ **Parameters:**
283
+ - `simulation_id` (str): Simulation ID
284
+ - `destination_dir` (str | Path): Directory where to save files
285
+ - `subdir` (str, optional): Filter by subdirectory
286
+
287
+ **Returns:** `BatchDownloadResponse` object
288
+
289
+ ```python
290
+ batch = client.files.download_batch(
291
+ simulation_id="sim_123",
292
+ destination_dir="./results/",
293
+ subdir="Results"
294
+ )
295
+ print(f"Downloaded {batch.total_files} files")
296
+ ```
297
+
298
+ ### Authentication
299
+
300
+ #### `client.auth.create_token(name, expires_days=30, firebase_token=None)`
301
+
302
+ Create a new API token from Firebase JWT.
303
+
304
+ ```python
305
+ response = client.auth.create_token(
306
+ name="My Application Token",
307
+ expires_days=90
308
+ )
309
+ print(f"New API key: {response.api_key}")
310
+ ```
311
+
312
+ #### `client.auth.list_tokens()`
313
+
314
+ List all API tokens for the current user.
315
+
316
+ ```python
317
+ tokens = client.auth.list_tokens()
318
+ for token in tokens.tokens:
319
+ print(f"{token.name}: {token.id}")
320
+ ```
321
+
322
+ #### `client.auth.revoke_token(token_id)`
323
+
324
+ Revoke a specific API token.
325
+
326
+ ```python
327
+ client.auth.revoke_token("tok_123")
328
+ ```
329
+
330
+ ## Error Handling
331
+
332
+ The SDK provides a comprehensive exception hierarchy:
333
+
334
+ ```python
335
+ from waveium import (
336
+ WaveiumError, # Base exception
337
+ AuthenticationError, # 401 errors
338
+ AuthorizationError, # 403 errors
339
+ NotFoundError, # 404 errors
340
+ ValidationError, # 400 errors
341
+ RateLimitError, # 429 errors
342
+ APIError, # 5xx errors
343
+ NetworkError, # Network failures
344
+ FileUploadError, # Upload failures
345
+ FileDownloadError, # Download failures
346
+ ConfigurationError, # Configuration issues
347
+ )
348
+
349
+ try:
350
+ simulation = client.simulations.create(name="Test")
351
+ except AuthenticationError as e:
352
+ print(f"Authentication failed: {e}")
353
+ except ValidationError as e:
354
+ print(f"Invalid request: {e}")
355
+ print(f"Error code: {e.code}")
356
+ print(f"Details: {e.details}")
357
+ except WaveiumError as e:
358
+ print(f"Waveium error: {e}")
359
+ ```
360
+
361
+ ## Configuration
362
+
363
+ ### Environment Variables
364
+
365
+ - `WAVEIUM_API_KEY`: API key for authentication
366
+ - `FIREBASE_TOKEN`: Firebase JWT token for authentication
367
+ - `WAVEIUM_BASE_URL`: Custom API base URL
368
+ - `WAVEIUM_TIMEOUT`: Request timeout in seconds
369
+ - `WAVEIUM_MAX_RETRIES`: Maximum retry attempts
370
+
371
+ ### Custom Configuration
372
+
373
+ ```python
374
+ client = waveium.init(
375
+ api_key="wvx_...",
376
+ base_url="https://custom-api.example.com",
377
+ timeout=120.0,
378
+ max_retries=5
379
+ )
380
+ ```
381
+
382
+ ## Examples
383
+
384
+ See the `examples/` directory for complete working examples:
385
+
386
+ - `basic_usage.py` - Basic synchronous usage
387
+ - `async_usage.py` - Asynchronous operations
388
+ - `upload_download.py` - File operations workflow
389
+
390
+ ## Development
391
+
392
+ ### Setup
393
+
394
+ ```bash
395
+ cd python313
396
+ pip install -e ".[dev]"
397
+ ```
398
+
399
+ ### Code Quality
400
+
401
+ ```bash
402
+ # Format code
403
+ black src/
404
+
405
+ # Lint
406
+ flake8 src/
407
+
408
+ # Type check
409
+ mypy src/
410
+ ```
411
+
412
+ ### Testing
413
+
414
+ ```bash
415
+ pytest
416
+ ```
417
+
418
+ ## License
419
+
420
+ MIT License
421
+
422
+ ## Support
423
+
424
+ - Documentation: https://docs.waveium.io
425
+ - Issues: https://github.com/waveium/waveium-sdk/issues
426
+ - Email: support@waveium.io