Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.aspect.build/llms.txt

Use this file to discover all available pages before exploring further.

type Arg type Arguments type ConfigContext type ExporterSpec type Exporters type FeatureContext type Future type Hash type Http type HttpResponse type Task type TaskContext type TaskInfo type Telemetry type Template type bool type bytes type dict type float type int type list type namespace type range type set type str type struct type tuple type type module args module aspect module bazel module futures module json module remote module std module typing module wasm function abs
def abs(
x: float | int,
/
) -> float | int
Take the absolute value of an int.
abs(0)   == 0
abs(-10) == 10
abs(10)  == 10
abs(10.0) == 10.0
abs(-12.34) == 12.34
function all
def all(
x: typing.Iterable,
/
) -> bool
all: returns true if all values in the iterable object have a truth value of true.
all([1, True]) == True
all([1, 1]) == True
all([0, 1, True]) == False
all([True, 1, True]) == True
all([0, 0]) == False
all([0, False]) == False
all([True, 0]) == False
all([1, False]) == False
function any
def any(
x: typing.Iterable,
/
) -> bool
any: returns true if any value in the iterable object have a truth value of true.
any([0, True]) == True
any([0, 1]) == True
any([0, 1, True]) == True
any([0, 0]) == False
any([0, False]) == False
function attr
def attr(
typ: typing.Any,
/,
*,
default: typing.Any = ,
description: None | str = None
) -> attr
Creates a field definition for a trait, with a type, optional default value, and optional description. default must match the declared type. Mutable defaults (lists, dicts) are deep-copied when a trait instance is created, so each instance gets its own independent copy. Example:
BazelTrait = trait(host=str, port=attr(int, default = 80))
r = BazelTrait(host="localhost")  # port defaults to 80
function breakpoint
def breakpoint() -> None
When a debugger is available, breaks into the debugger. function call_stack
def call_stack(
*,
strip_frames: int = 0
) -> str
Get a textual representation of the call stack. This is intended only for debugging purposes to display to a human and should not be considered stable or parseable. strip_frames will pop N frames from the top of the call stack, which can be useful to hide non-interesting lines - for example, strip_frames=1 will hide the call to and location of call_stack() itself. function call_stack_frame
def call_stack_frame(
n: int,
/
) -> None | StackFrame
Get a structural representation of the n-th call stack frame. With n=0 returns call_stack_frame itself. Returns None if n is greater than or equal to the stack size. function chr
def chr(
i: int,
/
) -> str
chr: returns a string encoding a codepoint. chr(i) returns a string that encodes the single Unicode code point whose value is specified by the integer i. chr fails unless 0 ≤ i ≤ 0x10FFFF.
chr(65) == 'A'
chr(1049) == 'Й'
chr(0x1F63F) == '😿'
function debug
def debug(
val: typing.Any,
/
) -> str
Print the value with full debug formatting. The result may not be stable over time. Intended for debugging purposes and guaranteed to produce verbose output not suitable for user display. function dir
def dir(
x: typing.Any,
/
) -> list[str]
dir: list attributes of a value. dir(x) returns a list of the names of the attributes (fields and methods) of its operand. The attributes of a value x are the names f such that x.f is a valid expression.
"capitalize" in dir("abc")
function enum
def enum(
*args: str
) -> typing.Any
The enum type represents one value picked from a set of values. For example:
MyEnum = enum("option1", "option2", "option3")
This statement defines an enumeration MyEnum that consists of the three values "option1", "option2" and option3. Now MyEnum is defined, it’s possible to do the following:
  • Create values of this type with MyEnum("option2"). It is a runtime error if the argument is not one of the predeclared values of the enumeration.
  • Get the type of the enum suitable for a type annotation with MyEnum.
  • Given a value of the enum (for example, v = MyEnum("option2")), get the underlying value v.value == "option2" or the index in the enumeration v.index == 1.
  • Get a list of the values that make up the array with MyEnum.values() == ["option1", "option2", "option3"].
  • Treat MyEnum a bit like an array, with len(MyEnum) == 3, MyEnum[1] == MyEnum("option2") and iteration over enums [x.value for x in MyEnum] == ["option1", "option2", "option3"].
Enumeration types store each value once, which are then efficiently referenced by enumeration values. function enumerate
def enumerate(
it: typing.Iterable,
/,
start: int = 0
) -> list[(int, typing.Any)]
enumerate: return a list of (index, element) from an iterable. enumerate(x) returns a list of (index, value) pairs, each containing successive values of the iterable sequence and the index of the value within the sequence. The optional second parameter, start, specifies an integer value to add to each index.
enumerate(["zero", "one", "two"]) == [(0, "zero"), (1, "one"), (2, "two")]
enumerate(["one", "two"], 1) == [(1, "one"), (2, "two")]
function eval_type
def eval_type(
ty: type,
/
) -> type
Create a runtime type object which can be used to check if a value matches the given type. function fail
def fail(
*args: typing.Any
) -> typing.Never
fail: fail the execution
fail("this is an error")  # fail: this is an error
fail("oops", 1, False)  # fail: oops 1 False
function feature
def feature(
*,
implementation: typing.Callable[[FeatureContext], None],
name: str = ,
display_name: str = ,
summary: str = ,
description: str = ,
enabled: bool = True,
args: dict[str, typing.Any] = 
) -> feature
Declares a feature — a composable behavior injector for the fragment system. The implementation function receives a FeatureContext and runs after all config.axl files have been evaluated. It can inject closures into fragment hook lists via ctx.fragments[FragmentType].hook.append(...). Feature CLI args are injected into every task subcommand on the CLI. Only named optional flags are supported — positional args and required = true are not allowed because features apply globally and would break any task that doesn’t supply the flag. Every feature automatically gets an enabled CLI arg. It shows up as --{name}:enabled on the command line and is accessible as ctx.args.enabled in the implementation. Set enabled = False for opt-in features.

Naming

Features must be exported as CamelCase (ArtifactUpload). This is enforced at definition time. Features are referenced as type keys (ctx.features[ArtifactUpload]), mirroring Bazel’s provider convention (dep[CcInfo]); CamelCase signals this type-key role. The name field sets the kebab-case slug used as a prefix for every CLI arg this feature declares: a feature named "artifact-upload" with arg mode exposes --artifact-upload:mode. The name is auto-derived from the CamelCase export name via to_command_name (ArtifactUploadartifact-upload) if not set explicitly. display_name overrides the Title Case name shown in CLI help section headings.

Arg names

Arg keys must be snake_case ([a-z][a-z0-9_]*). There are two kinds:
  • CLI args (args.string(...), args.boolean(...), etc.) — exposed as --{name}-{arg} flags on every task subcommand; must be optional.
  • Config-only args (args.custom(type, default = …)) — set in config.axl only, not shown in help.
Both kinds are accessible as ctx.args.arg_name in the implementation.

Example

def _impl(ctx: FeatureContext):
    ctx.fragments[BazelFragment].build_end.append(
        lambda task_ctx, state: upload_artifacts(
            task_ctx, ctx.args.bucket, ctx.args.mode
        )
    )

ArtifactUpload = feature(
    summary = "Upload build artifacts to S3 storage",
    implementation = _impl,
    args = {
        "bucket": args.custom(str | None, default = None),  # config.axl only
        "mode":   args.string(default = "auto"),             # CLI flag: --artifact-upload-mode
    },
)
function field
def field(
typ: typing.Any,
/,
default: typing.Any = 
) -> field
Creates a field record. Used as an argument to the record function.
rec_type = record(host=field(str), port=field(int), mask=field(int, default=255))
rec = rec_type(host="localhost", port=80)
rec.port == 80
rec.mask == 255
function filter
def filter(
func: None | typing.Callable,
seq: typing.Iterable,
/
) -> list
Apply a predicate to each element of the iterable, returning those that match. As a special case if the function is None then removes all the None values.
filter(bool, [0, 1, False, True]) == [1, True]
filter(lambda x: x > 2, [1, 2, 3, 4]) == [3, 4]
filter(None, [True, None, False]) == [True, False]
function getattr
def getattr(
a: typing.Any,
attr: str,
default: typing.Any = ,
/
) -> typing.Any
getattr: returns the value of an attribute getattr(x, name) returns the value of the attribute (field or method) of x named name. It is a dynamic error if x has no such attribute. getattr(x, "f") is equivalent to x.f. getattr(x, "f", d) is equivalent to x.f if hasattr(x, "f") else d and will never raise an error.
getattr("banana", "split")("a") == ["b", "n", "n", ""] # equivalent to "banana".split("a")
function hasattr
def hasattr(
a: typing.Any,
attr: str,
/
) -> bool
hasattr: test if an object has an attribute hasattr(x, name) reports whether x has an attribute (field or method) named name. function hash
def hash(
a: str,
/
) -> int
hash: returns the hash number of a value. hash(x) returns an integer hash value for x such that x == y implies hash(x) == hash(y). hash fails if x, or any value upon which its hash depends, is unhashable.
hash("hello") != hash("world")
function isinstance
def isinstance(
value: typing.Any,
ty: type,
/
) -> bool
Check if a value matches the given type. This operation can be very fast or very slow depending on how it is used. isinstance(x, list) is very fast, because it is compiled to a special bytecode instruction. isinstance(x, list[str]) is O(N) operation because it checks every element in this list. L = list; [isinstance(x, L) for x in y] is slow when L is not a constant: isinstance() first converts list to a type in a loop, which is slow. But last operation can be optimized like this: L = eval_type(list); [isinstance(x, L) for x in y]: eval_type() converts list value into prepared type matcher. function len
def len(
a: typing.Any,
/
) -> int
len: get the length of a sequence len(x) returns the number of elements in its argument. It is a dynamic error if its argument is not a sequence.
len(()) == 0
len({}) == 0
len([]) == 0
len([1]) == 1
len([1,2]) == 2
len({'16': 10}) == 1
len(True)    # error: not supported
function map
def map(
func: typing.Callable,
seq: typing.Iterable,
/
) -> list
Apply a function to each element of the iterable, returning the results.
map(abs, [7, -5, -6]) == [7, 5, 6]
map(lambda x: x * 2, [1, 2, 3, 4]) == [2, 4, 6, 8]
function max
def max(
*args: typing.Any,
key: typing.Any = 
) -> typing.Any
max: returns the maximum of a sequence. max(x) returns the greatest element in the iterable sequence x. It is an error if any element does not support ordered comparison, or if the sequence is empty. The optional named parameter key specifies a function to be applied to each element prior to comparison.
max([3, 1, 4, 1, 5, 9])               == 9
max("two", "three", "four")           == "two"    # the lexicographically greatest
max("two", "three", "four", key=len)  == "three"  # the longest
function min
def min(
*args: typing.Any,
key: typing.Any = 
) -> typing.Any
min: returns the minimum of a sequence. min(x) returns the least element in the iterable sequence x. It is an error if any element does not support ordered comparison, or if the sequence is empty.
min([3, 1, 4, 1, 5, 9])                 == 1
min("two", "three", "four")             == "four"  # the lexicographically least
min("two", "three", "four", key=len)    == "two"   # the shortest
function ord
def ord(
a: typing.Any,
/
) -> int
ord: returns the codepoint of a character ord(s) returns the integer value of the sole Unicode code point encoded by the string s. If s does not encode exactly one Unicode code point, ord fails. Each invalid code within the string is treated as if it encodes the Unicode replacement character, U+FFFD. Example:
ord("A")                                == 65
ord("Й")                                == 1049
ord("😿")                               == 0x1F63F
function partial
def partial(
func: typing.Any,
/,
*args: typing.Any,
**kwargs: typing.Any
) -> function
Construct a partial application. In almost all cases it is simpler to use a lamdba. function pprint
def pprint(
*args: typing.Any
) -> None
function prepr
def prepr(
a: typing.Any,
/
) -> str
Like repr, but produces more verbose pretty-printed output function print
def print(
*args: typing.Any
) -> None
Print some values to the output. function pstr
def pstr(
a: typing.Any,
/
) -> str
Like str, but produces more verbose pretty-printed output function record
def record(
**kwargs: typing.Any
) -> function
A record type represents a set of named values, each with their own type. For example:
MyRecord = record(host=str, port=int)
This above statement defines a record MyRecord with 2 fields, the first named host that must be of type str, and the second named port that must be of type int. Now MyRecord is defined, it’s possible to do the following:
  • Create values of this type with MyRecord(host="localhost", port=80). It is a runtime error if any arguments are missed, of the wrong type, or if any unexpected arguments are given.
  • Get the type of the record suitable for a type annotation with MyRecord.type.
  • Get the fields of the record. For example, v = MyRecord(host="localhost", port=80) will provide v.host == "localhost" and v.port == 80. Similarly, dir(v) == ["host", "port"].
It is also possible to specify default values for parameters using the field function. For example:
MyRecord = record(host=str, port=field(int, 80))
Now the port field can be omitted, defaulting to 80 is not present (for example, MyRecord(host="localhost").port == 80). Records are stored deduplicating their field names, making them more memory efficient than dictionaries. function repr
def repr(
a: typing.Any,
/
) -> str
repr: formats its argument as a string. All strings in the result are double-quoted.
repr(1)                 == '1'
repr("x")               == "\"x\""
repr([1, "x"])          == "[1, \"x\"]"
repr("test \"'")        == "\"test \\\"'\""
repr("x\"y😿 \\'")      == "\"x\\\"y\\U0001f63f \\\\'\""
function reversed
def reversed(
a: typing.Iterable,
/
) -> list
reversed: reverse a sequence reversed(x) returns a new list containing the elements of the iterable sequence x in reverse order.
reversed(['a', 'b', 'c'])              == ['c', 'b', 'a']
reversed(range(5))                     == [4, 3, 2, 1, 0]
reversed("stressed".elems())           == ["d", "e", "s", "s", "e", "r", "t", "s"]
reversed({"one": 1, "two": 2}.keys())  == ["two", "one"]
function sorted
def sorted(
x: typing.Iterable,
/,
*,
key: typing.Any = ,
reverse: bool = False
) -> list
sorted: sort a sequence sorted(x) returns a new list containing the elements of the iterable sequence x, in sorted order. The sort algorithm is stable. The optional named parameter reverse, if true, causes sorted to return results in reverse sorted order. The optional named parameter key specifies a function of one argument to apply to obtain the value’s sort key. The default behavior is the identity function.
sorted([3, 1, 4, 1, 5, 9])                               == [1, 1, 3, 4, 5, 9]
sorted([3, 1, 4, 1, 5, 9], reverse=True)                 == [9, 5, 4, 3, 1, 1]
sorted(["two", "three", "four"], key=len)                == ["two", "four", "three"] # shortest to longest
sorted(["two", "three", "four"], key=len, reverse=True)  == ["three", "four", "two"] # longest to shortest
function task
def task(
*,
implementation: typing.Callable[[TaskContext], None],
args: dict[str, typing.Any] = ,
summary: str = ,
description: str = ,
display_name: str = ,
group: list[str] = [],
name: str = ,
traits: list = []
) -> Task
Declares a task — a named CLI command with an implementation function.

Naming

Assign the result to a snake_case variable. The CLI command name is derived automatically by converting _ to - (axl_addaxl-add). Use name = "explicit-name" to override. Command names must match [a-z][a-z0-9-]*.

Args

Arg keys must be snake_case ([a-z][a-z0-9_]*). There are two kinds:
  • CLI args (args.string(...), args.int(...), etc.) — exposed as --kebab-flags on the CLI and accessible as ctx.args.arg_name in the implementation. Can be overridden in config.axl; an explicit CLI flag always wins over a config override.
  • Config-only args (args.custom(type, default = …)) — not shown in help; set by repo maintainers in config.axl via ctx.tasks["group/name"].args.arg_name = value.
All args are read as ctx.args.arg_name in the implementation regardless of kind.

Help text

  • summary — one-liner shown in the task list; falls back to "<name> task defined in <file>".
  • description — extended prose shown in --help (replaces summary in that view).
  • display_name — Title Case name for help section headings; auto-derived from command name.

Example

def _impl(ctx: TaskContext) -> int:
    ctx.std.io.stdout.write("Hello, " + ctx.args.recipient + "\n")
    return 0

greet = task(
    group = ["utils"],
    summary = "Say hello",
    implementation = _impl,
    args = {
        "recipient": args.string(default = "world", description = "Who to greet"),
        "greeting":  args.custom(str, default = "Hello", description = "Greeting word (config.axl only)"),
    },
)
function trait
def trait(
**kwargs: typing.Any
) -> trait
Creates a trait type — a shared configuration object that tasks opt into.

Naming

Traits must be exported as CamelCase (MyConfig, BazelTrait). This is enforced at definition time.

Fields

Each field must be an attr() definition with a default value. The default is used to construct the initial trait instance lazily on first access — there is no mechanism to inject values before that construction, so all fields must have defaults.

Example

BazelTrait = trait(
    extra_flags    = attr(list[str], default = [], description = "Extra Bazel flags for every build"),
    profile_upload = attr(bool,      default = False, description = "Upload Bazel profile after build"),
)
function zip
def zip(
*args: typing.Iterable
) -> list
zip: zip several iterables together zip() returns a new list of n-tuples formed from corresponding elements of each of the n iterable sequences provided as arguments to zip. That is, the first tuple contains the first element of each of the sequences, the second element contains the second element of each of the sequences, and so on. The result list is only as long as the shortest of the input sequences.
zip()                           == []
zip(range(5))                   == [(0,), (1,), (2,), (3,), (4,)]
zip(range(5), "abc".elems())    == [(0, "a"), (1, "b"), (2, "c")]
property False
False: bool
property None
None: None
property True
True: bool
property trace
trace: trace