synology_apm_repo.sdk.api.session module

Session: repository discovery, key material, and the store/repository lifetime it owns, part of the Repository Layer (the Session/ Repository/Catalog split CLI/TUI code imports directly). Hands out Repository instances (api.repository) it discovered/opened; never the other way around.

class synology_apm_repo.sdk.api.session.Session

Bases: object

Use as a context manager, or call close explicitly when done – or close_repo to release just one repository (and its store, if unshared) ahead of the rest of a longer-running session.

async discover(path, key=None, *, progress=None, trace=None)

Walk path for repositories, yielding each as it’s found — an indeterminate-progress generator (discovery has an unknowable total), not a blocking call; see open for that convenience wrapper.

A layout iter_repository_layouts finds that then fails Repository._confirm_real()’s own cheap, marker-only check is skipped rather than aborting the whole scan — for OBJECT_STORE this only catches a bucket with zero valid catalog ids; for VAULT this check never actually fails (its one marker check already ran when the layout was built). Neither kind’s own corrupt repo_info is caught here — that surfaces later instead, scoped to the one broken catalog, the first time Repository.catalogs() actually opens it.

Local paths only — see discover_remote for an already-constructed ObjectStore (S3/Azure/…).

Parameters:
  • key (str | None) – When omitted, each yielded repository’s encryption status is still resolved eagerly via dedup.keys.probe_encrypted (see KeyStatus); skipped when a key is given, since Repository.key_verification already answers it.

  • trace (Callable[[TraceEvent], None] | None) – When given, every ObjectStore call any repository found here ever makes, for that repository’s entire lifetime, is reported as a TraceEvent.

async discover_remote(store, key=None, *, root='', progress=None, trace=None)

Same as discover, against an already-constructed ObjectStore (S3/Azure/…) instead of building a LocalFsStore from a filesystem path — for a caller that already knows which backend and bucket/container it wants (the TUI’s connect dialog). This session takes ownership of store the same way discover does its own LocalFsStore, released by close.

root narrows the scan to a store-relative sub-path, the same role a deeper local directory plays for discover — needed because S3/Azure stores are scoped to a whole bucket/container with no “sub-root” constructor argument, so a bucket holding several sibling repositories otherwise has no way to be narrowed to one.

async open(path, key=None, *, progress=None, trace=None)

discover, fully drained — the blocking convenience form for callers that don’t need incremental results but still want progress/cancel support: every CLI command that opens a repository needs the final list, but discovery can still take a while on a directory tree with many candidate layouts, so there’s no reason to give up incremental feedback just because the caller is going to wait for the whole list anyway.

async open_remote(store, key=None, *, root='', progress=None, trace=None)

discover_remote, fully drained — the blocking convenience form for a caller that wants the final list but still needs progress/cancel support during a possibly slow scan.

async resolve(ref)

Turn a NodeRef (or its string form) back into a live node, re-derived from cheap catalog lookups and provider tree calls rather than stored anywhere.

This is two jobs stacked: which open Repository ref belongs to (this method’s own job — matches ref.repo_path against each open repository’s own catalogs, via Repository.owns_repo_path, relevant once several repositories are open in one long-lived Session, e.g. a TUI), then what node inside it ref names (Repository.resolve’s job — see its docstring for the canonical/raw/human dispatch). A caller that already has the right Repository in hand (the common case for a one-shot CLI invocation, which opens exactly one repository per run) should call Repository.resolve directly and skip this matching step entirely.

async close_repo(repo)

Close and forget one repository this session tracks, releasing its own store too if no other still-tracked repository shares it.

Unlike a bare repo.close() (idempotent, safe ahead of Session.close()), this also removes repo from this session’s bookkeeping and, when its store isn’t shared with another repository this session still tracks, actually releases that store’s connector/session instead of waiting for Session.close() at process end. For a caller that discards one repository at a time from a longer-running session — the TUI reconnecting to a different profile, or a tool walking many repositories sequentially — rather than tearing the whole session down at once.

The caller must have finished draining the discover()/ discover_remote() call that yielded repo before calling this — a not-yet-yielded sibling repository sharing the same store isn’t in self._repos yet, so the shared-store check below would false-negative and close the store out from under it. Both of this session’s real callers (a fully-drained sample loop, a fully-drained reconnect scan) already satisfy this.

Every tracked store sharing repo’s own backing store (there can be more than one distinct TracingStore wrapper around the same backing, from separate discover()/discover_remote() calls) is released together once nothing in self._repos still references it, not just the one wrapper repo itself used — otherwise a sibling wrapper from an earlier call would sit in self._stores forever, never reachable by identity again.

async close()

Close every repository this session opened, then release any backend client it owns.

S3Store/AzureStore own an aiohttp connector that must be released; local stores have no aclose() and are skipped.