Skip to content

__annotate__ Functions don't actually need to be functions #141388

@dr-carlos

Description

@dr-carlos

Bug report

Bug description:

According to the Python docs, annotate functions need to be actual functions. However, the C code doesn't enforce this, it enforces (using PyCallable_Check()) that the annotate 'function' is any callable:

static int
function___annotate___set_impl(PyFunctionObject *self, PyObject *value)
/*[clinic end generated code: output=05b7dfc07ada66cd input=eb6225e358d97448]*/
{
if (value == NULL) {
PyErr_SetString(PyExc_TypeError,
"__annotate__ cannot be deleted");
return -1;
}
if (Py_IsNone(value)) {
Py_XSETREF(self->func_annotate, value);
return 0;
}
else if (PyCallable_Check(value)) {
Py_XSETREF(self->func_annotate, Py_XNewRef(value));
Py_CLEAR(self->func_annotations);
return 0;
}
else {
PyErr_SetString(PyExc_TypeError,
"__annotate__ must be callable or None");
return -1;
}
}

This initially seems fine, but leads to some weird bugs.

class C:
  def __call__(self, format, /):
    if format > 2:
      raise NotImplementedError
    return {'x': int}

def f(x): ...
f.__annotate__ = C()

from annotationlib import get_annotations, Format
get_annotations(f, format=Format.STRING) # AttributeError: 'C' object has no attribute '__closure__' ...

And similar for get_annotations(f, format=Format.FORWARDREF), trying to find the annotation function's __builtins__, which only exists on functions (not arbitrary callables).

I'm happy to make a PR to replace the PyCallable_Check() with PyFunction_Check() in the relevant C code for setting __annotate__, but I guess this might be a breaking change? I'm not sure, so I thought I'd check first before making it. The other option would be checking for the presence of __closure__/__builtins__/etc. before accessing them in annotationlib.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.14bugs and security fixes3.15new features, bugs and security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)topic-typingtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions