Libraries¶
Third-party libraries are translated through a closed map: the calls listed here are reproduced by the runtime,
and any other call of a library is refused at compile time with file:line (leave the route to Python with
--python-side, see Hybrid mode).
Arguments follow the same rule: one the runtime does not implement is refused (at compile time when the call can be recognized there, else with a TypeError), never ignored. The exceptions are network settings that cannot change a response, only when a failure happens: httpx Timeout(write=, pool=), aiohttp ClientTimeout(ceil_threshold=), aio-pika connect(timeout=), publish(mandatory=, immediate=, timeout=) and channel(publisher_confirms=) (the binary always waits for the broker's confirmation).
| Library | Scope |
|---|---|
| nh3 (0.3) | clean (the same ammonia crate, configured like nh3, same ValueErrors), clean_text/escape without tags, is_html; attribute_filter, url_relative and id_prefix refused |
| httpx (0.28) | AsyncClient (auth= a BasicAuth or a (user, password) tuple), requests, timeouts, raise_for_status, exceptions; no redirects followed, no files=, cookies or custom transports |
| aiohttp (3.14) | ClientSession (timeout=None = default timeouts), async with session.post(...) as resp, ssl=False, proxy=None, status/reason/text()/json(), ClientTimeout; reason is the standard phrase; a per-request timeout=None keeps the session's timeout Replacing the request method (aiohttp.ClientSession._request = wrap(aiohttp.ClientSession._request), httpx.AsyncClient.request = ..., e.g. to time outgoing calls) is supported: the clients call the replacement with the session, the method, the URL and the keyword arguments aiohttp passes (allow_redirects= for get/options/head, data= for post/put/patch); httpx's replacement receives only the keyword arguments given by the caller (httpx passes all its defaults). Other assignments to library attributes are refused. |
| yarl | URL(str): host, port (scheme default), scheme, path, query_string, fragment, user, password, str() |
| redis.asyncio (redis-py 5+) | from_url/Redis(...), get/set (ex, px as an int or a timedelta, nx, xx, get)/setex/delete/exists/incr/decr/mget/expire/ttl/keys/scan_iter(match, count, _type)/hash commands/ping, Retry(backoff, n) (retries without the backoff delay), redis-py's encoding and exceptions |
| aio-pika 10 | connect/connect_robust (async with), channel(), set_qos(prefetch_count=, global_=), declare_queue(name, durable=, exclusive=, auto_delete=, passive=, arguments=) (declaration_result), get_queue, declare_exchange(name, type, durable=, auto_delete=, internal=, passive=, arguments=) of every type, get_exchange, queue_delete, exchange_delete; exchange.publish(Message(...), routing_key=) with publisher confirms (aiormq's defaults: delivery mode 1, priority 0, a random message_id), exchange.delete(); queue.get(no_ack=, fail=), bind/unbind (an exchange or its name, the queue's name as default key), purge, delete(if_unused=, if_empty=), consume(callback, no_ack=) (each delivery's callback a task of its own, as aiormq runs them) and cancel(tag), async with queue.iterator(timeout=) as it: async for message in it (TimeoutError at the timeout); a received message's properties and delivery fields, ack/nack/reject, async with message.process(requeue=, reject_on_redelivered=, ignore_processed=), info(); the broker's channel errors as aiormq raises them (ChannelNotFoundEntity, ChannelPreconditionFailed, ChannelAccessRefused, ChannelLockedResource, ChannelClosed, with the broker's reply text), AMQPConnectionError, QueueEmpty, MessageProcessError. connect_robust does not reconnect after a connection loss; DeliveryMode and ExchangeType members are their plain values (2, "topic"); repr() and str() of a received message and of MessageProcessError (aio-pika pretty-prints the message's fields) are not reproduced; aio_pika.patterns (its RPC and Master) is not translated: write the reply-to/correlation-id exchange with consume and asyncio.Future |
| tenacity 9 | @retry(stop=, wait=, retry=, before=, after=, before_sleep=, reraise=, retry_error_callback=) on async and sync functions (bare @retry too), stop_after_attempt/after_delay/never/any/all, wait_fixed/none/random/exponential/exponential_jitter/incrementing/combine/chain, retry_if_exception_type/not_exception_type/exception/result, retry_always/never/any/all, |/&/+ combinations, before_sleep_log, RetryError (last_attempt), the retry state seen by callbacks (attempt_number, outcome, fn, args...). str(RetryError) shows 0x0 instead of CPython's object address. Refused: sleep=, retry_error_cls=, before_sleep_log(exc_info=True), Retrying/AsyncRetrying objects |
| prometheus_client 0.26 | see Prometheus metrics |
| python-json-logger (4.0) | JsonFormatter from pythonjsonlogger.json or the old pythonjsonlogger.jsonlogger, see Logging; json_default=, json_encoder=, json_serializer=, exc_info_as_array= and stack_info_as_array= refused |
| uvicorn | uvicorn.logging.DefaultFormatter / AccessFormatter (in a dictConfig or called); the binary is the server and takes uvicorn's command-line options (Logging) |
| python-jose (3.5) | jwt.encode/decode with HMAC algorithms and decode options; identical tokens, same exceptions |
| PyJWT (2.15) | jwt.encode (algorithm=, headers=, sort_headers=; datetimes of exp/iat/nbf encoded from a copy), jwt.decode / decode_complete (algorithms=, options= with every verify_*, require, strict_aud, enforce_minimum_key_length; audience=, issuer=, subject=, leeway= as a number or a timedelta), get_unverified_header, with HS256/HS384/HS512 and none: identical tokens, the same checks in the same order, the jwt.exceptions hierarchy and messages (MissingRequiredClaimError.claim included). Refused at transpile time: an asymmetric algorithm (RS*, ES*, PS*, EdDSA) written as a literal, json_encoder=, detached_payload=, verify=, extra keyword arguments, PyJWK/PyJWKClient. At run time an asymmetric algorithm raises a py2axum RuntimeError (500), and so does an HMAC key shaped like a DER structure (PyJWT tries it as a public key). InsecureKeyLengthWarning is not emitted. |
| bcrypt (5.0), pyotp (2.9) | same hashes and codes |
| pwdlib (0.2+, Argon2) | PasswordHash.recommended(): hash, verify, verify_and_update (see Security) |
| anyio | anyio.sleep(delay) |
| itsdangerous (2.2) | URLSafeTimedSerializer(secret_key or key list, salt=) with the default signer and serializer: dumps(obj, salt), loads(s, max_age, return_timestamp, salt) (positional or keyword; return_timestamp=True gives (payload, aware datetime in UTC)), the exceptions' payload and date_signed. Any other argument, loads_unsafe and custom signers or serializers are refused at transpile time |
| cryptography | Fernet (tokens readable both ways), Fernet.generate_key() |
| jinja2, aiosmtplib, email | templates (minijinja with Jinja2's output), MIME messages, SMTP sending; Jinja2Templates(directory=) and TemplateResponse(request, name, context, status_code, headers, media_type) (request added to the context, date strftime/isoformat callable from a template; env.filters[name] = f with a project function or lambda, called synchronously during the render; context_processors, env= and url_for are not supported) |
| pywebpush (2.3) | aes128gcm encryption, VAPID (py-vapid rules), WebPushException |
| google-auth (2.49) | id_token.verify_oauth2_token / verify_token |
| alembic | alembic.config.Config ini reading only |
| psutil (7) | cpu_percent, virtual_memory, disk_usage, pids |
| sentry-sdk 2.x | see Sentry |
| python-dateutil (2.9) | relativedelta(years=, months=, weeks=, days=, hours=, minutes=, seconds=, microseconds=) with integers: normalization, repr, attributes, date/datetime +/- (month-end clamping), +/-/*/== between deltas. Refused: absolute fields (year=, day=, weekday=...), relativedelta(dt1, dt2); fractional days/hours raise a py2axum TypeError |
| icalendar (7.0) | Calendar(), Event(), Alarm(), add(name, value) of a text property (summary, description, uid, prodid, x-*...: TEXT escaping), url (as is), a date (dtstart, dtend, due: VALUE=DATE) or a timedelta (trigger, duration), add_component(), to_ical(): byte-identical output (canonical property order, folding at 75 octets). Parameters, datetimes, other property types and reading properties back raise a TypeError |
| orjson (3.13) | loads (str or bytes: the decoder's messages and positions, cut documents reported as "unexpected end of data", the depth and memory limits of orjson's parser) and dumps(obj, default=, option=) with every OPT_* (indentation, sorted and non-str keys, naive UTC, Z, microseconds, strict integers, the passthrough options), datetimes, dates, times, UUIDs, Enum members, dataclasses, tuples, default= (its recursion limit, the error chained to the TypeError), JSONDecodeError (a json.JSONDecodeError) and JSONEncodeError (TypeError); FastAPI's ORJSONResponse (returned, response_class=, default_response_class=). orjson.Fragment and NumPy values are refused. loads of an integer between 2^63 and 2^64 (an int for orjson) raises OverflowError, like any integer beyond 64 bits |
| Unidecode (1.4) | unidecode, unidecode_expect_ascii, unidecode_expect_nonascii with errors= (ignore, strict, replace, preserve) and replace_str=, UnidecodeError (index): Unidecode's own tables |
| ecs-logging (2.3) | StdlibFormatter(fmt, datefmt, style, validate, stack_trace_limit, extra, exclude_fields, ensure_ascii), called or named in a dictConfig ("()": "ecs_logging.StdlibFormatter"): the ECS document (@timestamp, log.*, process.*, error.*, extra= nested or dotted, Elastic APM fields), the same field order and errors; a subclass overriding format_to_ecs and the structlog processor are not translated |
| PyYAML (6.0) | safe_load(stream) and load(stream, Loader=yaml.SafeLoader) (a str, bytes in UTF-8 or UTF-16 with its BOM, or a file object: open(), io.StringIO, io.BytesIO, read by 4 096-character chunks as PyYAML does), safe_dump, safe_dump_all, dump/dump_all (Dumper=yaml.Dumper or yaml.SafeDumper) with every option (default_style, default_flow_style, canonical, indent, width, allow_unicode, line_break, encoding, explicit_start, explicit_end, version, tags, sort_keys) to a string, bytes or a file object: a port of PyYAML's pure-Python reader, scanner, parser, composer, resolver, SafeConstructor, representer, serializer and emitter, the same values (YAML 1.1 implicit types, timestamps, !!binary, !!set, !!omap, merge keys, aliases sharing one object) and the same text; its exceptions (yaml.YAMLError, MarkedYAMLError, yaml.reader.ReaderError, yaml.scanner.ScannerError, yaml.parser.ParserError, yaml.composer.ComposerError, yaml.constructor.ConstructorError, yaml.representer.RepresenterError, yaml.emitter.EmitterError, yaml.serializer.SerializerError) with their message, problem, context, note, problem_mark / context_mark (name, index, line, column, buffer, pointer, get_snippet()) and the reader's name, position, character, encoding, reason. Refused: load() without Loader=yaml.SafeLoader, full_load, unsafe_load, the libyaml classes (CSafeLoader, CDumper...), safe_load_all/load_all (a generator), compose/parse/scan/emit/serialize, add_constructor and the other extension points; differences in Supported |
| croniter (6.2) | croniter(expr, start_time, ret_type=, day_or=, max_years_between_matches=, is_prev=, hash_id=, implement_cron_bug=, second_at_beginning=, expand_from_start_time=): get_next/get_prev/get_current/set_current/next(), all_next()/all_prev() read with next(), the attributes cur, start_time, tzinfo, expanded, nth_weekday_of_month, expressions; croniter.is_valid/match/match_range/expand, croniter_range, datetime_to_timestamp, the six exceptions. A port of croniter.py: 5, 6 and 7 fields, L, W, #, ?, names, @hourly..., H hashed by hash_id (same CRC-32), naive, timezone and ZoneInfo start times with the DST rules of _add_tzinfo (folds included), float and datetime results with CPython's rounding. Differences: see croniter |
| xmltodict (1.0) | parse(str or UTF-8 bytes, process_namespaces=, namespaces=) with the default options (@ attributes, #text, lists for repeated elements, whitespace stripped, comments and processing instructions ignored); dict_constructor= is accepted, the result is always made of dicts. Malformed XML raises xml.parsers.expat.ExpatError with expat's message and position for the usual errors (others may word or place it differently). A DOCTYPE is refused at run time; other options are refused |
File formats left to Python¶
Libraries that write a file are translated only when their output is reproducible byte for byte. fpdf2
(the PDF carries its creation date), python-docx and openpyxl (the zip entries carry the time they were
written), reportlab, weasyprint, and the readers xlrd, olefile, pdfplumber stay in Python: declare
the routes --python-side auto (Hybrid mode).