wavespeed 2.0.1__tar.gz → 2.0.3__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 (39) hide show
  1. wavespeed-2.0.3/.github/dependabot.yml +11 -0
  2. {wavespeed-2.0.1 → wavespeed-2.0.3}/.github/workflows/pre-commit.yml +1 -0
  3. {wavespeed-2.0.1 → wavespeed-2.0.3}/.github/workflows/python-publish.yml +9 -3
  4. {wavespeed-2.0.1 → wavespeed-2.0.3}/PKG-INFO +1 -1
  5. {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed/_version.py +1 -1
  6. {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed/api/client.py +13 -7
  7. {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed.egg-info/PKG-INFO +1 -1
  8. {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed.egg-info/SOURCES.txt +1 -0
  9. {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed.egg-info/scm_file_list.json +1 -0
  10. wavespeed-2.0.3/src/wavespeed.egg-info/scm_version.json +8 -0
  11. {wavespeed-2.0.1 → wavespeed-2.0.3}/tests/test_api.py +24 -3
  12. wavespeed-2.0.1/src/wavespeed.egg-info/scm_version.json +0 -8
  13. {wavespeed-2.0.1 → wavespeed-2.0.3}/.github/workflows/claude.yml +0 -0
  14. {wavespeed-2.0.1 → wavespeed-2.0.3}/.github/workflows/python-packages.yml +0 -0
  15. {wavespeed-2.0.1 → wavespeed-2.0.3}/.github/workflows/tests.yml +0 -0
  16. {wavespeed-2.0.1 → wavespeed-2.0.3}/.gitignore +0 -0
  17. {wavespeed-2.0.1 → wavespeed-2.0.3}/.pre-commit-config.yaml +0 -0
  18. {wavespeed-2.0.1 → wavespeed-2.0.3}/CHANGELOG.md +0 -0
  19. {wavespeed-2.0.1 → wavespeed-2.0.3}/CLAUDE.md +0 -0
  20. {wavespeed-2.0.1 → wavespeed-2.0.3}/LICENSE +0 -0
  21. {wavespeed-2.0.1 → wavespeed-2.0.3}/MANIFEST.in +0 -0
  22. {wavespeed-2.0.1 → wavespeed-2.0.3}/README.md +0 -0
  23. {wavespeed-2.0.1 → wavespeed-2.0.3}/VERSIONING.md +0 -0
  24. {wavespeed-2.0.1 → wavespeed-2.0.3}/assets/wavespeed-logo-dark.svg +0 -0
  25. {wavespeed-2.0.1 → wavespeed-2.0.3}/assets/wavespeed-logo-light.svg +0 -0
  26. {wavespeed-2.0.1 → wavespeed-2.0.3}/pyproject.toml +0 -0
  27. {wavespeed-2.0.1 → wavespeed-2.0.3}/pytest.ini +0 -0
  28. {wavespeed-2.0.1 → wavespeed-2.0.3}/setup.cfg +0 -0
  29. {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed/__init__.py +0 -0
  30. {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed/_config_module.py +0 -0
  31. {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed/api/__init__.py +0 -0
  32. {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed/config.py +0 -0
  33. {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed.egg-info/dependency_links.txt +0 -0
  34. {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed.egg-info/not-zip-safe +0 -0
  35. {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed.egg-info/requires.txt +0 -0
  36. {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed.egg-info/top_level.txt +0 -0
  37. {wavespeed-2.0.1 → wavespeed-2.0.3}/tests/__init__.py +0 -0
  38. {wavespeed-2.0.1 → wavespeed-2.0.3}/tests/conftest.py +0 -0
  39. {wavespeed-2.0.1 → wavespeed-2.0.3}/tests/test_config.py +0 -0
@@ -0,0 +1,11 @@
1
+ version: 2
2
+ updates:
3
+ # Actions are pinned to commit SHAs; this keeps the pins moving.
4
+ - package-ecosystem: github-actions
5
+ directory: /
6
+ schedule:
7
+ interval: weekly
8
+ # One pull request per run instead of one per action.
9
+ groups:
10
+ actions:
11
+ patterns: ['*']
@@ -24,6 +24,7 @@ jobs:
24
24
  steps:
25
25
  - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3
26
26
  with:
27
+ persist-credentials: false
27
28
  path: "."
28
29
  - name: Set up Python
29
30
  uses: actions/setup-python@3542bca2639a428e1796aaa6a2ffef0c0f575566 # v3
@@ -8,9 +8,15 @@
8
8
 
9
9
  name: python-publish
10
10
 
11
+ # Triggered by the tag push itself, not by the GitHub Release.
12
+ # python-packages.yml creates that Release using GITHUB_TOKEN, and GitHub's
13
+ # anti-recursion rule means events produced by GITHUB_TOKEN never start another
14
+ # workflow — so `release: published` silently never fired here and the package
15
+ # was never uploaded, while every other check went green.
11
16
  on:
12
- release:
13
- types: [published]
17
+ push:
18
+ tags:
19
+ - "v[0-9]+*"
14
20
  workflow_dispatch:
15
21
 
16
22
  permissions:
@@ -20,7 +26,7 @@ jobs:
20
26
  deploy:
21
27
  if: >-
22
28
  github.repository == 'WaveSpeedAI/wavespeed-python' &&
23
- ((github.event_name == 'release' && github.ref_type == 'tag' &&
29
+ ((github.event_name == 'push' && github.ref_type == 'tag' &&
24
30
  startsWith(github.ref, 'refs/tags/v')) ||
25
31
  (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main'))
26
32
  runs-on: ubuntu-latest
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wavespeed
3
- Version: 2.0.1
3
+ Version: 2.0.3
4
4
  Summary: WaveSpeed Python Client — Official Python SDK for the WaveSpeed inference platform
5
5
  Author-email: WaveSpeedAI <support@wavespeed.ai>
6
6
  License: MIT
@@ -1,2 +1,2 @@
1
1
  # This file is automatically generated by setuptools_scm
2
- __version__ = "2.0.1"
2
+ __version__ = "2.0.3"
@@ -330,7 +330,7 @@ class Client:
330
330
  if status == "completed":
331
331
  return {"outputs": data.get("outputs", [])}
332
332
 
333
- if status in ("failed", "cancelled", "timeout"):
333
+ if status in ("failed", "cancelled", "timeout", "deleted"):
334
334
  error = data.get("error") or "Unknown error"
335
335
  raise RuntimeError(
336
336
  f"Prediction {status} (task_id: {request_id}): {error}"
@@ -371,13 +371,15 @@ class Client:
371
371
 
372
372
  return False
373
373
 
374
- @staticmethod
375
- def _format_sync_mode_error(data: dict[str, Any]) -> str:
374
+ def _format_sync_mode_error(self, data: dict[str, Any]) -> str:
376
375
  """Build an actionable error for a non-completed sync-mode response."""
377
376
  request_id = data.get("id") or "unknown"
378
377
  error = data.get("error") or "Unknown error"
379
- urls = data.get("urls") or {}
380
- result_url = urls.get("get") if isinstance(urls, dict) else None
378
+ result_url = (
379
+ f"{self.base_url}/api/v3/predictions/{request_id}/result"
380
+ if request_id != "unknown"
381
+ else None
382
+ )
381
383
 
382
384
  is_sync_timeout = data.get("code") == 5004 or (
383
385
  data.get("status") == "processing" and "Sync mode timed out" in error
@@ -585,7 +587,8 @@ class Client:
585
587
  URL of the uploaded file.
586
588
 
587
589
  Raises:
588
- ValueError: If API key is not configured.
590
+ ValueError: If API key is not configured, or a non-seekable
591
+ stream exceeds the 200 MB upload limit.
589
592
  FileNotFoundError: If file path does not exist.
590
593
  RuntimeError: If upload fails.
591
594
 
@@ -625,7 +628,10 @@ class Client:
625
628
  size = stream.tell() - start
626
629
  stream.seek(start)
627
630
  except (AttributeError, OSError, io.UnsupportedOperation):
628
- data = stream.read(200 * 1024 * 1024 + 1)
631
+ max_size = 200 * 1024 * 1024
632
+ data = stream.read(max_size + 1)
633
+ if len(data) > max_size:
634
+ raise ValueError("File size exceeds the 200 MB limit.")
629
635
  stream = io.BytesIO(data)
630
636
  size = len(data)
631
637
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wavespeed
3
- Version: 2.0.1
3
+ Version: 2.0.3
4
4
  Summary: WaveSpeed Python Client — Official Python SDK for the WaveSpeed inference platform
5
5
  Author-email: WaveSpeedAI <support@wavespeed.ai>
6
6
  License: MIT
@@ -9,6 +9,7 @@ VERSIONING.md
9
9
  pyproject.toml
10
10
  pytest.ini
11
11
  setup.cfg
12
+ .github/dependabot.yml
12
13
  .github/workflows/claude.yml
13
14
  .github/workflows/pre-commit.yml
14
15
  .github/workflows/python-packages.yml
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "files": [
3
+ ".github/dependabot.yml",
3
4
  ".github/workflows/claude.yml",
4
5
  ".github/workflows/pre-commit.yml",
5
6
  ".github/workflows/python-packages.yml",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "2.0.3",
3
+ "distance": 0,
4
+ "node": "g3e926667ae3ef93fc11dc277b6f09b081750ad7d",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-09-21"
8
+ }
@@ -253,7 +253,6 @@ class TestClient(unittest.TestCase):
253
253
  "Sync mode timed out after 90 seconds. The prediction is "
254
254
  "still processing asynchronously."
255
255
  ),
256
- "urls": {"get": result_url},
257
256
  }
258
257
  }
259
258
  mock_post.return_value = mock_post_response
@@ -314,7 +313,7 @@ class TestModuleLevelRun(unittest.TestCase):
314
313
 
315
314
 
316
315
  class TestTerminalStatuses(unittest.TestCase):
317
- """Tests for cancelled/timeout terminal statuses in the wait loop."""
316
+ """Tests for non-success terminal statuses in the wait loop."""
318
317
 
319
318
  def _run_with_status(self, status):
320
319
  with patch("wavespeed.api.client.requests.post") as mock_post, patch(
@@ -349,6 +348,13 @@ class TestTerminalStatuses(unittest.TestCase):
349
348
  self.assertIn("Task timeout by server", str(ctx.exception))
350
349
  self.assertIn("req-123", str(ctx.exception))
351
350
 
351
+ def test_deleted_is_terminal(self):
352
+ """Test that a deleted status raises with the API's error text."""
353
+ with self.assertRaises(RuntimeError) as ctx:
354
+ self._run_with_status("deleted")
355
+ self.assertIn("Task deleted by server", str(ctx.exception))
356
+ self.assertIn("req-123", str(ctx.exception))
357
+
352
358
 
353
359
  class TestRunNoThrow(unittest.TestCase):
354
360
  """Tests for Client.run_no_throw and the module-level run_no_throw."""
@@ -417,7 +423,6 @@ class TestRunNoThrow(unittest.TestCase):
417
423
  "Sync mode timed out after 90 seconds. The prediction is "
418
424
  "still processing asynchronously."
419
425
  ),
420
- "urls": {"get": result_url},
421
426
  }
422
427
  }
423
428
  mock_post.return_value = mock_post_response
@@ -577,6 +582,22 @@ class TestUpload(unittest.TestCase):
577
582
  with self.assertRaises(FileNotFoundError):
578
583
  client.upload("/nonexistent/path/to/file.png")
579
584
 
585
+ @patch("wavespeed.api.client.requests.post")
586
+ def test_upload_oversize_stream_raises(self, mock_post):
587
+ """A non-seekable stream over the limit fails before any request."""
588
+
589
+ class Pipe:
590
+ name = "clip.mp4"
591
+
592
+ def read(self, n):
593
+ return bytes(n)
594
+
595
+ client = Client(api_key="test-key")
596
+ with self.assertRaises(ValueError) as ctx:
597
+ client.upload(Pipe())
598
+ self.assertIn("200 MB", str(ctx.exception))
599
+ mock_post.assert_not_called()
600
+
580
601
  def test_upload_raises_without_api_key(self):
581
602
  """Test that upload raises ValueError without API key."""
582
603
  client = Client()
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "2.0.1",
3
- "distance": 0,
4
- "node": "ge359ef21229b4fe38981c8ce71661dddc874425a",
5
- "dirty": false,
6
- "branch": "main",
7
- "node_date": "2026-08-21"
8
- }
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