Skip to content

Commit 2c611aa

Browse files
authored
Merge pull request RustPython#4146 from oow214/warning_warn_explicit
Update warning warn_explicit
2 parents 2efe22e + ac28407 commit 2c611aa

File tree

4 files changed

+250
-12
lines changed

4 files changed

+250
-12
lines changed

vm/src/builtins/dict.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ impl PyDict {
288288
}
289289

290290
#[pymethod]
291-
fn clear(&self) {
291+
pub fn clear(&self) {
292292
self.entries.clear()
293293
}
294294

vm/src/vm/context.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ declare_const_name! {
213213
keys,
214214
items,
215215
values,
216+
version,
216217
update,
217218
copy,
218219
flush,

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: 241 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,53 @@
11
use crate::{
2-
builtins::{PyDict, PyStrRef, PyType, PyTypeRef},
3-
AsObject, Py, PyObjectRef, PyResult, VirtualMachine,
2+
builtins::{PyDict, PyDictRef, PyListRef, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef},
3+
convert::{IntoObject, TryFromObject},
4+
types::PyComparisonOp,
5+
AsObject, Context, Py, PyObjectRef, PyResult, VirtualMachine,
46
};
57

8+
pub struct WarningsState {
9+
filters: PyListRef,
10+
_once_registry: PyDictRef,
11+
default_action: PyStrRef,
12+
filters_version: usize,
13+
}
14+
15+
impl WarningsState {
16+
fn create_filter(ctx: &Context) -> PyListRef {
17+
ctx.new_list(vec![ctx
18+
.new_tuple(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+
.into()])
26+
}
27+
28+
pub fn init_state(ctx: &Context) -> WarningsState {
29+
WarningsState {
30+
filters: Self::create_filter(ctx),
31+
_once_registry: PyDict::new_ref(ctx),
32+
default_action: ctx.new_str("default"),
33+
filters_version: 0,
34+
}
35+
}
36+
}
37+
38+
fn check_matched(obj: &PyObjectRef, arg: &PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
39+
if obj.class().is(vm.ctx.types.none_type) {
40+
return Ok(true);
41+
}
42+
43+
if obj.rich_compare_bool(arg, PyComparisonOp::Eq, vm)? {
44+
return Ok(false);
45+
}
46+
47+
let result = vm.invoke(obj, (arg.to_owned(),));
48+
Ok(result.is_ok())
49+
}
50+
651
pub fn py_warn(
752
category: &Py<PyType>,
853
message: String,
@@ -31,19 +76,164 @@ pub fn warn(
3176
)
3277
}
3378

79+
fn get_default_action(vm: &VirtualMachine) -> PyResult<PyObjectRef> {
80+
vm.state
81+
.warnings
82+
.default_action
83+
.clone()
84+
.try_into()
85+
.map_err(|_| {
86+
vm.new_value_error(format!(
87+
"_warnings.defaultaction must be a string, not '{}'",
88+
vm.state.warnings.default_action
89+
))
90+
})
91+
}
92+
93+
fn get_filter(
94+
category: PyObjectRef,
95+
text: PyObjectRef,
96+
lineno: usize,
97+
module: PyObjectRef,
98+
mut _item: PyTupleRef,
99+
vm: &VirtualMachine,
100+
) -> PyResult {
101+
let filters = vm.state.warnings.filters.as_object().to_owned();
102+
103+
let filters: PyListRef = filters
104+
.try_into_value(vm)
105+
.map_err(|_| vm.new_value_error("_warnings.filters must be a list".to_string()))?;
106+
107+
/* WarningsState.filters could change while we are iterating over it. */
108+
for i in 0..filters.borrow_vec().len() {
109+
let tmp_item = if let Some(tmp_item) = filters.borrow_vec().get(i).cloned() {
110+
let tmp_item = PyTupleRef::try_from_object(vm, tmp_item)?;
111+
if tmp_item.len() == 5 {
112+
Some(tmp_item)
113+
} else {
114+
None
115+
}
116+
} else {
117+
None
118+
};
119+
let tmp_item = tmp_item.ok_or_else(|| {
120+
vm.new_value_error(format!("_warnings.filters item {} isn't a 5-tuple", i))
121+
})?;
122+
123+
/* Python code: action, msg, cat, mod, ln = item */
124+
let action = if let Some(action) = tmp_item.get(0) {
125+
action.str(vm).map(|action| action.into_object())
126+
} else {
127+
Err(vm.new_type_error("action must be a string".to_string()))
128+
};
129+
130+
let good_msg = if let Some(msg) = tmp_item.get(1) {
131+
check_matched(msg, &text, vm)?
132+
} else {
133+
false
134+
};
135+
136+
let is_subclass = if let Some(cat) = tmp_item.get(2) {
137+
category.fast_isinstance(&cat.class())
138+
} else {
139+
false
140+
};
141+
142+
let good_mod = if let Some(item_mod) = tmp_item.get(3) {
143+
check_matched(item_mod, &module, vm)?
144+
} else {
145+
false
146+
};
147+
148+
let ln = tmp_item.get(4).map_or(0, |ln_obj| {
149+
ln_obj.try_int(vm).map_or(0, |ln| ln.as_u32_mask() as _)
150+
});
151+
152+
if good_msg && good_mod && is_subclass && (ln == 0 || lineno == ln) {
153+
_item = tmp_item;
154+
return action;
155+
}
156+
}
157+
158+
get_default_action(vm)
159+
}
160+
161+
fn already_warned(
162+
registry: PyObjectRef,
163+
key: PyObjectRef,
164+
should_set: bool,
165+
vm: &VirtualMachine,
166+
) -> PyResult<bool> {
167+
let version_obj = registry.get_item(identifier!(&vm.ctx, version), vm).ok();
168+
let filters_version = vm.ctx.new_int(vm.state.warnings.filters_version).into();
169+
170+
match version_obj {
171+
Some(version_obj)
172+
if version_obj.try_int(vm).is_ok() || version_obj.is(&filters_version) =>
173+
{
174+
let already_warned = registry.get_item(key.as_ref(), vm)?;
175+
if already_warned.is_true(vm)? {
176+
return Ok(true);
177+
}
178+
}
179+
_ => {
180+
let registry = registry.dict();
181+
if let Some(registry) = registry.as_ref() {
182+
registry.clear();
183+
let r = registry.set_item("version", filters_version, vm);
184+
if r.is_err() {
185+
return Ok(false);
186+
}
187+
}
188+
}
189+
}
190+
191+
/* This warning wasn't found in the registry, set it. */
192+
if !should_set {
193+
return Ok(false);
194+
}
195+
196+
let item = vm.ctx.true_value.clone().into();
197+
let _ = registry.set_item(key.as_ref(), item, vm); // ignore set error
198+
Ok(true)
199+
}
200+
201+
fn normalize_module(filename: PyStrRef, vm: &VirtualMachine) -> Option<PyObjectRef> {
202+
let obj = match filename.char_len() {
203+
0 => vm.new_pyobj("<unknown>"),
204+
len if len >= 3 && filename.as_str().ends_with(".py") => {
205+
vm.new_pyobj(&filename.as_str()[..len - 3])
206+
}
207+
_ => filename.as_object().to_owned(),
208+
};
209+
Some(obj)
210+
}
211+
34212
#[allow(clippy::too_many_arguments)]
35213
fn warn_explicit(
36214
category: Option<PyTypeRef>,
37215
message: PyStrRef,
38-
_filename: PyStrRef,
39-
_lineno: usize,
40-
_module: PyObjectRef,
41-
_registry: PyObjectRef,
216+
filename: PyStrRef,
217+
lineno: usize,
218+
module: Option<PyObjectRef>,
219+
registry: PyObjectRef,
42220
_source_line: Option<PyObjectRef>,
43221
_source: Option<PyObjectRef>,
44222
vm: &VirtualMachine,
45223
) -> PyResult<()> {
46-
// TODO: Implement correctly
224+
let registry: PyObjectRef = registry
225+
.try_into_value(vm)
226+
.map_err(|_| vm.new_type_error("'registry' must be a dict or None".to_owned()))?;
227+
228+
// Normalize module.
229+
let module = match module.or_else(|| normalize_module(filename, vm)) {
230+
Some(module) => module,
231+
None => return Ok(()),
232+
};
233+
234+
// Normalize message.
235+
let text = message.as_str();
236+
47237
let category = if let Some(category) = category {
48238
if !category.fast_issubclass(vm.ctx.exceptions.warning) {
49239
return Err(vm.new_type_error(format!(
@@ -55,8 +245,49 @@ fn warn_explicit(
55245
} else {
56246
vm.ctx.exceptions.user_warning.to_owned()
57247
};
248+
249+
let category = if message.fast_isinstance(vm.ctx.exceptions.warning) {
250+
message.class().into_owned()
251+
} else {
252+
category
253+
};
254+
255+
// Create key.
256+
let key = PyTuple::new_ref(
257+
vec![
258+
vm.ctx.new_int(3).into(),
259+
vm.ctx.new_str(text).into(),
260+
category.as_object().to_owned(),
261+
vm.ctx.new_int(lineno).into(),
262+
],
263+
&vm.ctx,
264+
);
265+
266+
if !vm.is_none(registry.as_object()) && already_warned(registry, key.into_object(), false, vm)?
267+
{
268+
return Ok(());
269+
}
270+
271+
let item = vm.ctx.new_tuple(vec![]);
272+
let action = get_filter(
273+
category.as_object().to_owned(),
274+
vm.ctx.new_str(text).into(),
275+
lineno,
276+
module,
277+
item,
278+
vm,
279+
)?;
280+
281+
if action.str(vm)?.as_str().eq("error") {
282+
return Err(vm.new_type_error(message.to_string()));
283+
}
284+
285+
if action.str(vm)?.as_str().eq("ignore") {
286+
return Ok(());
287+
}
288+
58289
let stderr = crate::stdlib::sys::PyStderr(vm);
59-
writeln!(stderr, "{}: {}", category.name(), message.as_str(),);
290+
writeln!(stderr, "{}: {}", category.name(), text,);
60291
Ok(())
61292
}
62293

@@ -67,7 +298,7 @@ fn setup_context(
67298
vm: &VirtualMachine,
68299
) -> PyResult<
69300
// filename, lineno, module, registry
70-
(PyStrRef, usize, PyObjectRef, PyObjectRef),
301+
(PyStrRef, usize, Option<PyObjectRef>, PyObjectRef),
71302
> {
72303
let __warningregistry__ = "__warningregistry__";
73304
let __name__ = "__name__";
@@ -120,5 +351,5 @@ fn setup_context(
120351
let module = globals
121352
.get_item(__name__, vm)
122353
.unwrap_or_else(|_| vm.new_pyobj("<string>"));
123-
Ok((filename.to_owned(), lineno, module, registry))
354+
Ok((filename.to_owned(), lineno, Some(module), registry))
124355
}

0 commit comments

Comments
 (0)