Assets, Sensors and Inputs
An asset is a function whose output barca caches and tracks.
from barca import asset, Always, Manual, Schedule
@asset() # freshness=Always (default)def raw() -> dict: return {"x": 1}
@asset(inputs={"data": raw}, retries=3, retry_backoff=2.0, timeout_seconds=600)def clean(data: dict) -> dict: return {"x": data["x"] + 1}
@asset(freshness=Manual) # computed only when named in --refreshdef pinned() -> dict: return {"x": 0}
@asset(freshness=Schedule("0 5 * * *")) # fires daily at 05:00 under `barca serve`def daily() -> dict: return {"x": 2}Qualified and aliased imports
Section titled “Qualified and aliased imports”Barca recognizes top-level imports such as import barca, import barca as b
and from barca import asset as a, for decorators and their helpers:
import barca as b
@b.asset(partitions={"key": b.partitions(["a", "b"])})def rows(key): return {"key": key}Planning reads these bindings statically and never imports your module. A name
reassigned or imported from another library does not define a barca node; local
parameters or assignments also shadow task-body helpers such as parallel.
Dynamic alias assignments and conditional imports are not resolved. Explicit writes to a barca module export
conservatively disable recognition of that export through every module alias and
direct import, including earlier copies; unaffected exports remain available.
If a foreign decorator previously became a node merely because it was named
asset, sensor or task, import the actual barca decorator instead. To keep a
foreign wrapper on a node, stack it with a genuine barca decorator (qualified or
aliased imports work). Bare names with no competing binding retain legacy
source-snippet recognition. Namespace reflection retains conservative discovery,
but disables provenance-based validation and uses conservative hashing.
A later from shared import * preserves imported barca names when the project module has
one literal __all__ list or tuple of strings, with no other references to __all__,
that does not export those local names.
Only colliding names lose recognition, including imported aliases and group. Barca uses
the same package and sibling import paths as its workers and reads the source without
importing it. Missing, computed, conditional or mutated export sets, installed modules and
namespace reflection remain uncertain; put the barca import last or use explicit imports.
Star-import dependency hashing remains conservative.
Options on @asset
Section titled “Options on @asset”| Option | Meaning |
|---|---|
inputs={"param": upstream} |
Wire upstream nodes to parameters. The param name must match a function parameter; barca checks this when it reads the file (“Inputs and parameters” below). |
name="..." |
Explicit node name (the default is the function name). |
freshness= |
Always (default), Manual, or Schedule("<cron>", max_failures=5, timeout_seconds=3600). See barca docs scheduling. |
serializer= |
Force "json", "pickle", "parquet" or "csv". See barca docs types. |
partitions= |
Fan one asset out over keys. See barca docs partitions. |
timeout_seconds= |
Per-attempt time limit (default 300). |
retries= |
Total attempts on failure; 1 means no retry. |
retry_backoff= |
Base delay in seconds; the delay grows linearly with the attempt number. |
env=["NAME", ...] |
Environment variables the function reads. Their values are part of the cache key and are reported per step. See below. |
weight= |
How much of the pool’s capacity the step takes while it runs (default 1). Experimental. See “Heavy steps” below. |
deterministic=False |
@asset only: new bytes for the same code and inputs are expected, so barca does not warn that the function is not deterministic. Caching is unchanged. See barca docs cache, “Recompute byte checks”. |
cutoff=True |
What reads the asset depends on the bytes it wrote, not its run hash: a rerun that writes the same bytes leaves everything below it cached. @asset only. Experimental. See barca docs cache, “Early cutoff”. |
batch= |
On partitioned assets and tasks, group stale keys into calls: a positive integer, "all", batches([...]) or batches_by(fn). The function receives lists of keys; caching stays per key. See barca docs partitions, “Processing many keys per call”. |
description=, tags= |
Metadata. |
Stacked under @asset, @sink("path") writes the output to a file as well, and
@sink("prefix/", partition_by=[...], rows_per_file=N) (a path ending in /) writes it as a
Hive-partitioned dataset with one directory level per partition dimension of the asset. A sink
that fails fails the step. See barca docs sinks.
inputs=, serializer= and the dimension names in partitions= are part of the asset’s run
hash: changing one runs the asset again. The other options, the partition keys and the
formatting of the decorator are not: editing them re-runs nothing. barca docs cache (“Which
decorator arguments count”) has the full list and the reason for each.
A step that raises an exception fails without stopping its worker: the same Python process
runs the next step, or the retry, so module-level state carries over from a failed attempt. A
worker is replaced by a fresh one only when the step called sys.exit(), raised MemoryError,
was still running at its timeout_seconds, failed while its module was imported, or crashed
the interpreter.
These are the only options: any other is an error (“Accepted arguments” below).
Accepted arguments
Section titled “Accepted arguments”These are all the arguments barca’s decorators and helpers take:
| Call | Positional arguments | Keyword arguments |
|---|---|---|
group() |
one | members, output, description |
@asset |
none | name, inputs, partitions, serializer, freshness, timeout_seconds, retries, retry_backoff, description, tags, env, weight, deterministic, cutoff, batch |
@sensor |
none | name, partitions, serializer, freshness, timeout_seconds, retries, retry_backoff, description, tags, env, weight |
@task |
none | name, inputs, partitions, serializer, freshness, timeout_seconds, retries, retry_backoff, description, tags, env, weight, batch |
@sink |
one | serializer, partition_by, rows_per_file |
partitions() |
one | none |
partitions_from() |
one | none |
batches() |
one | strict |
batches_by() |
one | max_size |
collect() |
one | none |
asset_ref() |
one | none |
Schedule() |
one | max_failures, timeout_seconds, batch |
Every argument in the table does something, on each decorator it is listed for: on @task and
@sensor, partitions= runs the function once per key and serializer= forces the format its
value is stored in, as on @asset.
Any other argument is an error when barca reads the file, before anything runs: exit 2, kind
usage. Up to 0.18.1 such an argument was ignored without a message, so @asset(after=other)
or a misspelt input= planned, ran and did nothing. Only arguments that had no effect are
rejected: a pipeline whose arguments all did something plans exactly as before.
After from barca import asset:
@asset(input={"raw": raw}) # `input` for `inputs`def report(raw: dict) -> int: return raw["n"]$ barca list pipeline.py --prettyParse error: pipeline.py:report (line 9): `input` is not an argument of @asset. Did you mean `inputs`? @asset accepts: name, inputs, partitions, serializer, freshness, timeout_seconds, retries, retry_backoff, description, tags, env, weight, deterministic, cutoff, batchRename `input` to `inputs`, or remove it. See `barca docs assets`.In JSON mode the first line is the envelope’s error and the second its remediation
(barca docs contract).
- The error names the node (
file.py:function), the line of the argument, the argument and everything the call accepts. “Did you mean” names the closest accepted argument within one edit of what was written (two edits for a name longer than four characters), or two that tie:input,partition,serialiser.afterorwhenget the list and no guess. It is the rule every mistyped name follows (barca docs agents). - The helpers and
@sinktake exactly one argument by position. Passing it by keyword (partitions(values=[...]),collect(asset_fn=x),Schedule(cron="..."),@sink(path="...")) is rejected, and the error says to pass the value as the first argument. Before, these were read as “no keys”, “no input”, “an empty cron” and “no sink”. A call with none or with two (@sink(),partitions(a, b),@sink("out.txt", "json"): writeserializer="json") is rejected too; Python refuses those calls as well. @sink’spartition_by=(a list of column names) androws_per_file=(a whole number) apply to a dataset sink, a path ending in/; on a file sink, or written as anything but literals, they are rejected when the file is read, exit 2 (barca docs sinks).@asset,@sensorand@tasktake keyword arguments only.@asset(**options),@asset(*args)and@asset("x")are rejected: barca reads the arguments from the source without running it and cannot see what a**or*holds.@sensor(inputs=...)keeps its own error (sensor '...' cannot have inputs, exit 2).- Which commands fail. The check is made per file, like the check for a syntax error: a
command fails if it reads a file with such an argument, whatever its target.
list,plan,status,get,run(with or without--dry-run),statsandsqlexit 2 and run nothing. With no file arguments barca reads every pipeline file of the project (barca docs discovery), so one such argument anywhere stops all of them until it is corrected;barca get report good.pyreadsgood.pyonly.barca servestarts, and answers400with the same message on the routes that read the pipeline. - Only the first such argument in a file is reported. Fix it and run the command again.
- The Python signatures in
barca/__init__.pytake the same arguments and no others, so a type checker and an IDE flag the argument, and importing the module (python pipeline.py) raisesTypeError: asset() got an unexpected keyword argument 'after'.
Not checked:
- A decorator or helper that is not positively barca’s. Qualified and aliased top-level
barca imports are checked. Assignments, foreign imports,
def,class, loop orwithtargets andglobalwrites disable recognition of the affected name. A later star import disables recognition only when its exports are unknown or include that local name. Celery’s or Prefect’s@task(bind=True)stays untouched. Namespace reflection retains conservative discovery but disables argument checks. - What a helper receives through
*or**(partitions(*keys)). - The values: they are checked next (“Values barca reads”).
parallel()andparallel_map(): their arguments are your own.
Values barca reads
Section titled “Values barca reads”Barca reads each argument’s value from the source, so it has to be written in a form barca can
read without running the file. Any other value is an error when barca reads the file: exit 2,
kind usage, naming the argument, the file and line, and the form to write. Up to 0.33.1
most of these were read as if the argument were not written: no inputs, no tags, the default
timeout, and no sink at all for a @sink path that was not a string literal.
| Argument | Form |
|---|---|
name=, description= |
a string literal |
the path of @sink |
a string literal (@sink("exports/report.parquet")) |
inputs= |
a dict literal: keys are string literals, each value names a step (upstream, module.upstream, collect(upstream), asset_ref("file.py:upstream")) |
partitions= |
a dict literal: keys are string literals, each value is partitions(<keys>) or partitions_from(upstream). The keys inside partitions(...) may be any expression (“Static analysis” below) |
serializer= |
a string literal: "json", "pickle", "parquet" or "csv" (on @sink too) |
freshness= |
Always, Manual or Schedule("<cron>", ...), written in the decorator |
timeout_seconds=, retries= |
a whole-number literal |
retry_backoff= |
a number literal (2 or 2.0) |
tags= |
a dict literal whose keys and values are string literals |
env= |
a list of string literals |
weight= |
a whole-number literal of at least 1 |
deterministic=, cutoff= |
True or False |
None reads as “not written” for name=, description=, inputs=, partitions=,
serializer= and tags=. A module constant, an f-string, a call or ** in any of these is an
error, even when the constant holds a literal:
TAGS = {"team": "growth"}
@asset(tags=TAGS)def report() -> int: return 1Parse error: pipeline.py:report (line 3): `tags=` must be a dict literal, got `TAGS`; barca reads decorator arguments from the source without running it, so it cannot see what this isWrite it like `tags={"team": "growth"}`: a dict literal whose keys and values are string literals. See `barca docs assets`.Not checked:
- A call barca cannot identify as its own helper because the name is rebound or comes from
another library (
partitions = load_partitions,cron_lib.Schedule(...)), or because the file reassigns barca’s export (barca.partitions = other), as a partition dimension or asfreshness=: the value is left unread, as its arguments are. Ininputs=such a call names no step and is an error.
Inputs and parameters
Section titled “Inputs and parameters”Barca calls a step with one keyword argument per inputs= key and per partition dimension. Both
sides are in the source, so a mismatch is an error when barca reads the file, before any step
runs: exit 2, kind usage, on the same commands and with the same scope as an argument barca
does not define (above).
-
An
inputs=key (or apartitions=dimension) that is not a parameter of the function:@asset(inputs={"row": raw}) # the parameter is `rows`def amounts(rows: list) -> list:return rowsParse error: pipeline.py:amounts (line 9): the inputs= key `row` is not a parameter: amounts() takes `rows`. Did you mean `rows`?Rename the key to `rows`, or add a parameter `row` to amounts(). Barca passes each inputs= key to the function as a keyword argument. See `barca docs assets`. -
A parameter with no default that is neither an
inputs=key, a partition dimension nor the upstream of apartitions_from(...)(passed under its name,barca docs partitions). The line is the parameter’s:Parse error: pipeline.py:amounts (line 10): the parameter `rows` of amounts() has no input and no default: barca passes it no arguments, so calling it would fail.Wire it with `inputs={"rows": <upstream>}`, give it a default (`rows=...`), or remove it. See `barca docs assets`. -
A function with
**kwargstakes any key; a parameter with a default may be left unfilled. -
A
@task’s parameters are not reported missing: a task with parameters is how aparallel()branch receives its arguments (barca docs tasks). Itsinputs=keys are checked. -
When
inputs=orpartitions=is not a dict literal with string keys, which arguments arrive cannot be known, and no parameter is reported missing.
Up to 0.28 both mistakes failed only when the step ran, as a TypeError from the worker after
every upstream had run.
Environment variables: env=
Section titled “Environment variables: env=”An asset that reads an environment variable (a source path, a region, a model name) should
declare it. Barca reads the declared variables when it plans the run (from its environment, else from the
project’s .env), folds each name and value
into the run hash, and reports the values each step used.
import os
from barca import asset
@asset(env=["SOURCE_CSV", "API_TOKEN"])def raw() -> dict: return {"source": os.environ.get("SOURCE_CSV", "default.csv")}
@asset(inputs={"data": raw})def summary(data: dict) -> dict: return {"from": data["source"]}export SOURCE_CSV=a.csvbarca get summary pipeline.py --agent # raw and summary runbarca get summary pipeline.py --agent # both cachedexport SOURCE_CSV=b.csvbarca get summary pipeline.py --agent # raw and summary run again- Changing a declared variable re-materializes the asset and everything downstream of it. Unset is its own value, distinct from an empty string. Variables that are not declared are not part of the cache key.
- A declared variable that is not set in barca’s environment is read from the project’s
.env(in the project root, next tobarca.toml), so a pipeline that loads.envat import (load_dotenv(), before a settings class reads the environment) is hashed with the value it runs with: editingSOURCE_CSV=b.csvin.envre-runsrawlike anexportdoes. The environment wins over the file, as withload_dotenv()’s default. Barca reads the file without importing anything, understandsKEY=VALUElines,export,#comments, single and double quotes and${NAME}/${NAME:-default}, and does not set its values for the step: a pipeline that uses them loads the file itself. Another dotenv file (.env.local, a path given toload_dotenv(...)) is not read; declare such variables by exporting them. - The JSON result’s
stepsentry for the node carries"env": {"API_TOKEN": null, "SOURCE_CSV": "b.csv"}(null= unset), and--agentstep lines end withenv API_TOKEN=<unset> SOURCE_CSV=b.csv. - Names ending in
_TOKEN,_SECRET,_KEYor_PASSWORD(any case, or the bare word) are hashed like any other but shown as<redacted>in all output. barca listshows the declared names in an ENV column (envin--json).env=must be a literal list of string literals; anything else (a variable, a tuple, a computed name) is a parse error. It is also accepted on@taskand@sensor, which always run: there it only records the values used.
Undeclared reads are reported, not hashed. A variable your code reads without declaring it
is not part of the cache key, so changing it does not re-run anything. Barca finds such reads
statically, in the function and in the project helpers it reaches (os.environ["NAME"],
os.environ.get("NAME"), os.getenv("NAME"), pydantic settings fields), and prints one
line for the command before anything runs:
[barca] warning: 1 step reads environment variables not declared in env=: REGION; barca status lists themThe JSON result lists them per step as undeclared_env (also in --dry-run and in
barca status --json), and the warnings array has one undeclared_env item per variable.
Declare the variable to make it count, or ignore the warning if the value never changes the
result. barca docs cache, “Environment variables”, has the exact rules and what is not seen.
Heavy steps: weight=
Section titled “Heavy steps: weight=”Experimental. Each step runs in its own worker process, and independent steps run at the same
time, up to one per worker. Under a hard memory limit (a container’s mem_limit), several heavy
steps that become ready together can add up past it, and the kernel kills one: the step fails
with worker for pipeline.py:train exited with SIGKILL (possibly the OOM killer). The same step
succeeds alone. weight= limits how many such steps run at once without slowing the rest.
from barca import asset
@asset(weight=2)def embed_a() -> dict: return {"rows": 1}
@asset(weight=2)def embed_b() -> dict: return {"rows": 2}
@asset(weight=2)def embed_c() -> dict: return {"rows": 3}- The weights of the steps running at once add up to at most the capacity. It is the pool
size unless
[execution] capacityin barca.toml orBARCA_CAPACITYsets it; the environment variable wins. With a capacity of 4, the three steps above run two at a time, and two weight-1 steps can run beside one of them. - The pool size is how many worker processes a run starts at most:
BARCA_POOL_SIZE, else[execution] pool_sizein barca.toml, else the available cores, or the capacity when it is smaller (every step weighs at least 1, so no more thancapacitysteps ever run at once, and a worker past that would only start and import the project). Underbarca servewith a server capacity (below), that bounds it too. Weights above 1 do not shrink the pool further: workers start only for the steps the capacity admits. Withcapacity = 1and nothing else set, a run starts one worker. weight=is a whole number of at least 1; anything else is a parse error (exit 2). It is accepted on@asset,@taskand@sensor. It is not part of the run hash: changing it re-runs nothing and outputs and cache decisions are the same at any weight or capacity.- A step whose weight is above the capacity runs alone, with nothing beside it, rather than
never. Every command that plans it prints a warning on stderr:
[barca] warning: 'embed_a' has weight=8, more than the capacity 4 (pool size); it runs alone. - Steps start in the order they become ready. A heavy step waiting for room is not overtaken: steps that become ready after it wait until it has started, so a stream of light steps cannot hold it back for the rest of the run.
- Every partition key of a partitioned node takes the node’s weight, and so does every
parallel()branch, which takes the weight of the step that calledparallel(). While that step waits for its branches it is not counted (the branches carry its weight), so its own memory is not limited then. - The capacity is per run: each run has its own pool of workers. Two runs at the same time
(two
barca getcommands) each fill their own capacity. Underbarca serve,--capacity Nor[server] capacity = Nadds one capacity that every run of the server shares (experimental,barca docs serve-ops, “A capacity shared by every run”): a step starts only when it fits both its run’s capacity and the server’s. barca planshowspool_sizeandpool_source(where the pool size came from:BARCA_POOL_SIZE,[execution] pool_size,cores, or the capacity setting that bounded the cores),capacity,capacity_sourceandweights(the planned nodes with a weight above 1);--dry-runshowscapacityand aweighton each such step;barca doctorhas acapacitycheck naming both, and aserver_capacitycheck when[server] capacityis set.
[execution]capacity = 1 # one step at a time: the pool is one workerpool_size = 2 # or set the pool yourself (BARCA_POOL_SIZE wins over it)barca plan pipeline.pybarca get pipeline.py --dry-run --jsonbarca doctor pipeline.pyHow a node is identified
Section titled “How a node is identified”A node id is <file>:<function> (for example pipeline.py:clean), or the explicit name=.
Targets on the command line can use the bare function name (barca get clean pipeline.py).
Several targets are one comma-separated list (barca get clean,report pipeline.py): their
upstream cones are planned together, so an asset both need materializes once, and the JSON
output is keyed by target (barca docs agents).
With no target, barca get pipeline.py materializes every asset and sensor in the file and skips
tasks (previously it ran them too); run a task with barca run <task> pipeline.py.
An input defined in another file is imported like any Python name (from pipelines.sources import raw, then inputs={"r": raw}); barca resolves the import statically. Use
asset_ref("other/file.py:raw") to name one without importing it. See barca docs discovery.
Naming
Section titled “Naming”An asset is an ordinary Python function, so its name is an ordinary Python name. If a file
imports a module (import carry_forward_registry) and also defines an asset with the same
name, the def rebinds the name and shadows the module for the rest of the file. Give the
import an alias (import carry_forward_registry as cf_registry) or rename the asset. Barca does
not warn about this; Python simply uses the later definition.
Static analysis
Section titled “Static analysis”Static extraction reads source without importing user code. Literal partition key lists
need no resolver import; a nonliteral partitions(<expression>) imports the pipeline
module to evaluate its keys; so does a list with an element that is not a string or integer
literal (partitions(["us", OTHER_REGION])). The other decorator arguments must be written in
the forms of “Values barca reads” above; any other value is an error. Dynamic decorator construction
(building inputs in a loop, calling a decorator through a variable) is not visible to the
planner. An argument barca does not define, and **options in a decorator call, are errors
(“Accepted arguments” above). Mark code barca cannot reason about with @unsafe (silences purity warnings only).
Unused inputs
Section titled “Unused inputs”A declared input costs something even when the function ignores it: the upstream is materialized first, its value is loaded and passed, and it is part of the step’s cache key. At plan time barca reads each function body (statically: nothing is imported or run) and warns about an input the function never uses:
from barca import asset
@asset()def raw() -> list: return [1, 2, 3]
@asset(inputs={"raw": raw})def report(raw: list) -> int: # `raw` is never used return 42[barca] warning: pipeline.py:report never uses its input `raw`. It is still loaded in full each time the step runs, and it counts toward the step's cache key. Use it, remove it from inputs=, or rename the parameter `_raw` if it is there for ordering only (a `_` input is not loaded and never flagged)The rule. An input is reported when its parameter name does not start with _ and the
function body never mentions the name, or mentions it only as del name. A mention is the name
in code, anywhere in the body (reading it, passing it to a helper, a nested function, a
comprehension, an f-string, assigning to it), or the name as a whole word inside any string in
the body: duckdb.sql("select * from orders"), pl.sql("... from orders") and
df.query("amount > @threshold") read variables by name, so orders and threshold are used
there. The check is deliberately conservative, so a warning means the input really is unused;
when barca cannot tell, it says nothing.
When a local name shadows an imported module, calls through that name may resolve SQL or
expression inputs dynamically. This includes names captured by match and names bound by
nested def, async def or class definitions. Such calls are treated conservatively;
a module import alone does not prove the receiver still refers to that module.
The string match is on whole identifiers and is case-sensitive: orders is not found in
reorders, orders_v2 or Orders. Every string and bytes literal in the body counts,
multi-line, concatenated and the text of f-strings included, except a string that is a statement
on its own: a docstring that describes an input does not use it. Comments never count.
Never reported:
- an input whose name starts with
_. This is the way to say “unused on purpose”: the step still runs after the upstream and still re-runs when the upstream changes, but nothing is loaded and the parameter isNone(barca docs tasks). Rename both the key ininputs=and the parameter; - a function whose body is only a docstring,
pass,...orraise: a stub, or a gate that only raises, uses nothing by definition; - a function that takes
**kwargs, or whose body mentions a name through which Python can reach a parameter without naming it, as a bare name or as an attribute (builtins.locals(),inspect.currentframe().f_locals,sys._getframe(),**locals()). Then no input of that function is reported. Dynamic access names:locals,vars,eval,exec,currentframe,_getframe,f_locals,f_back,getargvalues,inspect.stack. - a function that passes text barca cannot read to a call that resolves names from text: an
argument is a variable, a module constant, an f-string or a concatenation rather than a
literal (
duckdb.sql(QUERY),con.execute(q),duckdb.table(name),pl.sql(q),pl.SQLContext(frames),df.query(expr);df.eval(expr)is covered byevalabove). Then no input of that function is reported. The call is recognised by its name alone, as a method or attribute of anything, or as a bare name, also under an import alias (from duckdb import sql as dsql); any argument that is not a literal makes it unreadable. Using such a call as a value instead of calling it (q = duckdb.sql,map(con.execute, queries)) silences the function too. The one exception: a call on a name thatimportbinds to a module of the Python standard library or to one of the packages listed below, and that the function never rebinds, is not a query entry point (pa.table(d),np.view(...),json.query(...)). A call on any other module stays silent: it may be your own (mylib.sql(QUERY)wheremylibdoesfrom duckdb import sql, adbhelper wrapping a connection) or a package barca does not know. Unrelated packages:pyarrow,numpy,matplotlib,scipy,sklearn,requests,httpx,aiohttp,urllib3,yaml,orjson,torch,tensorflow,xgboost,lightgbm,statsmodels,seaborn,plotly,networkx,sympy,boto3,botocore,fsspec,tqdm,pydantic,click,rich,PIL,cv2,jinja2,dateutil,pytz,barca. Query entry points:sql,execute,executemany,query,from_query,table,view,read_sql,read_sql_query,SQLContext. - an input annotated
duckdb.DuckDBPyRelation: barca binds it as a view named after the parameter, so SQL in a helper function, which this check does not read, can use it without the step’s body naming it at all (barca docs types); - an input that comes from a
@sensor: depending on a sensor without reading its value is how a step is made to re-run when outside state changes (barca docs cache).
Where it appears. barca plan, barca get, barca run and get|run --dry-run report the
same warnings in the same two places: one [barca] warning: ... line per unused input on
stderr, in every output mode (--pretty, --json, --agent), printed before any step runs; and
the warnings array of the JSON output, [] when there are none:
{"kind": "unused_input", "node": "pipeline.py:report", "param": "raw", "message": "pipeline.py:report never uses its input `raw`. ..."}Only the steps the command plans are checked: barca get report pipeline.py warns about
report and what is upstream of it, not about other steps in the file; barca plan and
barca get with no target cover the whole file. A step is reported once per unused input, however
many partition keys it has, and whether or not it is served from cache: the list depends on the
source and the target only. barca list and barca status do not report it. A warning never
changes the exit code or the cache.
For an input annotated pl.LazyFrame the message says, instead of “loaded in full”, that a
parquet artifact is opened but not read.
Limitations. The check works on names, in one function body, and prefers silence to a wrong warning. That costs missed warnings:
- It does not follow the value. An input that is only assigned to (
raw = None) or only passed to a helper that ignores it is not reported. - A string that happens to contain the input’s name counts as a use, whatever the string is
for:
return {"orders": 1}or a log message namingordershides an unusedorders. - Any call named like a query entry point with an argument that is not a literal silences the
whole function, SQL or not, unless it is a call on a standard-library module or one of the
listed packages:
client.query(params),cursor.execute(statement, values),tensor.view(n, -1)andmylib.table(name)silence, because a local variable or a module barca does not know may hold anything. So does any use of such an attribute as a value (request.query,self.table), and any mention of a dynamic access name. - An unused
duckdb.DuckDBPyRelationinput and an unused sensor input are never reported.
And one wrong warning it cannot avoid: a function whose input is read only from somewhere this
check does not look. That is a helper function that inspects its caller’s frame, DuckDB with
python_scan_all_frames reading a caller’s variable from SQL inside a helper, or an alias made
outside the body (grab = locals at module level, then grab()), or a file of your own that
is named like a standard-library module or a listed package and re-exports a query entry point
(a project json.py doing from duckdb import sql). Mention the input by name
in the body, or _-prefix it, to say otherwise. There is no flag or configuration key that
turns the warning off.
Sensors
Section titled “Sensors”@sensor observes external state and has no inputs. It is polled whenever a run needs
it, including with its default Manual freshness. Use Schedule(...) for its own ticks
under barca serve. Always is also accepted; sensors are polled either way.
| Return | Meaning |
|---|---|
False or (False, value) |
No change. Retain the previous observation and its value. On the first poll, initialize from the supplied value or None. |
True or (True, value) |
Changed. Advance freshness even if the value is absent or equal to the previous value. Without a value, downstream receives None and can fetch or generate the data. |
| A value other than a boolean/two-item tuple | Compare its content with the previous value. For example, return an mtime, ETag, or a dictionary of key versions. |
Consumers receive the optional value, never the change flag. Assets follow normal freshness
rules; tasks follow their normal execution rules. A dictionary used by partitions_from
is compared per key, so a snapshot reporting changes updates only the affected chains.
A source defining dynamic partition keys must supply a list or dictionary when it changes.
Migration: the change flag was ignored through 0.32.0. A sensor that always returned
(True, etag) to request comparison should now return etag alone. A boolean payload can
be wrapped in a dictionary for content-based comparison.
barca get <sensor> pipeline.py observes one sensor. barca get pipeline.py (no target) observes
every sensor, including one nothing depends on: a sensor is something get can target, and
observing is read-only. Tasks are the only nodes a bare get skips.
Explicit references to globals, locals, vars, exec, eval or __builtins__ make imported barca
names uncertain, even when the reference is aliased, qualified or shadowed. Such names are
left to Python’s argument checks and their nodes conservatively hash the whole module
(barca docs cache). The static checks do not resolve arbitrary reflective indirection
or runtime side effects.
See also: barca docs tasks, barca docs cache, barca docs scheduling.