1+ use itertools:: Itertools ;
2+
13use 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+
637pub 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) ]
35158fn 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