forked from Rust-related/RustPython
Merge pull request #4146 from oow214/warning_warn_explicit
Update warning warn_explicit
This commit is contained in:
@@ -288,7 +288,7 @@ impl PyDict {
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn clear(&self) {
|
||||
pub fn clear(&self) {
|
||||
self.entries.clear()
|
||||
}
|
||||
|
||||
|
||||
@@ -213,6 +213,7 @@ declare_const_name! {
|
||||
keys,
|
||||
items,
|
||||
values,
|
||||
version,
|
||||
update,
|
||||
copy,
|
||||
flush,
|
||||
|
||||
@@ -31,7 +31,9 @@ use crate::{
|
||||
import,
|
||||
protocol::PyIterIter,
|
||||
scope::Scope,
|
||||
signal, stdlib, AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
|
||||
signal, stdlib,
|
||||
warn::WarningsState,
|
||||
AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
|
||||
};
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
@@ -88,6 +90,7 @@ pub struct PyGlobalState {
|
||||
pub atexit_funcs: PyMutex<Vec<(PyObjectRef, FuncArgs)>>,
|
||||
pub codec_registry: CodecsRegistry,
|
||||
pub finalizing: AtomicBool,
|
||||
pub warnings: WarningsState,
|
||||
}
|
||||
|
||||
pub fn process_hash_secret_seed() -> u32 {
|
||||
@@ -136,6 +139,8 @@ impl VirtualMachine {
|
||||
|
||||
let codec_registry = CodecsRegistry::new(&ctx);
|
||||
|
||||
let warnings = WarningsState::init_state(&ctx);
|
||||
|
||||
let mut vm = VirtualMachine {
|
||||
builtins,
|
||||
sys_module,
|
||||
@@ -161,6 +166,7 @@ impl VirtualMachine {
|
||||
atexit_funcs: PyMutex::default(),
|
||||
codec_registry,
|
||||
finalizing: AtomicBool::new(false),
|
||||
warnings,
|
||||
}),
|
||||
initialized: false,
|
||||
recursion_depth: Cell::new(0),
|
||||
|
||||
251
vm/src/warn.rs
251
vm/src/warn.rs
@@ -1,8 +1,53 @@
|
||||
use crate::{
|
||||
builtins::{PyDict, PyStrRef, PyType, PyTypeRef},
|
||||
AsObject, Py, PyObjectRef, PyResult, VirtualMachine,
|
||||
builtins::{PyDict, PyDictRef, PyListRef, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef},
|
||||
convert::{IntoObject, TryFromObject},
|
||||
types::PyComparisonOp,
|
||||
AsObject, Context, Py, PyObjectRef, PyResult, VirtualMachine,
|
||||
};
|
||||
|
||||
pub struct WarningsState {
|
||||
filters: PyListRef,
|
||||
_once_registry: PyDictRef,
|
||||
default_action: PyStrRef,
|
||||
filters_version: usize,
|
||||
}
|
||||
|
||||
impl WarningsState {
|
||||
fn create_filter(ctx: &Context) -> PyListRef {
|
||||
ctx.new_list(vec![ctx
|
||||
.new_tuple(vec![
|
||||
ctx.new_str("__main__").into(),
|
||||
ctx.types.none_type.as_object().to_owned(),
|
||||
ctx.exceptions.warning.as_object().to_owned(),
|
||||
ctx.new_str("ACTION").into(),
|
||||
ctx.new_int(0).into(),
|
||||
])
|
||||
.into()])
|
||||
}
|
||||
|
||||
pub fn init_state(ctx: &Context) -> WarningsState {
|
||||
WarningsState {
|
||||
filters: Self::create_filter(ctx),
|
||||
_once_registry: PyDict::new_ref(ctx),
|
||||
default_action: ctx.new_str("default"),
|
||||
filters_version: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_matched(obj: &PyObjectRef, arg: &PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
if obj.class().is(vm.ctx.types.none_type) {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
if obj.rich_compare_bool(arg, PyComparisonOp::Eq, vm)? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let result = vm.invoke(obj, (arg.to_owned(),));
|
||||
Ok(result.is_ok())
|
||||
}
|
||||
|
||||
pub fn py_warn(
|
||||
category: &Py<PyType>,
|
||||
message: String,
|
||||
@@ -31,19 +76,164 @@ pub fn warn(
|
||||
)
|
||||
}
|
||||
|
||||
fn get_default_action(vm: &VirtualMachine) -> PyResult<PyObjectRef> {
|
||||
vm.state
|
||||
.warnings
|
||||
.default_action
|
||||
.clone()
|
||||
.try_into()
|
||||
.map_err(|_| {
|
||||
vm.new_value_error(format!(
|
||||
"_warnings.defaultaction must be a string, not '{}'",
|
||||
vm.state.warnings.default_action
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn get_filter(
|
||||
category: PyObjectRef,
|
||||
text: PyObjectRef,
|
||||
lineno: usize,
|
||||
module: PyObjectRef,
|
||||
mut _item: PyTupleRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
let filters = vm.state.warnings.filters.as_object().to_owned();
|
||||
|
||||
let filters: PyListRef = filters
|
||||
.try_into_value(vm)
|
||||
.map_err(|_| vm.new_value_error("_warnings.filters must be a list".to_string()))?;
|
||||
|
||||
/* WarningsState.filters could change while we are iterating over it. */
|
||||
for i in 0..filters.borrow_vec().len() {
|
||||
let tmp_item = if let Some(tmp_item) = filters.borrow_vec().get(i).cloned() {
|
||||
let tmp_item = PyTupleRef::try_from_object(vm, tmp_item)?;
|
||||
if tmp_item.len() == 5 {
|
||||
Some(tmp_item)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let tmp_item = tmp_item.ok_or_else(|| {
|
||||
vm.new_value_error(format!("_warnings.filters item {} isn't a 5-tuple", i))
|
||||
})?;
|
||||
|
||||
/* Python code: action, msg, cat, mod, ln = item */
|
||||
let action = if let Some(action) = tmp_item.get(0) {
|
||||
action.str(vm).map(|action| action.into_object())
|
||||
} else {
|
||||
Err(vm.new_type_error("action must be a string".to_string()))
|
||||
};
|
||||
|
||||
let good_msg = if let Some(msg) = tmp_item.get(1) {
|
||||
check_matched(msg, &text, vm)?
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let is_subclass = if let Some(cat) = tmp_item.get(2) {
|
||||
category.fast_isinstance(&cat.class())
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let good_mod = if let Some(item_mod) = tmp_item.get(3) {
|
||||
check_matched(item_mod, &module, vm)?
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let ln = tmp_item.get(4).map_or(0, |ln_obj| {
|
||||
ln_obj.try_int(vm).map_or(0, |ln| ln.as_u32_mask() as _)
|
||||
});
|
||||
|
||||
if good_msg && good_mod && is_subclass && (ln == 0 || lineno == ln) {
|
||||
_item = tmp_item;
|
||||
return action;
|
||||
}
|
||||
}
|
||||
|
||||
get_default_action(vm)
|
||||
}
|
||||
|
||||
fn already_warned(
|
||||
registry: PyObjectRef,
|
||||
key: PyObjectRef,
|
||||
should_set: bool,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<bool> {
|
||||
let version_obj = registry.get_item(identifier!(&vm.ctx, version), vm).ok();
|
||||
let filters_version = vm.ctx.new_int(vm.state.warnings.filters_version).into();
|
||||
|
||||
match version_obj {
|
||||
Some(version_obj)
|
||||
if version_obj.try_int(vm).is_ok() || version_obj.is(&filters_version) =>
|
||||
{
|
||||
let already_warned = registry.get_item(key.as_ref(), vm)?;
|
||||
if already_warned.is_true(vm)? {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let registry = registry.dict();
|
||||
if let Some(registry) = registry.as_ref() {
|
||||
registry.clear();
|
||||
let r = registry.set_item("version", filters_version, vm);
|
||||
if r.is_err() {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This warning wasn't found in the registry, set it. */
|
||||
if !should_set {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let item = vm.ctx.true_value.clone().into();
|
||||
let _ = registry.set_item(key.as_ref(), item, vm); // ignore set error
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn normalize_module(filename: PyStrRef, vm: &VirtualMachine) -> Option<PyObjectRef> {
|
||||
let obj = match filename.char_len() {
|
||||
0 => vm.new_pyobj("<unknown>"),
|
||||
len if len >= 3 && filename.as_str().ends_with(".py") => {
|
||||
vm.new_pyobj(&filename.as_str()[..len - 3])
|
||||
}
|
||||
_ => filename.as_object().to_owned(),
|
||||
};
|
||||
Some(obj)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn warn_explicit(
|
||||
category: Option<PyTypeRef>,
|
||||
message: PyStrRef,
|
||||
_filename: PyStrRef,
|
||||
_lineno: usize,
|
||||
_module: PyObjectRef,
|
||||
_registry: PyObjectRef,
|
||||
filename: PyStrRef,
|
||||
lineno: usize,
|
||||
module: Option<PyObjectRef>,
|
||||
registry: PyObjectRef,
|
||||
_source_line: Option<PyObjectRef>,
|
||||
_source: Option<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
// TODO: Implement correctly
|
||||
let registry: PyObjectRef = registry
|
||||
.try_into_value(vm)
|
||||
.map_err(|_| vm.new_type_error("'registry' must be a dict or None".to_owned()))?;
|
||||
|
||||
// Normalize module.
|
||||
let module = match module.or_else(|| normalize_module(filename, vm)) {
|
||||
Some(module) => module,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
// Normalize message.
|
||||
let text = message.as_str();
|
||||
|
||||
let category = if let Some(category) = category {
|
||||
if !category.fast_issubclass(vm.ctx.exceptions.warning) {
|
||||
return Err(vm.new_type_error(format!(
|
||||
@@ -55,8 +245,49 @@ fn warn_explicit(
|
||||
} else {
|
||||
vm.ctx.exceptions.user_warning.to_owned()
|
||||
};
|
||||
|
||||
let category = if message.fast_isinstance(vm.ctx.exceptions.warning) {
|
||||
message.class().into_owned()
|
||||
} else {
|
||||
category
|
||||
};
|
||||
|
||||
// Create key.
|
||||
let key = PyTuple::new_ref(
|
||||
vec![
|
||||
vm.ctx.new_int(3).into(),
|
||||
vm.ctx.new_str(text).into(),
|
||||
category.as_object().to_owned(),
|
||||
vm.ctx.new_int(lineno).into(),
|
||||
],
|
||||
&vm.ctx,
|
||||
);
|
||||
|
||||
if !vm.is_none(registry.as_object()) && already_warned(registry, key.into_object(), false, vm)?
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let item = vm.ctx.new_tuple(vec![]);
|
||||
let action = get_filter(
|
||||
category.as_object().to_owned(),
|
||||
vm.ctx.new_str(text).into(),
|
||||
lineno,
|
||||
module,
|
||||
item,
|
||||
vm,
|
||||
)?;
|
||||
|
||||
if action.str(vm)?.as_str().eq("error") {
|
||||
return Err(vm.new_type_error(message.to_string()));
|
||||
}
|
||||
|
||||
if action.str(vm)?.as_str().eq("ignore") {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let stderr = crate::stdlib::sys::PyStderr(vm);
|
||||
writeln!(stderr, "{}: {}", category.name(), message.as_str(),);
|
||||
writeln!(stderr, "{}: {}", category.name(), text,);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -67,7 +298,7 @@ fn setup_context(
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<
|
||||
// filename, lineno, module, registry
|
||||
(PyStrRef, usize, PyObjectRef, PyObjectRef),
|
||||
(PyStrRef, usize, Option<PyObjectRef>, PyObjectRef),
|
||||
> {
|
||||
let __warningregistry__ = "__warningregistry__";
|
||||
let __name__ = "__name__";
|
||||
@@ -120,5 +351,5 @@ fn setup_context(
|
||||
let module = globals
|
||||
.get_item(__name__, vm)
|
||||
.unwrap_or_else(|_| vm.new_pyobj("<string>"));
|
||||
Ok((filename.to_owned(), lineno, module, registry))
|
||||
Ok((filename.to_owned(), lineno, Some(module), registry))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user