Breviv0.24

How it works

What works, and limits

View as Markdown

Brevi's promise for a Python file is simple: the native program prints exactly what CPython prints, or Brevi refuses to build it. This page says how much Python that covers today and where the edges are.

In numbers#

CheckResult
Saved agent programs (591, from benchmark runs) built unchanged591 build, 591 match CPython on every test
Large programs (18; chess, a text adventure, an interpreter; 200 to 750 lines over 3 to 4 files)18 build, 18 match
Behaviour traps (13: big ints, // and % signs, float printing, dict order, unicode, aliasing, deep recursion, ...)13 match
Release test (24 tasks, Sonnet and Haiku writing fresh Python)92 to 98% of final programs native, the rest via the CPython fallback (the two that needed it in run w156a, both json_query, now build natively on main)
Random generated programs (bug hunt, 2 Nov 2026)400 of 400 match

"Match" means stdout byte for byte and the same exit status. See Benchmarks for the method.

Typed and dynamic builds#

Brevi has two native ways to build a Python file, and picks between them on its own.

Typed build, the fast path. Brevi infers a fixed type for every value across the whole program and writes plain Rust. Most agent-written programs build this way. Python behaviour that shows in output is reproduced: unlimited integers, shared mutable objects (r = rows[k]; r[0] -= 1 changes rows), dict printing and order, Python's float formatting and round, exception messages.

Dynamic build, the exact path. When a value changes type, a list mixes types, or the typed build would print something different from CPython (iterating a set, for example, where CPython's hash order shows), the whole program is built over a dynamic value type with CPython's own semantics: hashing and set order, formatting, exceptions, classes and method lookup. Still native, with no CPython involved, but slower than typed: typically somewhat faster than CPython rather than many times faster.

You do not choose; Brevi tries typed first. A file that does not build typed prints one {"path":...} line naming the function that kept it off the fast path; brevi check --explain x.py lists every reason.

Python features#

Works in native builds:

What runs native#

Standard-library modules are built by Brevi itself, with no CPython. The typed build is the fast path; what it lacks runs in the dynamic build, which is still native code. A program never needs CPython for the modules below (from v0.24.3):

ModuleTyped build (fastest)Dynamic build (native)
builtins and strmost, including bin, hex, oct, str.startswith/endswith with a start indexa tuple of prefixes, an end index
mathsqrt, floor, ceil, isqrt, gcd, lcm, comb, perm, factorial, isclose, log (with a base too), log2, log10, log1p, exp, expm1, trig and hyperbolic functions, atan2, hypot, fabs, trunc, degrees, radians, copysign, fmod, isnan, isinf, isfinite, pi, e, tau, inf, nan; domain errors are ValueErrors an except catchesthe rest (fsum, prod, dist, ...)
timetime, perf_counter, monotonicsleep, process_time
rematch, search, fullmatch, findall (no group or one), finditer, split, sub, compile, flags I, S, M, Afindall with two or more groups, sub with a function, groups tested against None, groupdict
jsonloads, load, dumpsdumps(indent=..., sort_keys=...) on mixed values, JSONDecoder().raw_decode and decode, json.decoder.scanstring
collectionsCounter, defaultdict, deque (popleft, appendleft), OrderedDictnamedtuple, deque(maxlen=...), rotate, a defaultdict read whose missing key matters
itertoolsgroupbychain, product, permutations, combinations, accumulate, islice, zip_longest, pairwise, count, cycle and the rest
functoolslru_cache, cachereduce, partial, cmp_to_key, total_ordering
dataclasses@dataclass (defaults, field(default_factory=...), order, frozen)asdict, astuple, replace
heapq, bisect, string, sys, typing, decimal, datetime (date)most functionsthe rest, datetime.datetime, strptime, strftime
os, os.path, pathlibpath text: os.path.join, basename, dirname, split, splitext, normpath, os.sep, pathlib.PurePath (from v0.24.4 outside the sandbox too); files, processes and the environment run in CPython
randomseed (int), random, randint, randrange, choice, choices, shuffle, sample, uniform, gauss, getrandbits, Random: CPython's generator, so seeded draws match
textwrapwrap, fill, shorten, dedent, indent, TextWrapper (also under --sandbox)
bytes, hashlib, base64, binasciib"..." literals, bytes(...), str.encode and bytes.decode (UTF-8, ASCII, Latin-1), indexing, slicing, in, +, split, strip, join, hex, fromhex and the other common methods; hashlib.md5, sha1, sha224, sha256, sha384, sha512 and new (update, digest, hexdigest, copy); base64 b64/urlsafe/b16 encode and decode, with CPython 3.13's padding rules and errors; binascii.hexlify, unhexlify, Error
argparseall of it but FileType and fromfile_prefix_chars (also under --sandbox): CPython 3.13's own argparse, so usage lines, help text and error messages match it
ioStringIO (also under --sandbox); BytesIO and the rest run in CPython
enumEnum (members, aliases, auto(), methods and properties, __init__ on tuple values, C(value), C[name], iteration, len, in, __members__), class C(str, Enum) and (int, Enum), @unique; IntEnum, StrEnum and Flag still run in CPython
statistics, copy, operator, csv, html, fractions, threading, abcparts (operator.lt and friends, csv.reader, csv.DictReader, csv.writer(sys.stdout), Fraction)the rest

Run in CPython for now (brevi run still works; under --sandbox the caller grants them with --allow-py): random beyond the common functions (betavariate, a str seed, ...), textwrap with fix_sentence_endings, argparse.FileType, io beyond StringIO, os beyond path text, enum.IntEnum, StrEnum and Flag, bytearray, binary files and sys.stdout.buffer, and hashlib beyond MD5 and the SHA-1/SHA-2 family. These are next on the roadmap.

Not built natively yet (brevi run falls back to CPython; --native refuses):

Libraries (numpy, pandas, ...)#

A Python file that imports a module Brevi does not build itself (numpy, pandas, requests, sqlite3, Pillow, ...) still builds natively: each library call runs in CPython, loaded into the native program, so results are CPython's own, and the rest of the program stays native and fast. Brevi's own modules (math, re, json, datetime, ...) stay native: what the typed build lacks of them goes to the dynamic build, never to CPython (from v0.24.3; see What runs native).

Limits today:

Integers#

Python integers have no size limit, and neither do Brevi's. In typed builds a value starts as a 64-bit integer and becomes a big integer as soon as it could pass 64 bits, through returns, closures, nonlocal and global and the // and % operators. Integers never silently wrap around, as they would in a release-mode Rust program. Powers of literals are worked out at compile time, so 3 ** 50 % 1000000007 gives Python's answer. Dynamic builds use big integers with CPython's semantics throughout, including bit operations on numbers of any size.

Errors and run-time behaviour#

A Python file keeps Python's run-time behaviour: a mistake Python only finds when a line runs (an index out of range, a bad int() argument) raises at that point, with the same exception type. The exception is a sure mistake, a name that is never defined, a method no Python type has, or text added to a number: brevi check and brevi build report that before the program runs, with a fix, and brevi run names it in its fallback note. Code that may never run (under sys.version_info, TYPE_CHECKING or if False, or in an except block) is left alone. Run-time error messages are shorter than CPython's tracebacks; error text on stderr is not part of the "same output" promise, but stdout and the exit status are.

Platforms#

LinuxmacOSWindows
Typed and dynamic buildsyesyesyes
Library calls through CPythonyesyesnot yet (falls back to CPython)
--standaloneyesnot yetnot yet
GUI (tkinter) programs, headless and --windowyesyesyes

Known gaps#

These are tracked on the roadmap: