Sources¶
The Source step, and the shorthands that build one. A source's methodology is a location, which it reads rows through and content-addresses them. It is where data enters a plan.
matchlab.sources.sources
¶
Source — the leaf of a plan.
A source reads rows from a Location and content-addresses them. It takes no inputs,
so it is where raw data, and therefore non-determinism, enters a plan. Its spec key
includes a hash of the data it read, which is what makes a freshly constructed Source
pick up changes at the origin while an existing object memoises its read.
Classes:
-
Source–A location's rows, read and content-addressed.
Functions:
-
add_location_class–Register a custom location so it can be named in a plan document.
-
resolve_location_class–Return a location class, looking a name up in the registry.
-
read_database–Read a source from a relational database.
-
read_dataframe–Read a source from a dataframe already in memory.
Source
¶
Source(location_class: type[Location] | str, name: str, location_settings: dict[str, Any] | None = None, location_resources: dict[str, Any] | None = None, key_field: str = 'id')
Bases: RecordStep
flowchart TD
matchlab.sources.sources.Source[Source]
matchlab.recordstep.RecordStep[RecordStep]
matchlab.steps.Step[Step]
matchlab.recordstep.RecordStep --> matchlab.sources.sources.Source
matchlab.steps.Step --> matchlab.recordstep.RecordStep
click matchlab.sources.sources.Source href "" "matchlab.sources.sources.Source"
click matchlab.recordstep.RecordStep href "" "matchlab.recordstep.RecordStep"
click matchlab.steps.Step href "" "matchlab.steps.Step"
A location's rows, read and content-addressed.
A source is a RecordStep. Read directly, its records are its extract, with id
set to each row's content-addressed leaf, so a model can match over it with no
intervening step.
Define a source.
Parameters:
-
(location_class¶type[Location] | str) –A
Locationsubclass, or its registered name. -
(name¶str) –The source's name within the plan. Prefixes every column this source contributes, so it must be usable in a SQL identifier.
-
(location_settings¶dict[str, Any] | None, default:None) –That location's configuration — the query, for a
RelationalDB. Serialisable, carried in a document, and hashed into this source's fingerprint. -
(location_resources¶dict[str, Any] | None, default:None) –Resources the location needs that cannot be serialised, keyed by field name, such as
{"client": Resource("warehouse", engine)}. Seematchlab.resources. -
(key_field¶str, default:'id') –The name of the unique identifier field. Read as a string whatever the location returns it as.
Raises:
-
ValueError–If the name could not prefix a SQL identifier, or if
key_fieldis not a column name. -
ResourceError–If a field was passed in the wrong one of
location_settingsandlocation_resources, or if one resource name covers two different objects.
Methods:
-
f–Prefix one or more field names with this source's name.
-
fetch–Read from the location and yield the resulting rows in batches.
-
sample–Peek at the first
nrows without collecting. -
leaves–Return
(key, leaf), each source key mapped to its leaf cluster. -
collect–Materialise this step and everything it depends on.
-
lineage–Return this step and all its inputs, upstream-first.
-
draw–Render this step's sub-plan as a tree.
-
fingerprints–Address every artifact this plan is made of, its own and its inputs'.
-
identifiers–Return
(id, source, key, leaf)for every record this record step reads. -
data–Return this record step's records, collecting the plan first if needed.
-
transform–Reshape this record step with a transformer.
-
select–Keep only the named columns, plus
id. -
clean–Derive columns with DuckDB SQL, keeping the rest.
-
group–Collapse each
idto one row using aggregate SQL. -
dedupe–Deduplicate this record step.
-
link–Link this record step to another. A
Sourceis one, needing no wrapping.
Attributes:
-
kind(StepKind) – -
location(Location) – -
location_class(type[Location]) – -
location_settings(dict[str, Any]) – -
location_resources(dict[str, Resource]) – -
name(str) – -
key_field(str) – -
parents(tuple[Step, ...]) –A source is a leaf in a plan.
-
spec(SourceSpec) –The serialisable spec for this source.
-
qualified_key(str) –This source's key field, prefixed with the source name.
-
index_fields(list[str]) –Every column the extract returns except the key, in sorted order.
-
is_collected(bool) –Whether this step has been materialised.
index_fields
property
¶
Every column the extract returns except the key, in sorted order.
Read from the location rather than declared, so it cannot drift from what the location actually returns.
f
¶
Prefix one or more field names with this source's name.
fetch
¶
fetch(qualify_names: bool = False, batch_size: int | None = None, return_type: DataFrameType = POLARS) -> Generator[DataFrameClass, None, None]
Read from the location and yield the resulting rows in batches.
sample
¶
sample(n: int = 100, return_type: DataFrameType = POLARS) -> DataFrameClass
Peek at the first n rows without collecting.
collect
¶
collect(store: Store | None = None, interactive: bool | None = None) -> Self
Materialise this step and everything it depends on.
Steps whose artifact is already stored are skipped without being run, so re-collecting after adding a downstream step only does the new work.
Reports as it goes: the plan, a record per step, and a closing summary of what
ran, what was cached, how long it took and what the store now holds. No logging
setup is needed for any of that — a collection lends the matchlab logger a
console handler where the application hasn't configured one, and leaves an
application that has entirely alone. See matchlab.core.logging.audible.
Parameters:
-
(store¶Store | None, default:None) –Where to read and write artifacts. Defaults to the module-level store (a DuckDB store in the user cache directory).
-
(interactive¶bool | None, default:None) –Whether someone is watching.
None, the default, takes a terminal or a notebook as a yes. When they are, the plan is drawn as a live tree redrawn in place, and not logged. The tree on screen is the key those[step N]records need, and it stays there. When they are not, the plan is logged instead. Seematchlab.progress.
Returns:
-
Self–This step, now collected.
fingerprints
¶
fingerprints() -> set[Fingerprint]
Address every artifact this plan is made of, its own and its inputs'.
Which artifacts belong to a plan is the plan's own business, so this is where
a store gets told: store.prune(keep=plan.fingerprints()) hands storage a set
of addresses it already understands, rather than a graph it would have to learn
to walk.
Returns:
-
set[Fingerprint]–One fingerprint per step in
lineage(). A set, because two steps in one -
set[Fingerprint]–plan can address the same artifact. Identical specs over identical
-
set[Fingerprint]–inputs is the same bytes, and it is stored once.
Raises:
-
RuntimeError–If any step has not been collected. An uncollected plan names no artifacts at all, so answering with a smaller set would quietly tell a caller that less is worth keeping than they think.
identifiers
¶
identifiers(store: Store) -> DataFrame
Return (id, source, key, leaf) for every record this record step reads.
id is the resolver's entity root when reading through one, otherwise the
source leaf. This is the upstream resolver output a downstream resolver needs to
carry every reachable leaf forward, including records no model matched.
data
¶
Return this record step's records, collecting the plan first if needed.
transform
¶
transform(transformer: Transformer | type[Transformer] | str, transformer_settings: dict | None = None, transformer_resources: dict | None = None) -> Transform
Reshape this record step with a transformer.
clean
¶
Derive columns with DuckDB SQL, keeping the rest.
group
¶
Collapse each id to one row using aggregate SQL.
add_location_class
¶
Register a custom location so it can be named in a plan document.
resolve_location_class
¶
Return a location class, looking a name up in the registry.
How Source accepts either the class or its registered name, and how
matchlab.document rebuilds the locations a plan reads.
Raises:
-
ValueError–If no location class of that name is registered here.
read_database
¶
read_database(name: str, *, sql: SQLQuery, client: DBClient | Resource[DBClient], key_field: str = 'id') -> Source
Read a source from a relational database.
Parameters:
-
(name¶str) –The source's name within the plan.
-
(sql¶SQLQuery) –The query producing the rows. Every column other than the key contributes to record identity. Run as written, so trust it: see
RelationalDB. -
(client¶DBClient | Resource[DBClient]) –A SQLAlchemy engine or ADBC connection, or a
Resourcenaming one. Name it to dump the plan; share the sameResourcebetween sources reading one warehouse. -
(key_field¶str, default:'id') –The unique identifier column.
Returns:
-
Source–The source, uncollected.
read_dataframe
¶
read_dataframe(name: str, *, df: DataFrameClass | Resource[DataFrameClass], key_field: str = 'id') -> Source
Read a source from a dataframe already in memory.
Parameters:
-
(name¶str) –The source's name within the plan.
-
(df¶DataFrameClass | Resource[DataFrameClass]) –A polars, pandas or arrow frame, or a
Resourcenaming one. Every column other than the key contributes to record identity, so shape the frame before handing it over. -
(key_field¶str, default:'id') –The unique identifier column.
Returns:
-
Source–The source, uncollected.
matchlab.sources.base
¶
The contract every location implements.
A Location is a methodology, exactly as a Deduper or a Transformer is. A Source
names the class and hands it settings and resources separately, so the query travels in
a document and the client never does. See matchlab.resources.
Classes:
-
Location–A place data is read from, holding everything needed to read it.
Location
¶
Bases: BaseModel, ABC
flowchart TD
matchlab.sources.base.Location[Location]
click matchlab.sources.base.Location href "" "matchlab.sources.base.Location"
A place data is read from, holding everything needed to read it.
Every field is a setting unless marked matchlab.resources.FromResources.
A location declares no version, unlike the methodologies the other kinds of step
run. A source hashes the rows it read into its own spec key, so a change in what a
location returns already moves the fingerprint.
Methods:
-
read–Get this location's rows, in batches.
Attributes:
model_config
class-attribute
instance-attribute
¶
read
abstractmethod
¶
read(batch_size: int | None = None, rename: dict[str, str] | Callable | None = None, return_type: DataFrameType = POLARS, schema_overrides: dict[str, DataType] | None = None) -> Iterator[DataFrameClass]
Get this location's rows, in batches.
Parameters:
-
(batch_size¶int | None, default:None) –The size used for internal batching.
-
(rename¶dict[str, str] | Callable | None, default:None) –Renaming to apply to the rows that come back.
- If a dictionary is provided, it will be used to rename the columns.
- If a callable is provided, it will take the old name as input and return the new name.
-
(return_type¶DataFrameType, default:POLARS) –The type of data to return. Defaults to "polars".
-
(schema_overrides¶dict[str, DataType] | None, default:None) –Types to force on the columns that come back, rather than letting the location infer them.