provcat.resolver
Resolver is the interface. TemplateResolver is the default implementation. It
reads the schemes and stages sections of the configuration::
schemes:
data+table:
parts: [org, domain, env, family, name] # names for authority + segments
target: "lakefs://{org}-{domain}/{ref}/tables/{family}/{name}"
ref: "{env}" # default for {ref}; may name parts
match: {org: "[a-z0-9]+", domain: "[a-z0-9]+"} # each part must match
code:
parts: [org, repo]
rest: path # extra segments are joined into {path}
target: "github://{org}/{repo}/{path}"
pass_selector: true # hand ?at= on to the physical URI
# tenant: org # the part that names the tenant (see below)
stages:
"data+table://acme/shared/prod/agreements/qa_outputs":
production: "commit:b8a2f9c"Selectors on a logical URI:
branch:X,tag:X,commit:Xset{ref}to X.version:Nsets{version}to N.{ref}keeps its default.- A bucket (the s3, gcs and azure drivers) keeps only branches in its path,
and a fixed version goes in
?at=. So for a target whose store is a bucket,commit:IDkeeps{ref}at its default and goes on to the physical URI as?at=commit:ID.version:IDdoes the same when the target has no{version}.tag:is refused, because a bucket has no tags. The resolver asksrefs_in_pathwhich stores are buckets; the catalog gives it one that reads the attribute of the same name on each store. A resolver without it treats every store as lakeFS does. - A selector that nothing uses is refused, never dropped.
version:needs{version}, andbranch:,tag:andcommit:need{ref}, unless the target has{at}, the scheme haspass_selector, or the store is a bucket that takes the selector in?at=. stage:NAMElooks up NAME instagesand uses the selector stored there.ts:TIMEkeeps{ref}at its default, and goes on to the physical URI as?at=ts:TIME, with or withoutpass_selector. Only the store can say which commit a branch had at TIME, so the lakefs and github drivers resolve it. A target without{ref}loses the branch, so the resolution keeps it asts_branch. The catalog gives a GitHub store?at=ts:BRANCH@TIME.
Template variables are the part names, the rest name, ref, version and
at (the whole selector as kind:value). Each value is percent-encoded
before it goes into the template, so a value can never add a path segment or a
?at= to the physical URI.
With pass_selector, the physical URI gets the selector that the resolver
used: a stage becomes the selector it points at, and a scheme with a ref
default passes branch:<ref> when the URI has no selector. With version:,
only the version is passed.
tenant names the part that says whose a URI is, for example org. A
resolver made for one tenant refuses a URI of another tenant of such a scheme.
A scheme without tenant serves every tenant.
match gives a regular expression for a part. A URI whose part does not
match the whole expression is refused, so that names such as lakeFS
repository names stay valid and cannot run together.
Entries send one logical name, or every name under a prefix, somewhere other than the template of its scheme::
entries:
"code://acme/tokenizer/prod":
target: "github://acme/tokenizer/{path}?at=branch:prod"
"code://acme/tokenizer/prod/weights":
target: "hf://models/acme/tokenizer/{path}"
"data+table://acme/shared/prod/corpora":
target: "lakefs-acme://acme-corpora/{ref}/{path}"
ref: main- The longest key that is a prefix of the URI wins, by whole parts. A URI that no key matches goes to the template of its scheme.
{path}is the rest of the URI after the key, each part encoded. A target without{path}serves only the URI that is its key.{ref}is for a store that keeps the branch in the path, such as lakeFS.branch:,tag:andcommit:on the URI fill it, andrefis the default. A target without{ref}takes the selector of the URI in place of its own?at=.ts:TIMEkeeps the branch of the target. A target that names a branch in?at=gives it asts_branch, as a template does. A target fixed to a tag or a commit refusests:.- A target whose store is a bucket follows the bucket rules above:
commit:andversion:keep{ref}atrefand go on as?at=, andtag:is refused. Any other store refusesversion:. - A stage of the URI comes first, and its selector then applies as above.
- A key must be a URI of a logical scheme, with no
?at=. Its parts must fit thepartsandmatchof the scheme as far as the key goes. The store of a target is fixed text.
class Resolution
Resolution(logical: Uri, physical: Uri, via: tuple[str, ...] = (), ts_branch: str | None = None)The answer of a lookup: the URI asked for, the physical URI it names, and how.
| Parameter | Type | Default | Description |
|---|---|---|---|
logical |
Uri | required | The URI asked for; for a physical URI, the URI itself |
physical |
Uri | required | The physical URI, whose scheme names a store |
via |
tuple[str, …] | () |
The steps taken, for people to read |
ts_branch |
str | None | None |
The branch of a ts: that the physical URI has no place for |
class Resolver
The interface of a resolver, which turns logical URIs into physical ones.
Resolver.handles
Resolver.handles(scheme: str) -> boolTell whether this resolver resolves the URIs of a scheme.
| Parameter | Type | Default | Description |
|---|---|---|---|
scheme |
str | required | A URI scheme |
| returns | bool | Whether scheme is a logical scheme of this resolver |
Resolver.resolve
Resolver.resolve(uri: Uri) -> ResolutionTurn a logical URI into a physical one.
| Parameter | Type | Default | Description |
|---|---|---|---|
uri |
Uri | required | A logical URI of a scheme that this resolver handles |
| returns | Resolution | The physical URI and the steps taken; ResolveError when the URI does not fit |
class TemplateResolver
TemplateResolver(schemes: dict | None = None, stages: dict | None = None, tenant: str | None = None, entries: dict | None = None, refs_in_path: Callable[[str], bool | None] | None = None)The default resolver: fills the target template of a scheme, or of an entry, from the parts of the URI.
| Parameter | Type | Default | Description |
|---|---|---|---|
schemes |
dict | None | None |
The schemes section: each logical scheme’s parts, target and rules |
stages |
dict | None | None |
The stages section: for each logical URI, stage names and their selectors |
tenant |
str | None | None |
The tenant of the catalog; a scheme with tenant: refuses another tenant’s URIs. None serves every tenant |
entries |
dict | None | None |
The entries section: targets for one logical name or a prefix |
refs_in_path |
Callable[[str], bool | None] | None | None |
Given a store name: False for a bucket, which keeps only branches in its path, True for a store such as lakeFS, None for a name it does not know. None takes every store to keep refs in its path |
TemplateResolver.handles
TemplateResolver.handles(scheme: str) -> boolTell whether scheme is one of the configured logical schemes.
| Parameter | Type | Default | Description |
|---|---|---|---|
scheme |
str | required | A URI scheme |
| returns | bool | Whether the schemes section names it |
TemplateResolver.serves
TemplateResolver.serves(uri: Uri) -> boolTell whether this resolver serves a URI; it refuses one of another tenant.
| Parameter | Type | Default | Description |
|---|---|---|---|
uri |
Uri | required | A logical URI |
| returns | bool | False when it names another tenant than this resolver’s |
TemplateResolver.resolve
TemplateResolver.resolve(uri: Uri) -> ResolutionResolve a URI through the entry with the longest matching key, or else the template of its scheme. A stage: selector becomes the selector it points at.
| Parameter | Type | Default | Description |
|---|---|---|---|
uri |
Uri | required | A logical URI of a configured scheme |
| returns | Resolution | The physical URI from the entry or the template, and the steps taken; ResolveError when the URI does not fit |