11use crate :: tools:: py_err;
22use pyo3:: exceptions:: { PyException , PyKeyError } ;
33use pyo3:: prelude:: * ;
4- use pyo3:: types:: { PyDict , PyList , PySet , PyString , PyTuple } ;
4+ use pyo3:: types:: { PyDict , PyList , PyNone , PySet , PyString , PyTuple } ;
55use pyo3:: { create_exception, intern, Bound , PyResult } ;
6- use std:: collections:: HashSet ;
76
87create_exception ! ( pydantic_core. _pydantic_core, GatherInvalidDefinitionError , PyException ) ;
98
9+ macro_rules! none {
10+ ( $py: expr) => {
11+ PyNone :: get_bound( $py)
12+ } ;
13+ }
14+
1015macro_rules! get {
1116 ( $dict: expr, $key: expr) => {
1217 $dict. get_item( intern!( $dict. py( ) , $key) ) ?
@@ -47,28 +52,32 @@ fn gather_definition_ref(schema_ref_dict: &Bound<'_, PyDict>, ctx: &mut GatherCt
4752 return py_err ! ( PyKeyError ; "Invalid definition-ref, missing schema_ref" ) ;
4853 } ;
4954 let schema_ref = schema_ref. downcast_exact :: < PyString > ( ) ?;
55+ let py = schema_ref_dict. py ( ) ;
5056
5157 if !ctx. recursively_seen_refs . contains ( schema_ref) ? {
52- // Check if we already gathered this definition ref instance .
53- // No need to again process the same def ref instance if we already did it .
54- if ctx. seen_ref_instances . insert ( schema_ref_dict . as_ptr ( ) as isize ) {
58+ // Def ref in no longer consider as inlinable if its re-encountered. Then its used multiple times .
59+ // No need to retraverse it either if we already encountered this .
60+ if ! ctx. inline_def_ref_candidates . contains ( schema_ref ) ? {
5561 let Some ( definition) = ctx. definitions . get_item ( schema_ref) ? else {
5662 return py_err ! ( GatherInvalidDefinitionError ; "{}" , schema_ref. to_str( ) ?) ;
5763 } ;
5864
59- defaultdict_list_append ! ( & ctx. def_refs, schema_ref, schema_ref_dict) ;
60-
65+ ctx. inline_def_ref_candidates . set_item ( schema_ref, schema_ref_dict) ?;
6166 ctx. recursively_seen_refs . add ( schema_ref) ?;
6267
6368 gather_schema ( definition. downcast_exact ( ) ?, ctx) ?;
6469 traverse_key_fn ! ( "serialization" , gather_schema, schema_ref_dict, ctx) ;
6570 gather_meta ( schema_ref_dict, ctx) ?;
6671
6772 ctx. recursively_seen_refs . discard ( schema_ref) ?;
73+ } else {
74+ ctx. inline_def_ref_candidates . set_item ( schema_ref, none ! ( py) ) ?; // Mark not inlinable (used multiple times)
6875 }
6976 } else {
77+ ctx. inline_def_ref_candidates . set_item ( schema_ref, none ! ( py) ) ?; // Mark not inlinable (used in recursion)
7078 ctx. recursive_def_refs . add ( schema_ref) ?;
7179 for seen_ref in ctx. recursively_seen_refs . iter ( ) {
80+ ctx. inline_def_ref_candidates . set_item ( & seen_ref, none ! ( py) ) ?; // Mark not inlinable (used in recursion)
7281 ctx. recursive_def_refs . add ( seen_ref) ?;
7382 }
7483 }
@@ -160,10 +169,9 @@ fn gather_schema(schema: &Bound<'_, PyDict>, ctx: &mut GatherCtx) -> PyResult<()
160169struct GatherCtx < ' a , ' py > {
161170 definitions : & ' a Bound < ' py , PyDict > ,
162171 meta_with_keys : Option < ( Bound < ' py , PyDict > , & ' a Bound < ' py , PySet > ) > ,
163- def_refs : Bound < ' py , PyDict > ,
172+ inline_def_ref_candidates : Bound < ' py , PyDict > ,
164173 recursive_def_refs : Bound < ' py , PySet > ,
165174 recursively_seen_refs : Bound < ' py , PySet > ,
166- seen_ref_instances : HashSet < isize > ,
167175}
168176
169177#[ pyfunction( signature = ( schema, definitions, find_meta_with_keys) ) ]
@@ -179,15 +187,21 @@ pub fn gather_schemas_for_cleaning<'py>(
179187 true => None ,
180188 false => Some ( ( PyDict :: new_bound ( py) , find_meta_with_keys. downcast_exact :: < PySet > ( ) ?) ) ,
181189 } ,
182- def_refs : PyDict :: new_bound ( py) ,
190+ inline_def_ref_candidates : PyDict :: new_bound ( py) ,
183191 recursive_def_refs : PySet :: empty_bound ( py) ?,
184192 recursively_seen_refs : PySet :: empty_bound ( py) ?,
185- seen_ref_instances : HashSet :: new ( ) ,
186193 } ;
187194 gather_schema ( schema. downcast_exact ( ) ?, & mut ctx) ?;
188195
196+ let inlinable_def_refs = PyDict :: new_bound ( py) ;
197+ for ( ref_str, def_ref_candidate) in ctx. inline_def_ref_candidates . iter ( ) {
198+ if !def_ref_candidate. is_none ( ) {
199+ inlinable_def_refs. set_item ( ref_str, def_ref_candidate) ?;
200+ }
201+ }
202+
189203 let res = PyDict :: new_bound ( py) ;
190- res. set_item ( intern ! ( py, "definition_refs " ) , ctx . def_refs ) ?;
204+ res. set_item ( intern ! ( py, "inlinable_def_refs " ) , inlinable_def_refs ) ?;
191205 res. set_item ( intern ! ( py, "recursive_refs" ) , ctx. recursive_def_refs ) ?;
192206 res. set_item ( intern ! ( py, "schemas_with_meta_keys" ) , ctx. meta_with_keys . map ( |v| v. 0 ) ) ?;
193207 Ok ( res)
0 commit comments