@@ -6,8 +6,10 @@ use crate::util::{
66use proc_macro2:: TokenStream ;
77use quote:: { quote, quote_spanned, ToTokens } ;
88use std:: collections:: HashMap ;
9+ use syn:: parse:: { Parse , ParseStream , Result as ParsingResult } ;
910use syn:: {
10- parse_quote, spanned:: Spanned , Attribute , AttributeArgs , Ident , Item , Meta , NestedMeta , Result ,
11+ parse_quote, spanned:: Spanned , Attribute , AttributeArgs , Ident , Item , LitStr , Meta , NestedMeta ,
12+ Result , Token ,
1113} ;
1214use syn_ext:: ext:: * ;
1315
@@ -288,6 +290,71 @@ pub(crate) fn impl_pyexception(
288290 Ok ( ret)
289291}
290292
293+ pub ( crate ) fn impl_define_exception (
294+ exc_def : PyExceptionDef ,
295+ ) -> std:: result:: Result < TokenStream , Diagnostic > {
296+ let PyExceptionDef {
297+ class_name,
298+ base_class,
299+ ctx_name,
300+ docs,
301+ tp_new,
302+ init,
303+ } = exc_def;
304+
305+ // We need this method, because of how `CPython` copies `__new__`
306+ // from `BaseException` in `SimpleExtendsException` macro.
307+ // See: `BaseException_new`
308+ let tp_new_slot = match tp_new {
309+ Some ( tp_call) => quote ! { #tp_call( cls, args, vm) } ,
310+ None => quote ! { #base_class:: tp_new( cls, args, vm) } ,
311+ } ;
312+
313+ // We need this method, because of how `CPython` copies `__init__`
314+ // from `BaseException` in `SimpleExtendsException` macro.
315+ // See: `(initproc)BaseException_init`
316+ let init_method = match init {
317+ Some ( init_def) => quote ! { #init_def( zelf, args, vm) } ,
318+ None => quote ! { #base_class:: init( zelf, args, vm) } ,
319+ } ;
320+
321+ let ret = quote ! {
322+ #[ pyexception( #class_name, #base_class) ]
323+ #[ derive( Debug ) ]
324+ #[ doc = #docs]
325+ struct #class_name { }
326+
327+ // We need this to make extend mechanism work:
328+ impl PyValue for #class_name {
329+ fn class( vm: & VirtualMachine ) -> & PyTypeRef {
330+ & vm. ctx. exceptions. #ctx_name
331+ }
332+ }
333+
334+ #[ pyimpl( flags( BASETYPE , HAS_DICT ) ) ]
335+ impl #class_name {
336+ #[ pyslot]
337+ pub ( crate ) fn tp_new(
338+ cls: PyTypeRef ,
339+ args: FuncArgs ,
340+ vm: & VirtualMachine ,
341+ ) -> PyResult {
342+ #tp_new_slot
343+ }
344+
345+ #[ pymethod( magic) ]
346+ pub ( crate ) fn init(
347+ zelf: PyRef <PyBaseException >,
348+ args: FuncArgs ,
349+ vm: & VirtualMachine ,
350+ ) -> PyResult <( ) > {
351+ #init_method
352+ }
353+ }
354+ } ;
355+ Ok ( ret)
356+ }
357+
291358/// #[pymethod] and #[pyclassmethod]
292359struct MethodItem {
293360 inner : ContentItemInner ,
@@ -971,6 +1038,50 @@ where
9711038 Ok ( ( result, cfgs) )
9721039}
9731040
1041+ #[ derive( Debug ) ]
1042+ pub ( crate ) struct PyExceptionDef {
1043+ pub class_name : Ident ,
1044+ pub base_class : Ident ,
1045+ pub ctx_name : Ident ,
1046+ pub docs : LitStr ,
1047+
1048+ /// Holds optional `tp_new` slot to be used instead of a default one:
1049+ pub tp_new : Option < Ident > ,
1050+ /// We also store `__init__` magic method, that can
1051+ pub init : Option < Ident > ,
1052+ }
1053+
1054+ impl Parse for PyExceptionDef {
1055+ fn parse ( input : ParseStream ) -> ParsingResult < Self > {
1056+ let class_name: Ident = input. parse ( ) ?;
1057+ input. parse :: < Token ! [ , ] > ( ) ?;
1058+
1059+ let base_class: Ident = input. parse ( ) ?;
1060+ input. parse :: < Token ! [ , ] > ( ) ?;
1061+
1062+ let ctx_name: Ident = input. parse ( ) ?;
1063+ input. parse :: < Token ! [ , ] > ( ) ?;
1064+
1065+ let docs: LitStr = input. parse ( ) ?;
1066+ input. parse :: < Option < Token ! [ , ] > > ( ) ?;
1067+
1068+ let tp_new: Option < Ident > = input. parse ( ) ?;
1069+ input. parse :: < Option < Token ! [ , ] > > ( ) ?;
1070+
1071+ let init: Option < Ident > = input. parse ( ) ?;
1072+ input. parse :: < Option < Token ! [ , ] > > ( ) ?; // leading `,`
1073+
1074+ Ok ( PyExceptionDef {
1075+ class_name,
1076+ base_class,
1077+ ctx_name,
1078+ docs,
1079+ tp_new,
1080+ init,
1081+ } )
1082+ }
1083+ }
1084+
9741085fn parse_vec_ident (
9751086 attr : & [ NestedMeta ] ,
9761087 item : & Item ,
0 commit comments