Skip to content

Commit fe84920

Browse files
committed
Add recipe to docs clarifying how non-function annotate functions work
1 parent e177621 commit fe84920

File tree

1 file changed

+66
-0
lines changed

1 file changed

+66
-0
lines changed

Doc/library/annotationlib.rst

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,72 @@ annotations from the class and puts them in a separate attribute:
510510
return typ
511511
512512
513+
514+
Creating a custom callable annotate function
515+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
516+
517+
Custom :term:`annotate functions <annotate function>` may be literal functions like those
518+
automatically generated for functions, classes, and modules. Or, they may wish to utilise
519+
the encapsulation provided by classes, in which case any :term:`callable` can be used as
520+
an :term:`annotate function`.
521+
522+
However, :term:`methods <method>`, class instances that implement
523+
:meth:`object.__call__`, and most other callables, do not provide the same attributes as
524+
true functions, which are needed for the :attr:`~Format.VALUE_WITH_FAKE_GLOBALS`
525+
machinery to work. :func:`call_annotate_function` and other :mod:`annotationlib`
526+
functions will attempt to infer those attributes where possible, but some of them must
527+
always be present for :attr:`~Format.VALUE_WITH_FAKE_GLOBALS` to work.
528+
529+
Below is an example of a callable class that provides the necessary attributes to be
530+
used with all formats, and takes advantage of class encapsulation:
531+
532+
.. code-block:: python
533+
534+
class Annotate:
535+
called_formats = []
536+
537+
def __call__(self, format=None, *, _self=None):
538+
# When called with fake globals, `_self` will be the
539+
# actual self value, and `self` will be the format.
540+
if _self is not None:
541+
self, format = _self, self
542+
543+
self.called_formats.append(format)
544+
if format <= 2: # VALUE or VALUE_WITH_FAKE_GLOBALS
545+
return {"x": MyType}
546+
raise NotImplementedError
547+
548+
@property
549+
def __defaults__(self):
550+
return (None,)
551+
552+
@property
553+
def __kwdefaults__(self):
554+
return {"_self": self}
555+
556+
@property
557+
def __code__(self):
558+
return self.__call__.__code__
559+
560+
This can then be called with:
561+
562+
.. doctest::
563+
564+
>>> from annotationlib import call_annotate_function, Format
565+
>>> call_annotate_function(Annotate(), format=Format.STRING)
566+
{'x': 'MyType'}
567+
568+
Or used as the annotate function for an object:
569+
570+
.. doctest::
571+
572+
>>> from annotationlib import get_annotations, Format
573+
>>> class C:
574+
... pass
575+
>>> C.__annotate__ = Annotate()
576+
>>> get_annotations(Annotate(), format=Format.STRING)
577+
{'x': 'MyType'}
578+
513579
Limitations of the ``STRING`` format
514580
------------------------------------
515581

0 commit comments

Comments
 (0)