mirror of https://github.com/python/cpython
gh-119180: Add evaluate functions for type params and type aliases (#122212)
This commit is contained in:
parent
cbac8a3888
commit
ae192262ad
|
@ -81,6 +81,7 @@ struct _Py_interp_cached_objects {
|
||||||
PyTypeObject *paramspec_type;
|
PyTypeObject *paramspec_type;
|
||||||
PyTypeObject *paramspecargs_type;
|
PyTypeObject *paramspecargs_type;
|
||||||
PyTypeObject *paramspeckwargs_type;
|
PyTypeObject *paramspeckwargs_type;
|
||||||
|
PyTypeObject *constevaluator_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define _Py_INTERP_STATIC_OBJECT(interp, NAME) \
|
#define _Py_INTERP_STATIC_OBJECT(interp, NAME) \
|
||||||
|
|
|
@ -16,6 +16,7 @@ extern PyObject *_Py_subscript_generic(PyThreadState *, PyObject *);
|
||||||
extern PyObject *_Py_set_typeparam_default(PyThreadState *, PyObject *, PyObject *);
|
extern PyObject *_Py_set_typeparam_default(PyThreadState *, PyObject *, PyObject *);
|
||||||
extern int _Py_initialize_generic(PyInterpreterState *);
|
extern int _Py_initialize_generic(PyInterpreterState *);
|
||||||
extern void _Py_clear_generic_types(PyInterpreterState *);
|
extern void _Py_clear_generic_types(PyInterpreterState *);
|
||||||
|
extern int _Py_typing_type_repr(PyUnicodeWriter *, PyObject *);
|
||||||
|
|
||||||
extern PyTypeObject _PyTypeAlias_Type;
|
extern PyTypeObject _PyTypeAlias_Type;
|
||||||
extern PyTypeObject _PyNoDefault_Type;
|
extern PyTypeObject _PyNoDefault_Type;
|
||||||
|
|
|
@ -413,7 +413,16 @@ class _StringifierDict(dict):
|
||||||
return fwdref
|
return fwdref
|
||||||
|
|
||||||
|
|
||||||
def call_annotate_function(annotate, format, owner=None):
|
def call_evaluate_function(evaluate, format, *, owner=None):
|
||||||
|
"""Call an evaluate function. Evaluate functions are normally generated for
|
||||||
|
the value of type aliases and the bounds, constraints, and defaults of
|
||||||
|
type parameter objects.
|
||||||
|
"""
|
||||||
|
return call_annotate_function(evaluate, format, owner=owner, _is_evaluate=True)
|
||||||
|
|
||||||
|
|
||||||
|
def call_annotate_function(annotate, format, *, owner=None,
|
||||||
|
_is_evaluate=False):
|
||||||
"""Call an __annotate__ function. __annotate__ functions are normally
|
"""Call an __annotate__ function. __annotate__ functions are normally
|
||||||
generated by the compiler to defer the evaluation of annotations. They
|
generated by the compiler to defer the evaluation of annotations. They
|
||||||
can be called with any of the format arguments in the Format enum, but
|
can be called with any of the format arguments in the Format enum, but
|
||||||
|
@ -459,8 +468,11 @@ def call_annotate_function(annotate, format, owner=None):
|
||||||
closure = tuple(new_closure)
|
closure = tuple(new_closure)
|
||||||
else:
|
else:
|
||||||
closure = None
|
closure = None
|
||||||
func = types.FunctionType(annotate.__code__, globals, closure=closure)
|
func = types.FunctionType(annotate.__code__, globals, closure=closure,
|
||||||
|
argdefs=annotate.__defaults__, kwdefaults=annotate.__kwdefaults__)
|
||||||
annos = func(Format.VALUE)
|
annos = func(Format.VALUE)
|
||||||
|
if _is_evaluate:
|
||||||
|
return annos if isinstance(annos, str) else repr(annos)
|
||||||
return {
|
return {
|
||||||
key: val if isinstance(val, str) else repr(val)
|
key: val if isinstance(val, str) else repr(val)
|
||||||
for key, val in annos.items()
|
for key, val in annos.items()
|
||||||
|
@ -511,7 +523,8 @@ def call_annotate_function(annotate, format, owner=None):
|
||||||
closure = tuple(new_closure)
|
closure = tuple(new_closure)
|
||||||
else:
|
else:
|
||||||
closure = None
|
closure = None
|
||||||
func = types.FunctionType(annotate.__code__, globals, closure=closure)
|
func = types.FunctionType(annotate.__code__, globals, closure=closure,
|
||||||
|
argdefs=annotate.__defaults__, kwdefaults=annotate.__kwdefaults__)
|
||||||
result = func(Format.VALUE)
|
result = func(Format.VALUE)
|
||||||
for obj in globals.stringifiers:
|
for obj in globals.stringifiers:
|
||||||
obj.__class__ = ForwardRef
|
obj.__class__ = ForwardRef
|
||||||
|
|
|
@ -773,6 +773,25 @@ class TestGetAnnotations(unittest.TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestCallEvaluateFunction(unittest.TestCase):
|
||||||
|
def test_evaluation(self):
|
||||||
|
def evaluate(format, exc=NotImplementedError):
|
||||||
|
if format != 1:
|
||||||
|
raise exc
|
||||||
|
return undefined
|
||||||
|
|
||||||
|
with self.assertRaises(NameError):
|
||||||
|
annotationlib.call_evaluate_function(evaluate, annotationlib.Format.VALUE)
|
||||||
|
self.assertEqual(
|
||||||
|
annotationlib.call_evaluate_function(evaluate, annotationlib.Format.FORWARDREF),
|
||||||
|
annotationlib.ForwardRef("undefined"),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
annotationlib.call_evaluate_function(evaluate, annotationlib.Format.SOURCE),
|
||||||
|
"undefined",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MetaclassTests(unittest.TestCase):
|
class MetaclassTests(unittest.TestCase):
|
||||||
def test_annotated_meta(self):
|
def test_annotated_meta(self):
|
||||||
class Meta(type):
|
class Meta(type):
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import annotationlib
|
||||||
import asyncio
|
import asyncio
|
||||||
import textwrap
|
import textwrap
|
||||||
import types
|
import types
|
||||||
|
@ -6,7 +7,7 @@ import pickle
|
||||||
import weakref
|
import weakref
|
||||||
from test.support import requires_working_socket, check_syntax_error, run_code
|
from test.support import requires_working_socket, check_syntax_error, run_code
|
||||||
|
|
||||||
from typing import Generic, NoDefault, Sequence, TypeVar, TypeVarTuple, ParamSpec, get_args
|
from typing import Generic, NoDefault, Sequence, TypeAliasType, TypeVar, TypeVarTuple, ParamSpec, get_args
|
||||||
|
|
||||||
|
|
||||||
class TypeParamsInvalidTest(unittest.TestCase):
|
class TypeParamsInvalidTest(unittest.TestCase):
|
||||||
|
@ -1394,3 +1395,43 @@ class DefaultsTest(unittest.TestCase):
|
||||||
|
|
||||||
self.assertEqual(ns["X1"].__type_params__[0].__default__, "A")
|
self.assertEqual(ns["X1"].__type_params__[0].__default__, "A")
|
||||||
self.assertEqual(ns["X2"].__type_params__[0].__default__, "B")
|
self.assertEqual(ns["X2"].__type_params__[0].__default__, "B")
|
||||||
|
|
||||||
|
|
||||||
|
class TestEvaluateFunctions(unittest.TestCase):
|
||||||
|
def test_general(self):
|
||||||
|
type Alias = int
|
||||||
|
Alias2 = TypeAliasType("Alias2", int)
|
||||||
|
def f[T: int = int, **P = int, *Ts = int](): pass
|
||||||
|
T, P, Ts = f.__type_params__
|
||||||
|
T2 = TypeVar("T2", bound=int, default=int)
|
||||||
|
P2 = ParamSpec("P2", default=int)
|
||||||
|
Ts2 = TypeVarTuple("Ts2", default=int)
|
||||||
|
cases = [
|
||||||
|
Alias.evaluate_value,
|
||||||
|
Alias2.evaluate_value,
|
||||||
|
T.evaluate_bound,
|
||||||
|
T.evaluate_default,
|
||||||
|
P.evaluate_default,
|
||||||
|
Ts.evaluate_default,
|
||||||
|
T2.evaluate_bound,
|
||||||
|
T2.evaluate_default,
|
||||||
|
P2.evaluate_default,
|
||||||
|
Ts2.evaluate_default,
|
||||||
|
]
|
||||||
|
for case in cases:
|
||||||
|
with self.subTest(case=case):
|
||||||
|
self.assertIs(case(1), int)
|
||||||
|
self.assertIs(annotationlib.call_evaluate_function(case, annotationlib.Format.VALUE), int)
|
||||||
|
self.assertIs(annotationlib.call_evaluate_function(case, annotationlib.Format.FORWARDREF), int)
|
||||||
|
self.assertEqual(annotationlib.call_evaluate_function(case, annotationlib.Format.SOURCE), 'int')
|
||||||
|
|
||||||
|
def test_constraints(self):
|
||||||
|
def f[T: (int, str)](): pass
|
||||||
|
T, = f.__type_params__
|
||||||
|
T2 = TypeVar("T2", int, str)
|
||||||
|
for case in [T, T2]:
|
||||||
|
with self.subTest(case=case):
|
||||||
|
self.assertEqual(case.evaluate_constraints(1), (int, str))
|
||||||
|
self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.VALUE), (int, str))
|
||||||
|
self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.FORWARDREF), (int, str))
|
||||||
|
self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.SOURCE), '(int, str)')
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
As part of :pep:`749`, add the following attributes for customizing
|
||||||
|
evaluation of annotation scopes:
|
||||||
|
|
||||||
|
* ``evaluate_value`` on :class:`typing.TypeAliasType`
|
||||||
|
* ``evaluate_bound``, ``evaluate_constraints``, and ``evaluate_default`` on :class:`typing.TypeVar`
|
||||||
|
* ``evaluate_default`` on :class:`typing.ParamSpec`
|
||||||
|
* ``evaluate_default`` on :class:`typing.TypeVarTuple`
|
|
@ -4,6 +4,7 @@
|
||||||
#include "pycore_ceval.h" // _PyEval_GetBuiltin()
|
#include "pycore_ceval.h" // _PyEval_GetBuiltin()
|
||||||
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
|
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
|
||||||
#include "pycore_object.h"
|
#include "pycore_object.h"
|
||||||
|
#include "pycore_typevarobject.h" // _Py_typing_type_repr
|
||||||
#include "pycore_unionobject.h" // _Py_union_type_or, _PyGenericAlias_Check
|
#include "pycore_unionobject.h" // _Py_union_type_or, _PyGenericAlias_Check
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,69 +51,6 @@ ga_traverse(PyObject *self, visitproc visit, void *arg)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
ga_repr_item(PyUnicodeWriter *writer, PyObject *p)
|
|
||||||
{
|
|
||||||
PyObject *qualname = NULL;
|
|
||||||
PyObject *module = NULL;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (p == Py_Ellipsis) {
|
|
||||||
// The Ellipsis object
|
|
||||||
rc = PyUnicodeWriter_WriteUTF8(writer, "...", 3);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((rc = PyObject_HasAttrWithError(p, &_Py_ID(__origin__))) > 0 &&
|
|
||||||
(rc = PyObject_HasAttrWithError(p, &_Py_ID(__args__))) > 0)
|
|
||||||
{
|
|
||||||
// It looks like a GenericAlias
|
|
||||||
goto use_repr;
|
|
||||||
}
|
|
||||||
if (rc < 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PyObject_GetOptionalAttr(p, &_Py_ID(__qualname__), &qualname) < 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (qualname == NULL) {
|
|
||||||
goto use_repr;
|
|
||||||
}
|
|
||||||
if (PyObject_GetOptionalAttr(p, &_Py_ID(__module__), &module) < 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (module == NULL || module == Py_None) {
|
|
||||||
goto use_repr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Looks like a class
|
|
||||||
if (PyUnicode_Check(module) &&
|
|
||||||
_PyUnicode_EqualToASCIIString(module, "builtins"))
|
|
||||||
{
|
|
||||||
// builtins don't need a module name
|
|
||||||
rc = PyUnicodeWriter_WriteStr(writer, qualname);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rc = PyUnicodeWriter_Format(writer, "%S.%S", module, qualname);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
error:
|
|
||||||
rc = -1;
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
use_repr:
|
|
||||||
rc = PyUnicodeWriter_WriteRepr(writer, p);
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
done:
|
|
||||||
Py_XDECREF(qualname);
|
|
||||||
Py_XDECREF(module);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ga_repr_items_list(PyUnicodeWriter *writer, PyObject *p)
|
ga_repr_items_list(PyUnicodeWriter *writer, PyObject *p)
|
||||||
{
|
{
|
||||||
|
@ -131,7 +69,7 @@ ga_repr_items_list(PyUnicodeWriter *writer, PyObject *p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PyObject *item = PyList_GET_ITEM(p, i);
|
PyObject *item = PyList_GET_ITEM(p, i);
|
||||||
if (ga_repr_item(writer, item) < 0) {
|
if (_Py_typing_type_repr(writer, item) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,7 +100,7 @@ ga_repr(PyObject *self)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ga_repr_item(writer, alias->origin) < 0) {
|
if (_Py_typing_type_repr(writer, alias->origin) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (PyUnicodeWriter_WriteChar(writer, '[') < 0) {
|
if (PyUnicodeWriter_WriteChar(writer, '[') < 0) {
|
||||||
|
@ -181,7 +119,7 @@ ga_repr(PyObject *self)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ga_repr_item(writer, p) < 0) {
|
else if (_Py_typing_type_repr(writer, p) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,6 +116,201 @@ PyTypeObject _PyNoDefault_Type = {
|
||||||
|
|
||||||
PyObject _Py_NoDefaultStruct = _PyObject_HEAD_INIT(&_PyNoDefault_Type);
|
PyObject _Py_NoDefaultStruct = _PyObject_HEAD_INIT(&_PyNoDefault_Type);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
PyObject *value;
|
||||||
|
} constevaluatorobject;
|
||||||
|
|
||||||
|
static void
|
||||||
|
constevaluator_dealloc(PyObject *self)
|
||||||
|
{
|
||||||
|
PyTypeObject *tp = Py_TYPE(self);
|
||||||
|
constevaluatorobject *ce = (constevaluatorobject *)self;
|
||||||
|
|
||||||
|
_PyObject_GC_UNTRACK(self);
|
||||||
|
|
||||||
|
Py_XDECREF(ce->value);
|
||||||
|
|
||||||
|
Py_TYPE(self)->tp_free(self);
|
||||||
|
Py_DECREF(tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
constevaluator_traverse(PyObject *self, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
constevaluatorobject *ce = (constevaluatorobject *)self;
|
||||||
|
Py_VISIT(ce->value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
constevaluator_clear(PyObject *self)
|
||||||
|
{
|
||||||
|
Py_CLEAR(((constevaluatorobject *)self)->value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
constevaluator_repr(PyObject *self, PyObject *repr)
|
||||||
|
{
|
||||||
|
PyObject *value = ((constevaluatorobject *)self)->value;
|
||||||
|
return PyUnicode_FromFormat("<constevaluator %R>", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
constevaluator_call(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
|
{
|
||||||
|
if (!_PyArg_NoKeywords("constevaluator.__call__", kwargs)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
int format;
|
||||||
|
if (!PyArg_ParseTuple(args, "i:constevaluator.__call__", &format)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyObject *value = ((constevaluatorobject *)self)->value;
|
||||||
|
if (format == 3) { // SOURCE
|
||||||
|
_PyUnicodeWriter writer;
|
||||||
|
_PyUnicodeWriter_Init(&writer);
|
||||||
|
if (PyTuple_Check(value)) {
|
||||||
|
if (_PyUnicodeWriter_WriteASCIIString(&writer, "(", 1) < 0) {
|
||||||
|
_PyUnicodeWriter_Dealloc(&writer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(value); i++) {
|
||||||
|
PyObject *item = PyTuple_GET_ITEM(value, i);
|
||||||
|
if (i > 0) {
|
||||||
|
if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) {
|
||||||
|
_PyUnicodeWriter_Dealloc(&writer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_Py_typing_type_repr(&writer, item) < 0) {
|
||||||
|
_PyUnicodeWriter_Dealloc(&writer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_PyUnicodeWriter_WriteASCIIString(&writer, ")", 1) < 0) {
|
||||||
|
_PyUnicodeWriter_Dealloc(&writer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (_Py_typing_type_repr(&writer, value) < 0) {
|
||||||
|
_PyUnicodeWriter_Dealloc(&writer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _PyUnicodeWriter_Finish(&writer);
|
||||||
|
}
|
||||||
|
return Py_NewRef(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
constevaluator_alloc(PyObject *value)
|
||||||
|
{
|
||||||
|
PyTypeObject *tp = _PyInterpreterState_GET()->cached_objects.constevaluator_type;
|
||||||
|
assert(tp != NULL);
|
||||||
|
constevaluatorobject *ce = PyObject_GC_New(constevaluatorobject, tp);
|
||||||
|
if (ce == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ce->value = Py_NewRef(value);
|
||||||
|
_PyObject_GC_TRACK(ce);
|
||||||
|
return (PyObject *)ce;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(constevaluator_doc,
|
||||||
|
"_ConstEvaluator()\n"
|
||||||
|
"--\n\n"
|
||||||
|
"Internal type for implementing evaluation functions.");
|
||||||
|
|
||||||
|
static PyType_Slot constevaluator_slots[] = {
|
||||||
|
{Py_tp_doc, (void *)constevaluator_doc},
|
||||||
|
{Py_tp_dealloc, constevaluator_dealloc},
|
||||||
|
{Py_tp_traverse, constevaluator_traverse},
|
||||||
|
{Py_tp_clear, constevaluator_clear},
|
||||||
|
{Py_tp_repr, constevaluator_repr},
|
||||||
|
{Py_tp_call, constevaluator_call},
|
||||||
|
{Py_tp_alloc, PyType_GenericAlloc},
|
||||||
|
{Py_tp_free, PyObject_GC_Del},
|
||||||
|
{0, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
PyType_Spec constevaluator_spec = {
|
||||||
|
.name = "_typing._ConstEvaluator",
|
||||||
|
.basicsize = sizeof(constevaluatorobject),
|
||||||
|
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE,
|
||||||
|
.slots = constevaluator_slots,
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
_Py_typing_type_repr(PyUnicodeWriter *writer, PyObject *p)
|
||||||
|
{
|
||||||
|
PyObject *qualname = NULL;
|
||||||
|
PyObject *module = NULL;
|
||||||
|
PyObject *r = NULL;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (p == Py_Ellipsis) {
|
||||||
|
// The Ellipsis object
|
||||||
|
r = PyUnicode_FromString("...");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p == (PyObject *)&_PyNone_Type) {
|
||||||
|
return _PyUnicodeWriter_WriteASCIIString(writer, "None", 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((rc = PyObject_HasAttrWithError(p, &_Py_ID(__origin__))) > 0 &&
|
||||||
|
(rc = PyObject_HasAttrWithError(p, &_Py_ID(__args__))) > 0)
|
||||||
|
{
|
||||||
|
// It looks like a GenericAlias
|
||||||
|
goto use_repr;
|
||||||
|
}
|
||||||
|
if (rc < 0) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyObject_GetOptionalAttr(p, &_Py_ID(__qualname__), &qualname) < 0) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (qualname == NULL) {
|
||||||
|
goto use_repr;
|
||||||
|
}
|
||||||
|
if (PyObject_GetOptionalAttr(p, &_Py_ID(__module__), &module) < 0) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (module == NULL || module == Py_None) {
|
||||||
|
goto use_repr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks like a class
|
||||||
|
if (PyUnicode_Check(module) &&
|
||||||
|
_PyUnicode_EqualToASCIIString(module, "builtins"))
|
||||||
|
{
|
||||||
|
// builtins don't need a module name
|
||||||
|
r = PyObject_Str(qualname);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
r = PyUnicode_FromFormat("%S.%S", module, qualname);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
use_repr:
|
||||||
|
r = PyObject_Repr(p);
|
||||||
|
exit:
|
||||||
|
Py_XDECREF(qualname);
|
||||||
|
Py_XDECREF(module);
|
||||||
|
if (r == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
rc = _PyUnicodeWriter_WriteStr(writer, r);
|
||||||
|
Py_DECREF(r);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
call_typing_func_object(const char *name, PyObject **args, size_t nargs)
|
call_typing_func_object(const char *name, PyObject **args, size_t nargs)
|
||||||
|
@ -364,10 +559,49 @@ typevar_constraints(typevarobject *self, void *Py_UNUSED(ignored))
|
||||||
return constraints;
|
return constraints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
typevar_evaluate_bound(typevarobject *self, void *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
if (self->evaluate_bound != NULL) {
|
||||||
|
return Py_NewRef(self->evaluate_bound);
|
||||||
|
}
|
||||||
|
if (self->bound != NULL) {
|
||||||
|
return constevaluator_alloc(self->bound);
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
typevar_evaluate_constraints(typevarobject *self, void *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
if (self->evaluate_constraints != NULL) {
|
||||||
|
return Py_NewRef(self->evaluate_constraints);
|
||||||
|
}
|
||||||
|
if (self->constraints != NULL) {
|
||||||
|
return constevaluator_alloc(self->constraints);
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
typevar_evaluate_default(typevarobject *self, void *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
if (self->evaluate_default != NULL) {
|
||||||
|
return Py_NewRef(self->evaluate_default);
|
||||||
|
}
|
||||||
|
if (self->default_value != NULL) {
|
||||||
|
return constevaluator_alloc(self->default_value);
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
static PyGetSetDef typevar_getset[] = {
|
static PyGetSetDef typevar_getset[] = {
|
||||||
{"__bound__", (getter)typevar_bound, NULL, NULL, NULL},
|
{"__bound__", (getter)typevar_bound, NULL, NULL, NULL},
|
||||||
{"__constraints__", (getter)typevar_constraints, NULL, NULL, NULL},
|
{"__constraints__", (getter)typevar_constraints, NULL, NULL, NULL},
|
||||||
{"__default__", (getter)typevar_default, NULL, NULL, NULL},
|
{"__default__", (getter)typevar_default, NULL, NULL, NULL},
|
||||||
|
{"evaluate_bound", (getter)typevar_evaluate_bound, NULL, NULL, NULL},
|
||||||
|
{"evaluate_constraints", (getter)typevar_evaluate_constraints, NULL, NULL, NULL},
|
||||||
|
{"evaluate_default", (getter)typevar_evaluate_default, NULL, NULL, NULL},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -995,10 +1229,23 @@ paramspec_default(paramspecobject *self, void *unused)
|
||||||
return default_value;
|
return default_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
paramspec_evaluate_default(paramspecobject *self, void *unused)
|
||||||
|
{
|
||||||
|
if (self->evaluate_default != NULL) {
|
||||||
|
return Py_NewRef(self->evaluate_default);
|
||||||
|
}
|
||||||
|
if (self->default_value != NULL) {
|
||||||
|
return constevaluator_alloc(self->default_value);
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
static PyGetSetDef paramspec_getset[] = {
|
static PyGetSetDef paramspec_getset[] = {
|
||||||
{"args", (getter)paramspec_args, NULL, PyDoc_STR("Represents positional arguments."), NULL},
|
{"args", (getter)paramspec_args, NULL, PyDoc_STR("Represents positional arguments."), NULL},
|
||||||
{"kwargs", (getter)paramspec_kwargs, NULL, PyDoc_STR("Represents keyword arguments."), NULL},
|
{"kwargs", (getter)paramspec_kwargs, NULL, PyDoc_STR("Represents keyword arguments."), NULL},
|
||||||
{"__default__", (getter)paramspec_default, NULL, "The default value for this ParamSpec.", NULL},
|
{"__default__", (getter)paramspec_default, NULL, "The default value for this ParamSpec.", NULL},
|
||||||
|
{"evaluate_default", (getter)paramspec_evaluate_default, NULL, NULL, NULL},
|
||||||
{0},
|
{0},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1437,8 +1684,21 @@ typevartuple_default(typevartupleobject *self, void *unused)
|
||||||
return default_value;
|
return default_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
typevartuple_evaluate_default(typevartupleobject *self, void *unused)
|
||||||
|
{
|
||||||
|
if (self->evaluate_default != NULL) {
|
||||||
|
return Py_NewRef(self->evaluate_default);
|
||||||
|
}
|
||||||
|
if (self->default_value != NULL) {
|
||||||
|
return constevaluator_alloc(self->default_value);
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
static PyGetSetDef typevartuple_getset[] = {
|
static PyGetSetDef typevartuple_getset[] = {
|
||||||
{"__default__", (getter)typevartuple_default, NULL, "The default value for this TypeVarTuple.", NULL},
|
{"__default__", (getter)typevartuple_default, NULL, "The default value for this TypeVarTuple.", NULL},
|
||||||
|
{"evaluate_default", (getter)typevartuple_evaluate_default, NULL, NULL, NULL},
|
||||||
{0},
|
{0},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1584,6 +1844,17 @@ typealias_value(PyObject *self, void *unused)
|
||||||
return typealias_get_value(ta);
|
return typealias_get_value(ta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
typealias_evaluate_value(PyObject *self, void *unused)
|
||||||
|
{
|
||||||
|
typealiasobject *ta = (typealiasobject *)self;
|
||||||
|
if (ta->compute_value != NULL) {
|
||||||
|
return Py_NewRef(ta->compute_value);
|
||||||
|
}
|
||||||
|
assert(ta->value != NULL);
|
||||||
|
return constevaluator_alloc(ta->value);
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
typealias_parameters(PyObject *self, void *unused)
|
typealias_parameters(PyObject *self, void *unused)
|
||||||
{
|
{
|
||||||
|
@ -1627,6 +1898,7 @@ static PyGetSetDef typealias_getset[] = {
|
||||||
{"__parameters__", typealias_parameters, (setter)NULL, NULL, NULL},
|
{"__parameters__", typealias_parameters, (setter)NULL, NULL, NULL},
|
||||||
{"__type_params__", typealias_type_params, (setter)NULL, NULL, NULL},
|
{"__type_params__", typealias_type_params, (setter)NULL, NULL, NULL},
|
||||||
{"__value__", typealias_value, (setter)NULL, NULL, NULL},
|
{"__value__", typealias_value, (setter)NULL, NULL, NULL},
|
||||||
|
{"evaluate_value", typealias_evaluate_value, (setter)NULL, NULL, NULL},
|
||||||
{"__module__", typealias_module, (setter)NULL, NULL, NULL},
|
{"__module__", typealias_module, (setter)NULL, NULL, NULL},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
@ -1952,6 +2224,7 @@ int _Py_initialize_generic(PyInterpreterState *interp)
|
||||||
MAKE_TYPE(paramspec);
|
MAKE_TYPE(paramspec);
|
||||||
MAKE_TYPE(paramspecargs);
|
MAKE_TYPE(paramspecargs);
|
||||||
MAKE_TYPE(paramspeckwargs);
|
MAKE_TYPE(paramspeckwargs);
|
||||||
|
MAKE_TYPE(constevaluator);
|
||||||
#undef MAKE_TYPE
|
#undef MAKE_TYPE
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1964,6 +2237,7 @@ void _Py_clear_generic_types(PyInterpreterState *interp)
|
||||||
Py_CLEAR(interp->cached_objects.paramspec_type);
|
Py_CLEAR(interp->cached_objects.paramspec_type);
|
||||||
Py_CLEAR(interp->cached_objects.paramspecargs_type);
|
Py_CLEAR(interp->cached_objects.paramspecargs_type);
|
||||||
Py_CLEAR(interp->cached_objects.paramspeckwargs_type);
|
Py_CLEAR(interp->cached_objects.paramspeckwargs_type);
|
||||||
|
Py_CLEAR(interp->cached_objects.constevaluator_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
// types.UnionType -- used to represent e.g. Union[int, str], int | str
|
// types.UnionType -- used to represent e.g. Union[int, str], int | str
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
#include "pycore_object.h" // _PyObject_GC_TRACK/UNTRACK
|
#include "pycore_object.h" // _PyObject_GC_TRACK/UNTRACK
|
||||||
#include "pycore_typevarobject.h" // _PyTypeAlias_Type
|
#include "pycore_typevarobject.h" // _PyTypeAlias_Type, _Py_typing_type_repr
|
||||||
#include "pycore_unionobject.h"
|
#include "pycore_unionobject.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static PyObject *make_union(PyObject *);
|
static PyObject *make_union(PyObject *);
|
||||||
|
|
||||||
|
|
||||||
|
@ -181,67 +180,6 @@ _Py_union_type_or(PyObject* self, PyObject* other)
|
||||||
return new_union;
|
return new_union;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
union_repr_item(PyUnicodeWriter *writer, PyObject *p)
|
|
||||||
{
|
|
||||||
PyObject *qualname = NULL;
|
|
||||||
PyObject *module = NULL;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (p == (PyObject *)&_PyNone_Type) {
|
|
||||||
return PyUnicodeWriter_WriteUTF8(writer, "None", 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((rc = PyObject_HasAttrWithError(p, &_Py_ID(__origin__))) > 0 &&
|
|
||||||
(rc = PyObject_HasAttrWithError(p, &_Py_ID(__args__))) > 0)
|
|
||||||
{
|
|
||||||
// It looks like a GenericAlias
|
|
||||||
goto use_repr;
|
|
||||||
}
|
|
||||||
if (rc < 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PyObject_GetOptionalAttr(p, &_Py_ID(__qualname__), &qualname) < 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (qualname == NULL) {
|
|
||||||
goto use_repr;
|
|
||||||
}
|
|
||||||
if (PyObject_GetOptionalAttr(p, &_Py_ID(__module__), &module) < 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (module == NULL || module == Py_None) {
|
|
||||||
goto use_repr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Looks like a class
|
|
||||||
if (PyUnicode_Check(module) &&
|
|
||||||
_PyUnicode_EqualToASCIIString(module, "builtins"))
|
|
||||||
{
|
|
||||||
// builtins don't need a module name
|
|
||||||
rc = PyUnicodeWriter_WriteStr(writer, qualname);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rc = PyUnicodeWriter_Format(writer, "%S.%S", module, qualname);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
error:
|
|
||||||
rc = -1;
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
use_repr:
|
|
||||||
rc = PyUnicodeWriter_WriteRepr(writer, p);
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
done:
|
|
||||||
Py_XDECREF(qualname);
|
|
||||||
Py_XDECREF(module);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
union_repr(PyObject *self)
|
union_repr(PyObject *self)
|
||||||
{
|
{
|
||||||
|
@ -260,7 +198,7 @@ union_repr(PyObject *self)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
PyObject *p = PyTuple_GET_ITEM(alias->args, i);
|
PyObject *p = PyTuple_GET_ITEM(alias->args, i);
|
||||||
if (union_repr_item(writer, p) < 0) {
|
if (_Py_typing_type_repr(writer, p) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1978,8 +1978,9 @@ compiler_type_param_bound_or_default(struct compiler *c, expr_ty e,
|
||||||
identifier name, void *key,
|
identifier name, void *key,
|
||||||
bool allow_starred)
|
bool allow_starred)
|
||||||
{
|
{
|
||||||
if (compiler_enter_scope(c, name, COMPILER_SCOPE_ANNOTATIONS,
|
PyObject *defaults = PyTuple_Pack(1, _PyLong_GetOne());
|
||||||
key, e->lineno, NULL) == -1) {
|
ADDOP_LOAD_CONST_NEW(c, LOC(e), defaults);
|
||||||
|
if (compiler_setup_annotations_scope(c, LOC(e), key, name) == -1) {
|
||||||
return ERROR;
|
return ERROR;
|
||||||
}
|
}
|
||||||
if (allow_starred && e->kind == Starred_kind) {
|
if (allow_starred && e->kind == Starred_kind) {
|
||||||
|
@ -1995,7 +1996,7 @@ compiler_type_param_bound_or_default(struct compiler *c, expr_ty e,
|
||||||
if (co == NULL) {
|
if (co == NULL) {
|
||||||
return ERROR;
|
return ERROR;
|
||||||
}
|
}
|
||||||
if (compiler_make_closure(c, LOC(e), co, 0) < 0) {
|
if (compiler_make_closure(c, LOC(e), co, MAKE_FUNCTION_DEFAULTS) < 0) {
|
||||||
Py_DECREF(co);
|
Py_DECREF(co);
|
||||||
return ERROR;
|
return ERROR;
|
||||||
}
|
}
|
||||||
|
@ -2566,8 +2567,10 @@ compiler_typealias_body(struct compiler *c, stmt_ty s)
|
||||||
{
|
{
|
||||||
location loc = LOC(s);
|
location loc = LOC(s);
|
||||||
PyObject *name = s->v.TypeAlias.name->v.Name.id;
|
PyObject *name = s->v.TypeAlias.name->v.Name.id;
|
||||||
|
PyObject *defaults = PyTuple_Pack(1, _PyLong_GetOne());
|
||||||
|
ADDOP_LOAD_CONST_NEW(c, loc, defaults);
|
||||||
RETURN_IF_ERROR(
|
RETURN_IF_ERROR(
|
||||||
compiler_enter_scope(c, name, COMPILER_SCOPE_FUNCTION, s, loc.lineno, NULL));
|
compiler_setup_annotations_scope(c, LOC(s), s, name));
|
||||||
/* Make None the first constant, so the evaluate function can't have a
|
/* Make None the first constant, so the evaluate function can't have a
|
||||||
docstring. */
|
docstring. */
|
||||||
RETURN_IF_ERROR(compiler_add_const(c, Py_None));
|
RETURN_IF_ERROR(compiler_add_const(c, Py_None));
|
||||||
|
@ -2578,7 +2581,7 @@ compiler_typealias_body(struct compiler *c, stmt_ty s)
|
||||||
if (co == NULL) {
|
if (co == NULL) {
|
||||||
return ERROR;
|
return ERROR;
|
||||||
}
|
}
|
||||||
if (compiler_make_closure(c, loc, co, 0) < 0) {
|
if (compiler_make_closure(c, loc, co, MAKE_FUNCTION_DEFAULTS) < 0) {
|
||||||
Py_DECREF(co);
|
Py_DECREF(co);
|
||||||
return ERROR;
|
return ERROR;
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,6 +260,7 @@ static int symtable_visit_pattern(struct symtable *st, pattern_ty s);
|
||||||
static int symtable_raise_if_annotation_block(struct symtable *st, const char *, expr_ty);
|
static int symtable_raise_if_annotation_block(struct symtable *st, const char *, expr_ty);
|
||||||
static int symtable_raise_if_not_coroutine(struct symtable *st, const char *msg, _Py_SourceLocation loc);
|
static int symtable_raise_if_not_coroutine(struct symtable *st, const char *msg, _Py_SourceLocation loc);
|
||||||
static int symtable_raise_if_comprehension_block(struct symtable *st, expr_ty);
|
static int symtable_raise_if_comprehension_block(struct symtable *st, expr_ty);
|
||||||
|
static int symtable_add_def(struct symtable *st, PyObject *name, int flag, _Py_SourceLocation loc);
|
||||||
|
|
||||||
/* For debugging purposes only */
|
/* For debugging purposes only */
|
||||||
#if _PY_DUMP_SYMTABLE
|
#if _PY_DUMP_SYMTABLE
|
||||||
|
@ -1388,6 +1389,16 @@ symtable_enter_block(struct symtable *st, identifier name, _Py_block_ty block,
|
||||||
return 0;
|
return 0;
|
||||||
int result = symtable_enter_existing_block(st, ste);
|
int result = symtable_enter_existing_block(st, ste);
|
||||||
Py_DECREF(ste);
|
Py_DECREF(ste);
|
||||||
|
if (block == AnnotationBlock || block == TypeVariableBlock || block == TypeAliasBlock) {
|
||||||
|
_Py_DECLARE_STR(format, ".format");
|
||||||
|
// We need to insert code that reads this "parameter" to the function.
|
||||||
|
if (!symtable_add_def(st, &_Py_STR(format), DEF_PARAM, loc)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!symtable_add_def(st, &_Py_STR(format), USE, loc)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2630,18 +2641,6 @@ symtable_visit_annotation(struct symtable *st, expr_ty annotation, void *key)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_Py_DECLARE_STR(format, ".format");
|
|
||||||
// The generated __annotate__ function takes a single parameter with the
|
|
||||||
// internal name ".format".
|
|
||||||
if (!symtable_add_def(st, &_Py_STR(format), DEF_PARAM,
|
|
||||||
LOCATION(annotation))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!symtable_add_def(st, &_Py_STR(format), USE,
|
|
||||||
LOCATION(annotation))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!symtable_enter_existing_block(st, parent_ste->ste_annotation_block)) {
|
if (!symtable_enter_existing_block(st, parent_ste->ste_annotation_block)) {
|
||||||
|
@ -2690,14 +2689,6 @@ symtable_visit_annotations(struct symtable *st, stmt_ty o, arguments_ty a, expr_
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_Py_DECLARE_STR(format, ".format");
|
|
||||||
// We need to insert code that reads this "parameter" to the function.
|
|
||||||
if (!symtable_add_def(st, &_Py_STR(format), DEF_PARAM, LOCATION(o))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!symtable_add_def(st, &_Py_STR(format), USE, LOCATION(o))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (a->posonlyargs && !symtable_visit_argannotations(st, a->posonlyargs))
|
if (a->posonlyargs && !symtable_visit_argannotations(st, a->posonlyargs))
|
||||||
return 0;
|
return 0;
|
||||||
if (a->args && !symtable_visit_argannotations(st, a->args))
|
if (a->args && !symtable_visit_argannotations(st, a->args))
|
||||||
|
|
Loading…
Reference in New Issue