drukbox-python-sdk 0.5.0__tar.gz → 0.5.1__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.
@@ -57,6 +57,7 @@ run.
57
57
  consumers unless the user asks for them.
58
58
  - Preserve user changes in the worktree; do not clean or rewrite unrelated
59
59
  files.
60
- - A version bump rides alone: one commit (`chore: bump to X`) that touches
61
- only `pyproject.toml`, with no other change in it. Never fold the bump into
62
- a feature or fix commit. The release tag is cut from the bump commit.
60
+ - A version bump rides alone: its own commit (`chore: bump to X`) that
61
+ touches only `pyproject.toml`, in its own PR with no other change in it.
62
+ Never fold the bump into a feature or fix commit or PR. The release tag is
63
+ cut from the bump commit.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: drukbox-python-sdk
3
- Version: 0.5.0
3
+ Version: 0.5.1
4
4
  Summary: Async Python client for Drukbox.
5
5
  Project-URL: Homepage, https://github.com/clawhaven/drukbox-python-sdk
6
6
  Project-URL: Repository, https://github.com/clawhaven/drukbox-python-sdk
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "drukbox-python-sdk"
7
- version = "0.5.0"
7
+ version = "0.5.1"
8
8
  description = "Async Python client for Drukbox."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -626,11 +626,11 @@ class SandboxAPI:
626
626
  raise SandboxUnavailableError(json_response.get("detail", "service unavailable"))
627
627
 
628
628
  if response.status_code == 422:
629
- # FastAPI's 422 detail is a list of error dicts; join the messages
630
- # so it reads as a line, not a raw list repr.
631
- errors = json_response.get("detail") or []
632
- detail = "; ".join(error.get("msg", "") for error in errors) or "validation error"
633
- raise SandboxValidationError(detail)
629
+ # The detail is FastAPI's list of error dicts or the service's own sentence.
630
+ detail = json_response.get("detail")
631
+ if isinstance(detail, list):
632
+ detail = "; ".join(error.get("msg", "") for error in detail)
633
+ raise SandboxValidationError(detail or "validation error")
634
634
 
635
635
  if response.status_code >= 400:
636
636
  raise SandboxResponseError(json_response.get("detail", "error"))
@@ -744,6 +744,20 @@ async def test_422_raises_sandbox_validation_error(api: SandboxAPI):
744
744
  await api.create_host(env={"FOO BAR": "x"})
745
745
 
746
746
 
747
+ @respx.mock
748
+ async def test_422_string_detail_raises_sandbox_validation_error(api: SandboxAPI):
749
+ # Drukbox answers a refused secret with the issuer's reason as a plain string.
750
+ respx.post(f"{BASE_URL}/hosts").mock(
751
+ return_value=httpx.Response(
752
+ 422,
753
+ json={"detail": "GitHub App not installed for acme", "error_code": "SECRET_REFUSED"},
754
+ ),
755
+ )
756
+
757
+ with pytest.raises(SandboxValidationError, match=r"^GitHub App not installed for acme$"):
758
+ await api.create_host()
759
+
760
+
747
761
  @respx.mock
748
762
  async def test_500_raises_sandbox_response_error(api: SandboxAPI):
749
763
  respx.get(f"{BASE_URL}/hosts").mock(