1pub(crate) use _warnings::module_def;
2
3use crate::{Py, PyResult, VirtualMachine, builtins::PyType};
4
5pub fn warn(
6 category: &Py<PyType>,
7 message: String,
8 stack_level: usize,
9 vm: &VirtualMachine,
10) -> PyResult<()> {
11 crate::warn::warn(
12 vm.new_pyobj(message),
13 Some(category.to_owned()),
14 isize::try_from(stack_level).unwrap_or(isize::MAX),
15 None,
16 vm,
17 )
18}
19
20#[pymodule]
21mod _warnings {
22 use crate::{
23 AsObject, PyObject, PyObjectRef, PyResult, VirtualMachine,
24 builtins::{PyDictRef, PyListRef, PyStrRef, PyTupleRef, PyTypeRef},
25 convert::TryFromObject,
26 function::OptionalArg,
27 };
28
29 #[pyattr]
30 fn filters(vm: &VirtualMachine) -> PyListRef {
31 vm.state.warnings.filters.to_owned()
32 }
33
34 #[pyattr]
35 fn _defaultaction(vm: &VirtualMachine) -> PyStrRef {
36 vm.state.warnings.default_action.to_owned()
37 }
38
39 #[pyattr]
40 fn _onceregistry(vm: &VirtualMachine) -> PyDictRef {
41 vm.state.warnings.once_registry.to_owned()
42 }
43
44 #[pyattr]
45 fn _warnings_context(vm: &VirtualMachine) -> PyObjectRef {
46 if let Some(ctx) = vm.state.warnings.context_var.get() {
47 return ctx.clone();
48 }
49 let created = vm
52 .import("_contextvars", 0)
53 .ok()
54 .and_then(|m| m.get_attr("ContextVar", vm).ok())
55 .and_then(|cv_cls| cv_cls.call(("_warnings_context",), vm).ok());
56 match created {
57 Some(cv) => match vm.state.warnings.context_var.set(cv.clone()) {
58 Ok(()) => cv,
59 Err(_) => vm.state.warnings.context_var.get().cloned().unwrap_or(cv),
60 },
61 None => vm.ctx.none(),
62 }
63 }
64
65 #[pyfunction]
66 fn _acquire_lock(vm: &VirtualMachine) {
67 vm.state.warnings.acquire_lock();
68 }
69
70 #[pyfunction]
71 fn _release_lock(vm: &VirtualMachine) -> PyResult<()> {
72 if !vm.state.warnings.release_lock() {
73 return Err(vm.new_runtime_error("cannot release un-acquired lock"));
74 }
75 Ok(())
76 }
77
78 #[pyfunction]
79 fn _filters_mutated_lock_held(vm: &VirtualMachine) {
80 vm.state.warnings.filters_mutated();
81 }
82
83 #[derive(FromArgs)]
84 struct WarnArgs {
85 #[pyarg(any)]
86 message: PyObjectRef,
87 #[pyarg(any, optional)]
88 category: OptionalArg<PyObjectRef>,
89 #[pyarg(any, default = 1)]
90 stacklevel: i32,
91 #[pyarg(any, optional)]
92 source: OptionalArg<PyObjectRef>,
93 #[pyarg(named, optional)]
94 skip_file_prefixes: OptionalArg<PyTupleRef>,
95 }
96
97 fn get_category(
99 message: &PyObject,
100 category: Option<PyObjectRef>,
101 vm: &VirtualMachine,
102 ) -> PyResult<Option<PyTypeRef>> {
103 let cat_obj = match category {
104 Some(c) if !vm.is_none(&c) => c,
105 _ => {
106 return Ok(if message.fast_isinstance(vm.ctx.exceptions.warning) {
107 Some(message.class().to_owned())
108 } else {
109 None });
111 }
112 };
113
114 let cat = PyTypeRef::try_from_object(vm, cat_obj.clone()).map_err(|_| {
115 vm.new_type_error(format!(
116 "category must be a Warning subclass, not '{}'",
117 cat_obj.class().name()
118 ))
119 })?;
120
121 if !cat.fast_issubclass(vm.ctx.exceptions.warning) {
122 return Err(vm.new_type_error(format!(
123 "category must be a Warning subclass, not '{}'",
124 cat.class().name()
125 )));
126 }
127
128 Ok(Some(cat))
129 }
130
131 #[pyfunction]
132 fn warn(args: WarnArgs, vm: &VirtualMachine) -> PyResult<()> {
133 let level = args.stacklevel as isize;
134
135 let category = get_category(&args.message, args.category.into_option(), vm)?;
136
137 let skip_prefixes = args.skip_file_prefixes.into_option();
139 if let Some(ref prefixes) = skip_prefixes {
140 for item in prefixes.as_slice() {
141 if !item.class().is(vm.ctx.types.str_type) {
142 return Err(vm.new_type_error("skip_file_prefixes must be a tuple of strs"));
143 }
144 }
145 }
146
147 crate::warn::warn_with_skip(
148 args.message,
149 category,
150 level,
151 args.source.into_option(),
152 skip_prefixes.as_deref(),
153 vm,
154 )
155 }
156
157 #[derive(FromArgs)]
158 struct WarnExplicitArgs {
159 #[pyarg(any)]
160 message: PyObjectRef,
161 #[pyarg(any)]
162 category: PyObjectRef,
163 #[pyarg(any)]
164 filename: PyStrRef,
165 #[pyarg(any)]
166 lineno: usize,
167 #[pyarg(any, optional)]
168 module: OptionalArg<PyObjectRef>,
169 #[pyarg(any, optional)]
170 registry: OptionalArg<PyObjectRef>,
171 #[pyarg(any, optional)]
172 module_globals: OptionalArg<PyObjectRef>,
173 #[pyarg(any, optional)]
174 source: OptionalArg<PyObjectRef>,
175 }
176
177 #[pyfunction]
178 fn warn_explicit(args: WarnExplicitArgs, vm: &VirtualMachine) -> PyResult<()> {
179 let registry = args.registry.into_option().unwrap_or_else(|| vm.ctx.none());
180
181 let module = args.module.into_option();
182
183 let source_line = if let Some(mg) = args.module_globals.into_option() {
184 if vm.is_none(&mg) {
185 None
186 } else if !mg.class().is(vm.ctx.types.dict_type) {
187 return Err(vm.new_type_error(format!(
188 "module_globals must be a dict, not '{}'",
189 mg.class().name()
190 )));
191 } else {
192 crate::warn::get_source_line(&mg, args.lineno, vm)?
193 }
194 } else {
195 None
196 };
197
198 let category = if vm.is_none(&args.category) {
199 None
200 } else {
201 Some(
202 PyTypeRef::try_from_object(vm, args.category)
203 .map_err(|_| vm.new_type_error("category must be a Warning subclass"))?,
204 )
205 };
206
207 crate::warn::warn_explicit(
208 category,
209 args.message,
210 args.filename,
211 args.lineno,
212 module,
213 registry,
214 source_line,
215 args.source.into_option(),
216 vm,
217 )
218 }
219}