Skip to content

SY-4130: Add Python client support for server-side import and export#2324

Open
pjdotson wants to merge 35 commits into
rcfrom
sy-4130-add-python-client-support-for-server-side-import-and-export
Open

SY-4130: Add Python client support for server-side import and export#2324
pjdotson wants to merge 35 commits into
rcfrom
sy-4130-add-python-client-support-for-server-side-import-and-export

Conversation

@pjdotson

@pjdotson pjdotson commented May 18, 2026

Copy link
Copy Markdown
Contributor

Issue Pull Request

Linear Issue

SY-4130

Description

Add Python client support for server-side import and export.

Basic Readiness

  • I have performed a self-review of my code.
  • I have added relevant, automated tests to cover the changes.
  • I have updated documentation to reflect the changes.

Greptile Summary

This PR adds Python client support for server-side import and export (imex) via three new transport modes: in-memory typed send, file-path-based streaming upload, and streaming download to a destination path.

  • freighter/py gains UploadClient and DownloadClient protocols; HTTPClient is refactored to accept an encoder and a list of decoders for content negotiation, replacing the single-codec approach.
  • A new synnax.imex module wraps these transports, and core/pkg/api/http/http.go renames the ImEx routes from /api/v1/{import,export} to /api/v1/imex/{import,export} to match.
  • MsgPackCodec is renamed MessagePackCodec, and both codecs gain a file_extension() method used to infer wire format from a file path.

Confidence Score: 5/5

Safe to merge; the new imex transport layer and server-side route rename are self-contained and well-tested.

The core transport refactor in HTTPClient is clean and backed by thorough upload/download tests. The imex client, Envelope type, and Go route rename all look correct. The only open items are known deferred clean-ups already acknowledged by the team, and a couple of minor documentation inaccuracies.

No files require special attention.

Important Files Changed

Filename Overview
freighter/py/freighter/http.py Major refactor: HTTPClient gains upload/download methods, splits codec into encoder+decoders, adds Accept/Content-Type negotiation. Minor docstring inaccuracy in upload (claims "chunked transfer" but seekable files get Content-Length).
client/py/synnax/imex/client.py New imex client wrapping unary, upload, and download transports. Clean overload design for export; shadows the built-in id (already noted in thread). Logic is straightforward.
client/py/synnax/imex/types.py Envelope model uses extra="allow" to pass through schema-specific fields at the top level; correctly round-trips via model_dump_json.
freighter/py/freighter/download.py New DownloadClient protocol; clean definition matching the HTTPClient.download implementation.
freighter/py/freighter/upload.py New UploadClient protocol; clean definition matching the HTTPClient.upload implementation.
freighter/py/freighter/codec.py MsgPackCodec renamed to MessagePackCodec; TracingCodec removed (dead code); file_extension() added to both codecs for content negotiation.
client/py/synnax/transport.py Updated to pass encoder/decoders list to HTTPClient and expose upload/download on Transport; MsgPackCodec renamed to MessagePackCodec.
client/py/tests/test_imex.py Good coverage: unit tests for Envelope (no server), plus live integration tests for envelope import/export, path-based upload, and path-based download.
core/pkg/api/http/http.go ImEx server routes renamed from /api/v1/import and /api/v1/export to /api/v1/imex/* to match the new Python client paths.
freighter/py/tests/test_http.py Expanded HTTPClient tests covering upload/download for JSON and MessagePack, unsupported extensions, and large payloads.

Sequence Diagram

sequenceDiagram
    participant User
    participant ImexClient
    participant HTTPClient
    participant Server

    Note over User,Server: import_(Envelope) — in-memory typed send
    User->>ImexClient: import_(envelope)
    ImexClient->>HTTPClient: send("/imex/import", envelope, _ImportResponse)
    HTTPClient->>Server: POST /api/v1/imex/import Content-Type: application/json
    Server-->>HTTPClient: "200 {key: uuid}"
    HTTPClient-->>ImexClient: (_ImportResponse, None)
    ImexClient-->>User: key: str

    Note over User,Server: import_(FilePath) — streaming file upload
    User->>ImexClient: import_(path)
    ImexClient->>HTTPClient: upload("/imex/import", path, _ImportResponse)
    HTTPClient->>Server: POST /api/v1/imex/import Content-Type: inferred from extension
    Server-->>HTTPClient: "200 {key: uuid}"
    HTTPClient-->>ImexClient: (_ImportResponse, None)
    ImexClient-->>User: key: str

    Note over User,Server: export(id) — in-memory typed response
    User->>ImexClient: export(id)
    ImexClient->>HTTPClient: send("/imex/export", id, Envelope)
    HTTPClient->>Server: POST /api/v1/imex/export Accept: application/json
    Server-->>HTTPClient: 200 Envelope JSON
    HTTPClient-->>ImexClient: (Envelope, None)
    ImexClient-->>User: Envelope

    Note over User,Server: export(id, dest=path) — streaming download
    User->>ImexClient: "export(id, dest=path)"
    ImexClient->>HTTPClient: download("/imex/export", id, dest)
    HTTPClient->>Server: POST /api/v1/imex/export Accept: inferred from dest extension
    Server-->>HTTPClient: 200 chunked body
    HTTPClient->>HTTPClient: stream chunks to dest file
    HTTPClient-->>ImexClient: None
    ImexClient-->>User: None
Loading

Comments Outside Diff (1)

  1. freighter/py/freighter/http.py, line 250-262 (link)

    P2 Error Content-Type mismatch silences the original error body

    _decode_error delegates to _resolve_decoder, which returns a generic ValueError when the error response carries an unregistered or absent Content-Type. The real error bytes are embedded in the message but not decoded, so callers see "no decoder" rather than the server's actual message. Any proxy or gateway returning text/html on a 4xx/5xx will lose its error message entirely.

Reviews (12): Last reviewed commit: "Merge branch 'rc' into sy-4130-add-pytho..." | Re-trigger Greptile

@pjdotson pjdotson self-assigned this May 18, 2026
@pjdotson pjdotson changed the base branch from main to rc May 18, 2026 05:25
@codecov

codecov Bot commented May 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.36585% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.18%. Comparing base (7963e01) to head (85179ec).

Files with missing lines Patch % Lines
freighter/py/freighter/http.py 77.58% 26 Missing ⚠️
client/py/synnax/imex/client.py 91.89% 3 Missing ⚠️
freighter/py/freighter/codec.py 90.90% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##               rc    #2324      +/-   ##
==========================================
- Coverage   66.53%   66.18%   -0.36%     
==========================================
  Files        2602     2444     -158     
  Lines      115123   113048    -2075     
  Branches     8610     8678      +68     
==========================================
- Hits        76601    74821    -1780     
+ Misses      32316    32120     -196     
+ Partials     6206     6107      -99     
Flag Coverage Δ
client-py 85.93% <94.91%> (+0.07%) ⬆️
client-ts 90.68% <ø> (ø)
console 22.43% <ø> (ø)
core 72.36% <ø> (-0.02%) ⬇️
freighter-py 80.05% <81.50%> (-0.18%) ⬇️
pluto 59.80% <ø> (-0.40%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…nt-support-for-server-side-import-and-export

# Conflicts:
#	freighter/integration/go.mod
#	freighter/integration/go.sum
Comment thread freighter/py/freighter/codec.py
Comment thread client/py/synnax/imex/client.py Outdated
Comment thread freighter/py/freighter/http.py
…nt-support-for-server-side-import-and-export
Comment thread freighter/py/freighter/http.py
Comment thread freighter/py/freighter/http.py
Comment thread freighter/py/freighter/http.py Outdated
Comment thread freighter/py/freighter/http.py
@pjdotson pjdotson requested a review from emilbon99 May 19, 2026 21:26

@emilbon99 emilbon99 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Concerns about the protocol class additions. Why can't we just use UnaryClient? The download and upload clients seem like relatively minor variations.
  2. What happens when we do import/export of compound data structures i.e. entire system configurations? Do we care about supporting that in the imex client?

codec) need only satisfy Codec.
"""

def file_extension(self) -> str:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this just be called extension?

from freighter.transport import RQ, FilePath, Transport


class DownloadClient(Transport, Protocol):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't totally understand why these need a different protocol than Unary. Why isn't the DownloadClient something like a DownloadTransport that is a concrete implementation of Unary whose request type accepts a destination as its file type? Seems like we should be trying to avoid adding different transport types to freighter if possible.

…nt-support-for-server-side-import-and-export
…nt-support-for-server-side-import-and-export
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants