Conversation
razor-x
commented
Aug 13, 2026
Member
- build: Migrate from Poetry to uv (build: Migrate from Poetry to uv #606)
- 3.0.0b1
- docs: name the page cursor property as Python spells it (docs: name the page cursor property as Python spells it #607)
- Type check the whole project instead of seam/resources with error codes disabled (Type check the whole project instead of seam/resources with error codes disabled #608)
- feat: Replace niquests with httpx (feat: Replace niquests with httpx #604)
- feat: Remove lts_version unused
- Update to uv v12
- Fix prune workflow job name
* build: Migrate from Poetry to uv Replace Poetry with uv for dependency management, packaging, and publishing: - Convert pyproject.toml to PEP 621 project metadata with the uv_build backend and PEP 735 dependency groups. - Replace poetry.lock with uv.lock, keeping the previously locked versions of the lint toolchain (black, pylint, mypy, rstcheck, docutils) to avoid behavior drift. - Update the justfile to use uv run, uv build, and uv version. - Replace the Poetry setup and caching in GitHub Actions with astral-sh/setup-uv, and switch publishing to uv publish. - Install uv instead of Poetry in the devcontainer. - Update the README development docs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xi2DoWdLxtV7AqqxtbQVqN * style: Format code with black targeting Python 3.10 With PEP 621 metadata, black now infers its target versions from requires-python and applies newer formatting (trailing commas after **kwargs). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xi2DoWdLxtV7AqqxtbQVqN * build: Resolve uv version from pyproject.toml in CI Declare required-version under [tool.uv] and let setup-uv discover it from the config file instead of passing a version through an action input. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xi2DoWdLxtV7AqqxtbQVqN --------- Co-authored-by: Claude <noreply@anthropic.com>
The pagination heading called it nextPageCursor, which is how the JavaScript SDK spells it. In this SDK it is pagination.next_page_cursor, as the example under that very heading already shows. Claude-Session: https://claude.ai/code/session_01PpqwDhZmyikGbjmCPqFW2A Co-authored-by: Claude <noreply@anthropic.com>
…es disabled (#608) * fix: Export type information from the package root seam/__init__.py carried a blanket "# type: ignore", which hid the module from type checkers entirely. Since the package also ships py.typed, every downstream project that type checks its own code saw the documented import fail: app.py:1: error: Module "seam" has no attribute "Seam" [attr-defined] The lint step only pointed mypy at seam/resources, so nothing in this repository could catch it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8duZRXUbcxu7y5WeoACqM * fix: Type the decoded response body on the HTTP client SeamHttpClient overrides request to return the decoded body rather than the Response that niquests.Session promises, but the verb helpers kept the inherited Response return type. Indexing what post and get actually return did not type check, which accounted for every "Value of type Response is not indexable" error in the generated routes. Both helpers now name the arguments they forward: Session.request takes params in the position Session.post gives data, so collecting them into *args would have rerouted positional calls. Handling of a response without a status code is no longer a TypeError. niquests types status_code as optional because a Response exists before it has one, so the comparison against it is guarded and the narrowed value is passed to the error path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8duZRXUbcxu7y5WeoACqM * fix: Correct optional handling across the hand-written modules Type checking these files for the first time turned up a latent crash: SeamPaginator.flatten_to_list read pagination.has_next_page without the None guard its sibling flatten already had, so a page whose response carried no pagination raised AttributeError instead of ending the loop. The rest are annotations that did not match their callers. request_id is read from a response header that may be absent, so the HTTP exceptions take Optional[str]. SeamActionAttemptTimeoutError documented its timeout as str while every caller passes a float. poll_until_ready declared its timeout and polling interval Optional though neither is ever None, which made arithmetic on them unsound. SeamPaginator.params used an implicit Optional default, and its response hook is handed either side of the exchange, so the request side is turned away before the pagination is read. A failed action attempt is assumed to carry an error; reading through it unguarded would raise AttributeError over the actual failure. Two invariants are beyond what the checker can follow and are ignored in place: the auth option guards return True only once the values they check are set, and Seam borrows Routes.__init__ to attach the route namespaces even though the two are siblings under AbstractRoutes rather than parent and child. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8duZRXUbcxu7y5WeoACqM * feat: Declare resource fields the API may omit as Optional Generated dataclasses declared every field as present and non-null, but from_dict reads each one with dict.get, so a field the payload omits arrives as None behind a type that rules None out. That mismatch was the whole of the ~1720 arg-type errors the lint step disabled the error code to hide. Fields the blueprint marks optional or nullable are now generated as Optional. So are the ones a merged shape cannot guarantee: a property only some variants of a discriminated union carry is absent whenever the dataclass holds a variant that omits it. Nested objects are Optional regardless of what the schema says, because the schema is not a reliable guide to when they arrive. An action attempt documents both error and result as required, yet a pending one carries neither, so from_dict keeps its None fallback and the field admits it. No field gains a default, so the constructors are unchanged. from_dict now takes Any. The payload is decoded JSON and every value read out of it is untyped, so keeping that at the boundary avoids casting each read while the fields carry the real types. Route methods annotate json_payload as Dict[str, Any]. Left bare, its type was inferred from whichever parameter was written first, and every later parameter of a different type was reported as an incompatible assignment. BREAKING CHANGE: Resource fields that the API may omit or send as null are now typed Optional. Code that reads them under a type checker may need a None check. Runtime behavior is unchanged: those fields could already be None. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8duZRXUbcxu7y5WeoACqM * build: Type check the whole project The mypy step checked seam/resources with two error codes disabled. That covered 34 of the 94 files in the package and left every hand-written module unchecked, including the package root whose blanket ignore broke type checking for every downstream user. It now checks seam and test with nothing disabled. The import-not-found disable is dropped outright: it suppressed nothing, since pointing mypy at the whole package resolves the imports it was added for. The paginator tests narrow the pagination they assert on, the way test_paginator_first_page already did, and the test that passes None as a cursor on purpose says so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8duZRXUbcxu7y5WeoACqM --------- Co-authored-by: Claude <noreply@anthropic.com>
* feat!: Replace niquests with httpx as the HTTP client The niquests dependency urllib3-future installs into the urllib3 package namespace, which conflicts with packages depending on genuine urllib3 in customer environments and cannot be mitigated from the SDK. httpx is already in the dependency tree via svix and its dependency chain does not touch urllib3. BREAKING CHANGE: The niquests_options option is now httpx_options and is passed to the underlying httpx.Client. The retries option now takes a seam.Retry instead of a urllib3.util.Retry; the fields mirror the urllib3 names. Seam.client is now an httpx.Client subclass, and request errors are raised as httpx exceptions, e.g. httpx. HTTPStatusError instead of niquests.HTTPError and httpx. TimeoutException instead of niquests.exceptions.Timeout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QxwdLpWpFhxSn7mZ2Fb6p1 * feat: Use httpx-retries instead of a hand-rolled retry transport The retries option now takes an httpx_retries.Retry, re-exported as seam.Retry. Its fields mirror the urllib3.util.Retry names. Unlike the previous urllib3 default, the default policy does not retry connection errors for POST requests; pass allowed_methods=["POST"] to opt in to retrying Seam API requests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QxwdLpWpFhxSn7mZ2Fb6p1 * feat: Apply configured retry policy to API requests regardless of method Which HTTP methods the Seam API uses is not part of the SDK's public API, so a configured retry policy must not require consumers to name them in allowed_methods. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QxwdLpWpFhxSn7mZ2Fb6p1 * chore: Remove unused dataclasses-json dependency Resource classes define their own from_dict on standard library dataclasses and nothing imports dataclasses_json. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QxwdLpWpFhxSn7mZ2Fb6p1 * fix: Relax httpx version constraints The SDK is a library, so keep dependency constraints as permissive as possible. The httpx floor matches svix, and the httpx-retries floor is the release that introduced Retry.copy_with. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QxwdLpWpFhxSn7mZ2Fb6p1 * fix: Do not override the configured retry policy allowed_methods The retries option should not silently modify the policy it is given. Since httpx-retries does not treat POST as retryable, the retry policy currently has no effect on Seam API requests; the affected tests are marked xfail. A follow-up PR will apply the retry policy to API requests without exposing the HTTP method in the SDK's public API. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QxwdLpWpFhxSn7mZ2Fb6p1 * style: Import httpx names directly except for the client Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QxwdLpWpFhxSn7mZ2Fb6p1 * style: Reword fixme comment flagged by pylint Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QxwdLpWpFhxSn7mZ2Fb6p1 --------- Co-authored-by: Claude <noreply@anthropic.com>
This reverts commit cb12e23.
The required parameter guard generated one any() argument per parameter, so every call raised TypeError instead of validating: any() takes a single iterable, not a variadic list of conditions. Wrap the conditions in a list and regenerate. Also fix the fallout that this masked in the rest of the suite: - Type the put and patch client helpers as returning the decoded body, matching get, post and delete, now that generated routes use them. - Set the route metadata attributes through Any, since a function does not declare them. - Allow TODO comments, which the test suite uses to track routes that cannot use the generated method yet. - Assert the GET request line and empty body in the default headers test, and drive the invalid input test through a route that still takes a JSON payload, now that /devices/get is a GET route. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EFYHA1UHvARfcMBkUnWgGs
The guard restated every parameter, which ran to seventeen lines on the widest routes. The payload gains a key exactly when its parameter is not None, so checking it after it is built says the same thing in one line and drops 446 lines from the generated routes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EFYHA1UHvARfcMBkUnWgGs
semantic-release computes semver versions, but uv normalizes prerelease versions to PEP 440 when cutting them, so the tag it produces (v3.0.0b1) is not valid semver. semantic-release silently skips tags it cannot parse, so every run on a prerelease branch reported the channel's first prerelease as the next version and tried to cut a version that had already been published. Mirror each PEP 440 prerelease tag onto its semver equivalent before running semantic-release. The mirrored tags are local to the workflow run, so the PEP 440 tags stay the only real ones and continue to match the published package version. Recording the channel a prerelease went out on, which semantic-release requires before it will treat a prerelease tag as released, stays in the Version workflow where the release is cut. It now keys the note to the PEP 440 tag that actually gets created, rather than to a semver tag name that never exists, and derives the channel from the normalized version so a release cut by hand is recorded the same way. Notes attach to commits rather than tags, so the mirrored tag picks the channel up. Claude-Session: https://claude.ai/code/session_01WKRv7ht6XA9Sm8brKctEva Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.