String formatting
Monty implements CPython 3.14’s format mini-language for f-string
interpolations, str.format() replacement fields and the format()
builtin. str.format() supports
positional and keyword fields, automatic and manual numbering, attribute and
item access, !s / !r / !a conversions, nested replacement fields in
format specs, and escaped braces.
Attribute access is limited to lookups that complete inside the sandbox.
If a replacement field needs a host operation, such as
'{0.environ}'.format(os), Monty raises
NotImplementedError: str.format attribute access cannot suspend instead of
returning the attribute as CPython does.
str.format_map() is not implemented and raises AttributeError.
str % args and bytes % args implement CPython’s printf-style directives:
%s, %r, %a, %c, %d / %i / %u, %o, %x / %X, %e / %E,
%f / %F, %g / %G and %% (plus %b for bytes), with the -, +,
space, # and 0 flags, a width and precision given literally or as *
arguments, %(key)s mapping lookups, and the ignored h / l / L length
modifiers. The divergences:
-
bytes%s/%baccept onlybytes. Monty has nobytearray,memoryviewor__bytes__protocol, sob'%s' % objraisesTypeError: %b requires a bytes-like object, or an object that implements __bytes__, not 'C'even whenCdefines__bytes__. -
Operands are coerced through
__index__only. A class that defines just__int__raisesTypeError: %d format: a real number is required, not Cwhere CPython would call it; likewise one defining just__float__under%fraisesmust be real number, not C. -
A user class is never a mapping. CPython lets
%(key)sindex any object with__getitem__and skips the leftover-arguments check for it; Monty recognises onlydict(and itscollectionssubclasses),list,bytesandrange, so'%(k)s' % instanceraisesTypeError: format requires a mappingand'abc' % instanceraisesnot all arguments converted during string formatting. -
%crejects surrogate code points.'%c' % 0xD800raisesOverflowError: %c arg not in range(0x110000), the same error as an out-of-range code point, because Monty strings cannot hold lone surrogates (CPython returns'\ud800').
f-strings, str.format() and format() dispatch to a type’s __format__
only for date, datetime and time, which interpret the spec as a
strftime string (f'{dt:%Y-%m-%d}', '{:%Y-%m-%d}'.format(dt) or
format(dt, '%Y-%m-%d')); see
datetime.md. There is no general __format__ protocol: user
classes can’t customise formatting (see classes.md), and all
other types use the builtin mini-language formatter. A format spec on a
user-class instance is silently applied to str(obj) (f'{obj:>10}' pads),
where CPython raises TypeError: unsupported format string passed to Foo.__format__.
n always behaves as in the C/POSIX locale (Monty has no locale support):
like d for integers and g for floats, with no digit grouping. CPython
under a grouping locale would insert locale-specific separators; Monty never
does.
repr escapes non-printable code points via the unicode-general-category
crate, whose Unicode version may lag CPython’s, so a code point assigned in a
newer Unicode release than the crate ships could be escaped by Monty while
CPython prints it literally, or the reverse. Common text is unaffected.
- A
widthorprecisionwhose decimal value overflowsusizeraisesSyntaxError: Invalid format specifier '...': width or precision overflows usizein a literal f-string spec. Runtime specs, includingstr.format()andformat(), raiseValueErrorinstead, with an additionalfor object of type '...'suffix. - Very large widths/precisions are additionally bounded by the resource tracker; see resource_limits.md.
CPython validates a static (literal) f-string spec only when the f-string
executes, so a malformed spec in dead code never raises. Monty validates
literal f-string specs at compile time for the structurally-malformed cases:
two or more trailing
characters after the type field (f'{1:kk}', f'{1:10xyz}') and usize
overflow, raising SyntaxError instead of CPython’s runtime ValueError. The
message text otherwise matches, minus CPython’s for object of type '...'
suffix, which needs the runtime value type. Specs whose error is
value-type-dependent or only resolvable at format time (Unknown format code 'k', the Cannot specify … grouping conflicts, and Format specifier missing precision) are deferred to runtime and raise the exact CPython ValueError,
as do all dynamically-built specs (f'{1:{spec}}') and all str.format()
and format() specs.