Skip to content

Commit 8bd4f60

Browse files
authored
Optimize annotation lookups (#940)
1 parent 3d46562 commit 8bd4f60

File tree

1 file changed

+41
-48
lines changed

1 file changed

+41
-48
lines changed

src/msgspec/_core.c

Lines changed: 41 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,6 @@ typedef struct {
519519
#endif
520520
PyObject *astimezone;
521521
PyObject *re_compile;
522-
PyObject *get_annotate_from_class_namespace;
523522
uint8_t gc_cycle;
524523
} MsgspecState;
525524

@@ -658,6 +657,28 @@ strbuilder_build(strbuilder *self) {
658657
return out;
659658
}
660659

660+
static MS_INLINE PyObject *
661+
ms_get_annotate_from_class_namespace(PyObject *namespace) {
662+
/* We replicate the behavior of the standard library utility to avoid
663+
* unnecessary function call overhead.
664+
* https://docs.python.org/3/library/annotationlib.html#annotationlib.get_annotate_from_class_namespace */
665+
PyObject *annotate;
666+
667+
annotate = PyDict_GetItemString(namespace, "__annotate__");
668+
if (annotate != NULL) {
669+
Py_INCREF(annotate);
670+
return annotate;
671+
}
672+
673+
annotate = PyDict_GetItemString(namespace, "__annotate_func__");
674+
if (annotate != NULL) {
675+
Py_INCREF(annotate);
676+
return annotate;
677+
}
678+
679+
Py_RETURN_NONE;
680+
}
681+
661682
/*************************************************************************
662683
* PathNode *
663684
*************************************************************************/
@@ -5953,36 +5974,28 @@ structmeta_collect_fields(StructMetaInfo *info, MsgspecState *mod, bool kwonly)
59535974
info->namespace, "__annotations__"
59545975
);
59555976
if (annotations == NULL) {
5956-
if (mod->get_annotate_from_class_namespace != NULL) {
5957-
PyObject *annotate = PyObject_CallOneArg(
5958-
mod->get_annotate_from_class_namespace, info->namespace
5959-
);
5960-
if (annotate == NULL) {
5961-
return -1;
5962-
}
5963-
if (annotate == Py_None) {
5964-
Py_DECREF(annotate);
5965-
return 0;
5966-
}
5967-
PyObject *format = PyLong_FromLong(1); /* annotationlib.Format.VALUE */
5968-
if (format == NULL) {
5969-
Py_DECREF(annotate);
5970-
return -1;
5971-
}
5972-
annotations = PyObject_CallOneArg(
5973-
annotate, format
5974-
);
5977+
PyObject *annotate = ms_get_annotate_from_class_namespace(info->namespace);
5978+
if (annotate == NULL) {
5979+
return -1;
5980+
}
5981+
if (annotate == Py_None) {
59755982
Py_DECREF(annotate);
5976-
Py_DECREF(format);
5977-
if (annotations == NULL) {
5978-
return -1;
5979-
}
5983+
return 0;
59805984
}
5981-
else {
5982-
return 0; // No annotations, nothing to do
5985+
PyObject *format = PyLong_FromLong(1); /* annotationlib.Format.VALUE */
5986+
if (format == NULL) {
5987+
Py_DECREF(annotate);
5988+
return -1;
59835989
}
5984-
}
5985-
else {
5990+
annotations = PyObject_CallOneArg(
5991+
annotate, format
5992+
);
5993+
Py_DECREF(annotate);
5994+
Py_DECREF(format);
5995+
if (annotations == NULL) {
5996+
return -1;
5997+
}
5998+
} else {
59865999
Py_INCREF(annotations);
59876000
}
59886001

@@ -22653,26 +22666,6 @@ PyInit__core(void)
2265322666
Py_DECREF(temp_module);
2265422667
if (st->re_compile == NULL) return NULL;
2265522668

22656-
/* annotationlib.get_annotate_from_class_namespace */
22657-
temp_module = PyImport_ImportModule("annotationlib");
22658-
if (temp_module == NULL) {
22659-
if (PyErr_ExceptionMatches(PyExc_ModuleNotFoundError)) {
22660-
// Below Python 3.14
22661-
PyErr_Clear();
22662-
st->get_annotate_from_class_namespace = NULL;
22663-
}
22664-
else {
22665-
return NULL;
22666-
}
22667-
}
22668-
else {
22669-
st->get_annotate_from_class_namespace = PyObject_GetAttrString(
22670-
temp_module, "get_annotate_from_class_namespace"
22671-
);
22672-
Py_DECREF(temp_module);
22673-
if (st->get_annotate_from_class_namespace == NULL) return NULL;
22674-
}
22675-
2267622669
/* Initialize cached constant strings */
2267722670
#define CACHED_STRING(attr, str) \
2267822671
if ((st->attr = PyUnicode_InternFromString(str)) == NULL) return NULL

0 commit comments

Comments
 (0)