@@ -258,6 +258,36 @@ pub(crate) fn impl_pyclass(
258
258
Ok ( ret)
259
259
}
260
260
261
+ /// Special macro to create exception types.
262
+ ///
263
+ /// Why do we need it and why can't we just use `pyclass` macro instead?
264
+ /// We generate exception types with a `macro_rules`,
265
+ /// similar to how CPython does it.
266
+ /// But, inside `macro_rules` we don't have an opportunity
267
+ /// to add non-literal attributes to `pyclass`.
268
+ /// That's why we have to use this proxy.
269
+ pub ( crate ) fn impl_pyexception (
270
+ attr : AttributeArgs ,
271
+ item : Item ,
272
+ ) -> std:: result:: Result < TokenStream , Diagnostic > {
273
+ let class_name = parse_vec_ident ( & attr, & item, 0 , "first 'class_name'" ) ?;
274
+ let base_class_name = parse_vec_ident ( & attr, & item, 1 , "second 'base_class_name'" ) ?;
275
+
276
+ // We also need to strip `Py` prefix from `class_name`,
277
+ // due to implementation and Python naming conventions mismatch:
278
+ // `PyKeyboardInterrupt` -> `KeyboardInterrupt`
279
+ let class_name = class_name. strip_prefix ( "Py" ) . ok_or_else ( || {
280
+ syn:: Error :: new_spanned ( & item, "We require 'class_name' to have 'Py' prefix" )
281
+ } ) ?;
282
+
283
+ // We just "proxy" it into `pyclass` macro, because, exception is a class.
284
+ let ret = quote ! {
285
+ #[ pyclass( module = false , name = #class_name, base = #base_class_name) ]
286
+ #item
287
+ } ;
288
+ Ok ( ret)
289
+ }
290
+
261
291
/// #[pymethod] and #[pyclassmethod]
262
292
struct MethodItem {
263
293
inner : ContentItemInner ,
@@ -973,3 +1003,24 @@ where
973
1003
}
974
1004
Ok ( ( result, cfgs) )
975
1005
}
1006
+
1007
+ fn parse_vec_ident (
1008
+ attr : & [ NestedMeta ] ,
1009
+ item : & Item ,
1010
+ index : usize ,
1011
+ message : & str ,
1012
+ ) -> std:: result:: Result < String , Diagnostic > {
1013
+ Ok ( attr
1014
+ . get ( index)
1015
+ . ok_or_else ( || {
1016
+ syn:: Error :: new_spanned ( & item, format ! ( "We require {} argument to be set" , & message) )
1017
+ } ) ?
1018
+ . get_ident ( )
1019
+ . ok_or_else ( || {
1020
+ syn:: Error :: new_spanned (
1021
+ & item,
1022
+ format ! ( "We require {} argument to be ident or string" , & message) ,
1023
+ )
1024
+ } ) ?
1025
+ . to_string ( ) )
1026
+ }
0 commit comments