Align local monitoring events and final warning-format tests

Allow final exception-event masks locally and remove only the local event registration on DISABLE. Match final bytes unsupported-letter diagnostics. Copy the three affected canonical test definitions through update_lib from pinned final CPython.

Assisted-by: OpenAI Codex:model identifier unavailable
This commit is contained in:
dot committed 2026-10-13 10:47:47 +09:00
1 parent 7ecedcf187
commit ec206b1125
4 files changed
+50 -21

No files matched your search

+1 -1
View File
@@ -626,7 +626,7 @@ class FormatTest(unittest.TestCase):
with self.assertRaisesRegex(ValueError, error_msg): with self.assertRaisesRegex(ValueError, error_msg):
f"{'x':zs}" # can't apply to string f"{'x':zs}" # can't apply to string
error_msg = re.escape("unsupported format character 'z'") error_msg = re.escape("unsupported format %z at position 0")
with self.assertRaisesRegex(ValueError, error_msg): with self.assertRaisesRegex(ValueError, error_msg):
"%z.1f" % 0 # not allowed in old style string interpolation "%z.1f" % 0 # not allowed in old style string interpolation
with self.assertRaisesRegex(ValueError, error_msg): with self.assertRaisesRegex(ValueError, error_msg):
+21 -10
View File
@@ -250,10 +250,23 @@ class FilterTests(BaseTest):
self.module.warn_explicit('msg', UserWarning, 'filename', 42, self.module.warn_explicit('msg', UserWarning, 'filename', 42,
module='package.module') module='package.module')
self.assertEqual(len(w), 1) self.assertEqual(len(w), 1)
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module', 42)
self.assertEqual(len(w), 2)
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module.py', 42)
self.assertEqual(len(w), 3)
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module/__init__.py', 42)
self.assertEqual(len(w), 4)
with self.assertRaises(UserWarning): with self.assertRaises(UserWarning):
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module', 42) self.module.warn_explicit('msg', UserWarning, '/path/to/package/module/__init__', 42)
with self.assertRaises(UserWarning): if MS_WINDOWS:
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module.py', 42) self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PY', 42)
self.assertEqual(len(w), 5)
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module\__INIT__.PY', 42)
self.assertEqual(len(w), 6)
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PYW', 42)
self.assertEqual(len(w), 7)
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module\__INIT__.PYW', 42)
self.assertEqual(len(w), 8)
with self.module.catch_warnings(record=True) as w: with self.module.catch_warnings(record=True) as w:
self.module.simplefilter('error') self.module.simplefilter('error')
@@ -277,9 +290,8 @@ class FilterTests(BaseTest):
with self.assertRaises(UserWarning): with self.assertRaises(UserWarning):
self.module.warn_explicit('msg', UserWarning, '/PATH/TO/PACKAGE/MODULE', 42) self.module.warn_explicit('msg', UserWarning, '/PATH/TO/PACKAGE/MODULE', 42)
if MS_WINDOWS: if MS_WINDOWS:
if self.module is py_warnings: self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module.PY', 42)
self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module.PY', 42) self.assertEqual(len(w), 3)
self.assertEqual(len(w), 3)
with self.assertRaises(UserWarning): with self.assertRaises(UserWarning):
self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module/__init__.py', 42) self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module/__init__.py', 42)
with self.assertRaises(UserWarning): with self.assertRaises(UserWarning):
@@ -303,9 +315,8 @@ class FilterTests(BaseTest):
self.assertEqual(len(w), 1) self.assertEqual(len(w), 1)
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.py', 42) self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.py', 42)
self.assertEqual(len(w), 2) self.assertEqual(len(w), 2)
if self.module is py_warnings: self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PY', 42)
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PY', 42) self.assertEqual(len(w), 3)
self.assertEqual(len(w), 3)
with self.assertRaises(UserWarning): with self.assertRaises(UserWarning):
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.pyw', 42) self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.pyw', 42)
with self.assertRaises(UserWarning): with self.assertRaises(UserWarning):
@@ -400,7 +411,7 @@ class FilterTests(BaseTest):
def test_mutate_filter_list(self): def test_mutate_filter_list(self):
class X: class X:
def match(self, a): def match(self, a, start=0):
L[:] = [] L[:] = []
L = [("default",X(),UserWarning,X(),0) for i in range(2)] L = [("default",X(),UserWarning,X(),0) for i in range(2)]
+11 -3
View File
@@ -73,8 +73,14 @@ fn spec_format_bytes(
spec: &CFormatSpec, spec: &CFormatSpec,
obj: PyObjectRef, obj: PyObjectRef,
argument: FormatArgument<'_>, argument: FormatArgument<'_>,
format_start: usize,
) -> PyResult<Vec<u8>> { ) -> PyResult<Vec<u8>> {
match &spec.format_type { match &spec.format_type {
CFormatType::Unsupported { ch, .. } if ch.to_char_lossy().is_ascii_alphabetic() => Err(vm
.new_value_error(format!(
"unsupported format %{} at position {format_start}",
ch.to_char_lossy(),
))),
CFormatType::Unsupported { ch, index } => Err(vm.new_value_error(format!( CFormatType::Unsupported { ch, index } => Err(vm.new_value_error(format!(
"unsupported format character '{}' ({:#x}) at index {index}", "unsupported format character '{}' ({:#x}) at index {index}",
ch.to_char_lossy(), ch.to_char_lossy(),
@@ -407,7 +413,7 @@ pub(crate) fn cformat_bytes(
} }
// dict // dict
for (_, part) in format { for (format_start, part) in format {
match part { match part {
CFormatPart::Literal(literal) => result.extend(literal), CFormatPart::Literal(literal) => result.extend(literal),
CFormatPart::Spec(CFormatSpecKeyed { mapping_key, spec }) => { CFormatPart::Spec(CFormatSpecKeyed { mapping_key, spec }) => {
@@ -419,6 +425,7 @@ pub(crate) fn cformat_bytes(
&spec, &spec,
value, value,
FormatArgument::Mapping(key.as_object()), FormatArgument::Mapping(key.as_object()),
format_start,
)?; )?;
result.extend(part_result); result.extend(part_result);
} }
@@ -443,7 +450,7 @@ pub(crate) fn cformat_bytes(
let mut value_iter = value_iter.inspect(|_| argument_index.set(argument_index.get() + 1)); let mut value_iter = value_iter.inspect(|_| argument_index.set(argument_index.get() + 1));
let is_tuple = values_obj.downcast_ref::<tuple::PyTuple>().is_some(); let is_tuple = values_obj.downcast_ref::<tuple::PyTuple>().is_some();
for (_, part) in format { for (format_start, part) in format {
match part { match part {
CFormatPart::Literal(literal) => result.extend(literal), CFormatPart::Literal(literal) => result.extend(literal),
CFormatPart::Spec(CFormatSpecKeyed { mut spec, .. }) => { CFormatPart::Spec(CFormatSpecKeyed { mut spec, .. }) => {
@@ -464,7 +471,8 @@ pub(crate) fn cformat_bytes(
} else { } else {
FormatArgument::Scalar FormatArgument::Scalar
}; };
let part_result = spec_format_bytes(vm, &spec, value.to_owned(), argument)?; let part_result =
spec_format_bytes(vm, &spec, value.to_owned(), argument, format_start)?;
result.extend(part_result); result.extend(part_result);
} }
} }
+17 -7
View File
@@ -9,7 +9,8 @@ use std::collections::{HashMap, HashSet};
pub(crate) const TOOL_LIMIT: usize = 6; pub(crate) const TOOL_LIMIT: usize = 6;
const EVENTS_COUNT: usize = 19; const EVENTS_COUNT: usize = 19;
const LOCAL_EVENTS_COUNT: usize = 11; const INSTRUMENTED_EVENTS_COUNT: usize = 11;
const LOCAL_EVENTS_COUNT: usize = 16;
const UNGROUPED_EVENTS_COUNT: usize = 18; const UNGROUPED_EVENTS_COUNT: usize = 18;
/// Event identifier (`PY_MONITORING_EVENT_*`), stored in `tstate->what_event`. /// Event identifier (`PY_MONITORING_EVENT_*`), stored in `tstate->what_event`.
@@ -905,10 +906,21 @@ fn fire(
for (tool, cb) in callbacks { for (tool, cb) in callbacks {
let result = cb.call(args.clone(), vm)?; let result = cb.call(args.clone(), vm)?;
if disable_sentinel.as_ref().is_some_and(|d| result.is(d)) { if disable_sentinel.as_ref().is_some_and(|d| result.is(d)) {
// Only local events (event_id < LOCAL_EVENTS_COUNT) can be disabled. if event_id < INSTRUMENTED_EVENTS_COUNT {
// Non-local events (RAISE, EXCEPTION_HANDLED, PY_UNWIND, etc.) let mut state = vm.state.monitoring.lock();
// cannot be disabled per code object. state.disabled.insert((code_id, offset as usize, tool));
if event_id >= LOCAL_EVENTS_COUNT { } else if event_id < LOCAL_EVENTS_COUNT {
// Non-instrumented events disable their local registration
// for this code object, leaving global events active.
let mut state = vm.state.monitoring.lock();
if let Some(events) = state.local_events.get_mut(&(tool, code_id)) {
*events &= !event_mask;
if *events == 0 {
state.local_events.remove(&(tool, code_id));
}
}
update_events_mask(vm, &state);
} else {
// Remove the callback. // Remove the callback.
let mut state = vm.state.monitoring.lock(); let mut state = vm.state.monitoring.lock();
state.callbacks.remove(&(tool, event_id)); state.callbacks.remove(&(tool, event_id));
@@ -917,8 +929,6 @@ fn fire(
EVENT_NAMES[event_id] EVENT_NAMES[event_id]
))); )));
} }
let mut state = vm.state.monitoring.lock();
state.disabled.insert((code_id, offset as usize, tool));
} }
} }
Ok(()) Ok(())