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.
- wavespeed-2.0.3/.github/dependabot.yml +11 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/.github/workflows/pre-commit.yml +1 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/.github/workflows/python-publish.yml +9 -3
- {wavespeed-2.0.1 → wavespeed-2.0.3}/PKG-INFO +1 -1
- {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed/_version.py +1 -1
- {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed/api/client.py +13 -7
- {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed.egg-info/PKG-INFO +1 -1
- {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed.egg-info/SOURCES.txt +1 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed.egg-info/scm_file_list.json +1 -0
- wavespeed-2.0.3/src/wavespeed.egg-info/scm_version.json +8 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/tests/test_api.py +24 -3
- wavespeed-2.0.1/src/wavespeed.egg-info/scm_version.json +0 -8
- {wavespeed-2.0.1 → wavespeed-2.0.3}/.github/workflows/claude.yml +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/.github/workflows/python-packages.yml +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/.github/workflows/tests.yml +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/.gitignore +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/.pre-commit-config.yaml +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/CHANGELOG.md +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/CLAUDE.md +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/LICENSE +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/MANIFEST.in +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/README.md +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/VERSIONING.md +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/assets/wavespeed-logo-dark.svg +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/assets/wavespeed-logo-light.svg +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/pyproject.toml +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/pytest.ini +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/setup.cfg +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed/__init__.py +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed/_config_module.py +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed/api/__init__.py +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed/config.py +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed.egg-info/dependency_links.txt +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed.egg-info/not-zip-safe +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed.egg-info/requires.txt +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/src/wavespeed.egg-info/top_level.txt +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/tests/__init__.py +0 -0
- {wavespeed-2.0.1 → wavespeed-2.0.3}/tests/conftest.py +0 -0
- {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: ['*']
|
|
@@ -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
|
-
|
|
13
|
-
|
|
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 == '
|
|
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,2 +1,2 @@
|
|
|
1
1
|
# This file is automatically generated by setuptools_scm
|
|
2
|
-
__version__ = "2.0.
|
|
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
|
-
|
|
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
|
-
|
|
380
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -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
|
|
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()
|
|
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
|
|
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
|