Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 2 additions & 23 deletions Doc/c-api/frame.rst
Original file line number Diff line number Diff line change
Expand Up @@ -269,29 +269,8 @@ Unless using :pep:`523`, you will not need this.
* - .. c:macro:: PyUnstable_EXECUTABLE_KIND_METHOD_DESCRIPTOR
- The frame corresponds to a method on a class instance.

However, Python's C API lacks a function to read the executable kind from
a frame. Instead, use this recipe:

.. code-block:: c

int
get_executable_kind(PyFrameObject *frame)
{
_PyInterpreterFrame *f = frame->f_frame;
PyObject *exec = PyStackRef_AsPyObjectBorrow(f->f_executable);

if (PyCode_Check(exec)) {
return PyUnstable_EXECUTABLE_KIND_PY_FUNCTION;
}
if (PyMethod_Check(exec)) {
return PyUnstable_EXECUTABLE_KIND_BUILTIN_FUNCTION;
}
if (Py_IS_TYPE(exec, &PyMethodDescr_Type)) {
return PyUnstable_EXECUTABLE_KIND_METHOD_DESCRIPTOR;
}

return PyUnstable_EXECUTABLE_KIND_SKIP;
}
Note that reading the executable kind from a frame is currently only
possible with undocumented internal APIs.

.. versionadded:: 3.13

Expand Down
6 changes: 6 additions & 0 deletions Doc/library/argparse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1679,6 +1679,12 @@ The Namespace object
Simple class used by default by :meth:`~ArgumentParser.parse_args` to create
an object holding attributes and return it.

:class:`!Namespace` objects support :func:`copy.replace`,
which returns a copy of the object with the specified attributes replaced.

.. versionchanged:: next
Added support for :func:`copy.replace`.

This class is deliberately simple, just an :class:`object` subclass with a
readable string representation. If you prefer to have dict-like view of the
attributes, you can use the standard Python idiom, :func:`vars`::
Expand Down
3 changes: 2 additions & 1 deletion Doc/library/ast.rst
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,8 @@ Comprehensions
List and set comprehensions, generator expressions, and dictionary
comprehensions. ``elt`` (or ``key`` and ``value``) is a single node
representing the part that will be evaluated for each item.

For dictionary comprehensions using unpacking, for example
``{**item for item in items}``, ``value`` is ``None``,
``generators`` is a list of :class:`comprehension` nodes.

.. doctest::
Expand Down
42 changes: 23 additions & 19 deletions Doc/library/dis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1899,25 +1899,29 @@ iterations of the loop.

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

Expand Down
3 changes: 2 additions & 1 deletion Include/internal/pycore_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@
#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);
Expand Down
4 changes: 2 additions & 2 deletions Include/internal/pycore_magic_number.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ Known values:
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
Expand All @@ -312,7 +312,7 @@ Known values:
*/

#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 \
Expand Down
6 changes: 6 additions & 0 deletions Lib/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -1545,6 +1545,12 @@ def __eq__(self, other):
def __contains__(self, key):
return key in self.__dict__

def __replace__(self, /, **changes):
new = self.__class__()
new.__dict__.update(self.__dict__)
new.__dict__.update(changes)
return new


class _ActionsContainer(object):

Expand Down
14 changes: 14 additions & 0 deletions Lib/test/test_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import _colorize
import contextlib
import copy
import functools
import io
import operator
Expand Down Expand Up @@ -6267,6 +6268,19 @@ def test_equality_returns_notimplemented(self):
self.assertIs(ns.__eq__(None), NotImplemented)
self.assertIs(ns.__ne__(None), NotImplemented)

def test_replace(self):
ns = argparse.Namespace(a=1, b=2)
new = copy.replace(ns, b=3, c=4)
self.assertIsInstance(new, argparse.Namespace)
self.assertEqual(new, argparse.Namespace(a=1, b=3, c=4))
self.assertEqual(ns, argparse.Namespace(a=1, b=2))

class MyNamespace(argparse.Namespace):
pass
new = copy.replace(MyNamespace(a=1), a=2)
self.assertIsInstance(new, MyNamespace)
self.assertEqual(new.a, 2)


# ===================
# File encoding tests
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,12 +395,12 @@ def wrap_func_w_kwargs():
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
Expand Down
16 changes: 16 additions & 0 deletions Lib/test/test_type_annotations.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import annotationlib
import inspect
import itertools
import textwrap
import types
import unittest
Expand Down Expand Up @@ -896,3 +897,18 @@ class Generic:
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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a crash when ``__conditional_annotations__`` is rebound to a non-set
object.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:class:`argparse.Namespace` objects now support :func:`copy.replace`.
Loading
Loading