Skip to content

Commit e3893b5

Browse files
committed
Merge branch 'main' into gh-118527-code-consts
2 parents aee1184 + 2ba2c14 commit e3893b5

File tree

20 files changed

+383
-92
lines changed

20 files changed

+383
-92
lines changed

.github/workflows/posix-deps-apt.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ apt-get -yq install \
1515
libgdbm-dev \
1616
libgdbm-compat-dev \
1717
liblzma-dev \
18+
libmpdec-dev \
1819
libncurses5-dev \
1920
libreadline6-dev \
2021
libsqlite3-dev \

Doc/using/configure.rst

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -804,11 +804,18 @@ Libraries options
804804

805805
.. option:: --with-system-libmpdec
806806

807-
Build the ``_decimal`` extension module using an installed ``mpdec``
808-
library, see the :mod:`decimal` module (default is no).
807+
Build the ``_decimal`` extension module using an installed ``mpdecimal``
808+
library, see the :mod:`decimal` module (default is yes).
809809

810810
.. versionadded:: 3.3
811811

812+
.. versionchanged:: 3.13
813+
Default to using the installed ``mpdecimal`` library.
814+
815+
.. deprecated-removed:: 3.13 3.15
816+
A copy of the ``mpdecimal`` library sources will no longer be distributed
817+
with Python 3.15.
818+
812819
.. seealso:: :option:`LIBMPDEC_CFLAGS` and :option:`LIBMPDEC_LIBS`.
813820

814821
.. option:: --with-readline=readline|editline

Doc/whatsnew/3.13.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,6 +1807,10 @@ Changes in the Python API
18071807
Build Changes
18081808
=============
18091809

1810+
* The :file:`configure` option :option:`--with-system-libmpdec` now defaults
1811+
to ``yes``. The bundled copy of ``libmpdecimal`` will be removed in Python
1812+
3.15.
1813+
18101814
* Autoconf 2.71 and aclocal 1.16.4 are now required to regenerate
18111815
the :file:`configure` script.
18121816
(Contributed by Christian Heimes in :gh:`89886`.)
@@ -2336,6 +2340,7 @@ Pending Removal in Python 3.14
23362340
Pending Removal in Python 3.15
23372341
------------------------------
23382342

2343+
* The bundled copy of ``libmpdecimal``.
23392344
* :c:func:`PyImport_ImportModuleNoBlock`: use :c:func:`PyImport_ImportModule`.
23402345
* :c:func:`PyWeakref_GET_OBJECT`: use :c:func:`PyWeakref_GetRef` instead.
23412346
* :c:func:`PyWeakref_GetObject`: use :c:func:`PyWeakref_GetRef` instead.

Include/cpython/optimizer.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,16 +141,22 @@ void _Py_ExecutorDetach(_PyExecutorObject *);
141141
void _Py_BloomFilter_Init(_PyBloomFilter *);
142142
void _Py_BloomFilter_Add(_PyBloomFilter *bloom, void *obj);
143143
PyAPI_FUNC(void) _Py_Executor_DependsOn(_PyExecutorObject *executor, void *obj);
144-
PyAPI_FUNC(void) _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is_invalidation);
145-
PyAPI_FUNC(void) _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation);
146-
147144
/* For testing */
148145
PyAPI_FUNC(PyObject *)PyUnstable_Optimizer_NewCounter(void);
149146
PyAPI_FUNC(PyObject *)PyUnstable_Optimizer_NewUOpOptimizer(void);
150147

151148
#define _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS 3
152149
#define _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS 6
153150

151+
#ifdef _Py_TIER2
152+
PyAPI_FUNC(void) _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is_invalidation);
153+
PyAPI_FUNC(void) _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation);
154+
#else
155+
# define _Py_Executors_InvalidateDependency(A, B, C) ((void)0)
156+
# define _Py_Executors_InvalidateAll(A, B) ((void)0)
157+
#endif
158+
159+
154160
#ifdef __cplusplus
155161
}
156162
#endif

Lib/_pyrepl/__main__.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import os
22
import sys
33

4-
CAN_USE_PYREPL = True
4+
CAN_USE_PYREPL = sys.platform != "win32"
5+
56

67
def interactive_console(mainmodule=None, quiet=False, pythonstartup=False):
78
global CAN_USE_PYREPL
@@ -21,7 +22,7 @@ def interactive_console(mainmodule=None, quiet=False, pythonstartup=False):
2122
sys.ps1 = ">>> "
2223
if not hasattr(sys, "ps2"):
2324
sys.ps2 = "... "
24-
#
25+
2526
run_interactive = None
2627
try:
2728
import errno
@@ -33,7 +34,10 @@ def interactive_console(mainmodule=None, quiet=False, pythonstartup=False):
3334
from .simple_interact import run_multiline_interactive_console
3435
run_interactive = run_multiline_interactive_console
3536
except Exception as e:
36-
print(f"warning: can't use pyrepl: {e}", file=sys.stderr)
37+
from .trace import trace
38+
msg = f"warning: can't use pyrepl: {e}"
39+
trace(msg)
40+
print(msg, file=sys.stderr)
3741
CAN_USE_PYREPL = False
3842
if run_interactive is None:
3943
return sys._baserepl()

Lib/pdb.py

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,12 @@
7777
import code
7878
import glob
7979
import token
80+
import types
8081
import codeop
8182
import pprint
8283
import signal
8384
import inspect
85+
import textwrap
8486
import tokenize
8587
import traceback
8688
import linecache
@@ -624,11 +626,96 @@ def _disable_command_completion(self):
624626
self.completenames = completenames
625627
return
626628

629+
def _exec_in_closure(self, source, globals, locals):
630+
""" Run source code in closure so code object created within source
631+
can find variables in locals correctly
632+
633+
returns True if the source is executed, False otherwise
634+
"""
635+
636+
# Determine if the source should be executed in closure. Only when the
637+
# source compiled to multiple code objects, we should use this feature.
638+
# Otherwise, we can just raise an exception and normal exec will be used.
639+
640+
code = compile(source, "<string>", "exec")
641+
if not any(isinstance(const, CodeType) for const in code.co_consts):
642+
return False
643+
644+
# locals could be a proxy which does not support pop
645+
# copy it first to avoid modifying the original locals
646+
locals_copy = dict(locals)
647+
648+
locals_copy["__pdb_eval__"] = {
649+
"result": None,
650+
"write_back": {}
651+
}
652+
653+
# If the source is an expression, we need to print its value
654+
try:
655+
compile(source, "<string>", "eval")
656+
except SyntaxError:
657+
pass
658+
else:
659+
source = "__pdb_eval__['result'] = " + source
660+
661+
# Add write-back to update the locals
662+
source = ("try:\n" +
663+
textwrap.indent(source, " ") + "\n" +
664+
"finally:\n" +
665+
" __pdb_eval__['write_back'] = locals()")
666+
667+
# Build a closure source code with freevars from locals like:
668+
# def __pdb_outer():
669+
# var = None
670+
# def __pdb_scope(): # This is the code object we want to execute
671+
# nonlocal var
672+
# <source>
673+
# return __pdb_scope.__code__
674+
source_with_closure = ("def __pdb_outer():\n" +
675+
"\n".join(f" {var} = None" for var in locals_copy) + "\n" +
676+
" def __pdb_scope():\n" +
677+
"\n".join(f" nonlocal {var}" for var in locals_copy) + "\n" +
678+
textwrap.indent(source, " ") + "\n" +
679+
" return __pdb_scope.__code__"
680+
)
681+
682+
# Get the code object of __pdb_scope()
683+
# The exec fills locals_copy with the __pdb_outer() function and we can call
684+
# that to get the code object of __pdb_scope()
685+
ns = {}
686+
try:
687+
exec(source_with_closure, {}, ns)
688+
except Exception:
689+
return False
690+
code = ns["__pdb_outer"]()
691+
692+
cells = tuple(types.CellType(locals_copy.get(var)) for var in code.co_freevars)
693+
694+
try:
695+
exec(code, globals, locals_copy, closure=cells)
696+
except Exception:
697+
return False
698+
699+
# get the data we need from the statement
700+
pdb_eval = locals_copy["__pdb_eval__"]
701+
702+
# __pdb_eval__ should not be updated back to locals
703+
pdb_eval["write_back"].pop("__pdb_eval__")
704+
705+
# Write all local variables back to locals
706+
locals.update(pdb_eval["write_back"])
707+
eval_result = pdb_eval["result"]
708+
if eval_result is not None:
709+
print(repr(eval_result))
710+
711+
return True
712+
627713
def default(self, line):
628714
if line[:1] == '!': line = line[1:].strip()
629715
locals = self.curframe_locals
630716
globals = self.curframe.f_globals
631717
try:
718+
buffer = line
632719
if (code := codeop.compile_command(line + '\n', '<stdin>', 'single')) is None:
633720
# Multi-line mode
634721
with self._disable_command_completion():
@@ -661,7 +748,8 @@ def default(self, line):
661748
sys.stdin = self.stdin
662749
sys.stdout = self.stdout
663750
sys.displayhook = self.displayhook
664-
exec(code, globals, locals)
751+
if not self._exec_in_closure(buffer, globals, locals):
752+
exec(code, globals, locals)
665753
finally:
666754
sys.stdout = save_stdout
667755
sys.stdin = save_stdin

Lib/test/test_capi/test_opt.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,5 +1321,18 @@ def testfunc(n):
13211321
self.assertIsNotNone(ex)
13221322
self.assertIn("_FOR_ITER_GEN_FRAME", get_opnames(ex))
13231323

1324+
def test_modified_local_is_seen_by_optimized_code(self):
1325+
l = sys._getframe().f_locals
1326+
a = 1
1327+
s = 0
1328+
for j in range(1 << 10):
1329+
a + a
1330+
l["xa"[j >> 9]] = 1.0
1331+
s += a
1332+
self.assertIs(type(a), float)
1333+
self.assertIs(type(s), float)
1334+
self.assertEqual(s, 1024.0)
1335+
1336+
13241337
if __name__ == "__main__":
13251338
unittest.main()

Lib/test/test_free_threading/test_monitoring.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,14 @@
88

99
from sys import monitoring
1010
from test.support import is_wasi
11-
from threading import Thread
11+
from threading import Thread, _PyRLock
1212
from unittest import TestCase
1313

1414

1515
class InstrumentationMultiThreadedMixin:
16-
if not hasattr(sys, "gettotalrefcount"):
17-
thread_count = 50
18-
func_count = 1000
19-
fib = 15
20-
else:
21-
# Run a little faster in debug builds...
22-
thread_count = 25
23-
func_count = 500
24-
fib = 15
16+
thread_count = 10
17+
func_count = 200
18+
fib = 12
2519

2620
def after_threads(self):
2721
"""Runs once after all the threads have started"""
@@ -175,9 +169,6 @@ def during_threads(self):
175169
@unittest.skipIf(is_wasi, "WASI has no threads.")
176170
class SetProfileMultiThreaded(InstrumentationMultiThreadedMixin, TestCase):
177171
"""Uses sys.setprofile and repeatedly toggles instrumentation on and off"""
178-
thread_count = 25
179-
func_count = 200
180-
fib = 15
181172

182173
def setUp(self):
183174
self.set = False
@@ -227,6 +218,28 @@ def test_register_callback(self):
227218
for ref in self.refs:
228219
self.assertEqual(ref(), None)
229220

221+
def test_set_local_trace_opcodes(self):
222+
def trace(frame, event, arg):
223+
frame.f_trace_opcodes = True
224+
return trace
225+
226+
sys.settrace(trace)
227+
try:
228+
l = _PyRLock()
229+
230+
def f():
231+
for i in range(3000):
232+
with l:
233+
pass
234+
235+
t = Thread(target=f)
236+
t.start()
237+
for i in range(3000):
238+
with l:
239+
pass
240+
t.join()
241+
finally:
242+
sys.settrace(None)
230243

231244
if __name__ == "__main__":
232245
unittest.main()

Lib/test/test_pdb.py

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2224,8 +2224,71 @@ def test_pdb_multiline_statement():
22242224
(Pdb) c
22252225
"""
22262226

2227+
def test_pdb_closure():
2228+
"""Test for all expressions/statements that involve closure
2229+
2230+
>>> k = 0
2231+
>>> g = 1
2232+
>>> def test_function():
2233+
... x = 2
2234+
... g = 3
2235+
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
2236+
2237+
>>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE
2238+
... 'k',
2239+
... 'g',
2240+
... 'y = y',
2241+
... 'global g; g',
2242+
... 'global g; (lambda: g)()',
2243+
... '(lambda: x)()',
2244+
... '(lambda: g)()',
2245+
... 'lst = [n for n in range(10) if (n % x) == 0]',
2246+
... 'lst',
2247+
... 'sum(n for n in lst if n > x)',
2248+
... 'x = 1; raise Exception()',
2249+
... 'x',
2250+
... 'def f():',
2251+
... ' return x',
2252+
... '',
2253+
... 'f()',
2254+
... 'c'
2255+
... ]):
2256+
... test_function()
2257+
> <doctest test.test_pdb.test_pdb_closure[2]>(4)test_function()
2258+
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
2259+
(Pdb) k
2260+
0
2261+
(Pdb) g
2262+
3
2263+
(Pdb) y = y
2264+
*** NameError: name 'y' is not defined
2265+
(Pdb) global g; g
2266+
1
2267+
(Pdb) global g; (lambda: g)()
2268+
1
2269+
(Pdb) (lambda: x)()
2270+
2
2271+
(Pdb) (lambda: g)()
2272+
3
2273+
(Pdb) lst = [n for n in range(10) if (n % x) == 0]
2274+
(Pdb) lst
2275+
[0, 2, 4, 6, 8]
2276+
(Pdb) sum(n for n in lst if n > x)
2277+
18
2278+
(Pdb) x = 1; raise Exception()
2279+
*** Exception
2280+
(Pdb) x
2281+
1
2282+
(Pdb) def f():
2283+
... return x
2284+
...
2285+
(Pdb) f()
2286+
1
2287+
(Pdb) c
2288+
"""
2289+
22272290
def test_pdb_show_attribute_and_item():
2228-
"""Test for multiline statement
2291+
"""Test for expressions with command prefix
22292292
22302293
>>> def test_function():
22312294
... n = lambda x: x
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The :file:`configure` option :option:`--with-system-libmpdec` now defaults to ``yes``.
2+
The bundled copy of ``libmpdecimal`` will be removed in Python 3.15.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Enabled arbitrary statements and evaluations in :mod:`pdb` shell to access the
2+
local variables of the current frame, which made it possible for multi-scope
3+
code like generators or nested function to work.

Modules/_tkinter.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2972,6 +2972,7 @@ Tkapp_Dealloc(PyObject *self)
29722972
ENTER_TCL
29732973
Tcl_DeleteInterp(Tkapp_Interp(self));
29742974
LEAVE_TCL
2975+
Py_XDECREF(((TkappObject *)self)->trace);
29752976
PyObject_Free(self);
29762977
Py_DECREF(tp);
29772978
DisableEventHook();

0 commit comments

Comments
 (0)