Skip to content

Commit dc5ce40

Browse files
committed
Align local monitoring events and exception lifecycle state with final 3.15
Replay reviewed compiler CI repairs ec206b1, 191fe58 on the frozen current prerequisite. Intermediate compile/diagnostic corrections are combined where they address the same failure. Assisted-by: OpenAI Codex: model identifier unavailable
1 parent 37d6d9d commit dc5ce40

5 files changed

Lines changed: 61 additions & 23 deletions

File tree

‎Lib/test/test_format.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ def test_specifier_z_error(self):
626626
with self.assertRaisesRegex(ValueError, error_msg):
627627
f"{'x':zs}" # can't apply to string
628628

629-
error_msg = re.escape("unsupported format character 'z'")
629+
error_msg = re.escape("unsupported format %z at position 0")
630630
with self.assertRaisesRegex(ValueError, error_msg):
631631
"%z.1f" % 0 # not allowed in old style string interpolation
632632
with self.assertRaisesRegex(ValueError, error_msg):

‎Lib/test/test_warnings/__init__.py‎

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -250,10 +250,23 @@ def test_filter_module(self):
250250
self.module.warn_explicit('msg', UserWarning, 'filename', 42,
251251
module='package.module')
252252
self.assertEqual(len(w), 1)
253+
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module', 42)
254+
self.assertEqual(len(w), 2)
255+
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module.py', 42)
256+
self.assertEqual(len(w), 3)
257+
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module/__init__.py', 42)
258+
self.assertEqual(len(w), 4)
253259
with self.assertRaises(UserWarning):
254-
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module', 42)
255-
with self.assertRaises(UserWarning):
256-
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module.py', 42)
260+
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module/__init__', 42)
261+
if MS_WINDOWS:
262+
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PY', 42)
263+
self.assertEqual(len(w), 5)
264+
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module\__INIT__.PY', 42)
265+
self.assertEqual(len(w), 6)
266+
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PYW', 42)
267+
self.assertEqual(len(w), 7)
268+
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module\__INIT__.PYW', 42)
269+
self.assertEqual(len(w), 8)
257270

258271
with self.module.catch_warnings(record=True) as w:
259272
self.module.simplefilter('error')
@@ -277,9 +290,8 @@ def test_filter_module(self):
277290
with self.assertRaises(UserWarning):
278291
self.module.warn_explicit('msg', UserWarning, '/PATH/TO/PACKAGE/MODULE', 42)
279292
if MS_WINDOWS:
280-
if self.module is py_warnings:
281-
self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module.PY', 42)
282-
self.assertEqual(len(w), 3)
293+
self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module.PY', 42)
294+
self.assertEqual(len(w), 3)
283295
with self.assertRaises(UserWarning):
284296
self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module/__init__.py', 42)
285297
with self.assertRaises(UserWarning):
@@ -303,9 +315,8 @@ def test_filter_module(self):
303315
self.assertEqual(len(w), 1)
304316
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.py', 42)
305317
self.assertEqual(len(w), 2)
306-
if self.module is py_warnings:
307-
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PY', 42)
308-
self.assertEqual(len(w), 3)
318+
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PY', 42)
319+
self.assertEqual(len(w), 3)
309320
with self.assertRaises(UserWarning):
310321
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.pyw', 42)
311322
with self.assertRaises(UserWarning):
@@ -400,7 +411,7 @@ def test_message_matching(self):
400411

401412
def test_mutate_filter_list(self):
402413
class X:
403-
def match(self, a):
414+
def match(self, a, start=0):
404415
L[:] = []
405416

406417
L = [("default",X(),UserWarning,X(),0) for i in range(2)]

‎crates/vm/src/cformat.rs‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,14 @@ fn spec_format_bytes(
7373
spec: &CFormatSpec,
7474
obj: PyObjectRef,
7575
argument: FormatArgument<'_>,
76+
format_start: usize,
7677
) -> PyResult<Vec<u8>> {
7778
match &spec.format_type {
79+
CFormatType::Unsupported { ch, .. } if ch.to_char_lossy().is_ascii_alphabetic() => Err(vm
80+
.new_value_error(format!(
81+
"unsupported format %{} at position {format_start}",
82+
ch.to_char_lossy(),
83+
))),
7884
CFormatType::Unsupported { ch, index } => Err(vm.new_value_error(format!(
7985
"unsupported format character '{}' ({:#x}) at index {index}",
8086
ch.to_char_lossy(),
@@ -407,7 +413,7 @@ pub(crate) fn cformat_bytes(
407413
}
408414

409415
// dict
410-
for (_, part) in format {
416+
for (format_start, part) in format {
411417
match part {
412418
CFormatPart::Literal(literal) => result.extend(literal),
413419
CFormatPart::Spec(CFormatSpecKeyed { mapping_key, spec }) => {
@@ -419,6 +425,7 @@ pub(crate) fn cformat_bytes(
419425
&spec,
420426
value,
421427
FormatArgument::Mapping(key.as_object()),
428+
format_start,
422429
)?;
423430
result.extend(part_result);
424431
}
@@ -443,7 +450,7 @@ pub(crate) fn cformat_bytes(
443450
let mut value_iter = value_iter.inspect(|_| argument_index.set(argument_index.get() + 1));
444451
let is_tuple = values_obj.downcast_ref::<tuple::PyTuple>().is_some();
445452

446-
for (_, part) in format {
453+
for (format_start, part) in format {
447454
match part {
448455
CFormatPart::Literal(literal) => result.extend(literal),
449456
CFormatPart::Spec(CFormatSpecKeyed { mut spec, .. }) => {
@@ -464,7 +471,8 @@ pub(crate) fn cformat_bytes(
464471
} else {
465472
FormatArgument::Scalar
466473
};
467-
let part_result = spec_format_bytes(vm, &spec, value.to_owned(), argument)?;
474+
let part_result =
475+
spec_format_bytes(vm, &spec, value.to_owned(), argument, format_start)?;
468476
result.extend(part_result);
469477
}
470478
}

‎crates/vm/src/frame.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4171,6 +4171,7 @@ impl ExecutingFrame<'_> {
41714171
Ok(None) => next_idx = lasti_cell.load(Relaxed),
41724172
Ok(Some(result)) => break Ok(Some(result)),
41734173
Err(exception) => {
4174+
monitoring::reset_reraise_pending();
41744175
// Fire PY_UNWIND: exception escapes this frame
41754176
let exception = if vm.state.monitoring_events.load()
41764177
& MonitoringEvent::PyUnwind.mask()
@@ -4454,6 +4455,7 @@ impl ExecutingFrame<'_> {
44544455
}
44554456
Ok(Some(result)) => Ok(result),
44564457
Err(exception) => {
4458+
monitoring::reset_reraise_pending();
44574459
// Fire PY_UNWIND: exception escapes the generator frame.
44584460
let exception =
44594461
if vm.state.monitoring_events.load() & MonitoringEvent::PyUnwind.mask() != 0 {
@@ -8922,6 +8924,7 @@ impl ExecutingFrame<'_> {
89228924
if let Some(entry) =
89238925
bytecode::find_exception_handler(&self.code.exceptiontable, offset)
89248926
{
8927+
monitoring::reset_reraise_pending();
89258928
// Fire EXCEPTION_HANDLED before setting up handler.
89268929
// If the callback raises, the handler is NOT set up and the
89278930
// new exception propagates instead.

‎crates/vm/src/stdlib/sys/monitoring.rs‎

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use std::collections::{HashMap, HashSet};
99

1010
pub(crate) const TOOL_LIMIT: usize = 6;
1111
const EVENTS_COUNT: usize = 19;
12-
const LOCAL_EVENTS_COUNT: usize = 11;
12+
const INSTRUMENTED_EVENTS_COUNT: usize = 11;
13+
const LOCAL_EVENTS_COUNT: usize = 16;
1314
const UNGROUPED_EVENTS_COUNT: usize = 18;
1415

1516
/// Event identifier (`PY_MONITORING_EVENT_*`), stored in `tstate->what_event`.
@@ -905,10 +906,21 @@ fn fire(
905906
for (tool, cb) in callbacks {
906907
let result = cb.call(args.clone(), vm)?;
907908
if disable_sentinel.as_ref().is_some_and(|d| result.is(d)) {
908-
// Only local events (event_id < LOCAL_EVENTS_COUNT) can be disabled.
909-
// Non-local events (RAISE, EXCEPTION_HANDLED, PY_UNWIND, etc.)
910-
// cannot be disabled per code object.
911-
if event_id >= LOCAL_EVENTS_COUNT {
909+
if event_id < INSTRUMENTED_EVENTS_COUNT {
910+
let mut state = vm.state.monitoring.lock();
911+
state.disabled.insert((code_id, offset as usize, tool));
912+
} else if event_id < LOCAL_EVENTS_COUNT {
913+
// Non-instrumented events disable their local registration
914+
// for this code object, leaving global events active.
915+
let mut state = vm.state.monitoring.lock();
916+
if let Some(events) = state.local_events.get_mut(&(tool, code_id)) {
917+
*events &= !event_mask;
918+
if *events == 0 {
919+
state.local_events.remove(&(tool, code_id));
920+
}
921+
}
922+
update_events_mask(vm, &state);
923+
} else {
912924
// Remove the callback.
913925
let mut state = vm.state.monitoring.lock();
914926
state.callbacks.remove(&(tool, event_id));
@@ -917,8 +929,6 @@ fn fire(
917929
EVENT_NAMES[event_id]
918930
)));
919931
}
920-
let mut state = vm.state.monitoring.lock();
921-
state.disabled.insert((code_id, offset as usize, tool));
922932
}
923933
}
924934
Ok(())
@@ -1073,6 +1083,12 @@ pub(crate) fn fire_raise(
10731083
)
10741084
}
10751085

1086+
/// End suppression when the exception reaches a handler or escapes a frame,
1087+
/// independently of whether either lifecycle event has a registered callback.
1088+
pub(crate) fn reset_reraise_pending() {
1089+
RERAISE_PENDING.with(|f| f.set(false));
1090+
}
1091+
10761092
/// Only fires if no RERAISE has been fired since the last EXCEPTION_HANDLED,
10771093
/// preventing duplicate events from chained cleanup handlers.
10781094
pub(crate) fn fire_reraise(
@@ -1104,7 +1120,7 @@ pub(crate) fn fire_exception_handled(
11041120
offset: u32,
11051121
exception: &PyObject,
11061122
) -> PyResult<()> {
1107-
RERAISE_PENDING.with(|f| f.set(false));
1123+
reset_reraise_pending();
11081124
fire(
11091125
vm,
11101126
MonitoringEvent::ExceptionHandled,
@@ -1120,7 +1136,7 @@ pub(crate) fn fire_py_unwind(
11201136
offset: u32,
11211137
exception: &PyObject,
11221138
) -> PyResult<()> {
1123-
RERAISE_PENDING.with(|f| f.set(false));
1139+
reset_reraise_pending();
11241140
fire(
11251141
vm,
11261142
MonitoringEvent::PyUnwind,

0 commit comments

Comments
 (0)