@@ -228,37 +228,31 @@ def _construct_object(self, schema, label=None, root=False):
228
228
elements = {}
229
229
230
230
# Store the conditional information from the schema in the following form:
231
- # cprop -> (schema, widget)
231
+ # [ (schema, cprop, element), ..]
232
232
# with the following meaning:
233
- # cprop: The property that is maybe added
234
233
# schema: The schema that the data needs to match
235
- # widget: The widget that implements the form
236
- conditionals = {}
234
+ # cprop: The property that is maybe added
235
+ # element: The subelement for the property
236
+ conditionals = []
237
237
for prop , subschema in schema ["properties" ].items ():
238
238
elements [prop ] = self ._construct (subschema , label = prop )
239
239
240
- # Add conditional elements
241
- def add_conditional_elements (s ):
242
- # Check whether we have an if statement
243
- cond = s .get ("if" , None )
244
- if cond is None :
245
- return
240
+ # Add conditional elements
241
+ def add_conditional_elements (s ):
242
+ # Check whether we have an if statement
243
+ cond = s .get ("if" , None )
244
+ if cond is None :
245
+ return
246
246
247
- for cprop , csubschema in (
248
- s .get ("then" , {}).get ("properties" , {}).items ()
249
- ):
250
- celem = self ._construct (csubschema , label = cprop )
251
- cwidgets = celem .widgets [0 ].children
252
- celem .widgets [:] = [
253
- ipywidgets .HBox (layout = ipywidgets .Layout (width = "100%" ))
254
- ]
255
- elements [cprop ] = celem
256
- conditionals [cprop ] = [cond , cwidgets ]
247
+ for cprop , csubschema in s .get ("then" , {}).get ("properties" , {}).items ():
248
+ celem = self ._construct (csubschema , label = cprop )
249
+ conditionals .append ((cond , cprop , celem ))
250
+ elements [cprop ] = celem
257
251
258
- if "else" in s :
259
- add_conditional_elements (s ["else" ])
252
+ if "else" in s :
253
+ add_conditional_elements (s ["else" ])
260
254
261
- add_conditional_elements (schema )
255
+ add_conditional_elements (schema )
262
256
263
257
# Apply sorting to the keys
264
258
keys = schema ["properties" ].keys ()
@@ -271,29 +265,16 @@ def add_conditional_elements(s):
271
265
# Collect the list of widgets: First the regular ones, then conditional ones
272
266
widget_list = sum ((elements [k ].widgets for k in keys ), [])
273
267
widget_list .extend (
274
- sum ((e .widgets for k , e in elements .items () if k not in keys ), [])
268
+ [
269
+ ipywidgets .HBox (layout = ipywidgets .Layout (width = "100%" ))
270
+ for _ in range (len (conditionals ))
271
+ ]
275
272
)
276
- if not root :
277
- widget_list = self ._wrap_accordion (widget_list , schema , label = label )
278
273
279
- # Add the conditional information
280
- for cprop , (cschema , cwidgets ) in conditionals .items ():
281
-
282
- def create_observer (prop , s , w ):
283
- def _cond_observer (_ ):
284
- # Check whether our data matches the given schema
285
- try :
286
- jsonschema .validate (instance = _getter (), schema = s )
287
- elements [prop ].widgets [0 ].children = w
288
- except jsonschema .ValidationError :
289
- elements [prop ].widgets [0 ].children = []
290
-
291
- return _cond_observer
292
-
293
- for k in cschema .get ("properties" , {}).keys ():
294
- elements [k ].register_observer (
295
- create_observer (cprop , cschema , cwidgets ), "value" , "change"
296
- )
274
+ # Maybe wrap this in an Accordion widget
275
+ wrapped_widget_list = widget_list
276
+ if not root :
277
+ wrapped_widget_list = self ._wrap_accordion (widget_list , schema , label = label )
297
278
298
279
def _getter ():
299
280
# Get all regular properties
@@ -302,10 +283,10 @@ def _getter():
302
283
result [k ] = elements [k ].getter ()
303
284
304
285
# Add conditional properties
305
- for cprop , ( cschema , _ ) in conditionals . items () :
286
+ for cschema , cprop , celem in conditionals :
306
287
try :
307
288
jsonschema .validate (instance = result , schema = cschema )
308
- result [cprop ] = elements [ cprop ] .getter ()
289
+ result [cprop ] = celem .getter ()
309
290
except jsonschema .ValidationError :
310
291
pass
311
292
@@ -326,13 +307,39 @@ def _resetter():
326
307
for e in elements .values ():
327
308
e .resetter ()
328
309
310
+ # Add the conditional information
311
+ for i , (cschema , cprop , celem ) in enumerate (conditionals ):
312
+
313
+ def create_observer (j , s , prop , e ):
314
+ def _cond_observer (_ ):
315
+ # Check whether our data matches the given schema
316
+ try :
317
+ jsonschema .validate (instance = _getter (), schema = s )
318
+ elements [prop ] = e
319
+ widget_list [len (keys ) + j ].children = e .widgets
320
+ except jsonschema .ValidationError :
321
+ widget_list [len (keys ) + j ].children = []
322
+
323
+ # We need to call the observer once so that we get a correctly
324
+ # initialized widget, because otherwise it triggers only if it
325
+ # differs from the default.
326
+ _cond_observer ({})
327
+
328
+ return _cond_observer
329
+
330
+ for k in cschema .get ("properties" , {}).keys ():
331
+ elements [k ].register_observer (
332
+ create_observer (i , cschema , cprop , celem ), "value" , "change"
333
+ )
334
+
335
+ # Ensure that defaults are initialized
329
336
_resetter ()
330
337
331
338
return self .construct_element (
332
339
getter = _getter ,
333
340
setter = _setter ,
334
341
resetter = _resetter ,
335
- widgets = widget_list ,
342
+ widgets = wrapped_widget_list ,
336
343
subelements = elements ,
337
344
register_observer = _register_observer ,
338
345
)
@@ -372,7 +379,7 @@ def _construct_simple(self, schema, widget, label=None, root=False):
372
379
# Apply regex pattern matching
373
380
def pattern_checker (val ):
374
381
# This only makes sense for strings
375
- if schema ["type" ] != "string" :
382
+ if schema ["type" ] != "string" or val is None :
376
383
return True
377
384
378
385
# Try matching the given data against the pattern
0 commit comments