@@ -80,9 +80,12 @@ class BaseConverter:
8080 def _naucse__converter (self ):
8181 return self
8282
83- def load (self , data , ** kwargs ):
83+ def load (self , data , context , ** kwargs ):
8484 """Convert a JSON-compatible data to a Python value.
8585
86+ `context` is a `LoadContext`, which holds options (like the API
87+ version) for loading an entire tree of objects.
88+
8689 `kwargs` are extra keyword arguments passed to `__init__`.
8790 The Converter's `load_arg_names` attribute specifies which kwargs
8891 are supported.
@@ -91,9 +94,12 @@ def load(self, data, **kwargs):
9194 """
9295 return data
9396
94- def dump (self , value ):
97+ def dump (self , value , context ):
9598 """Convert a Python value to JSON-compatible data.
9699
100+ `context` is a `DumpContext`, which holds options (like the API
101+ version) for dumping an entire tree of objects.
102+
97103 The base implementation returns `value` unchanged.
98104 """
99105 return value
@@ -125,15 +131,15 @@ def get_schema(self, context):
125131
126132
127133class IntegerConverter (BaseConverter ):
128- def load (self , data ):
134+ def load (self , data , context ):
129135 return int (data )
130136
131137 def get_schema (self , context ):
132138 return {'type' : 'integer' }
133139
134140
135141class FloatConverter (BaseConverter ):
136- def load (self , data ):
142+ def load (self , data , context ):
137143 return float (data )
138144
139145 def get_schema (self , context ):
@@ -173,16 +179,16 @@ def __init__(self, item_converter, *, index_arg=None):
173179 self .load_arg_names = self .item_converter .load_arg_names
174180 self .index_arg = index_arg
175181
176- def load (self , data , ** kwargs ):
182+ def load (self , data , context , ** kwargs ):
177183 result = []
178184 for index , d in enumerate (data ):
179185 if self .index_arg :
180186 kwargs [self .index_arg ] = index
181- result .append (self .item_converter .load (d , ** kwargs ))
187+ result .append (self .item_converter .load (d , context , ** kwargs ))
182188 return result
183189
184- def dump (self , value ):
185- return [self .item_converter .dump (v ) for v in value ]
190+ def dump (self , value , context ):
191+ return [self .item_converter .dump (v , context ) for v in value ]
186192
187193 def get_schema (self , context ):
188194 return {
@@ -207,16 +213,19 @@ def __init__(self, item_converter, *, key_arg=None, required=()):
207213 self .key_arg = key_arg
208214 self .required = required
209215
210- def load (self , data , ** kwargs ):
216+ def load (self , data , context , ** kwargs ):
211217 result = {}
212218 for k , v in data .items ():
213219 if self .key_arg :
214220 kwargs [self .key_arg ] = k
215- result [k ] = self .item_converter .load (v , ** kwargs )
221+ result [k ] = self .item_converter .load (v , context , ** kwargs )
216222 return result
217223
218- def dump (self , value ):
219- return {str (k ): self .item_converter .dump (v ) for k , v in value .items ()}
224+ def dump (self , value , context ):
225+ return {
226+ str (k ): self .item_converter .dump (v , context )
227+ for k , v in value .items ()
228+ }
220229
221230 def get_schema (self , context ):
222231 schema = {
@@ -243,17 +252,17 @@ def __init__(self, item_converter, *, key_attr, index_arg=None):
243252 self .index_arg = index_arg
244253 self .load_arg_names = set (self .item_converter .load_arg_names )
245254
246- def load (self , data , ** kwargs ):
255+ def load (self , data , context , ** kwargs ):
247256 result = {}
248257 for index , value in enumerate (data ):
249258 if self .index_arg :
250259 kwargs [self .index_arg ] = index
251- item = self .item_converter .load (value , ** kwargs )
260+ item = self .item_converter .load (value , context , ** kwargs )
252261 result [getattr (item , self .key_attr )] = item
253262 return result
254263
255- def dump (self , value ):
256- return [self .item_converter .dump (v ) for k , v in value .items ()]
264+ def dump (self , value , context ):
265+ return [self .item_converter .dump (v , context ) for k , v in value .items ()]
257266
258267 def get_schema (self , context ):
259268 return {
@@ -327,7 +336,7 @@ def __set_name__(self, cls, name):
327336 self .name = name
328337 self .data_key = self .data_key or self .name
329338
330- def load_into (self , instance , data , ** kwargs ):
339+ def load_into (self , instance , data , context , ** kwargs ):
331340 """Load this field's data into the given Python object.
332341
333342 `instance` is the Python object being initialized.
@@ -357,10 +366,10 @@ def load_into(self, instance, data, **kwargs):
357366 n : v for n , v in kwargs .items ()
358367 if n in self .converter .load_arg_names
359368 }
360- value = self .converter .load (item_data , ** kwargs )
369+ value = self .converter .load (item_data , context , ** kwargs )
361370 setattr (instance , self .name , value )
362371 for func in self ._after_load_hooks :
363- func (instance )
372+ func (instance , context )
364373
365374 def _get_default (self , instance ):
366375 """Return the default value (for optional fields).
@@ -369,7 +378,7 @@ def _get_default(self, instance):
369378 """
370379 return None
371380
372- def dump_into (self , instance , data ):
381+ def dump_into (self , instance , data , context ):
373382 """Dump the given Python object into the given JSON-compatible dict
374383
375384 If the field is not marked `output`, or is optional and has the default
@@ -380,7 +389,7 @@ def dump_into(self, instance, data):
380389 value = getattr (instance , self .name )
381390 if self .optional and value == self .default :
382391 return
383- data [self .data_key ] = self .converter .dump (value )
392+ data [self .data_key ] = self .converter .dump (value , context )
384393
385394 def put_schema_into (self , object_schema , context ):
386395 if context .is_input and not self .input :
@@ -462,16 +471,16 @@ def __init__(
462471 def __repr__ (self ):
463472 return f'<{ _classname (type (self ))} for { _classname (self .cls )} >'
464473
465- def load (self , data , ** kwargs ):
474+ def load (self , data , context , ** kwargs ):
466475 result = self .cls (** kwargs )
467476 for field in self .fields .values ():
468- field .load_into (result , data , parent = result )
477+ field .load_into (result , data , context , parent = result )
469478 return result
470479
471- def dump (self , value ):
480+ def dump (self , value , context ):
472481 result = {}
473482 for field in self .fields .values ():
474- field .dump_into (value , result )
483+ field .dump_into (value , result , context )
475484 return result
476485
477486 def get_schema (self , context ):
@@ -487,16 +496,37 @@ def get_schema(self, context):
487496 return schema
488497
489498
499+ class LoadContext :
500+ """Holds "global" options for loading data
501+
502+ `version` is the API version, as a tuple of ints (major, minor).
503+ """
504+ def __init__ (self , version ):
505+ self .version = version
506+
507+
508+ class DumpContext :
509+ """Holds "global" options for dumping data
510+
511+ `version` is the API version, as a tuple of ints (major, minor).
512+ """
513+ def __init__ (self , version ):
514+ self .version = version
515+
516+
490517class SchemaContext :
491518 """Holds "global" definitions and options for getting a context
492519
493520 `is_input` determines whether schema for input (data from forks) or output
494521 (naucse's exported API).
522+
523+ `version` is the API version, as a tuple of ints (major, minor).
495524 """
496- def __init__ (self , * , is_input ):
525+ def __init__ (self , * , is_input , version ):
497526 self .definition_refs = {}
498527 self .definitions = {}
499528 self .is_input = is_input
529+ self .version = version
500530
501531 def get_schema (self , converter ):
502532 """Get schema for the given converter
@@ -516,10 +546,10 @@ def get_schema(self, converter):
516546 return converter .get_schema (self )
517547
518548
519- def get_schema (converter , * , is_input ):
549+ def get_schema (converter , * , is_input , version ):
520550 """Get schema for the given converter"""
521551 converter = get_converter (converter )
522- context = SchemaContext (is_input = is_input )
552+ context = SchemaContext (is_input = is_input , version = version )
523553 context .definitions .update ({
524554 'ref' : {
525555 'type' : 'object' ,
@@ -575,7 +605,7 @@ def _get_schema_url(converter, instance):
575605 raise ValueError (f"{ converter } .get_schema_url is None" )
576606
577607
578- def dump (instance , converter = None ):
608+ def dump (instance , converter = None , * , version ):
579609 """Dump a Python object
580610
581611 If converter is None, the default is used.
@@ -584,23 +614,26 @@ def dump(instance, converter=None):
584614 converter = get_converter (instance )
585615 converter = get_converter (converter )
586616 slug = converter .slug or 'data'
617+ context = DumpContext (version = version )
587618 result = {
588- 'api_version' : [ 0 , 0 ] ,
589- slug : converter .dump (instance ),
619+ 'api_version' : context . version ,
620+ slug : converter .dump (instance , context ),
590621 }
591622 result ['$schema' ] = _get_schema_url (converter , instance )
592- schema = get_schema (converter , is_input = False )
623+ schema = get_schema (converter , is_input = False , version = context . version )
593624 jsonschema .validate (result , schema )
594625 return result
595626
596627
597628def load (converter , data , ** kwargs ):
598629 """Load a Python object from the given data"""
630+ version = data ['api_version' ]
599631 converter = get_converter (converter )
600- schema = get_schema (converter , is_input = True )
632+ context = LoadContext (version = version )
633+ schema = get_schema (converter , is_input = True , version = context .version )
601634 jsonschema .validate (data , schema )
602635 slug = converter .slug or 'data'
603- return converter .load (data [slug ], ** kwargs )
636+ return converter .load (data [slug ], context , ** kwargs )
604637
605638
606639def register_model (cls , converter = None ):
0 commit comments