bpo-42202: Store func annotations as a tuple (GH-23316)

Reduce memory footprint and improve performance of loading modules having many func annotations.

  >>> sys.getsizeof({"a":"int","b":"int","return":"int"})
  232
  >>> sys.getsizeof(("a","int","b","int","return","int"))
  88

The tuple is converted into dict on the fly when `func.__annotations__` is accessed first.

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Co-authored-by: Inada Naoki <songofacandy@gmail.com>
This commit is contained in:
Yurii Karabas 2020-11-25 12:43:18 +02:00 committed by GitHub
parent 85c84920f5
commit 7301979b23
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 174 additions and 155 deletions

View file

@ -2027,26 +2027,24 @@ compiler_visit_annexpr(struct compiler *c, expr_ty annotation)
static int
compiler_visit_argannotation(struct compiler *c, identifier id,
expr_ty annotation, PyObject *names)
expr_ty annotation, Py_ssize_t *annotations_len)
{
if (annotation) {
PyObject *mangled;
VISIT(c, annexpr, annotation);
mangled = _Py_Mangle(c->u->u_private, id);
PyObject *mangled = _Py_Mangle(c->u->u_private, id);
if (!mangled)
return 0;
if (PyList_Append(names, mangled) < 0) {
Py_DECREF(mangled);
return 0;
}
ADDOP_LOAD_CONST(c, mangled);
Py_DECREF(mangled);
VISIT(c, annexpr, annotation);
*annotations_len += 2;
}
return 1;
}
static int
compiler_visit_argannotations(struct compiler *c, asdl_arg_seq* args,
PyObject *names)
Py_ssize_t *annotations_len)
{
int i;
for (i = 0; i < asdl_seq_LEN(args); i++) {
@ -2055,7 +2053,7 @@ compiler_visit_argannotations(struct compiler *c, asdl_arg_seq* args,
c,
arg->arg,
arg->annotation,
names))
annotations_len))
return 0;
}
return 1;
@ -2065,58 +2063,44 @@ static int
compiler_visit_annotations(struct compiler *c, arguments_ty args,
expr_ty returns)
{
/* Push arg annotation dict.
/* Push arg annotation names and values.
The expressions are evaluated out-of-order wrt the source code.
Return 0 on error, -1 if no dict pushed, 1 if a dict is pushed.
Return 0 on error, -1 if no annotations pushed, 1 if a annotations is pushed.
*/
static identifier return_str;
PyObject *names;
Py_ssize_t len;
names = PyList_New(0);
if (!names)
return 0;
Py_ssize_t annotations_len = 0;
if (!compiler_visit_argannotations(c, args->args, names))
goto error;
if (!compiler_visit_argannotations(c, args->posonlyargs, names))
goto error;
if (!compiler_visit_argannotations(c, args->args, &annotations_len))
return 0;
if (!compiler_visit_argannotations(c, args->posonlyargs, &annotations_len))
return 0;
if (args->vararg && args->vararg->annotation &&
!compiler_visit_argannotation(c, args->vararg->arg,
args->vararg->annotation, names))
goto error;
if (!compiler_visit_argannotations(c, args->kwonlyargs, names))
goto error;
args->vararg->annotation, &annotations_len))
return 0;
if (!compiler_visit_argannotations(c, args->kwonlyargs, &annotations_len))
return 0;
if (args->kwarg && args->kwarg->annotation &&
!compiler_visit_argannotation(c, args->kwarg->arg,
args->kwarg->annotation, names))
goto error;
args->kwarg->annotation, &annotations_len))
return 0;
if (!return_str) {
return_str = PyUnicode_InternFromString("return");
if (!return_str)
goto error;
return 0;
}
if (!compiler_visit_argannotation(c, return_str, returns, names)) {
goto error;
if (!compiler_visit_argannotation(c, return_str, returns, &annotations_len)) {
return 0;
}
len = PyList_GET_SIZE(names);
if (len) {
PyObject *keytuple = PyList_AsTuple(names);
Py_DECREF(names);
ADDOP_LOAD_CONST_NEW(c, keytuple);
ADDOP_I(c, BUILD_CONST_KEY_MAP, len);
if (annotations_len) {
ADDOP_I(c, BUILD_TUPLE, annotations_len);
return 1;
}
else {
Py_DECREF(names);
return -1;
}
error:
Py_DECREF(names);
return 0;
return -1;
}
static int