Skip to content

API reference

The public API of cuere. Everything here is re-exported from the top-level cuere package (see cuere.__all__); the canonical module is shown for each symbol. Each entry is rendered from its source docstring.

Terminal API

The high-level entry points.

render

render(data, *, mode=RenderMode.HALF, invert=False, dark=None, light=None, **options)

Encode data (unless it is already a QRMatrix) and render it.

dark / light customize ANSI mode's module colors (see cuere.render.render_matrix); they are only valid for mode="ansi".

Example:

from cuere import render

text = render("HELLO")                    # half-block QR as a str
colored = render("HELLO", mode="ansi")    # theme-proof SGR colors
wide = render("HELLO", mode="block", invert=True)

show

show(data, *, mode=RenderMode.HALF, invert=False, out=None, width=None, on_too_wide='error', force=False, dark=None, light=None, **options)

Render data and write it to out (default sys.stdout).

The fit check is width-only: wrapping destroys a code, but a code taller than the terminal merely scrolls and stays scannable, so height is not gated here (use fits for a height-aware predicate).

ANSI mode silently falls back to HALF when NO_COLOR is set or the stream is not a tty, unless force=True: raw SGR codes in logs or pipelines are worse than a theme-dependent code. dark / light customize ANSI mode's colors (only valid for mode="ansi"); when ANSI falls back, they are dropped with it so a no-color terminal gets plain glyphs.

Example:

from cuere import show

show("https://example.com")             # to sys.stdout
show("HELLO", mode="ansi", force=True)  # keep colors even when piped

fits

fits(data, *, mode=RenderMode.HALF, width=None, height=None, **options)

Whether the rendered code fits the terminal in both dimensions.

Width is the hard constraint (wrapping destroys a scan); height is checked too because a code taller than the screen scrolls out of view. width and height default to the current terminal size.

Example:

from cuere import fits, show

if fits("https://example.com"):
    show("https://example.com")   # known to fit the current terminal

SupportsWrite

Bases: Protocol

A text sink: any object with a write(str) method.

show writes a single string and probes isatty defensively via getattr, so it needs only this much of a stream — sys.stdout, io.StringIO, and minimal write-only wrappers all qualify. This is deliberately narrower than typing.IO[str].

Example:

import io
from cuere import show

buf = io.StringIO()      # any object with .write(str) is a SupportsWrite
show("HELLO", out=buf)
captured = buf.getvalue()

write

write(s)

Write s to the sink; the return value is ignored.

Matrix & encoding

QRMatrix dataclass

Immutable QR module matrix with the quiet zone baked in.

modules rows must be rectangular; True is a dark module.

Example:

from cuere import QRMatrix, render

m = QRMatrix.encode("HELLO")        # build the matrix once
print(m.version, m.width, m.height)
print(render(m))                    # render a pre-built matrix
print(render(m.inverted()))         # dark/light flipped

size property

size

Number of rows (== columns for encoded matrices), border included.

height property

height

Number of module rows.

width property

width

Number of module columns.

encode classmethod

encode(data, *, error=ECLevel.L, border=4, micro=False, boost_error=False)

Encode data into a QR matrix.

boost_error is off by default on purpose: segno would otherwise silently raise the error-correction level when space allows, growing the module count for no benefit on a screen.

inverted

inverted()

Return a copy with every module flipped.

ECLevel

Bases: StrEnum

QR error-correction level.

L is the default everywhere in cuere: codes shown on screens are not subject to physical damage, and lower correction means fewer modules, so the code fits a terminal (this matches what Claude Code CLI does).

Example:

from cuere import ECLevel, render

render("HELLO", error=ECLevel.M)   # more robust, slightly larger code

Rendering

RenderMode

Bases: StrEnum

How a QR matrix is turned into text.

Example:

from cuere import RenderMode, render

render("HELLO", mode=RenderMode.BLOCK)   # or just mode="block"

HALF class-attribute instance-attribute

HALF = 'half'

Unicode half-blocks, 1 column x ½ row per module (default).

BLOCK class-attribute instance-attribute

BLOCK = 'block'

Two full-width chars per module: most robust glyphs, twice as wide.

ANSI class-attribute instance-attribute

ANSI = 'ansi'

HALF glyphs with forced black-on-white SGR colors: theme-proof.

render_matrix

render_matrix(matrix, *, mode=RenderMode.HALF, invert=False, dark=None, light=None)

Render matrix as text; lines are equal-width, no trailing newline.

The quiet zone is rendered as part of the output (spaces / light modules) because a scanner needs it in the code's own background, not whatever the surrounding terminal happens to show.

dark / light customize the SGR colors of ANSI mode (dark modules and the light ground respectively); they default to the theme-proof black-on-white (palette 16 on 231). They are only valid for mode="ansi" — passing a color for half / block raises ColorError.

Example:

from cuere import QRMatrix, RenderMode, render_matrix

m = QRMatrix.encode("HELLO")
plain = render_matrix(m)  # default half-blocks
colored = render_matrix(m, mode=RenderMode.ANSI, dark="cyan")  # custom ANSI ink

render_svg

render_svg(matrix, *, scale=DEFAULT_SCALE, invert=False)

Render matrix as a standalone SVG document, one module per unit.

Dark modules become a single <path> over a light background <rect>. viewBox is the module grid, so the path coordinates stay small integers; width/height scale it to scale pixels per module. shape-rendering="crispEdges" keeps module borders sharp (anti-aliasing blur hurts scanning). invert flips dark and light via matrix.inverted() — the same single code path the glyph renderers use.

Example:

from cuere import QRMatrix, render_svg

svg = render_svg(QRMatrix.encode("HELLO"), scale=8)
with open("hello.svg", "w", encoding="utf-8") as f:
    f.write(svg)

render_width

render_width(matrix, mode=RenderMode.HALF)

Terminal columns the rendered matrix occupies (ignoring SGR codes).

Example:

from cuere import QRMatrix, render_width, render_height

m = QRMatrix.encode("HELLO")
cols, rows = render_width(m), render_height(m)   # half-block footprint

render_height

render_height(matrix, mode=RenderMode.HALF)

Terminal rows the rendered matrix occupies.

Example:

from cuere import QRMatrix, RenderMode, render_height

rows = render_height(QRMatrix.encode("HELLO"), mode=RenderMode.BLOCK)

Color

Color = str | int | tuple[int, int, int]

A terminal color for ANSI mode's dark / light modules. One of:

  • a name — one of the 16 standard ANSI colors ("black", "red", …, "white" and their "bright_*" variants), matched case- and separator-insensitively;
  • a palette index — an int in 0–255 (the xterm 256-color cube), or its decimal string form ("16");
  • a truecolor value — a "#rrggbb" / "#rgb" hex string, an (r, g, b) tuple of 0–255 ints, or its "r,g,b" string form.

A malformed color raises ColorError.

from cuere import render

render("HI", mode="ansi", dark="cyan", light="black")     # named
render("HI", mode="ansi", dark=16, light=231)             # palette index
render("HI", mode="ansi", dark="#00ffaa", light=(0, 0, 0))  # truecolor

Output formats

OutputFormat

Bases: StrEnum

A target format for render_bytes / save.

Example:

import io

from cuere import OutputFormat, save

stream = io.BytesIO()
save("HELLO", "out.svg")  # inferred from the suffix
save("HELLO", stream, format=OutputFormat.SVG)  # a stream has no suffix, be explicit

TEXT class-attribute instance-attribute

TEXT = 'text'

The terminal glyph rendering (honors mode/invert), UTF-8 bytes.

SVG class-attribute instance-attribute

SVG = 'svg'

A standalone SVG vector document, UTF-8 bytes.

PNG class-attribute instance-attribute

PNG = 'png'

A PNG raster image; requires the optional cuere[image] extra.

render_bytes

render_bytes(data, *, format=OutputFormat.TEXT, mode=RenderMode.HALF, invert=False, scale=DEFAULT_SCALE, **options)

Encode data and render it to bytes in format.

mode selects the glyphs for the text format and is ignored by the image formats; scale is pixels-per-module for svg/png and is ignored by text. Encoding keywords (error/border/…) apply only when data is not already a QRMatrix.

Example:

from cuere import render_bytes

svg = render_bytes("HELLO", format="svg")   # UTF-8 bytes
png = render_bytes("HELLO", format="png")   # needs cuere[image]

save

save(data, dest, *, format=None, mode=RenderMode.HALF, invert=False, scale=DEFAULT_SCALE, **options)

Render data and write the bytes to dest.

dest is a filesystem path or an open binary stream. When format is omitted it is inferred from a path's suffix (.txt/.svg/.png); a stream needs an explicit format. Unknown formats and unguessable destinations raise UnknownFormatError.

Example:

from cuere import save

save("HELLO", "qr.png")              # format from the .png suffix
with open("qr.svg", "wb") as fh:
    save("HELLO", fh, format="svg")  # a stream needs an explicit format

SupportsWriteBytes

Bases: Protocol

A binary sink: any object with a write(bytes) method.

The byte-oriented counterpart to cuere.terminal.SupportsWrite; sys.stdout.buffer, io.BytesIO, and an open binary file all qualify. Runtime-checkable so save can tell a stream from a path — neither str nor os.PathLike carries a write method.

Example:

import io
from cuere import save

buf = io.BytesIO()              # any object with .write(bytes) qualifies
save("HELLO", buf, format="svg")

write

write(b)

Write b to the sink; the return value is ignored.

Wallet URIs

bitcoin_uri

bitcoin_uri(address, *, amount=None, label=None, message=None)

Build a well-formed BIP-21 bitcoin: payment URI.

address is validated structurally (base58 or bech32/bech32m alphabet); amount is a BTC value (see _format_amount); label and message are free text and get percent-encoded. Parameters left as None are omitted; an explicit empty label/message is kept as an empty value. Invalid input raises WalletURIError.

The result is a plain str: pass it to optimize_uri (a bare address with no query shrinks to QR-alphanumeric) and then to render / show to draw the code.

Example:

from decimal import Decimal
from cuere import bitcoin_uri, optimize_uri, show

addr = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"
uri = bitcoin_uri(addr, amount=Decimal("0.01"), label="Tip")
show(optimize_uri(uri))

lightning_uri

lightning_uri(payload)

Build a lightning: URI from a bech32 Lightning payload.

payload is a BOLT11 invoice (lnbc… / lntb… / …), an LNURL (lnurl1…), or a BOLT12 offer (lno1…) — any bech32 string whose human-readable part begins ln. It is validated structurally — a single-case run of ASCII alphanumerics beginning ln (the bech32 charset is a subset of that) — but its checksum is not verified, mirroring bitcoin_uri. An empty string, a mixed-case payload, or one carrying a : / @ / query character raises WalletURIError.

The result is a plain str. A lowercase payload composes with optimize_uri, which uppercases the URI to QR-alphanumeric mode for a smaller code (lightning: is SchemeCase.INSENSITIVE); pass the result to render / show to draw it.

Example:

from cuere import lightning_uri, optimize_uri, show

show(optimize_uri(lightning_uri("lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqf")))

ethereum_uri

ethereum_uri(address, *, value=None, chain_id=None, gas_limit=None, gas_price=None)

Build an EIP-681 ethereum: native-payment URI.

address is the recipient, validated structurally as 0x + 40 hex digits with its case preserved (it carries the EIP-55 checksum when mixed-case). value is the amount in wei (1 ETH = 10**18 wei), a non-negative integer uint256; gas_price is likewise in wei and gas_limit is a gas-unit count. chain_id (EIP-155, e.g. 1 for mainnet) is emitted as @<chain_id>. Parameters left as None are omitted. Invalid input raises WalletURIError.

The result is a plain str to pass to render / show. Do not run it through optimize_uri: the EIP-55 checksum is case-significant, so uppercasing would corrupt the address (optimize_uri already refuses the ethereum: scheme for this reason).

Example:

from cuere import ethereum_uri, show

# 0.01 ETH = 10**16 wei, on Ethereum mainnet (chain_id=1)
show(ethereum_uri("0xfb6916095ca1df60bb79Ce92ce3ea74c37c5d359",
                  value=10**16, chain_id=1))

erc20_transfer_uri

erc20_transfer_uri(token, *, to, amount, chain_id=None, gas_limit=None, gas_price=None)

Build an EIP-681 ERC-20 transfer URI: ethereum:<token>/transfer?….

Encodes a call to the token's transfer(address,uint256) function. token is the ERC-20 contract address and to the recipient, both validated as 0x + 40 hex digits with case preserved. amount is a non-negative integer in the token's base units (its own decimals — which this library cannot know — so no scaling is applied); it is emitted as the uint256 argument. chain_id / gas_limit / gas_price behave as in ethereum_uri. Invalid input raises WalletURIError.

As with ethereum_uri, never pass the result to optimize_uri.

Example:

from cuere import erc20_transfer_uri, show

# transfer 1 USDC (6 decimals -> 1_000_000 base units) on mainnet
uri = erc20_transfer_uri(
    "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
    to="0x8e23ee67d1332ad560396262c48ffbb01f93d052",
    amount=1_000_000,
    chain_id=1,
)
show(uri)

optimize_uri

optimize_uri(uri)

Uppercase a bech32-scheme URI when that provably shrinks its QR code.

Applied only when all of these hold: the scheme is case-insensitive (SchemeCase.INSENSITIVEbitcoin: or lightning:), the URI is already entirely lowercase (so uppercasing cannot destroy significant case), and the uppercased form is fully QR-alphanumeric (so a query string or any other non-alphanumeric byte rules it out). Idempotent; returns the input unchanged in every other case — including case-significant (ethereum:, wc:) and unrecognized schemes.

Example:

from cuere import optimize_uri

optimize_uri("bitcoin:bc1q...")    # -> "BITCOIN:BC1Q..." (smaller QR)
optimize_uri("ethereum:0xAbC...")  # unchanged (case-significant)

scheme_case

scheme_case(uri)

Classify uri by how optimize_uri treats its scheme's case.

The scheme is the text before the first : (compared case-insensitively, per RFC 3986). A string with no : — hence no scheme — is SchemeCase.UNKNOWN. Only an SchemeCase.INSENSITIVE scheme is a candidate for optimization; see optimize_uri.

Example:

from cuere import scheme_case, SchemeCase

scheme_case("bitcoin:bc1q...") is SchemeCase.INSENSITIVE    # True
scheme_case("ethereum:0xAbC...") is SchemeCase.SIGNIFICANT  # True
scheme_case("mailto:a@b.com") is SchemeCase.UNKNOWN         # True

SchemeCase

Bases: Enum

How a URI scheme's payload responds to case folding.

This is what decides whether optimize_uri may uppercase a URI to reach QR alphanumeric mode (a smaller code): only an INSENSITIVE scheme is ever folded, the other two are always returned verbatim.

Example:

from cuere import scheme_case, SchemeCase

scheme_case("lightning:lnbc1...") is SchemeCase.INSENSITIVE   # True

INSENSITIVE class-attribute instance-attribute

INSENSITIVE = 'insensitive'

bech32 payload (BIP-173) — case carries no meaning, so uppercasing the whole URI is lossless. bitcoin: and lightning: (BOLT11 / LNURL).

SIGNIFICANT class-attribute instance-attribute

SIGNIFICANT = 'significant'

A scheme cuere knows to be case-significant — folding would corrupt it. ethereum: (EIP-55 checksum case) and wc: (WalletConnect: a case-significant relay protocol, a percent-encoded bridge URL, and an opaque pairing key).

UNKNOWN class-attribute instance-attribute

UNKNOWN = 'unknown'

A scheme cuere does not recognize, or a string with no scheme at all. Treated like SIGNIFICANT — never folded — because cuere cannot prove the transform is lossless.

is_qr_alphanumeric

is_qr_alphanumeric(data)

Whether data is non-empty and encodable in QR alphanumeric mode.

Example:

from cuere import is_qr_alphanumeric

is_qr_alphanumeric("BITCOIN:BC1Q...")   # True  — fits the compact mode
is_qr_alphanumeric("bitcoin:bc1q...")   # False — lowercase is byte mode

Rich integration

QRCode

A QR code that can be printed, centered, or framed by Rich.

Example:

from rich.console import Console
from rich.panel import Panel
from cuere.rich import QRCode

Console().print(Panel(QRCode("bitcoin:BC1Q..."), title="scan to pay"))

Exceptions

CuereError

Bases: Exception

Base class for all cuere errors.

Catch this to handle any cuere-specific failure in one place.

from cuere import CuereError, render

try:
    render("x" * 10_000)        # more data than any QR version holds
except CuereError as exc:
    print(exc)

EncodingError

Bases: CuereError

Raised when data cannot be encoded as a QR code.

from cuere import EncodingError, render

try:
    render("x" * 10_000)        # exceeds QR capacity
except EncodingError:
    ...

WidthError

Bases: CuereError

Raised when a rendered QR code does not fit the available width.

from cuere import WidthError, show

try:
    show("a long payload", width=10)   # narrower than the code
except WidthError as exc:
    print(exc.required, exc.available)

ColorError

Bases: CuereError

Raised when a color argument is malformed or used where no color applies.

Carries the offending value — the bad color, or the render mode that cannot take one (only ANSI mode is colored). The message is composed here so call sites pass only a reason constant plus the value, never a raise-site string literal (keeping the public exception contract a pure CuereError, mirroring WalletURIError).

from cuere import ColorError, render

try:
    render("HI", mode="ansi", dark="not-a-color")
except ColorError:
    ...

WalletURIError

Bases: CuereError

Raised when arguments to a wallet-URI builder are invalid.

Carries the offending value; the human-readable reason is built here so call sites never pass a message string (keeping the public exception contract a pure CuereError, never a bare ValueError).

from cuere import WalletURIError, bitcoin_uri

try:
    bitcoin_uri("not an address!")
except WalletURIError as exc:
    print(exc.value)            # the offending input

UnknownFormatError

Bases: CuereError

Raised when an output format is unrecognized or cannot be inferred.

Like the other cuere errors, the human-readable message is composed here so call sites pass only structured data (hint is a module constant, not a raise-site literal): the offending value plus a hint that explains what was expected.

from cuere import UnknownFormatError, save

try:
    save("HI", "out.bmp")       # unrecognized suffix
except UnknownFormatError:
    ...

MissingDependencyError

Bases: CuereError

Raised when an output format needs an optional dependency that is absent.

Carries the format that was requested and the extra that provides it, so the message always names the right cuere[<extra>] to install.

from cuere import MissingDependencyError, render_bytes

try:
    render_bytes("HI", format="png")   # raises only without the cuere[image] extra
except MissingDependencyError as exc:
    print(exc.extra)                   # "image" (the missing extra)