11use 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+
651pub 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) ]
35213fn 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