Synchronoid is in private beta. New workspaces open in small batches every few weeks. Join the waitlist →

Python SDK reference

The synchronoid package contains everything you need to define assets, steps, pipelines, sources, schedules and sensors. It requires Python 3.10 or newer.

terminal
$ pip install synchronoid

By convention the package is imported as sy:

pipelines/example.py
import synchronoid as sy

Decorators

@sy.asset

Declares a table-like dataset in one layer, and the function that produces it. The function receives the context and one argument per dependency. It returns or yields records.

ParameterTypeDescription
layer"raw" | "staged" | "published"Storage layer. Required.
sourcesy.SourceSource this asset reads from.
depslist[Asset]Upstream assets.
partitionssy.PartitionsPartition definition.
retrysy.RetryRetry policy for the asset.
namestrDefaults to the function name.
descriptionstrShown in the control room.
checkslist[sy.Check]Data quality checks that run after the asset. Built-in checks: sy.checks.schema, sy.checks.unique, sy.checks.fresh.
max_concurrencyintLimit for parallel partition runs.
retentionsy.RetentionHow long data is kept. Defaults depend on the layer, see Storage layers.
schemaclassTyped schema that records must match, for example a dataclass.
provenanceboolRecord provenance per value. Default True.

@sy.step

Declares one unit of work inside a pipeline. A step is the smallest part that is retried and resumed.

ParameterTypeDescription
retrysy.RetryOverrides the pipeline policy.
sourcesy.SourceEnables ctx.source and rate limiting.
timeoutstrFor example "15m". Default "1h".

@sy.pipeline

Groups steps into an ordered graph.

pipelines/listings.py
@sy.pipeline(schedule=sy.Schedule(cron="0 * * * *"))
def listings_sync():
    ref = fetch_listings()
    clean = normalize(ref)
    publish(clean)

Parameters: schedule (sy.Schedule), retry (sy.Retry), name (str), tags (dict[str, str]).

@sy.sensor

Polls for an outside event and starts a run when it finds one. The function returns a list of run requests, or nothing.

pipelines/sensors.py
@sy.sensor(target=companies_current, every="5m")
def new_export_file(ctx):
    latest = ctx.source.head("/exports/latest.json")
    if latest.etag != ctx.cursor:
        ctx.cursor = latest.etag
        return sy.RunRequest(run_key=latest.etag)
    return sy.SkipRun(reason="no new file")

Classes

ClassSignatureDescription
sy.SourceSource(name, connector, base_url=None, auth=None, rate_limit=None, **options)A data provider and how to reach it. See Sources and connectors.
sy.RateLimitRateLimit(per_second=None, per_minute=None, per_hour=None, burst=None, reserve=0.0, max_wait="10m", concurrency=None, adaptive=True, retry_after_header="Retry-After")Shared request budget for a source.
sy.RetryRetry(max_attempts=3, backoff="exponential", initial_delay="1s", max_delay="10m", jitter=True, retry_on=None)Retry policy.
sy.ScheduleSchedule(cron, target=None, timezone="UTC", on_missed="skip", max_concurrent_runs=1, partition=None, enabled=True)Time-based trigger. See Schedules and sensors.
sy.PartitionsPartitions.daily(start, end=None), Partitions.years(a, b), Partitions.static(keys)Partition definitions.
sy.RetentionRetention(days)How long an asset's data is kept.
sy.SecretSecret(name)Reference to a secret, for use in sy.Source and sy.Destination options.
sy.DestinationDestination(connector, name=None, **options)A warehouse that receives published assets. Call .publish(asset, table=...). See Snowflake.
sy.dbt_projectdbt_project(project_dir, profiles, select=None, command="build", after=None)Runs dbt models after assets. See dbt.
sy.CheckResultCheckResult(passed, detail="")Returned by functions decorated with @sy.check(asset=..., severity="error"|"warn").
sy.RunRequest, sy.SkipRunRunRequest(run_key, partition=None), SkipRun(reason)Returned by sensors. The run key prevents duplicate runs.
sy.OnChange, sy.OnCreate, sy.OnDeleteOnChange(*fields)Event rules for merge(events=...). See Change feeds.

The context object

The first argument of every asset, step and sensor is ctx.

MemberTypeDescription
ctx.logLoggerStructured logger: debug, info, warning, error. Lines appear in the run page.
ctx.provenanceProvenanceSource, fetch time and run id. Use ctx.provenance.note(field=..., reason=...) to add a note to a produced value.
ctx.run_idstrId of the current run.
ctx.run_timedatetimeLogical time of the run. Use it instead of the system clock.
ctx.partitionstr | NoneCurrent partition key.
ctx.attemptintAttempt number, starting at 1.
ctx.idempotency_keystrStable per step and partition. Send it with outside writes.
ctx.sourceSourceClientRate-limited client: get, head, acquire(cost).
ctx.rawRawStoreRaw layer client: put(payload) or put(key, payload) returns a reference, exists(key), ref(key).
ctx.secret(name)strReads a secret for the current environment.
ctx.cursorstr | NonePersistent value for sensors.
ctx.add_metadata(dict)NoneAttaches metadata, such as a row count, to the current materialization. Shown in the control room.

Provenance is automaticYou rarely call ctx.provenance yourself. Every value you yield gets its source, fetch time and run id. See Provenance.

Errors and client

Raise sy.errors.Timeout or sy.errors.HttpStatus(429) to trigger the matching retry_on rule. Raise sy.errors.CheckFailed("...") inside an asset to stop it before its output is published. To read published data from other Python code, use sy.Client().assets.get("companies_current").record(key, with_provenance=True). Provenance contains source, fetched_at, run_id, step, payload_ref, pointer and observed_since.

Custom connectors are declared with @sy.connector(name=...) and extend Connector from synchronoid.connectors.

Layer data objects

MethodDescription
raw.records(**filters)Iterates over decoded records from a raw payload.
raw.payloads()Iterates over untouched payload bytes.
data.merge(key, track_changes=False, events=None)Merges records into the published layer by key.
data.filter(fn), data.map(fn)Lazy transformations. Nothing loads until the step ends.
data.count()Number of records, computed in the storage layer.

Testing

Use sy.testing.run_asset(asset, inputs=...) in unit tests. It runs the function with a fake context and returns the yielded records, so no network or account is needed.

tests/test_companies.py
from synchronoid.testing import run_asset

def test_names_are_trimmed():
    out = run_asset(company_profiles, raw=[{"id": "1", "name": " Acme ", "address": "x"}])
    assert out[0]["name"] == "Acme"