6
6
use std:: {
7
7
collections:: hash_map:: Entry ,
8
8
fmt:: Debug ,
9
- sync:: { Arc , OnceLock } ,
9
+ sync:: {
10
+ atomic:: { AtomicBool , Ordering } ,
11
+ Arc , OnceLock ,
12
+ } ,
10
13
} ;
11
14
12
15
use pyo3:: { prelude:: * , PyTraverseError , PyVisit } ;
@@ -35,14 +38,19 @@ impl<T> Definitions<T> {
35
38
}
36
39
37
40
/// Internal type which contains a definition to be filled
38
- pub struct Definition < T > ( Arc < OnceLock < T > > ) ;
41
+ pub struct Definition < T > ( Arc < DefinitionInner < T > > ) ;
39
42
40
43
impl < T > Definition < T > {
41
44
pub fn get ( & self ) -> Option < & T > {
42
- self . 0 . get ( )
45
+ self . 0 . value . get ( )
43
46
}
44
47
}
45
48
49
+ struct DefinitionInner < T > {
50
+ value : OnceLock < T > ,
51
+ name : LazyName ,
52
+ }
53
+
46
54
/// Reference to a definition.
47
55
pub struct DefinitionRef < T > {
48
56
name : Arc < String > ,
@@ -64,12 +72,15 @@ impl<T> DefinitionRef<T> {
64
72
Arc :: as_ptr ( & self . value . 0 ) as usize
65
73
}
66
74
67
- pub fn name ( & self ) -> & str {
68
- & self . name
75
+ pub fn get_or_init_name ( & self , init : impl FnOnce ( & T ) -> String ) -> & str {
76
+ match self . value . 0 . value . get ( ) {
77
+ Some ( value) => self . value . 0 . name . get_or_init ( || init ( value) ) ,
78
+ None => "..." ,
79
+ }
69
80
}
70
81
71
82
pub fn get ( & self ) -> Option < & T > {
72
- self . value . 0 . get ( )
83
+ self . value . 0 . value . get ( )
73
84
}
74
85
}
75
86
@@ -83,7 +94,17 @@ impl<T: Debug> Debug for DefinitionRef<T> {
83
94
84
95
impl < T : Debug > Debug for Definitions < T > {
85
96
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
86
- self . 0 . fmt ( f)
97
+ // Formatted as a list for backwards compatibility; in principle
98
+ // this could be formatted as a map. Maybe change in a future
99
+ // minor release of pydantic.
100
+ write ! [ f, "[" ] ?;
101
+ let mut first = true ;
102
+ for def in self . 0 . values ( ) {
103
+ write ! [ f, "{sep}{def:?}" , sep = if first { "" } else { ", " } ] ?;
104
+ first = false ;
105
+ }
106
+ write ! [ f, "]" ] ?;
107
+ Ok ( ( ) )
87
108
}
88
109
}
89
110
@@ -95,7 +116,7 @@ impl<T> Clone for Definition<T> {
95
116
96
117
impl < T : Debug > Debug for Definition < T > {
97
118
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
98
- match self . 0 . get ( ) {
119
+ match self . 0 . value . get ( ) {
99
120
Some ( value) => value. fmt ( f) ,
100
121
None => "..." . fmt ( f) ,
101
122
}
@@ -104,7 +125,7 @@ impl<T: Debug> Debug for Definition<T> {
104
125
105
126
impl < T : PyGcTraverse > PyGcTraverse for DefinitionRef < T > {
106
127
fn py_gc_traverse ( & self , visit : & PyVisit < ' _ > ) -> Result < ( ) , PyTraverseError > {
107
- if let Some ( value) = self . value . 0 . get ( ) {
128
+ if let Some ( value) = self . value . 0 . value . get ( ) {
108
129
value. py_gc_traverse ( visit) ?;
109
130
}
110
131
Ok ( ( ) )
@@ -114,7 +135,7 @@ impl<T: PyGcTraverse> PyGcTraverse for DefinitionRef<T> {
114
135
impl < T : PyGcTraverse > PyGcTraverse for Definitions < T > {
115
136
fn py_gc_traverse ( & self , visit : & PyVisit < ' _ > ) -> Result < ( ) , PyTraverseError > {
116
137
for value in self . 0 . values ( ) {
117
- if let Some ( value) = value. 0 . get ( ) {
138
+ if let Some ( value) = value. 0 . value . get ( ) {
118
139
value. py_gc_traverse ( visit) ?;
119
140
}
120
141
}
@@ -142,7 +163,10 @@ impl<T: std::fmt::Debug> DefinitionsBuilder<T> {
142
163
let name = Arc :: new ( reference. to_string ( ) ) ;
143
164
let value = match self . definitions . 0 . entry ( name. clone ( ) ) {
144
165
Entry :: Occupied ( entry) => entry. into_mut ( ) ,
145
- Entry :: Vacant ( entry) => entry. insert ( Definition ( Arc :: new ( OnceLock :: new ( ) ) ) ) ,
166
+ Entry :: Vacant ( entry) => entry. insert ( Definition ( Arc :: new ( DefinitionInner {
167
+ value : OnceLock :: new ( ) ,
168
+ name : LazyName :: new ( ) ,
169
+ } ) ) ) ,
146
170
} ;
147
171
DefinitionRef {
148
172
name,
@@ -156,23 +180,75 @@ impl<T: std::fmt::Debug> DefinitionsBuilder<T> {
156
180
let value = match self . definitions . 0 . entry ( name. clone ( ) ) {
157
181
Entry :: Occupied ( entry) => {
158
182
let definition = entry. into_mut ( ) ;
159
- match definition. 0 . set ( value) {
183
+ match definition. 0 . value . set ( value) {
160
184
Ok ( ( ) ) => definition. clone ( ) ,
161
185
Err ( _) => return py_schema_err ! ( "Duplicate ref: `{}`" , name) ,
162
186
}
163
187
}
164
- Entry :: Vacant ( entry) => entry. insert ( Definition ( Arc :: new ( OnceLock :: from ( value) ) ) ) . clone ( ) ,
188
+ Entry :: Vacant ( entry) => entry
189
+ . insert ( Definition ( Arc :: new ( DefinitionInner {
190
+ value : OnceLock :: from ( value) ,
191
+ name : LazyName :: new ( ) ,
192
+ } ) ) )
193
+ . clone ( ) ,
165
194
} ;
166
195
Ok ( DefinitionRef { name, value } )
167
196
}
168
197
169
198
/// Consume this Definitions into a vector of items, indexed by each items ReferenceId
170
199
pub fn finish ( self ) -> PyResult < Definitions < T > > {
171
200
for ( reference, def) in & self . definitions . 0 {
172
- if def. 0 . get ( ) . is_none ( ) {
201
+ if def. 0 . value . get ( ) . is_none ( ) {
173
202
return py_schema_err ! ( "Definitions error: definition `{}` was never filled" , reference) ;
174
203
}
175
204
}
176
205
Ok ( self . definitions )
177
206
}
178
207
}
208
+
209
+ struct LazyName {
210
+ initialized : OnceLock < String > ,
211
+ in_recursion : AtomicBool ,
212
+ }
213
+
214
+ impl LazyName {
215
+ fn new ( ) -> Self {
216
+ Self {
217
+ initialized : OnceLock :: new ( ) ,
218
+ in_recursion : AtomicBool :: new ( false ) ,
219
+ }
220
+ }
221
+
222
+ /// Gets the validator name, returning the default in the case of recursion loops
223
+ fn get_or_init ( & self , init : impl FnOnce ( ) -> String ) -> & str {
224
+ if let Some ( s) = self . initialized . get ( ) {
225
+ return s. as_str ( ) ;
226
+ }
227
+
228
+ if self
229
+ . in_recursion
230
+ . compare_exchange ( false , true , Ordering :: SeqCst , Ordering :: SeqCst )
231
+ . is_err ( )
232
+ {
233
+ return "..." ;
234
+ }
235
+ let result = self . initialized . get_or_init ( init) . as_str ( ) ;
236
+ self . in_recursion . store ( false , Ordering :: SeqCst ) ;
237
+ result
238
+ }
239
+ }
240
+
241
+ impl Debug for LazyName {
242
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
243
+ self . initialized . get ( ) . map_or ( "..." , String :: as_str) . fmt ( f)
244
+ }
245
+ }
246
+
247
+ impl Clone for LazyName {
248
+ fn clone ( & self ) -> Self {
249
+ Self {
250
+ initialized : OnceLock :: new ( ) ,
251
+ in_recursion : AtomicBool :: new ( false ) ,
252
+ }
253
+ }
254
+ }
0 commit comments