Language
The Python subset
What compiles and runs today, construct by construct, each giving CPython's answer.
On this page
Waspy compiles a typed subset of Python ahead of time. The rule it holds to: a construct either gives CPython's answer or is refused at compile time, never a quietly different one. The few exceptions, integer width first, are listed under Where the answer differs. The modules board tracks each feature, every one backed by a test asserting CPython's answer.
Syntax and control flow
- Functions, several per module, calling each other; multiple files compile into one module
- Control flow:
if/else,whileandforloops withbreakandcontinue, andelse:clauses onfor,while, andtry, run exactly when CPython runs them - Conditional expressions (
a if cond else b), evaluating only the arm the condition picks. Both arms must read as one type: two classes share their nearest common base, a class andNonemake anOptional, and an empty collection takes the other arm's element type; arms that would read differently (1 if c else 2.5,True if c else 0,"a" if c else None, or an int arm where a float is held, since CPython keeps the int) are refused - Variables, assignment, and type annotations on parameters and return values. For a collection passed into or out of a function, the parameterised form (
List[str],Dict[str, int],Tuple[int, str]) is what gives the compiler its element type; a barelistordictthere carries only ints, bools, and None (see Limitations) - Calls pass arguments by position or by keyword (
f(1, c=7),Point(x=1)), with defaults filled in for the parameters skipped. Functions and__init__take keyword-only parameters (def f(x, *, factor=2)),*args: T(a tuple ofT), and**kwargs: T(adict[str, T]), andf(*xs)passes a list or tuple on as*args. A call to one of the module's own functions is checked against its signature, so a surplus or missing argument is a compile-timeTypeError assert test, msgraisesAssertionErrorwhen the test is false, evaluating the message only thenglobal namein a function or method rebinds the module definition for every reader; a rebinding keeps the definition's typedel xs[i],del xs[a:b], anddel d[k], raisingIndexError/KeyErrorfor what is not there, anddel name, which lets go of what the name held- Tuple unpacking types each target from the value, and a value of the wrong length raises
ValueError. Extended unpacking (a, *b, c = xs) binds the starred target to the middle slice as a real list - Augmented assignment through a name, an attribute, or a subscript (
counts[w] += 1,self.items[k].qty += n), each target evaluated once, and annotated fields (self.items: Dict[str, Item] = {}) - Unsupported syntax is rejected up front with a located error and a hint, instead of failing deep in code generation
Numbers and operators
- Types: integers (32-bit, see Numbers), floats, booleans, strings, bytes,
bytearray, and the collections below.bytes(),bytes(n), andbytes(ints)make bytes (a value out of range(0, 256) raisesValueError), andsep.join(parts)joins bytes - Arithmetic (
+,-,*,/,%,//,**), comparisons (==,!=,<,<=,>,>=, chained as in0 <= x <= 100), boolean operators (and,or, short-circuiting and answering the operand that decides, as CPython's do:0 or 7is 7,name or "anon"a str), and bitwise operators (&,|,^,<<,>>)
Strings and formatting
- String operations: slicing (with any step,
s[::-1]included), concatenation, repetition (s * n), 20+ methods, and formatting: f-strings,.format(),format(), and%, all through CPython's format specification mini-language (fill, alignment, sign,z,#,0, width,,/_grouping, precision, and every type letter), digit for digit with CPython..format()takes automatic, positional, and named fields, attribute and index lookups ({0.x},{1[2]},{d[key]}), conversions, and specs with nested fields;%takes every conversion with its flags, a tuple operand, and%(key)sover a dict - String methods run on a string built at run time, not only on a literal: case and trim transforms (
swapcaseandcasefoldincluded),find/rfind/index/rindex/count/startswith/endswith,split/rsplit(withmaxsplit)/splitlines/partition/rpartition/join/replace(with a count),removeprefix/removesuffix, the layout methods (zfillandexpandtabsincluded), and the predicates (istitle,isascii,isprintable, andisidentifierincluded).translate()takes adict[int, int]ordict[int, str]table, or the onestr.maketrans(x, y)makes, andencode()answers the bytes in UTF-8, ASCII, or Latin-1 (witherrorsofstrict,ignore, orreplace), raising CPython'sUnicodeEncodeError. A method folded on a literal answers what it answers at run time. A%format string known only at run time is not supported yet. Strings compare by content everywhere, so a key built at run time matches an equal one already in a dict - A
stris a sequence of code points, as in CPython:len, indexing, slicing,find/index, padding, iteration, and comprehensions count characters, not UTF-8 bytes. Case conversion (upper,lower,capitalize,title,casefold, with CPython's final-sigma and titlecase rules andßtoSS) and theis*predicates (isalpha,isalnum,isdigit,isdecimal,isnumeric,isspace,isupper,islower,isprintable,isidentifier) cover the whole of Unicode, from tables generated from CPython 3.13 (Unicode 15.1), and so dostrip()andsplit()'s idea of whitespace - F-strings interpolate every placeholder, rendering each value with
str().{x!r},{x!a}, and{x:spec}(with nested fields,{x:{w}.{p}f}) format as CPython does. Constant placeholders fold into the literal at compile time str()andrepr()of every value: a float by its shortest round-trip digits (CPython's repr, exponent notation from 1e16), bools, None, quoted and escaped strings and bytes, ranges, nested lists, tuples, dicts, and sets, and instances through their__str__and__repr__.float()parses a string correctly rounded, as CPython does, and raises a catchableValueErroron bad input
Built-in functions
- Built-in functions:
int()(including parsing a string, with a catchableValueErroron bad input),float(),str(),repr(),ascii(),format(),bool(),len()(of strings, collections, and ranges),abs(),round(x[, ndigits])(rounding the exact binary value half-to-even, as CPython does),min()/max()(several arguments, all ints, all floats, or all strings, or one list, tuple, range, set, dict, or string, withkey=anddefault=),sum()over lists and tuples (with an optional start value),isinstance()against a class, a builtin type, or a tuple of them,sorted(iterable[, key][, reverse]),dict(mapping)(a copy),dict(pairs)anddict(**kwargs), andreversed(seq)where it is walked (a loop, a comprehension,list(),sorted(),str.join()).print()writes to standard output (withsep,end,file=sys.stderr, andflush), andinput()reads a line of standard input, both through WASI (see Running compiled modules)
Collections
- Collections: lists, dicts, sets, tuples, and ranges: literals, indexing, slicing, methods, and membership (
in/not in), with full-precision f64 elements and hash-table sets; lists, dicts, and sets grow at run time. A collection's elements live in a block its header points at, so growing one never moves the collection: a list grown inside a function it was passed to is grown for every other name for it too. An empty collection is falsy as in Python bytearray, a mutable sequence of bytes:bytearray(n),bytearray(b)of bytes or a bytearray, andbytearray(ints); indexing and item assignment, slicing (a new bytearray),len(), iteration,in,==and ordering against bytes or a bytearray,+,*,+=,*=,del,bytes(b), andappend,extend,insert,pop,remove,clear,reverse,copy,count,find,index,startswith, andendswith, with CPython'sValueErrorfor a value outside range(0, 256) andIndexErrorfor an index out of range- List and tuple methods:
sort(in place and stable, of numbers, strs, bytes, and tuples of them),reverse,pop(i),index,count,append,extend,insert,remove. Lists and tuples concatenate (xs + ys) and repeat ([0] * n,(a, b) * 3) - Sets take
|,&,^, and-(and their in-place forms) and the full method set (union,intersection,difference,symmetric_difference, the_updateforms,issubset,issuperset,isdisjoint, taking a set, list, or tuple;copy,pop,clear;set(iterable)). A set iterates in CPython's order: its hashes for ints, bools, floats, and tuples of them, and its table algorithm, are CPython's, so a loop over a set,pop(), and the order a set prints in agree with CPython 3.13 for those members. A set of strings has no reproducible order in CPython, whose string hash is keyed per process; here the order is fixed but unspecified - Dicts merge with
d1 | d2and|=in CPython's order and haveget(key, default),keys,values,items,update,pop,popitem,setdefault,copy,clear, anddict.fromkeys.dict.get(key)without a default needs number or instance values (see Limitations) - A walk over a collection visits what CPython's does: a str its characters, a dict its keys, a set its members in CPython's order, in a comprehension,
sum,sorted,min, andmaxalike - Values compare by what they hold:
==,!=, and the orderings on tuples, lists, and strings follow CPython, and so doin,list.index/count, set membership, and dict keys, so a tuple can be a set member or a dict key. Dicts compare by their entries, whatever their order. Sets compare as sets (==, and<=/<as subset tests).iscompares identity: two instances, an instance andNone, or astragainstNone
Classes and objects
- Module-level variables and class definitions. A module-level definition that is not a plain constant (a collection, an instance, a call) is evaluated once at instantiation, in source order, and shared by every function, so mutating it is seen everywhere
- Classes with heap-allocated instances, several live per class, usable as arguments and return values. Class-level variables are read through an instance or the class (a subclass's own when it defines one) and rebound through the class that defines them (
Counter.count += 1) - Object-oriented Python: inheritance from one base or several, with CPython's C3 method resolution order and cooperative
super(),isinstance/issubclassover the class hierarchy,@staticmethod/@classmethod/@property(with setters),@dataclass(generated__init__/__eq__/__repr__), abstract base classes viaabc.ABC, and rich comparisons:==calls__eq__, and</<=/>/>=between instances call__lt__/__le__/__gt__/__ge__(the left operand's own, then the right operand's reflection, as CPython does) - Special methods: binary operators call
__add__,__sub__,__mul__, and the rest (the left operand's, or the right one's reflected__radd__, a subclass's reflection first, as CPython orders them),-x,+x, and~xcall__neg__,__pos__, and__invert__,x in ccalls__contains__,c[k]andc[k] = vcall__getitem__and__setitem__,a += bcalls__iadd__(or__add__),len()calls__len__(a negative answer is CPython'sValueError),if x:calls__bool__(or__len__),abs(),int(),float(), andround(x)call theirs, and calling an instance calls__call__. A method's defaulted parameters can be left out - Virtual dispatch: a method call reaches the override the receiver's run-time class defines, so a base method calling
self.method()gets the subclass's version (the template-method pattern), including through a variable, a parameter, or a list element declared as the base.@propertygetters and__eq__dispatch the same way, andsuper().method()stays non-virtual as in Python - Finalizers:
__del__runs when the last reference to an instance goes, as it does under CPython's reference counting: when the function holding it returns (its arguments first, then its locals, in order), when a name is rebound or deleted, when a collection lets go of it (a list and a tuple last to first, a dict and an instance's attributes first to last), and for whatever is left as the program ends. A subclass inherits it, it runs once, and objects that only refer to each other are finalized when the collector finds them. See Limitations for where the moment differs - Classes are values:
type(x)andx.__class__give an instance's class, a class name is its class object, compared by identity (type(s) is Square), named by__name__, shown as<class '__main__.Square'>, read for its class variables, and annotatedtype[A]. Calling one held in a variable, andtype()of a builtin value, are refused - Metaclasses:
class M(type)withmetaclass=M, inherited by subclasses. M's__new__and__init__run once as each class is created, specialized for it at compile time, so a registry or a counter works and the attributes they give the class (cls.plugin_id = ...) are its class variables;M.__call__replaces instantiation (a singleton works); M's other methods and class variables are reached through the class (Plugin.describe()). The class namespace argument,__prepare__, and creating a class by calling the metaclass are refused - Decorators: the standard ones (
@staticmethod,@classmethod,@property/@x.setter,@abstractmethod,@dataclass),functools's@singledispatchwith@f.register(dispatch is static, on the first argument's type) and@total_ordering(the missing ordering methods are derived from the one defined),@lru_cache/@cache(accepted, and change nothing observable), and decorators the program writes, on module functions. A user-written decorator is applied at compile time: its wrapper is specialized for each function it decorates, so a wrapper forwarding*argsand**kwargsgets the function's own parameters, state it keeps throughnonlocalis one per decorated function as in CPython, and a factory (@repeat(3)) and a stack of decorators work
Exceptions and context managers
- Exceptions are objects, as in CPython:
raisetransfers control,try/except/finallycatch and clean up, and an exception propagates out of a call into the caller's handler.raise E(args)raises an instance,except E as ebinds it, andstr(e),repr(e),e.args, a user exception's own fields and__str__, andraise X from Y(e.__cause__) work.except (A, B):catches either type, andexcept Basecatches its subclasses, along user classes and CPython's builtin hierarchy (LookupErroroverIndexErrorandKeyError,ArithmeticErroroverZeroDivisionError, and the rest). A bareraise(orraise e) in a handler re-raises. The run-time's own faults carry CPython's messages (list index out of range,invalid literal for int() with base 10: 'x',KeyError('k')) - Faults raise where CPython raises, catchably: division or modulo by zero (
ZeroDivisionError, integer and float), an index outside a sequence (IndexError; a negative index counts from the end, and slicing clamps), a missing dict key (KeyError),list.index(v)andlist.remove(v)of a missing value (ValueError),set.remove(v)of a missing member (KeyError), andlist.pop()from an empty list (IndexError). Item assignment into a tuple or a string is a compile error - Context managers:
with obj as name:over a user class implementing__enter__/__exit__, including nested blocks and inherited protocols.__exit__runs on every way out, receives an exception leaving the block, and suppresses it by answering true, as in CPython. The block holds its manager, which is let go of when the block is done
Iteration, generators, and closures
- Comprehensions: list, set, and dict comprehensions with filters, multiple generators, nesting, and
{k: v for k, v in pairs}unpacking. A generator expression goes where something walks it once: an argument of a builtin or standard-library call (sum(x * x for x in xs),", ".join(str(x) for x in xs)), aforstatement, or another comprehension (see Limitations). A comprehension over a generator or iterator pulls one value at a time, as CPython's does, so their side effects interleave the same way;list(),tuple(),sum(),sorted(),min(),max(),any(),all(), andset()take one too - Generators with real state preservation:
yieldsuspends and resumes (insidetry/except/finallyandwithtoo),yield fromdelegates, andnext()/send()/close()work,close()raisingGeneratorExitat the suspendedyieldsofinallyblocks and handlers run; generator methods; user classes implementing__iter__/__next__iterate inforloops withStopIterationending the loop,next()of an exhausted iterator raises a catchableStopIteration(passed on by a__next__that callsnext()of another, and madeRuntimeErrorin a generator, as CPython 3.7+ does), a method declared to return an iterator class iterates like a call of the class, andzip()pulls a generator one value per round in any argument position. A generator left part way through atry(a loop that breaks out, anext()or two and no more) is closed when the last reference to it goes, so itsfinallyblocks run then, as CPython's do - Tuple targets in
forloops (for a, b in pairs, star targets included) and the iterator-shaped builtins:enumerate(xs[, start])(start=too),zip(...), and.items()/.keys()/.values()of a dict, of acollectionsmapping, or of an instance whose class defines them - Closures: lambdas compile to real functions dispatched through a
call_indirecttable, capture enclosing variables by reference, and work as first-class values: returned, passed as arguments, and stored in collections. A module function,len, orabsnamed as a value works too (sorted(words, key=len)) - Nested functions: a
definside a function or method keeps its annotations, closes over the enclosing variables by reference (floats and strings included), recurses, calls its siblings, and takes keywords and defaults. Called by name it is a direct, fully typed call; returned or passed on it is a closure value called at its signature where that is known (Callable[[float], float]annotations are understood).nonlocal namerebinds the enclosing variable for every reader
Coroutines and JavaScript
async defandawait, coroutine methods,async withandasync forover classes of the program, andasyncio'srun,create_task,ensure_future,gather,sleep,wait_for, andtimeout,TaskGroupandExceptionGroup, its locks, events, semaphores, and queues, on CPython 3.13's event loop, so tasks interleave in CPython's order (see JavaScript and async)- The page through Pyodide's surface:
js.documentand its elements, listeners throughpyodide.ffi.create_proxy, and HTTP requests throughpyodide.http.pyfetch(see JavaScript and async) - JavaScript values through Pyodide's
jsandpyodide.ffi: attributes, calls,new, items, iteration, conversions as Pyodide makes them,to_js(),.to_py(),JsException, anawaitof a promise, and exports a page calls with JavaScript values (anasync defanswering a Promise), through the loaderwaspy::js_loader()(see JavaScript and async)
Modules, files, and the standard library
- Imports:
import mod,import mod as m, andfrom mod import f [as g], of your own modules and the standard library's, at module level, in a function (which binds the name in that function), or in theif __name__ == "__main__":block. Your own modules resolve sibling.pyfiles (andpkg/mod.pypackages) and are statically linked into the single output module, each compiled once however many import paths reach it importlib.import_module("name")and__import__("name")with a constant name (a string literal or a module-level constant) are the import they name, so the module is linked when the program is compiled:json = importlib.import_module("json"), in a function or at module level, or used in place (importlib.import_module("math").sqrt(2.0)).__import__("os.path")answers the packageosandimportlib.import_module("os.path")the submodule, as in CPython.importlib.import_module(name)of a name computed at run time loads a separately compiled module the host holds (see Modules loaded by name)- File I/O through WASI:
open(),read([n]),write(s),close(), andwith open(...) as f:(see File I/O) - The standard library, module by module (the modules board tracks each function; anything not listed is refused at compile time, naming the call):
math: every function on the board, on runtime values, with CPython'sValueErrorandOverflowError;math.pi,e,tau,inf,nan. The transcendental functions give musl libm's results, bit for bit (see Numbers)random: CPython's generator, so a seed gives CPython's sequence:seed,random,getrandbits,randrange,randint,uniform,choice,shuffle,sample,gauss,normalvariate,expovariate. Unseeded, it seeds from the host's entropysys:argv,stdin/stdout/stderr,exit(),maxsize,platform("wasi"),version, andpath(empty: imports are linked at compile time)os:environandgetenv()from the host,urandom(),remove(),unlink(),rename(),replace(),listdir(),os.path.getsize(), andos.path.getmtime()through the host's file system with CPython'sOSErrorsubclasses,getcwd()("/") andgetpid()(42), which is what CPython on WASI answers, and the constants (os.nameis"posix",os.sepand friends)os.path:join,basename,dirname,split,splitext,normpath,abspath,isabs, asposixpathcomputes them, andexists,isfile,isdirthrough the hostfunctools:reduce,partial,partialmethod,cmp_to_key,@cached_property,@singledispatchmethod, and the decorators aboveitertools: every function, in CPython's order and with its argument checks:count,cycle,repeat,accumulate,batched,chain(andchain.from_iterable),compress,dropwhile,takewhile,filterfalse,starmap,islice,pairwise,tee,zip_longest,groupby(whose groups end when it moves on, as CPython's do),product,permutations,combinations, andcombinations_with_replacementcollections:namedtuple()andtyping.NamedTuple(a module-level class whose instances are their fields as a tuple, with_replace,_asdict,_make, and_fields;namedtuple()'s fields take their types from the calls of the class), andCounter,defaultdict,OrderedDict,deque, andChainMap. Each of those is a class specialized by its element types, writtenCounter[str]ordefaultdict[str, list[int]]in an annotation, and found otherwise from the constructor's arguments or from what the function does with the variable (c[w] += 1keys a Counter byw's type,d[k].append(x)makes adefaultdict(list)hold lists ofx's type). They read as the dict or list they hold wherever CPython reads them as one, a parameter declareddictincluded, and add their own behaviour: Counter's missing counts of 0,most_common,elements,total,update,subtract, and multiset operators; defaultdict's factory; OrderedDict'smove_to_endandpopitem(last=False); deque's ends,maxlen,rotate, and indexing; ChainMap's maps,new_child, andparents; and their reprs and errors.UserDict,UserList, andUserStringare classes to subclass, asclass Lower(UserDict[str, int])or bare,class Stack(UserList), whose types come from what its methods declare and how the module uses it. As in CPython they go through their special methods, soupdate()and the constructor store through an overridden__setitem__,get(),pop(),items(), andvalues()read through an overridden__getitem__,__missing__answers a missing key, and an override's unannotated parameters take the base's types. A slice,+,*,|,copy(), andUserString's string methods answer the subclassjson:dumpsanddumpof ints, floats (CPython's repr, andNaN/Infinity), bools, None, strings (withensure_ascii's escapes), lists, tuples, dicts (keys of str, int, float, bool, or None), and thecollectionsclasses that are dicts, withindent,separators,sort_keys,ensure_ascii, andallow_nanas constants;loadsandloadinto the type the value is declared to have (data: dict[str, list[int]] = json.loads(text)), raising CPython'sJSONDecodeError(aValueError) with its message and position for a document that is not JSON.dumps(default=f)writes whatfanswers for a value JSON has no form for (an instance of a class), andloads(object_hook=f)givesfeach object and keeps what it answers (p: Point = json.loads(text, object_hook=as_point)).JSONEncoderandJSONDecoderare classes whose options are fields, so one made withindent=2and held in a variable writes as CPython's does:encode(),decode(), a subclass'sdefault()(withsuper().default(o)'sTypeError), anddumps(cls=...)/loads(cls=...)datetime:timedelta,date,time,datetime, andtimezone, CPython's own algorithms with its messages and reprs. Construction checks every field;timedeltanormalizes days, seconds, and microseconds from any of its arguments, ints or floats, rounding halves to even; the classes add, subtract, negate, multiply, divide, compare, and print as CPython's;dateanddatetimegivetoordinal,weekday,isoweekday,isocalendar,isoformat,ctime,strftime(the C locale's directives,%f,%z,%:z, and%Z),replace, and f-string specs;fromisoformatreads every ISO 8601 form CPython 3.13 reads;datetime.strptimematches its format as CPython's regular expression does, backtracking included;timestamp,fromtimestamp,utcfromtimestamp,now,today, andutcnowread the host's clock (the local time zone is UTC, as on every WASI host); and an aware value compares, subtracts, and converts (astimezone) by its offset.isinstance(dt, date)is True for a datetime, as in CPythonlogging:getLogger()and the logger hierarchy (a logger made after its descendants becomes their parent, as in CPython),debug/info/warning/error/critical/logon a logger and as the module's functions (which configure the root logger first, as CPython's do),basicConfig()withlevel,format,datefmt,style,stream,filename,filemode,handlers, andforce,setLevel(by number or name),disable(),propagate,disabled,getChild,hasHandlers,getLevelName, andlastResortfor a record no handler takes.Handler,StreamHandler(tosys.stdoutorsys.stderr),FileHandler(modesaandw,delay),NullHandler,Formatterin the%,{, and$styles with CPython's validation errors,Filter, andLogRecord, each subclassable. A call's arguments are always evaluated and its message formatted only when the level is enabled; a record carries the call's line (%(lineno)d) and function (%(funcName)s), its time (%(asctime)s, from the host's clock, local time being UTC), andprocess42, as CPython on WASI reports.addLevelName()names a level for%(levelname)s,getLevelName(), andsetLevel().exception()andexc_info=Truelog the exception anexceptblock caught (exc_info=ethe one given),extra={...}written out in the call adds fields to the record that a format orrecord.ipreads, as does a filter'srecord.ip = ....logging.config.dictConfig()takes a dict written out in the call or a module constant, and follows CPython's order: formatters, filters, handlers,disable_existing_loggers, loggers, and the root.logging.handlershasRotatingFileHandler,TimedRotatingFileHandler(everywhenandinterval, withbackupCount),WatchedFileHandler,BufferingHandler, andMemoryHandler. Handlers are flushed and closed when the__main__block ends, asatexitdoes throughlogging.shutdown()re:compile,search,match,fullmatch,findall,finditer(a lazy iterator, as CPython'scallable_iteratoris: eachnext()finds the next match),split,subandsubn(with a template or a function),escape, andpurge, the flags (I,M,S,X,A,U) as arguments and inline ((?i),(?i:...)), andPatternandMatchobjects (groupby number or name,groups,groupdict,start,end,span,expand,lastindex,lastgroup,pos,endpos,m[k], and their reprs, andPattern.groupindex, a read-onlymappingproxyof the group names' numbers).\N{name}escapes take a character's name or alias as CPython 3.13 looks it up (Unicode 15.1, case-insensitively). Bytes patterns (re.compile(rb"\d+")) match bytes as CPython matches them, by ASCII's classes and case, and answer bytes for their groups,findall,split,sub, andescape;re.Pattern[bytes]andre.Match[bytes]annotate them. Patterns are parsed as CPython 3.13 parses them, so a bad one is reported when compiling withre.error's message and position, and matched as CPython'ssrematches: backtracking in its order, greedy, lazy, and possessive repeats with its zero-width rule, atomic groups, lookahead and fixed-width lookbehind, back references, conditional groups,\b, the Unicode classes (\dis any decimal digit,\wany alphanumeric) or ASCII ones underre.A, and case folding with CPython's special cases (the Kelvin sign, long s). Empty matches infindall,split, andsubfollow CPython 3.7+. A group that did not take part is None, anOptional[str]that compares, concatenates, and converts as CPython's None wouldre.subandre.escapeover constant arguments are folded at compile time where the result is provably CPython's, and so are the modules' constants: thereflags, thelogginglevels, anddatetime.MINYEAR/MAXYEAR