API reference (the engine surface)¶
The public surface a pipeline or plugin author calls. Signatures are the source of truth
(../rung/); this is the map. Everything is psycopg-based synchronous SQL except the
access-method runners, which are async.
rung.db — connection + schema¶
db.get_connection() -> DBConn # reads DATABASE_URL (default: local dev Postgres)
db.create_engine_tables(conn) -> None # create ONLY the generic infra tables (idempotent); commits
db.one(conn, sql, params=()) -> tuple # fetch a single row
DBConn # = psycopg.Connection; every signature takes one
db.create_engine_tables() builds only the domain-neutral engine tables (jobs, access_methods,
token_buckets, proxies, proxy_tiers) — your pipeline then creates and writes its own tables directly.
(db.create_tables() also builds the cannabis reference schema via db.create_reference_tables(); the
reference application uses that.)
rung.access — the cost-ranked ladder¶
@dataclass(frozen=True)
class AccessMethod:
name: str
cost_rank: int # try-priority: lower is tried first
run: MethodRunner
# a runner is: async (conn, target_key, hint) -> (records, resource_url, params)
MethodHint = tuple[str | None, dict | None] # the locator that won last time (None, None on first run)
MethodResult = tuple[list, str | None, dict | None]
async def run_target(
conn, target_type, target_key, catalog, *,
min_records=1,
governor=None, # ReExploreGovernor for staleness re-walks
plausible=access.is_plausible, # your "is this a good record?" predicate
max_yield=False, quality_key=len, # "best data now" mode (walk full ladder, keep richest)
) -> tuple[str | None, list]: # (winning_method_name, records)
run_target tries the stored winner first, re-walks cheapest-first on failure/staleness, persists
each attempt in access_methods, and commits per attempt. Provide plausible when your records
aren't store-shaped (name + address/city/zip). ReExploreGovernor and is_plausible/is_success are
also in this module.
rung.queue — the work queue (jobs table, FOR UPDATE SKIP LOCKED)¶
queue.worker_id() -> str # identifies this process
queue.enqueue(conn, task_type, target_key, payload=None) -> bool # insert if no live job exists; caller commits
queue.claim_next(conn, task_type, worker, target_prefix=None) -> Job | None # claim oldest pending; commits
queue.claim_target(conn, task_type, target_key, worker) -> Job | None # claim one specific job
queue.complete(conn, job_id, status, *, worker, error=None) -> bool # worker-scoped; caller commits
queue.reap_expired(conn, task_type) -> int # recover dead-worker leases
queue.prune_completed(conn, *, older_than_hours=168) -> int
@dataclass
class Job:
id: int
task_type: str
target_key: str
payload: dict | None
attempts: int
rung.registry — the plugin seam¶
registry.register(name, impl, *, override=True) -> None # provide a stage implementation
registry.resolve(name) -> Callable # the impl, or a stub raising StageNotAvailable
registry.load_plugins() -> list[str] # discover the rung.plugins entry-point group (idempotent)
registry.StageNotAvailable # raised by a stub when an unplugged stage is invoked
Register stages under the names the CLI resolves (see CONTRIBUTING.md's stage table), or any names
your own driver resolves.
rung.http — the honest session chokepoint¶
http.make_session(*, proxy=None, ...) -> AsyncSession # the ONLY sanctioned way to get a client
http.set_impersonation(profile | None) -> None # opt-in TLS impersonation (off by default)
http.HONEST_USER_AGENT # the self-identifying default UA
All network access must go through make_session() (AST-enforced by tests/test_http.py).
rung.rate_limit / rung.browser¶
rate_limit.try_acquire(conn, host, *, rate_per_sec, burst, cost) -> bool # cross-worker token bucket
browser.render_html(...) / browser.make_browser_options() / browser.get_script_value(...) # pydoll/Chrome
See concepts.md for what these do together, and
build-your-own-domain.md for a worked pipeline.