mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
Fix final 3.15 code names, warning filters and constant folding
Retain public co_name subclass owners for comparison and hashing. Match omitted warning modules against filenames through the exact final CPython helper imported with update_lib. Port final constant sequence folding order and align the string-format diagnostics exposed by canonical CI. Assisted-by: OpenAI Codex:model identifier unavailable
This commit is contained in:
1 parent
476bb74f3c
commit
6482adf66f
6 files changed
+257
-78
No files matched your search
Vendored
+83
-24
@@ -369,9 +369,15 @@ def _setoption(arg):
|
||||
if message or module:
|
||||
import re
|
||||
if message:
|
||||
message = re.escape(message)
|
||||
if len(message) >= 2 and message[0] == message[-1] == '/':
|
||||
message = message[1:-1]
|
||||
else:
|
||||
message = re.escape(message)
|
||||
if module:
|
||||
module = re.escape(module) + r'\z'
|
||||
if len(module) >= 2 and module[0] == module[-1] == '/':
|
||||
module = module[1:-1]
|
||||
else:
|
||||
module = re.escape(module) + r'\z'
|
||||
if lineno:
|
||||
try:
|
||||
lineno = int(lineno)
|
||||
@@ -381,7 +387,23 @@ def _setoption(arg):
|
||||
raise _wm._OptionError("invalid lineno %r" % (lineno,)) from None
|
||||
else:
|
||||
lineno = 0
|
||||
_wm.filterwarnings(action, message, category, module, lineno)
|
||||
try:
|
||||
_wm.filterwarnings(action, message, category, module, lineno)
|
||||
except re.PatternError if message or module else ():
|
||||
if message:
|
||||
try:
|
||||
re.compile(message)
|
||||
except re.PatternError:
|
||||
raise _wm._OptionError(f"invalid regular expression for "
|
||||
f"message: {message!r}") from None
|
||||
if module:
|
||||
try:
|
||||
re.compile(module)
|
||||
except re.PatternError:
|
||||
raise _wm._OptionError(f"invalid regular expression for "
|
||||
f"module: {module!r}") from None
|
||||
# Should never happen.
|
||||
raise
|
||||
|
||||
|
||||
# Helper for _setoption()
|
||||
@@ -449,9 +471,12 @@ def warn(message, category=None, stacklevel=1, source=None,
|
||||
# Check category argument
|
||||
if category is None:
|
||||
category = UserWarning
|
||||
if not (isinstance(category, type) and issubclass(category, Warning)):
|
||||
raise TypeError("category must be a Warning subclass, "
|
||||
"not '{:s}'".format(type(category).__name__))
|
||||
elif not isinstance(category, type):
|
||||
raise TypeError(f"category must be a Warning subclass, not "
|
||||
f"'{type(category).__name__}'")
|
||||
elif not issubclass(category, Warning):
|
||||
raise TypeError(f"category must be a Warning subclass, not "
|
||||
f"class '{category.__name__}'")
|
||||
if not isinstance(skip_file_prefixes, tuple):
|
||||
# The C version demands a tuple for implementation performance.
|
||||
raise TypeError('skip_file_prefixes must be a tuple of strs.')
|
||||
@@ -495,14 +520,43 @@ def warn(message, category=None, stacklevel=1, source=None,
|
||||
)
|
||||
|
||||
|
||||
def _match_filename(pattern, filename, *, MS_WINDOWS=(sys.platform == 'win32')):
|
||||
if not filename:
|
||||
return pattern.match('<unknown>') is not None
|
||||
if filename[0] == '<' and filename[-1] == '>':
|
||||
return pattern.match(filename) is not None
|
||||
|
||||
is_py = (filename[-3:].lower() == '.py'
|
||||
if MS_WINDOWS else
|
||||
filename.endswith('.py'))
|
||||
if is_py:
|
||||
filename = filename[:-3]
|
||||
if pattern.match(filename): # for backward compatibility
|
||||
return True
|
||||
if MS_WINDOWS:
|
||||
if not is_py and filename[-4:].lower() == '.pyw':
|
||||
filename = filename[:-4]
|
||||
is_py = True
|
||||
if is_py and filename[-9:].lower() in (r'\__init__', '/__init__'):
|
||||
filename = filename[:-9]
|
||||
filename = filename.replace('\\', '/')
|
||||
else:
|
||||
if is_py and filename.endswith('/__init__'):
|
||||
filename = filename[:-9]
|
||||
filename = filename.replace('/', '.')
|
||||
i = 0
|
||||
while True:
|
||||
if pattern.match(filename, i):
|
||||
return True
|
||||
i = filename.find('.', i) + 1
|
||||
if not i:
|
||||
return False
|
||||
|
||||
|
||||
def warn_explicit(message, category, filename, lineno,
|
||||
module=None, registry=None, module_globals=None,
|
||||
source=None):
|
||||
lineno = int(lineno)
|
||||
if module is None:
|
||||
module = filename or "<unknown>"
|
||||
if module[-3:].lower() == ".py":
|
||||
module = module[:-3] # XXX What about leading pathname?
|
||||
if isinstance(message, Warning):
|
||||
text = str(message)
|
||||
category = message.__class__
|
||||
@@ -524,9 +578,11 @@ def warn_explicit(message, category, filename, lineno,
|
||||
action, msg, cat, mod, ln = item
|
||||
if ((msg is None or msg.match(text)) and
|
||||
issubclass(category, cat) and
|
||||
(mod is None or mod.match(module)) and
|
||||
(ln == 0 or lineno == ln)):
|
||||
break
|
||||
(ln == 0 or lineno == ln) and
|
||||
(mod is None or (_match_filename(mod, filename)
|
||||
if module is None else
|
||||
mod.match(module)))):
|
||||
break
|
||||
else:
|
||||
action = _wm.defaultaction
|
||||
# Early exit actions
|
||||
@@ -564,17 +620,18 @@ def warn_explicit(message, category, filename, lineno,
|
||||
linecache.getlines(filename, module_globals)
|
||||
|
||||
# Print message and context
|
||||
msg = _wm.WarningMessage(message, category, filename, lineno, source=source)
|
||||
msg = _wm.WarningMessage(message, category, filename, lineno,
|
||||
module=module, source=source)
|
||||
_wm._showwarnmsg(msg)
|
||||
|
||||
|
||||
class WarningMessage(object):
|
||||
|
||||
_WARNING_DETAILS = ("message", "category", "filename", "lineno", "file",
|
||||
"line", "source")
|
||||
"line", "source", "module")
|
||||
|
||||
def __init__(self, message, category, filename, lineno, file=None,
|
||||
line=None, source=None):
|
||||
line=None, source=None, module=None):
|
||||
self.message = message
|
||||
self.category = category
|
||||
self.filename = filename
|
||||
@@ -582,12 +639,14 @@ class WarningMessage(object):
|
||||
self.file = file
|
||||
self.line = line
|
||||
self.source = source
|
||||
self.module = module
|
||||
self._category_name = category.__name__ if category else None
|
||||
|
||||
def __str__(self):
|
||||
return ("{message : %r, category : %r, filename : %r, lineno : %s, "
|
||||
"line : %r}" % (self.message, self._category_name,
|
||||
self.filename, self.lineno, self.line))
|
||||
return ("{message : %r, category : %r, module : %r, "
|
||||
"filename : %r, lineno : %s, line : %r}" % (
|
||||
self.message, self._category_name, self.module,
|
||||
self.filename, self.lineno, self.line))
|
||||
|
||||
def __repr__(self):
|
||||
return f'<{type(self).__qualname__} {self}>'
|
||||
@@ -647,8 +706,8 @@ class catch_warnings(object):
|
||||
context = None
|
||||
self._filters = self._module.filters
|
||||
self._module.filters = self._filters[:]
|
||||
self._showwarning = self._module.showwarning
|
||||
self._showwarnmsg_impl = self._module._showwarnmsg_impl
|
||||
self._showwarning = self._module.showwarning
|
||||
self._module._filters_mutated_lock_held()
|
||||
if self._record:
|
||||
if _use_context:
|
||||
@@ -656,9 +715,9 @@ class catch_warnings(object):
|
||||
else:
|
||||
log = []
|
||||
self._module._showwarnmsg_impl = log.append
|
||||
# Reset showwarning() to the default implementation to make sure
|
||||
# that _showwarnmsg() calls _showwarnmsg_impl()
|
||||
self._module.showwarning = self._module._showwarning_orig
|
||||
# Reset showwarning() to the default implementation to make sure
|
||||
# that _showwarnmsg() calls _showwarnmsg_impl()
|
||||
self._module.showwarning = self._module._showwarning_orig
|
||||
else:
|
||||
log = None
|
||||
if self._filter is not None:
|
||||
@@ -673,8 +732,8 @@ class catch_warnings(object):
|
||||
self._module._warnings_context.set(self._saved_context)
|
||||
else:
|
||||
self._module.filters = self._filters
|
||||
self._module.showwarning = self._showwarning
|
||||
self._module._showwarnmsg_impl = self._showwarnmsg_impl
|
||||
self._module.showwarning = self._showwarning
|
||||
self._module._filters_mutated_lock_held()
|
||||
|
||||
|
||||
|
||||
+50
-22
@@ -2251,14 +2251,17 @@ impl Blocks {
|
||||
AnyInstruction::Real(Instruction::CallIntrinsic1 { func }) => {
|
||||
match func.get(inst.arg) {
|
||||
IntrinsicFunction1::ListToTuple => {
|
||||
if matches!(nextop, Some(Instruction::GetIter { .. })) {
|
||||
fold_constant_sequence_into_load_const(
|
||||
metadata,
|
||||
&mut self[block_idx],
|
||||
i,
|
||||
)?;
|
||||
if matches!(
|
||||
self[block_idx].instructions[i].instr.real(),
|
||||
Some(Instruction::CallIntrinsic1 { .. })
|
||||
) && matches!(nextop, Some(Instruction::GetIter { .. }))
|
||||
{
|
||||
self[block_idx].instructions[i].set_to_nop();
|
||||
} else {
|
||||
fold_constant_intrinsic_list_to_tuple(
|
||||
metadata,
|
||||
&mut self[block_idx],
|
||||
i,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
IntrinsicFunction1::UnaryPositive => {
|
||||
@@ -2267,6 +2270,16 @@ impl Blocks {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
AnyInstruction::Real(
|
||||
Instruction::ListAppend { .. } | Instruction::SetAdd { .. },
|
||||
) if u32::from(inst.arg) == 1
|
||||
&& matches!(
|
||||
nextop,
|
||||
Some(Instruction::GetIter { .. } | Instruction::ContainsOp { .. })
|
||||
) =>
|
||||
{
|
||||
fold_constant_sequence_into_load_const(metadata, &mut self[block_idx], i)?;
|
||||
}
|
||||
AnyInstruction::Real(Instruction::BinaryOp { .. }) => {
|
||||
fold_const_binop(metadata, &mut self[block_idx], i)?;
|
||||
}
|
||||
@@ -5456,20 +5469,24 @@ fn fold_tuple_of_constants(
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn fold_constant_intrinsic_list_to_tuple(
|
||||
fn fold_constant_sequence_into_load_const(
|
||||
metadata: &mut CodeUnitMetadata,
|
||||
block: &mut Block,
|
||||
i: usize,
|
||||
) -> crate::InternalResult<bool> {
|
||||
let Some(Instruction::CallIntrinsic1 { func }) = block.instructions[i].instr.real() else {
|
||||
return Ok(false);
|
||||
let (intrinsic, is_set) = match block.instructions[i].instr.real() {
|
||||
Some(Instruction::CallIntrinsic1 { func })
|
||||
if func.get(block.instructions[i].arg) == IntrinsicFunction1::ListToTuple =>
|
||||
{
|
||||
(true, false)
|
||||
}
|
||||
Some(Instruction::ListAppend { .. }) => (false, false),
|
||||
Some(Instruction::SetAdd { .. }) => (false, true),
|
||||
_ => return Ok(false),
|
||||
};
|
||||
if func.get(block.instructions[i].arg) != IntrinsicFunction1::ListToTuple {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let mut consts_found = 0usize;
|
||||
let mut expect_append = true;
|
||||
let mut expect_append = intrinsic;
|
||||
let mut pos = i;
|
||||
while let Some(prev) = pos.checked_sub(1) {
|
||||
pos = prev;
|
||||
@@ -5478,9 +5495,12 @@ fn fold_constant_intrinsic_list_to_tuple(
|
||||
continue;
|
||||
}
|
||||
|
||||
if matches!(instr.instr.real(), Some(Instruction::BuildList { .. }))
|
||||
&& u32::from(instr.arg) == 0
|
||||
{
|
||||
let is_start = if is_set {
|
||||
matches!(instr.instr.real(), Some(Instruction::BuildSet { .. }))
|
||||
} else {
|
||||
matches!(instr.instr.real(), Some(Instruction::BuildList { .. }))
|
||||
};
|
||||
if is_start && u32::from(instr.arg) == 0 {
|
||||
if !expect_append {
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -5489,7 +5509,8 @@ fn fold_constant_intrinsic_list_to_tuple(
|
||||
elements
|
||||
.try_reserve_exact(consts_found)
|
||||
.map_err(|_| InternalError::MalformedControlFlowGraph)?;
|
||||
for idx in (pos..i).rev() {
|
||||
let end = if intrinsic { i } else { i + 1 };
|
||||
for idx in (pos..end).rev() {
|
||||
if matches!(block.instructions[idx].instr.real(), Some(Instruction::Nop)) {
|
||||
continue;
|
||||
}
|
||||
@@ -5506,15 +5527,22 @@ fn fold_constant_intrinsic_list_to_tuple(
|
||||
instr_make_load_const(
|
||||
metadata,
|
||||
&mut block.instructions[i],
|
||||
ConstantData::Tuple { elements },
|
||||
if is_set {
|
||||
ConstantData::Frozenset { elements }
|
||||
} else {
|
||||
ConstantData::Tuple { elements }
|
||||
},
|
||||
)?;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
if expect_append {
|
||||
if !matches!(instr.instr.real(), Some(Instruction::ListAppend { .. }))
|
||||
|| u32::from(instr.arg) != 1
|
||||
{
|
||||
let is_append = if is_set {
|
||||
matches!(instr.instr.real(), Some(Instruction::SetAdd { .. }))
|
||||
} else {
|
||||
matches!(instr.instr.real(), Some(Instruction::ListAppend { .. }))
|
||||
};
|
||||
if !is_append || u32::from(instr.arg) != 1 {
|
||||
return Ok(false);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -20,6 +20,7 @@ pub enum CFormatErrorType {
|
||||
MissingModuloSign,
|
||||
UnsupportedFormatChar(CodePoint),
|
||||
IncompleteFormat,
|
||||
StrayModulo,
|
||||
WidthTooBig,
|
||||
PrecisionTooBig,
|
||||
// Unimplemented,
|
||||
@@ -39,6 +40,7 @@ impl fmt::Display for CFormatError {
|
||||
match self.typ {
|
||||
CFormatErrorType::UnmatchedKeyParentheses => write!(f, "incomplete format key"),
|
||||
CFormatErrorType::IncompleteFormat => write!(f, "incomplete format"),
|
||||
CFormatErrorType::StrayModulo => write!(f, "stray % at position {}", self.index),
|
||||
CFormatErrorType::UnsupportedFormatChar(c) => write!(
|
||||
f,
|
||||
"unsupported format character '{}' ({:#x}) at index {}",
|
||||
@@ -832,8 +834,20 @@ impl<S> CFormatStrOrBytes<S> {
|
||||
|
||||
let spec =
|
||||
CFormatSpecKeyed::parse(iter, context).map_err(|err| CFormatError {
|
||||
typ: err.0,
|
||||
index: err.1,
|
||||
typ: if context == CFormatContext::Str
|
||||
&& err.0 == CFormatErrorType::IncompleteFormat
|
||||
{
|
||||
CFormatErrorType::StrayModulo
|
||||
} else {
|
||||
err.0
|
||||
},
|
||||
index: if context == CFormatContext::Str
|
||||
&& err.0 == CFormatErrorType::IncompleteFormat
|
||||
{
|
||||
index
|
||||
} else {
|
||||
err.1
|
||||
},
|
||||
})?;
|
||||
|
||||
parts.push((index, CFormatPart::Spec(spec)));
|
||||
@@ -842,8 +856,16 @@ impl<S> CFormatStrOrBytes<S> {
|
||||
}
|
||||
} else {
|
||||
return Err(CFormatError {
|
||||
typ: CFormatErrorType::IncompleteFormat,
|
||||
index: index + 1,
|
||||
typ: if context == CFormatContext::Str {
|
||||
CFormatErrorType::StrayModulo
|
||||
} else {
|
||||
CFormatErrorType::IncompleteFormat
|
||||
},
|
||||
index: if context == CFormatContext::Str {
|
||||
index
|
||||
} else {
|
||||
index + 1
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -991,8 +1013,8 @@ mod tests {
|
||||
assert_eq!(
|
||||
"Hello %".parse::<CFormatString>(),
|
||||
Err(CFormatError {
|
||||
typ: CFormatErrorType::IncompleteFormat,
|
||||
index: 7
|
||||
typ: CFormatErrorType::StrayModulo,
|
||||
index: 6
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -427,7 +427,6 @@ pub struct PyCode {
|
||||
#[pymember(name = "co_posonlyargcount", path = "posonlyarg_count")]
|
||||
#[pymember(name = "co_kwonlyargcount", path = "kwonlyarg_count")]
|
||||
#[pymember(name = "co_stacksize", path = "max_stackdepth")]
|
||||
#[pymember(name = "co_name", path = "obj_name")]
|
||||
#[pymember(name = "co_qualname", path = "qualname")]
|
||||
#[pymember(name = "co_flags", path = "flags")]
|
||||
// Logical code fields are finalized by `new`. Execution may specialize or
|
||||
@@ -436,6 +435,8 @@ pub struct PyCode {
|
||||
pub code: CodeObject,
|
||||
/// Canonical bytes captured before execution can instrument the code.
|
||||
canonical_code: Box<[u8]>,
|
||||
/// Preserve a public co_name subclass; exact strings use the interned owner.
|
||||
name_owner: Option<PyStrRef>,
|
||||
/// The stable Python object exposed by co_code, allocated on first access.
|
||||
code_bytes: OnceCell<PyBytesRef>,
|
||||
/// Slot-indexed names, equivalent to CPython's `co_localsplusnames`.
|
||||
@@ -578,7 +579,7 @@ impl PyCode {
|
||||
CodeField::Names => self.names_tuple.as_object(),
|
||||
CodeField::LocalsPlusNames => self.localsplus_names_tuple.as_object(),
|
||||
CodeField::LocalsPlusKinds => self.localsplus_kinds_bytes.as_object(),
|
||||
CodeField::Name => self.code.obj_name.as_object(),
|
||||
CodeField::Name => self.name_object(),
|
||||
CodeField::Qualname => self.code.qualname.as_object(),
|
||||
CodeField::LineTable => self.linetable_bytes.as_object(),
|
||||
CodeField::ExceptionTable => self.exceptiontable_bytes.as_object(),
|
||||
@@ -661,6 +662,7 @@ impl PyCode {
|
||||
cellvars_tuple: OnceCell::new(),
|
||||
freevars_tuple: OnceCell::new(),
|
||||
canonical_code,
|
||||
name_owner: None,
|
||||
code_bytes: OnceCell::new(),
|
||||
localsplus_names,
|
||||
source_path: AtomicPtr::new(sp),
|
||||
@@ -739,6 +741,18 @@ impl PyCode {
|
||||
self.localsplus_names[index]
|
||||
}
|
||||
|
||||
fn name_object(&self) -> &PyObject {
|
||||
self.name_owner
|
||||
.as_ref()
|
||||
.map_or_else(|| self.code.obj_name.as_object(), |name| name.as_object())
|
||||
}
|
||||
|
||||
fn retain_name_subclass(&mut self, name: PyStrRef, ctx: &Context) {
|
||||
if !name.class().is(ctx.types.str_type) {
|
||||
self.name_owner = Some(name);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source_path(&self) -> &'static PyStrInterned {
|
||||
// SAFETY: always points to a valid &'static PyStrInterned (interned strings are never deallocated)
|
||||
unsafe { &*self.source_path.load(Ordering::Relaxed) }
|
||||
@@ -912,7 +926,7 @@ impl Comparable for PyCode {
|
||||
let other = class_or_notimplemented!(Self, other);
|
||||
let a = &zelf.code;
|
||||
let b = &other.code;
|
||||
let eq = a.obj_name == b.obj_name
|
||||
let eq = vm.bool_eq(zelf.name_object(), other.name_object())?
|
||||
&& a.arg_count == b.arg_count
|
||||
&& a.posonlyarg_count == b.posonlyarg_count
|
||||
&& a.kwonlyarg_count == b.kwonlyarg_count
|
||||
@@ -951,7 +965,7 @@ impl Hashable for PyCode {
|
||||
let code = &zelf.code;
|
||||
// Hash a tuple of key attributes, matching CPython's code_hash
|
||||
let tuple = vm.ctx.new_tuple(vec![
|
||||
vm.ctx.new_str(code.obj_name.as_str()).into(),
|
||||
zelf.name_object().to_owned(),
|
||||
vm.ctx.new_int(code.arg_count).into(),
|
||||
vm.ctx.new_int(code.posonlyarg_count).into(),
|
||||
vm.ctx.new_int(code.kwonlyarg_count).into(),
|
||||
@@ -1127,6 +1141,7 @@ impl Constructor for PyCode {
|
||||
};
|
||||
|
||||
let mut result = Self::new(code, &vm.ctx);
|
||||
result.retain_name_subclass(args.name, &vm.ctx);
|
||||
result.linetable_bytes = args.linetable;
|
||||
result.exceptiontable_bytes = args.exceptiontable;
|
||||
Ok(result)
|
||||
@@ -1160,6 +1175,13 @@ impl Py<PyCode> {
|
||||
self.code.first_line_number.map_or(0, |n| n.get() as _)
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn co_name(&self) -> PyStrRef {
|
||||
self.name_owner
|
||||
.clone()
|
||||
.unwrap_or_else(|| self.code.obj_name.to_owned())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn co_consts(&self) -> PyTupleRef {
|
||||
self.code
|
||||
@@ -1606,7 +1628,7 @@ impl Py<PyCode> {
|
||||
|
||||
let obj_name = match co_name {
|
||||
OptionalArg::Present(obj_name) => obj_name,
|
||||
OptionalArg::Missing => self.code.obj_name.to_owned(),
|
||||
OptionalArg::Missing => self.co_name(),
|
||||
};
|
||||
|
||||
let names = match co_names {
|
||||
@@ -1735,6 +1757,7 @@ impl Py<PyCode> {
|
||||
};
|
||||
|
||||
let mut result = PyCode::new(new_code, &vm.ctx);
|
||||
result.retain_name_subclass(obj_name, &vm.ctx);
|
||||
result.retain_normalized_names(names, None);
|
||||
result.linetable_bytes = linetable;
|
||||
result.exceptiontable_bytes = exceptiontable;
|
||||
|
||||
@@ -203,8 +203,14 @@ fn spec_format_string(
|
||||
spec: &CFormatSpec,
|
||||
obj: PyObjectRef,
|
||||
argument: FormatArgument<'_>,
|
||||
format_start: usize,
|
||||
) -> PyResult<Wtf8Buf> {
|
||||
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!(
|
||||
"unsupported format character '{}' ({:#x}) at index {index}",
|
||||
ch.to_char_lossy(),
|
||||
@@ -245,15 +251,27 @@ fn spec_format_string(
|
||||
Ok(spec.format_number(i.as_bigint()).into())
|
||||
} else {
|
||||
Err(vm.new_type_error(format!(
|
||||
"%{} format: an integer is required, not {}",
|
||||
"{}: %{} requires an integer, not {}",
|
||||
argument.description(vm)?,
|
||||
spec.format_type.to_char(),
|
||||
obj.class().name()
|
||||
obj.class().slot_name()
|
||||
)))
|
||||
}
|
||||
}
|
||||
},
|
||||
CFormatType::Float(_) => {
|
||||
let value = ArgIntoFloat::try_from_object(vm, obj)?;
|
||||
let value = match ArgIntoFloat::try_from_object(vm, obj.clone()) {
|
||||
Ok(value) => value,
|
||||
Err(error) if error.fast_isinstance(vm.ctx.exceptions.type_error) => {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"{}: %{} requires a real number, not {}",
|
||||
argument.description(vm)?,
|
||||
spec.format_type.to_char(),
|
||||
obj.class().slot_name()
|
||||
)));
|
||||
}
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
Ok(spec.format_float(value.into()).into())
|
||||
}
|
||||
CFormatType::Character(CCharacterType::Character) => {
|
||||
@@ -504,7 +522,7 @@ pub(crate) fn cformat_string(
|
||||
}
|
||||
|
||||
// dict
|
||||
for (_, part) in format {
|
||||
for (format_start, part) in format {
|
||||
match part {
|
||||
CFormatPart::Literal(literal) => result.push_wtf8(&literal),
|
||||
CFormatPart::Spec(CFormatSpecKeyed { mapping_key, spec }) => {
|
||||
@@ -516,6 +534,7 @@ pub(crate) fn cformat_string(
|
||||
&spec,
|
||||
value,
|
||||
FormatArgument::Mapping(key.as_object()),
|
||||
format_start,
|
||||
)?;
|
||||
result.push_wtf8(&part_result);
|
||||
}
|
||||
@@ -540,7 +559,7 @@ pub(crate) fn cformat_string(
|
||||
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();
|
||||
|
||||
for (_, part) in format {
|
||||
for (format_start, part) in format {
|
||||
match part {
|
||||
CFormatPart::Literal(literal) => result.push_wtf8(&literal),
|
||||
CFormatPart::Spec(CFormatSpecKeyed { mut spec, .. }) => {
|
||||
@@ -561,7 +580,8 @@ pub(crate) fn cformat_string(
|
||||
} else {
|
||||
FormatArgument::Scalar
|
||||
};
|
||||
let part_result = spec_format_string(vm, &spec, value.to_owned(), argument)?;
|
||||
let part_result =
|
||||
spec_format_string(vm, &spec, value.to_owned(), argument, format_start)?;
|
||||
result.push_wtf8(&part_result);
|
||||
}
|
||||
}
|
||||
|
||||
+42
-15
@@ -336,22 +336,38 @@ fn update_registry(
|
||||
already_warned(registry, &altkey, true, vm)
|
||||
}
|
||||
|
||||
fn normalize_module(filename: &Py<PyStr>, vm: &VirtualMachine) -> PyObjectRef {
|
||||
match filename.byte_len() {
|
||||
0 => vm.new_pyobj("<unknown>"),
|
||||
len if len >= 3 && filename.as_bytes().ends_with(b".py") => {
|
||||
vm.new_pyobj(&filename.as_wtf8()[..len - 3])
|
||||
}
|
||||
_ => filename.as_object().to_owned(),
|
||||
fn check_module_matched(
|
||||
pattern: &PyObject,
|
||||
module: Option<&PyObject>,
|
||||
filename: &Py<PyStr>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<bool> {
|
||||
if let Some(module) = module {
|
||||
return check_matched(pattern, module, vm);
|
||||
}
|
||||
if vm.is_none(pattern) {
|
||||
return Ok(true);
|
||||
}
|
||||
if pattern.class().is(vm.ctx.types.str_type) {
|
||||
return Ok(false);
|
||||
}
|
||||
let warnings = vm.import("_py_warnings", 0)?;
|
||||
vm.call_method(
|
||||
&warnings,
|
||||
"_match_filename",
|
||||
(pattern.to_owned(), filename.to_owned()),
|
||||
)?
|
||||
.is_true(vm)
|
||||
}
|
||||
|
||||
/// Search a filters list for a matching action.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn filter_search(
|
||||
category: &PyObject,
|
||||
text: &PyObject,
|
||||
lineno: usize,
|
||||
module: &PyObject,
|
||||
module: Option<&PyObject>,
|
||||
filename: &Py<PyStr>,
|
||||
filters: &PyListRef,
|
||||
list_name: &str,
|
||||
vm: &VirtualMachine,
|
||||
@@ -380,7 +396,7 @@ fn filter_search(
|
||||
}
|
||||
let good_msg = check_matched(&tmp_item.as_slice()[1], text, vm)?;
|
||||
let is_subclass = category.is_subclass(&tmp_item.as_slice()[2], vm)?;
|
||||
let good_mod = check_matched(&tmp_item.as_slice()[3], module, vm)?;
|
||||
let good_mod = check_module_matched(&tmp_item.as_slice()[3], module, filename, vm)?;
|
||||
let ln: usize = tmp_item.as_slice()[4]
|
||||
.try_int(vm)
|
||||
.map_or(0, |v| v.as_u32_mask() as _);
|
||||
@@ -397,7 +413,8 @@ fn get_filter(
|
||||
category: &PyObject,
|
||||
text: &PyObject,
|
||||
lineno: usize,
|
||||
module: &PyObject,
|
||||
module: Option<&PyObject>,
|
||||
filename: &Py<PyStr>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
if let Some(context_filters) = get_warnings_context_filters(vm)? {
|
||||
@@ -406,6 +423,7 @@ fn get_filter(
|
||||
text,
|
||||
lineno,
|
||||
module,
|
||||
filename,
|
||||
&context_filters,
|
||||
"_warnings_context _filters",
|
||||
vm,
|
||||
@@ -416,7 +434,9 @@ fn get_filter(
|
||||
}
|
||||
|
||||
let filters = get_warnings_filters(vm)?;
|
||||
if let Some(action) = filter_search(category, text, lineno, module, &filters, "filters", vm)? {
|
||||
if let Some(action) = filter_search(
|
||||
category, text, lineno, module, filename, &filters, "filters", vm,
|
||||
)? {
|
||||
return Ok(action);
|
||||
}
|
||||
get_default_action(vm)
|
||||
@@ -504,9 +524,9 @@ pub fn warn_explicit(
|
||||
source: Option<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
// Normalize module. None → silent return (late-shutdown safety).
|
||||
let module = module.unwrap_or_else(|| normalize_module(&filename, vm));
|
||||
if vm.is_none(&module) {
|
||||
// Explicit None remains a late-shutdown silent return. An omitted module
|
||||
// is matched against the filename by the canonical warnings helper.
|
||||
if module.as_ref().is_some_and(|module| vm.is_none(module)) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -543,7 +563,14 @@ pub fn warn_explicit(
|
||||
}
|
||||
|
||||
// Get filter action
|
||||
let action = get_filter(category.as_object(), text.as_object(), lineno, &module, vm)?;
|
||||
let action = get_filter(
|
||||
category.as_object(),
|
||||
text.as_object(),
|
||||
lineno,
|
||||
module.as_deref(),
|
||||
&filename,
|
||||
vm,
|
||||
)?;
|
||||
let action_str = PyStrRef::try_from_object(vm, action)
|
||||
.map_err(|_| vm.new_type_error("action must be a string"))?;
|
||||
|
||||
|
||||
Reference in new issue
Block a user