The operand determines which intrinsic function is called:
- +----------------------------------------+-----------------------------------+
- | Operand | Description |
- +========================================+===================================+
- | ``INTRINSIC_2_INVALID`` | Not valid |
- +----------------------------------------+-----------------------------------+
- | ``INTRINSIC_PREP_RERAISE_STAR`` | Calculates the |
- | | :exc:`ExceptionGroup` to raise |
- | | from a ``try-except*``. |
- +----------------------------------------+-----------------------------------+
- | ``INTRINSIC_TYPEVAR_WITH_BOUND`` | Creates a :class:`typing.TypeVar` |
- | | with a bound. |
- +----------------------------------------+-----------------------------------+
- | ``INTRINSIC_TYPEVAR_WITH_CONSTRAINTS`` | Creates a |
- | | :class:`typing.TypeVar` with |
- | | constraints. |
- +----------------------------------------+-----------------------------------+
- | ``INTRINSIC_SET_FUNCTION_TYPE_PARAMS`` | Sets the ``__type_params__`` |
- | | attribute of a function. |
- +----------------------------------------+-----------------------------------+
+ +------------------------------------------+-----------------------------------+
+ | Operand | Description |
+ +==========================================+===================================+
+ | ``INTRINSIC_2_INVALID`` | Not valid |
+ +------------------------------------------+-----------------------------------+
+ | ``INTRINSIC_PREP_RERAISE_STAR`` | Calculates the |
+ | | :exc:`ExceptionGroup` to raise |
+ | | from a ``try-except*``. |
+ +------------------------------------------+-----------------------------------+
+ | ``INTRINSIC_TYPEVAR_WITH_BOUND`` | Creates a :class:`typing.TypeVar` |
+ | | with a bound. |
+ +------------------------------------------+-----------------------------------+
+ | ``INTRINSIC_TYPEVAR_WITH_CONSTRAINTS`` | Creates a |
+ | | :class:`typing.TypeVar` with |
+ | | constraints. |
+ +------------------------------------------+-----------------------------------+
+ | ``INTRINSIC_SET_FUNCTION_TYPE_PARAMS`` | Sets the ``__type_params__`` |
+ | | attribute of a function. |
+ +------------------------------------------+-----------------------------------+
+ | ``INTRINSIC_ADD_CONDITIONAL_ANNOTATION`` | Adds an annotation index to the |
+ | | ``__conditional_annotations__`` |
+ | | set. |
+ +------------------------------------------+-----------------------------------+
.. versionadded:: 3.12
#define INTRINSIC_TYPEVAR_WITH_CONSTRAINTS 3
#define INTRINSIC_SET_FUNCTION_TYPE_PARAMS 4
#define INTRINSIC_SET_TYPEPARAM_DEFAULT 5
+#define INTRINSIC_ADD_CONDITIONAL_ANNOTATION 6
-#define MAX_INTRINSIC_2 5
+#define MAX_INTRINSIC_2 6
typedef PyObject *(*intrinsic_func1)(PyThreadState* tstate, PyObject *value);
typedef PyObject *(*intrinsic_func2)(PyThreadState* tstate, PyObject *value1, PyObject *value2);
Python 3.16a1 3702 (Replace DELETE_NAME with PUSH_NULL; STORE_NAME)
Python 3.16a1 3703 (Replace DELETE_GLOBAL with PUSH_NULL; STORE_GLOBAL)
Python 3.16a1 3704 (Replace DELETE_ATTR with PUSH_NULL; STORE_ATTR)
-
+ Python 3.16a1 3705 (Add INTRINSIC_ADD_CONDITIONAL_ANNOTATION)
Python 3.17 will start with 3750
*/
-#define PYC_MAGIC_NUMBER 3704
+#define PYC_MAGIC_NUMBER 3705
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
(little-endian) and then appending b'\r\n'. */
#define PYC_MAGIC_NUMBER_TOKEN \
STORE_NAME 1 (x)
LOAD_NAME 0 (__conditional_annotations__)
LOAD_SMALL_INT 0
- SET_ADD 1
+ CALL_INTRINSIC_2 6 (INTRINSIC_ADD_CONDITIONAL_ANNOTATION)
POP_TOP
3 LOAD_NAME 0 (__conditional_annotations__)
LOAD_SMALL_INT 1
- SET_ADD 1
+ CALL_INTRINSIC_2 6 (INTRINSIC_ADD_CONDITIONAL_ANNOTATION)
POP_TOP
4 LOAD_SMALL_INT 1
import annotationlib
import inspect
+import itertools
import textwrap
import types
import unittest
mod = build_module(code)
annos = mod.__annotations__
self.assertEqual(annos, {"annotated_name": 0})
+
+ # gh-154902
+ def test_conditional_annotations_rebound(self):
+ # user code can rebind __conditional_annotations__ to any object
+ lefts = ("__conditional_annotations__",
+ 'globals()["__conditional_annotations__"]')
+ values = ("0", "{}", "[]", "''", "object()", "frozenset()")
+ for left, value in itertools.product(lefts, values):
+ with self.subTest(left=left, value=value):
+ code = f"""
+ {left} = {value}
+ x: int
+ """
+ with self.assertRaises(TypeError):
+ run_code(code)
--- /dev/null
+Fix a crash when ``__conditional_annotations__`` is rebound to a non-set
+object.
ADDOP_NAME(c, loc, LOAD_NAME, &_Py_ID(__conditional_annotations__), names);
}
ADDOP_LOAD_CONST_NEW(c, loc, conditional_annotation_index);
- ADDOP_I(c, loc, SET_ADD, 1);
+ // gh-154902: change SET_ADD to new INTRINSIC_ADD_CONDITIONAL_ANNOTATION intrinsic
+ ADDOP_I(c, loc, CALL_INTRINSIC_2,
+ INTRINSIC_ADD_CONDITIONAL_ANNOTATION);
ADDOP(c, loc, POP_TOP);
}
}
return _Py_make_typevar(name, NULL, evaluate_constraints);
}
+static PyObject *
+add_conditional_annotation(PyThreadState* tstate, PyObject *conditional_annotations,
+ PyObject *index)
+{
+ // gh-154902: user code can rebind __conditional_annotations__ to any object
+ if (!PySet_CheckExact(conditional_annotations)) {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "__conditional_annotations__ must be a set, not %T",
+ conditional_annotations);
+ return NULL;
+ }
+ if (PySet_Add(conditional_annotations, index) < 0) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
const intrinsic_func2_info
_PyIntrinsics_BinaryFunctions[] = {
INTRINSIC_FUNC_ENTRY(INTRINSIC_2_INVALID, no_intrinsic2)
INTRINSIC_FUNC_ENTRY(INTRINSIC_TYPEVAR_WITH_CONSTRAINTS, make_typevar_with_constraints)
INTRINSIC_FUNC_ENTRY(INTRINSIC_SET_FUNCTION_TYPE_PARAMS, _Py_set_function_type_params)
INTRINSIC_FUNC_ENTRY(INTRINSIC_SET_TYPEPARAM_DEFAULT, _Py_set_typeparam_default)
+ INTRINSIC_FUNC_ENTRY(INTRINSIC_ADD_CONDITIONAL_ANNOTATION, add_conditional_annotation)
};
#undef INTRINSIC_FUNC_ENTRY