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