Skip to content

Commit eb27d1e

Browse files
mumallaengyouknowone
authored andcommitted
Update warning warn_explicit
1 parent 683e5af commit eb27d1e

File tree

2 files changed

+186
-11
lines changed

2 files changed

+186
-11
lines changed

vm/src/vm/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ use crate::{
3131
import,
3232
protocol::PyIterIter,
3333
scope::Scope,
34-
signal, stdlib, AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
34+
signal, stdlib,
35+
warn::WarningsState,
36+
AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
3537
};
3638
use crossbeam_utils::atomic::AtomicCell;
3739
use std::sync::atomic::AtomicBool;
@@ -88,6 +90,7 @@ pub struct PyGlobalState {
8890
pub atexit_funcs: PyMutex<Vec<(PyObjectRef, FuncArgs)>>,
8991
pub codec_registry: CodecsRegistry,
9092
pub finalizing: AtomicBool,
93+
pub warnings: WarningsState,
9194
}
9295

9396
pub fn process_hash_secret_seed() -> u32 {
@@ -136,6 +139,8 @@ impl VirtualMachine {
136139

137140
let codec_registry = CodecsRegistry::new(&ctx);
138141

142+
let warnings = WarningsState::init_state(&ctx);
143+
139144
let mut vm = VirtualMachine {
140145
builtins,
141146
sys_module,
@@ -161,6 +166,7 @@ impl VirtualMachine {
161166
atexit_funcs: PyMutex::default(),
162167
codec_registry,
163168
finalizing: AtomicBool::new(false),
169+
warnings,
164170
}),
165171
initialized: false,
166172
recursion_depth: Cell::new(0),

vm/src/warn.rs

Lines changed: 179 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,39 @@
1+
use itertools::Itertools;
2+
13
use crate::{
2-
builtins::{PyDict, PyStrRef, PyType, PyTypeRef},
3-
AsObject, Py, PyObjectRef, PyResult, VirtualMachine,
4+
builtins::{PyDict, PyDictRef, PyListRef, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef},
5+
convert::TryFromObject,
6+
AsObject, Context, Py, PyObjectRef, PyResult, VirtualMachine,
47
};
58

9+
pub struct WarningsState {
10+
filters: PyListRef,
11+
_once_registry: PyDictRef,
12+
_default_action: PyStrRef,
13+
filters_version: usize,
14+
}
15+
16+
impl WarningsState {
17+
fn create_filter(ctx: &Context) -> PyListRef {
18+
ctx.new_list(vec![
19+
ctx.new_str("__main__").into(),
20+
ctx.types.none_type.as_object().to_owned(),
21+
ctx.exceptions.warning.as_object().to_owned(),
22+
ctx.new_str("ACTION").into(),
23+
ctx.new_int(0).into(),
24+
])
25+
}
26+
27+
pub fn init_state(ctx: &Context) -> WarningsState {
28+
WarningsState {
29+
filters: Self::create_filter(ctx),
30+
_once_registry: PyDict::new_ref(ctx),
31+
_default_action: ctx.new_str("default"),
32+
filters_version: 0,
33+
}
34+
}
35+
}
36+
637
pub fn py_warn(
738
category: &Py<PyType>,
839
message: String,
@@ -31,19 +62,130 @@ pub fn warn(
3162
)
3263
}
3364

65+
fn get_filter(
66+
_category: PyObjectRef,
67+
_text: PyObjectRef,
68+
_lineno: usize,
69+
_module: PyObjectRef,
70+
mut _item: PyObjectRef,
71+
vm: &VirtualMachine,
72+
) -> PyResult<()> {
73+
let filters = vm.state.warnings.filters.as_object().to_owned();
74+
75+
let filters: PyListRef = filters
76+
.try_into_value(vm)
77+
.map_err(|_| vm.new_value_error("_warnings.filters must be a list".to_string()))?;
78+
79+
/* WarningsState.filters could change while we are iterating over it. */
80+
for i in 0..filters.borrow_vec().len() {
81+
let tmp_item = filters.borrow_vec().get(i).cloned();
82+
let tmp_item = if let Some(tmp_item) = tmp_item {
83+
let tmp_item = PyTupleRef::try_from_object(vm, tmp_item)?;
84+
if tmp_item.len() != 5 {
85+
Err(vm.new_value_error(format!("_warnings.filters item {} isn't a 5-tuple", i)))
86+
} else {
87+
Ok(tmp_item)
88+
}
89+
} else {
90+
Err(vm.new_value_error(format!("_warnings.filters item {} isn't a 5-tuple", i)))
91+
}?;
92+
93+
/* Python code: action, msg, cat, mod, ln = item */
94+
let _action = tmp_item.get(0);
95+
let _msg = tmp_item.get(1);
96+
let _cat = tmp_item.get(2);
97+
let _item_mod = tmp_item.get(3);
98+
let _ln_obj = tmp_item.get(4);
99+
}
100+
101+
Ok(())
102+
}
103+
104+
fn already_warned(
105+
registry: PyObjectRef,
106+
key: PyObjectRef,
107+
should_set: usize,
108+
vm: &VirtualMachine,
109+
) -> bool {
110+
let version_obj = registry.get_item("version", vm).ok();
111+
let filters_version = vm
112+
.ctx
113+
.new_int(vm.state.warnings.filters_version)
114+
.as_object()
115+
.to_owned();
116+
117+
if version_obj.is_none()
118+
|| version_obj.as_ref().unwrap().try_int(vm).is_err()
119+
|| !version_obj.unwrap().is(&filters_version)
120+
{
121+
let registry = registry.dict();
122+
registry.as_ref().map(|registry| {
123+
registry.into_iter().collect_vec().clear();
124+
registry
125+
});
126+
127+
if let Some(registry) = registry {
128+
if registry.set_item("version", filters_version, vm).is_err() {
129+
return false;
130+
}
131+
}
132+
} else {
133+
let already_warned = registry.get_item(key.as_ref(), vm);
134+
return already_warned.is_ok();
135+
}
136+
137+
/* This warning wasn't found in the registry, set it. */
138+
if should_set != 0 {
139+
registry.set_item(key.as_ref(), vm.new_pyobj(""), vm).ok();
140+
}
141+
142+
false
143+
}
144+
145+
fn normalize_module(filename: PyStrRef, vm: &VirtualMachine) -> Option<PyObjectRef> {
146+
let len = filename.char_len();
147+
148+
if len == 0 {
149+
Some(vm.new_pyobj("<unknown>"))
150+
} else if len >= 3 && filename.as_str().contains(".py") {
151+
Some(vm.new_pyobj(filename.as_str().replace(".py", "")))
152+
} else {
153+
Some(filename.as_object().to_owned())
154+
}
155+
}
156+
34157
#[allow(clippy::too_many_arguments)]
35158
fn warn_explicit(
36159
category: Option<PyTypeRef>,
37160
message: PyStrRef,
38-
_filename: PyStrRef,
39-
_lineno: usize,
40-
_module: PyObjectRef,
41-
_registry: PyObjectRef,
161+
filename: PyStrRef,
162+
lineno: usize,
163+
module: Option<PyObjectRef>,
164+
registry: PyObjectRef,
42165
_source_line: Option<PyObjectRef>,
43166
_source: Option<PyObjectRef>,
44167
vm: &VirtualMachine,
45168
) -> PyResult<()> {
46-
// TODO: Implement correctly
169+
if registry.clone().is_true(vm)?
170+
&& !registry.class().is(vm.ctx.types.dict_type)
171+
&& !registry.class().is(vm.ctx.types.none_type)
172+
{
173+
return Err(vm.new_type_error("'registry' must be a dict or None".to_string()));
174+
}
175+
176+
// Normalize module.
177+
let module = module.and(normalize_module(filename, vm));
178+
let module = if let Some(module) = module {
179+
module
180+
} else {
181+
return Ok(());
182+
};
183+
184+
// Normalize message.
185+
let text = message.as_str();
186+
if text.is_empty() {
187+
return Ok(());
188+
}
47189
let category = if let Some(category) = category {
48190
if !category.fast_issubclass(vm.ctx.exceptions.warning) {
49191
return Err(vm.new_type_error(format!(
@@ -55,8 +197,35 @@ fn warn_explicit(
55197
} else {
56198
vm.ctx.exceptions.user_warning.to_owned()
57199
};
200+
201+
// Create key.
202+
let key = PyTuple::new_ref(
203+
vec![
204+
vm.ctx.new_int(3).into(),
205+
vm.ctx.new_str(text).into(),
206+
category.as_object().to_owned(),
207+
vm.ctx.new_int(lineno).into(),
208+
],
209+
&vm.ctx,
210+
);
211+
212+
if already_warned(registry, key.as_object().to_owned(), 0, vm) {
213+
return Ok(());
214+
}
215+
// Else this warning hasn't been generated before.
216+
217+
let item = vm.ctx.new_tuple(vec![]).into();
218+
let _action = get_filter(
219+
category.as_object().to_owned(),
220+
vm.ctx.new_str(text).into(),
221+
lineno,
222+
module,
223+
item,
224+
vm,
225+
);
226+
58227
let stderr = crate::stdlib::sys::PyStderr(vm);
59-
writeln!(stderr, "{}: {}", category.name(), message.as_str(),);
228+
writeln!(stderr, "{}: {}", category.name(), text,);
60229
Ok(())
61230
}
62231

@@ -67,7 +236,7 @@ fn setup_context(
67236
vm: &VirtualMachine,
68237
) -> PyResult<
69238
// filename, lineno, module, registry
70-
(PyStrRef, usize, PyObjectRef, PyObjectRef),
239+
(PyStrRef, usize, Option<PyObjectRef>, PyObjectRef),
71240
> {
72241
let __warningregistry__ = "__warningregistry__";
73242
let __name__ = "__name__";
@@ -120,5 +289,5 @@ fn setup_context(
120289
let module = globals
121290
.get_item(__name__, vm)
122291
.unwrap_or_else(|_| vm.new_pyobj("<string>"));
123-
Ok((filename.to_owned(), lineno, module, registry))
292+
Ok((filename.to_owned(), lineno, Some(module), registry))
124293
}

0 commit comments

Comments
 (0)