Differences from Python
Atmos Automation Language looks like Python and reads like Python, but it is a smaller language with its own rules. This page lists the differences that most often surprise Python programmers, with the closest equivalent for each. Every example here was run on the language that ships with Atmos.
Quick lookup
| In Python | In Atmos Automation Language |
|---|---|
try / except / raise | fail("message") stops the script. Nothing catches it. Use exec.run(..., check=False) to inspect a failing command. |
import os | load("file.star", "name") for your own code. Atmos names such as exec, fs, json, regex, and env are already available. |
class | Use dictionaries and functions. |
x ** 2 | x * x, or a loop. There is no ** operator and no pow(). |
f"{name}" | "{}".format(name) or "%s" % name. |
a < b < c | a < b and b < c. |
x is None | x == None. |
l.sort() | sorted(l), which returns a new list. |
l.copy(), d.copy() | list(l), dict(d). |
del l[0] | l.pop(0), d.pop("key"). |
sum(xs), round(x) | Available as Atmos built-ins; see sum and round. |
isinstance(x, list) | type(x) == "list". |
(x for x in xs) | [x for x in xs]. There are no generators. |
for c in "abc" | for c in "abc".codepoints(). |
global x | Not available. Top-level names are assigned once. |
Statements
No try, except, finally, or raise. Errors propagate to the top and stop the script.
The fail() function is the only way to raise one. See
Errors and tracebacks.
No import. Use load to bring names from another .star file:
load("lib/helpers.star", "deploy")
No class, with, yield, async, del, global, or nonlocal. Each of these
is a syntax error, as are decorators (@name) and the walrus operator (:=). There is no
assert statement either; the word is an ordinary identifier. Write a check as
if not condition: fail("message").
No chained assignment or star-unpacking. Both x = y = 1 and a, *rest = items are syntax
errors. Plain tuple unpacking works: a, b = 1, 2, and (c, d), e = (3, 4), 5.
No else clause on loops. A for or while loop followed by else is a syntax error.
Common statements and expressions work as in Python. That includes pass, break, continue,
elif, comprehensions, and lambda. A lambda
body is a single expression, and a comprehension can contain several for and if clauses.
while and top-level for and if statements are available. Atmos enables them, so a script
can compute at the top level without wrapping everything in a function.
Names and scope
Top-level names are assigned once. A script cannot rebind a global, including with x += 1,
a loop variable that is reused, or a swap such as a, b = b, a. Compute changing values inside a
function, where local variables can be rebound freely:
x = 1
x = 2 # fails: cannot reassign global x
See Globals are assigned once.
Functions cannot assign to globals. There is no global statement. Assigning inside a function
creates a local variable, and count += 1 on a global inside a function fails with
local variable count referenced before assignment. Mutate a dictionary or list instead:
state["count"] += 1.
Loaded and shared values are frozen. Collections that come from load() are immutable, and
collections reachable from steps.parallel tasks become immutable when the tasks start. See
Frozen values.
Names are checked before the script runs. A misspelled name is an error even on a line that would never execute. A global must still be assigned before it is read, though functions may refer to names that are defined later in the file.
There is no __name__. A standalone program defines a main function and passes it to
cli.command instead of using
if __name__ == "__main__".
Numbers and operators
Division with / always produces a float, and // floors. For example, 7 / 2 is 3.5, 6 / 3 is 2.0, 7 // 2 is
3, and -7 // 2 is -4. The % operator takes the sign of the divisor, as in Python: -7 % 3
is 2. Dividing by zero is an error for both / and //.
There is no ** operator. Use x * x, a loop, or a function. The functions pow and divmod are also unavailable.
Atmos supplies sum and
round for adding and rounding numbers.
Bitwise operators work. &, |, ^, ~, <<, and >> operate on integers, and integers have
arbitrary precision: 1 << 70 is 1180591620717411303424.
Number literals. 0x1f, 0o17, and 0b101 work. Underscores in numbers, as in 1_000, do not.
Booleans are separate from numbers. The expression True + 1 is an error, and True == 1 is
False. As dictionary
keys, True and 1 are different. The comparison 1 == 1.0 is True, and the keys 1 and 1.0 collide.
Comparison chains are not allowed. Writing 1 < 2 < 3 fails with
< does not associate with < (use parens). Write 1 < 2 and 2 < 3 instead.
No is and is not. Compare with == and !=, including for None: x == None.
The operators and, or, and not behave as in Python. 0 or "x" evaluates to "x". The conditional
expression a if cond else b works too.
Strings
Strings are not iterable. A loop such as for c in "abc" fails with string value is not iterable. Use
"abc".codepoints() for characters or "abc".elems() for bytes.
Strings hold UTF-8 text and are measured in bytes. The call len("héllo") returns 6, and indexing and slicing
count bytes, so "héllo"[1:3] is "é" and "é"[0] is a single byte that is not valid text.
Methods such as upper() and lower() handle Unicode correctly. Use len(list(s.codepoints()))
to count characters.
No f-strings. Use "{} is {}".format(a, b), "{name}".format(name=n), or "%s is %d" % (a, b).
The format method supports {}, {0}, {name}, and the !r conversion, but not format
specifications such as {:>5} or lookups such as {a.b}. The % operator supports %s, %d,
%i, %r, %x, %X, %o, %e, %f, %g, %c, and %%, without width or precision.
Fewer methods. Strings have 35 methods, listed under
String methods. Methods that do not exist include
zfill, center, ljust, rjust, swapcase, and expandtabs.
Number parsing does not trim whitespace. The call int(" 7 ") fails, so call .strip() first.
Collections
Dictionaries keep insertion order. Iterating, keys(), values(), and items() follow the
order in which keys were first added, as in modern Python. The methods items(), keys(), and values()
return lists, not views. Two dictionaries merge with |: {"a": 1} | {"b": 2}.
Sets are available through set(). There are no set literals, so {1, 2} is a syntax error;
write set([1, 2]). Sets keep insertion order, and s.pop() removes the first element. Sets
support |, &, -, ^, and the comparisons <, <=, >, >=.
Keys and set members must be hashable. Strings, numbers, booleans, None, and tuples of those
are hashable. Lists, dictionaries, and sets are not, and using one as a key fails with
unhashable type: list. A missing key in d[key] fails with key "x" not in dict; use
d.get("x") for None or d.get("x", default).
Mutating a collection while looping over it is an error. Appending to a list, assigning to one
of its elements, or assigning to a dictionary key inside a for loop over that collection fails,
even when the key already exists. This fails with messages such as
cannot append to list during iteration. Loop over a copy, such as for k in list(d), or build a
new collection.
Fewer methods. Lists have seven methods (append, clear, extend, index, insert,
pop, remove), so there is no sort(), reverse(), count(), or copy(). Dictionaries have no
fromkeys(), and there is no collections module. See Methods.
Sorting is stable. sorted(items, key=...) keeps the original order of elements that compare
equal. It returns a new list. Values of different types cannot be ordered, so sorted([1, "a"])
fails with string < int not implemented. The same holds for min, max, and the < operators.
Strings compare by their UTF-8 bytes.
Tuples cannot change. t[0] = 1 fails with tuple value does not support item assignment.
Functions
Keyword-only arguments and *args/**kwargs work. def f(a, b=2, *args, c, **kw) is valid, and
so is a bare * before keyword-only parameters. Call-site unpacking with f(*list) and
f(**dict) works.
Defaults are evaluated once, when the function is defined, as in Python.
Built-in functions take positional arguments. enumerate(items, start=1) fails, while
enumerate(items, 1) works. See Built-in Functions.
The print function writes a line to standard output and accepts only sep. There is no end or file
argument. Status messages belong in ui.info() and diagnostics in log.debug(). See
Output streams.
Collections print the way the language writes them. Strings inside collections use double quotes,
sets print as set([1, 2]), floats always show a decimal point (1.0), and None prints as None.
The type() function returns a string. Test for a type with type(1) == "int". There is no
isinstance, callable, iter, next, map, filter, id, open, or input. Comprehensions
cover map and filter, and exec.run and fs.read_file cover process and file access.
Recursion works, within a limit. Calls nest to a depth of 10,000. See Recursion.
Standard library
The Python standard library and Python packages are not part of the runtime, and there is no
import. The built-in modules that Atmos supplies cover what automation needs:
| Python | In Atmos Automation Language |
|---|---|
subprocess | exec.run and atmos.run. |
os.environ | env, a read-only dictionary of the step's declared inputs. |
open(path).read() | fs.read_file. File writes are not available. |
json | json.encode, json.decode, json.indent. |
re | regex.search, regex.findall, and regex.replace. These use Go (RE2) syntax with no lookaround or backreferences. |
argparse | cli.command, cli.arg, and cli.flag. |
concurrent.futures, threading | steps.parallel and steps.task. |
logging | log and ui. |
There is no math, time, datetime, random, pathlib, itertools, functools,
collections, or typing equivalent.
Values that come from Atmos
Some values are not dictionaries. ctx is a struct: read its fields
with dot notation, as in ctx.flags, and look them up by name with getattr(ctx, "flags"). Indexing
a struct with ctx["flags"] fails. The flags and arguments fields are read-only
dictionaries, so ctx.flags["name"] works but assigning to it does not.
Related pages
- Execution Model explains single-assignment globals, frozen values, and error reporting in detail.
- Built-in Functions and Methods list what is available.
- Atmos Automation Language introduces the language and why Atmos uses it.