annotationlib module
annotationlib provides Format, ForwardRef and get_annotations(), the pieces
typing.get_type_hints() is built on.
typing.ForwardRef is the same type.
call_annotate_function,call_evaluate_functionandget_annotate_from_class_namespace: there are no__annotate__functions, since annotations are stored stringized (see typing.md).annotations_to_stringandtype_repr.
Format.VALUE, Format.VALUE_WITH_FAKE_GLOBALS, Format.FORWARDREF and Format.STRING are the ints
1 to 4, so format=Format.STRING and format=4 agree, as in CPython.
Divergences:
Formatis a plain class, not anenum.IntEnum.Format.STRINGis theint4:repr(Format.STRING)is4rather than<Format.STRING: 4>,Format.STRING.nameand.valueraiseAttributeError,Format(4)raisesTypeError, andrepr(Format)is<class 'Format'>rather than<enum 'Format'>.
ForwardRef(arg, *, module=None, owner=None, is_argument=True, is_class=False) keeps an annotation as source
text, with __forward_arg__, __forward_module__, __forward_is_argument__, __forward_is_class__, __owner__
and __resolved_str__, equality and hashing, |, and
evaluate(*, globals=None, locals=None, type_params=None, owner=None, format=Format.VALUE).
As in CPython, a reference made while evaluating an annotation remembers the globals it was evaluated against and
equals no constructed reference, and evaluate() without globals or an owner sees builtins only.
Divergences:
type_paramsis accepted and ignored: Monty has no type parameters.is_argumentandis_classare stored asbool, soForwardRef('a', is_argument=1).__forward_is_argument__isTruewhere CPython keeps the1.__globals__,__cell__,__extra_names__,__code__and__ast_node__raiseAttributeError.__forward_arg__is the resolved text of a reference an evaluation built, and a class is written by its bare name; see typing.md.- A reference an earlier evaluation returned still composes text when a later
FORWARDREFevaluation passes it to code it calls: withold = get_annotations(g, format=Format.FORWARDREF)['a'], an annotationuse(old)withdef use(x): return x | intgivesForwardRef('Later | int')where CPython givesForwardRef('Later') | int. CPython turns its stringifiers into plain references when their evaluation ends. evaluate()inVALUEorFORWARDREFformat takes a dict orNoneforglobals/locals. Anything else raisesTypeError: ForwardRef.evaluate() globals must be a dict, not list, where CPython accepts some non-dicts (a list behaves as empty) and fails insideeval()on others.module=names a bundled module whether or not it was imported, soForwardRef('pi', module='math')evaluates withoutimport math, where CPython requires the module insys.modules. A host module is never found, so its name falls back to the reference’s other globals.- A class owner supplies the session globals to
evaluate(), where CPython reads the class’s module fromsys.modules. NotImplementedErrorfor an unsupportedformatcarries the text, sorepr(exc)isNotImplementedError('2')where CPython’s isNotImplementedError(2);str(exc)agrees.- Arity errors do not count
self:ForwardRef('a', 'b')raisesTypeError: ForwardRef.__init__() takes 1 positional argument but 2 were givenwhere CPython counts 2 and 3. copy.deepcopy()shares a reference rather than rebuilding it.
get_annotations(obj, *, globals=None, locals=None, eval_str=False, format=Format.VALUE) returns obj’s
annotations evaluated where they were written, or as stored under from __future__ import annotations or in
STRING format; a string stays a string unless eval_str=True.
It shares the get_type_hints divergences on function scopes, exec() classes,
re-evaluation, modules, tracebacks and host objects, and adds:
globals/localsmust be a dict orNone. Anything else raisesTypeError: get_annotations() globals must be a dict, not int, where CPython fails later insideeval().- Host functions and classes answer with the host’s
typing.get_type_hints(), so their forward references come back resolved andNoneasNoneType;eval_strhas no effect on them.