typedpy 2.2.2__tar.gz → 2.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ from typedpy.structures import (
10
10
  Structure,
11
11
  _get_all_fields_by_name,
12
12
  ADDITIONAL_PROPERTIES,
13
- REQUIRED_FIELDS,
13
+ REQUIRED_FIELDS, IGNORE_NONE_VALUES,
14
14
  )
15
15
  from typedpy.fields import (
16
16
  Field,
@@ -39,7 +39,8 @@ from typedpy.fields import (
39
39
 
40
40
 
41
41
  def deserialize_list_like(
42
- field, content_type, value, name, *, keep_undefined=True, mapper=None
42
+ field, content_type, value, name, *, keep_undefined=True, mapper=None,
43
+ camel_case_convert=False
43
44
  ):
44
45
  if not isinstance(value, (list, tuple, set)):
45
46
  raise ValueError("{}: must be list, set, or tuple; got {}".format(name, value))
@@ -47,11 +48,14 @@ def deserialize_list_like(
47
48
  values = []
48
49
  items = field.items
49
50
  if isinstance(items, Field):
51
+ ignore_none = getattr(items, IGNORE_NONE_VALUES, False)
50
52
  for i, v in enumerate(value):
51
53
  item_name = "{}_{}".format(name, i)
52
54
  try:
53
55
  list_item = deserialize_single_field(
54
- items, v, item_name, keep_undefined=keep_undefined, mapper=mapper
56
+ items, v, item_name, keep_undefined=keep_undefined, mapper=mapper,
57
+ camel_case_convert=camel_case_convert,
58
+ ignore_none=ignore_none
55
59
  )
56
60
  except (ValueError, TypeError) as e:
57
61
  prefix = (
@@ -62,8 +66,11 @@ def deserialize_list_like(
62
66
  elif isinstance(items, (list, tuple)):
63
67
  for i, item in enumerate(items):
64
68
  try:
69
+ ignore_none = getattr(item, IGNORE_NONE_VALUES, False)
65
70
  res = deserialize_single_field(
66
- item, value[i], name, keep_undefined=keep_undefined, mapper=mapper
71
+ item, value[i], name, keep_undefined=keep_undefined, mapper=mapper,
72
+ camel_case_convert=camel_case_convert,
73
+ ignore_none=ignore_none
67
74
  )
68
75
  except (ValueError, TypeError) as e:
69
76
  raise ValueError("{}_{}: {}".format(name, i, str(e))) from e
@@ -74,13 +81,14 @@ def deserialize_list_like(
74
81
  return content_type(values)
75
82
 
76
83
 
77
- def deserialize_array(array_field, value, name, *, keep_undefined=True, mapper):
84
+ def deserialize_array(array_field, value, name, *, keep_undefined=True, mapper,camel_case_convert=False):
78
85
  return deserialize_list_like(
79
- array_field, list, value, name, keep_undefined=keep_undefined, mapper=mapper
86
+ array_field, list, value, name, keep_undefined=keep_undefined, mapper=mapper,
87
+ camel_case_convert=camel_case_convert
80
88
  )
81
89
 
82
90
 
83
- def deserialize_deque(array_field, value, name, *, keep_undefined=True, mapper):
91
+ def deserialize_deque(array_field, value, name, *, keep_undefined=True, mapper, camel_case_convert=False):
84
92
  return deserialize_list_like(
85
93
  array_field,
86
94
  collections.deque,
@@ -88,23 +96,27 @@ def deserialize_deque(array_field, value, name, *, keep_undefined=True, mapper):
88
96
  name,
89
97
  keep_undefined=keep_undefined,
90
98
  mapper=mapper,
99
+ camel_case_convert=camel_case_convert
91
100
  )
92
101
 
93
102
 
94
- def deserialize_tuple(tuple_field, value, name, *, keep_undefined=True, mapper):
103
+ def deserialize_tuple(tuple_field, value, name, *, keep_undefined=True, mapper, camel_case_convert=False):
95
104
  return deserialize_list_like(
96
- tuple_field, tuple, value, name, keep_undefined=keep_undefined, mapper=mapper
105
+ tuple_field, tuple, value, name, keep_undefined=keep_undefined, mapper=mapper,
106
+ camel_case_convert=camel_case_convert
97
107
  )
98
108
 
99
109
 
100
- def deserialize_set(set_field, value, name, *, keep_undefined=True, mapper):
110
+ def deserialize_set(set_field, value, name, *, keep_undefined=True, mapper, camel_case_convert=False):
101
111
  return deserialize_list_like(
102
- set_field, set, value, name, keep_undefined=keep_undefined, mapper=mapper
112
+ set_field, set, value, name, keep_undefined=keep_undefined, mapper=mapper,
113
+ camel_case_convert=camel_case_convert
103
114
  )
104
115
 
105
116
 
106
117
  def deserialize_multifield_wrapper(
107
- field, source_val, name, *, keep_undefined=True, mapper=None
118
+ field, source_val, name, *, keep_undefined=True, mapper=None,
119
+ camel_case_convert=False
108
120
  ):
109
121
  """
110
122
  Only primitive values are supported, otherwise deserialization is ambiguous,
@@ -114,12 +126,16 @@ def deserialize_multifield_wrapper(
114
126
  found_previous_match = False
115
127
  for field_option in field.get_fields():
116
128
  try:
129
+ ignore_none = getattr(field_option, IGNORE_NONE_VALUES, False)
130
+
117
131
  deserialized = deserialize_single_field(
118
132
  field_option,
119
133
  source_val,
120
134
  name,
121
135
  keep_undefined=keep_undefined,
122
136
  mapper=mapper,
137
+ camel_case_convert=camel_case_convert,
138
+ ignore_none=ignore_none
123
139
  )
124
140
  if isinstance(field, AnyOf):
125
141
  return deserialized
@@ -146,7 +162,7 @@ def deserialize_multifield_wrapper(
146
162
  return deserialized
147
163
 
148
164
 
149
- def deserialize_map(map_field, source_val, name):
165
+ def deserialize_map(map_field, source_val, name, camel_case_convert=False):
150
166
  if not isinstance(source_val, dict):
151
167
  raise TypeError(
152
168
  "{}: expected a dict. Got {}".format(name, wrap_val(source_val))
@@ -157,15 +173,26 @@ def deserialize_map(map_field, source_val, name):
157
173
  key_field, value_field = None, None
158
174
  res = {}
159
175
  for key, val in source_val.items():
160
- res[deserialize_single_field(key_field, key, name)] = deserialize_single_field(
161
- value_field, val, name
176
+ ignore_none = getattr(value_field, IGNORE_NONE_VALUES, False)
177
+
178
+ res[deserialize_single_field(key_field, key, name,
179
+ camel_case_convert=camel_case_convert)] = deserialize_single_field(
180
+ value_field, val, name,
181
+ camel_case_convert=camel_case_convert,
182
+ ignore_none=ignore_none
162
183
  )
163
184
  return res
164
185
 
165
186
 
166
187
  def deserialize_single_field(
167
- field, source_val, name, *, mapper=None, keep_undefined=True
188
+ field, source_val, name, *,
189
+ mapper=None,
190
+ keep_undefined=True,
191
+ camel_case_convert=False,
192
+ ignore_none=False,
168
193
  ):
194
+ if source_val is None and ignore_none:
195
+ return source_val
169
196
  if isinstance(field, (Number, String, Enum, Boolean)):
170
197
  field._validate(source_val)
171
198
  value = source_val
@@ -177,23 +204,28 @@ def deserialize_single_field(
177
204
  value = source_val
178
205
  elif isinstance(field, Array):
179
206
  value = deserialize_array(
180
- field, source_val, name, keep_undefined=keep_undefined, mapper=mapper
207
+ field, source_val, name, keep_undefined=keep_undefined, mapper=mapper,
208
+ camel_case_convert=camel_case_convert
181
209
  )
182
210
  elif isinstance(field, Deque):
183
211
  value = deserialize_deque(
184
- field, source_val, name, keep_undefined=keep_undefined, mapper=mapper
212
+ field, source_val, name, keep_undefined=keep_undefined, mapper=mapper,
213
+ camel_case_convert=camel_case_convert
185
214
  )
186
215
  elif isinstance(field, Tuple):
187
216
  value = deserialize_tuple(
188
- field, source_val, name, keep_undefined=keep_undefined, mapper=mapper
217
+ field, source_val, name, keep_undefined=keep_undefined, mapper=mapper,
218
+ camel_case_convert=camel_case_convert
189
219
  )
190
220
  elif isinstance(field, Set):
191
221
  value = deserialize_set(
192
- field, source_val, name, keep_undefined=keep_undefined, mapper=mapper
222
+ field, source_val, name, keep_undefined=keep_undefined, mapper=mapper,
223
+ camel_case_convert=camel_case_convert
193
224
  )
194
225
  elif isinstance(field, MultiFieldWrapper):
195
226
  value = deserialize_multifield_wrapper(
196
- field, source_val, name, keep_undefined=keep_undefined, mapper=mapper
227
+ field, source_val, name, keep_undefined=keep_undefined, mapper=mapper,
228
+ camel_case_convert=camel_case_convert
197
229
  )
198
230
  elif isinstance(field, ClassReference):
199
231
  value = deserialize_structure_internal(
@@ -202,6 +234,7 @@ def deserialize_single_field(
202
234
  name,
203
235
  keep_undefined=keep_undefined,
204
236
  mapper=mapper,
237
+ camel_case_convert=camel_case_convert
205
238
  )
206
239
  elif isinstance(field, StructureReference):
207
240
  value = deserialize_structure_reference(
@@ -209,9 +242,10 @@ def deserialize_single_field(
209
242
  source_val,
210
243
  keep_undefined=keep_undefined,
211
244
  mapper=mapper,
245
+ camel_case_convert=camel_case_convert
212
246
  )
213
247
  elif isinstance(field, Map):
214
- value = deserialize_map(field, source_val, name)
248
+ value = deserialize_map(field, source_val, name, camel_case_convert=camel_case_convert)
215
249
  elif isinstance(field, SerializableField):
216
250
  value = field.deserialize(source_val)
217
251
  elif isinstance(field, Anything) or field is None:
@@ -231,7 +265,7 @@ def deserialize_single_field(
231
265
  return value
232
266
 
233
267
 
234
- def deserialize_structure_reference(cls, the_dict: dict, *, keep_undefined, mapper):
268
+ def deserialize_structure_reference(cls, the_dict: dict, *, keep_undefined, mapper, camel_case_convert=False):
235
269
  field_by_name = {k: v for k, v in cls.__dict__.items() if isinstance(v, Field)}
236
270
  kwargs = dict(
237
271
  [
@@ -241,30 +275,36 @@ def deserialize_structure_reference(cls, the_dict: dict, *, keep_undefined, mapp
241
275
  ]
242
276
  )
243
277
 
244
- kwargs.update(construct_fields_map(field_by_name, keep_undefined, mapper, the_dict))
278
+ kwargs.update(construct_fields_map(field_by_name, keep_undefined, mapper, the_dict,
279
+ camel_case_convert=camel_case_convert))
245
280
  cls(**kwargs)
246
281
  return kwargs
247
282
 
248
283
 
249
- def construct_fields_map(field_by_name, keep_undefined, mapper, the_dict):
284
+ def construct_fields_map(field_by_name, keep_undefined, mapper, input_dict,
285
+ camel_case_convert=False,
286
+ ignore_none=False):
250
287
  result = {}
251
288
  for key, field in field_by_name.items():
289
+ converted_key = _convert_to_camel_case_if_required(key, camel_case_convert=camel_case_convert)
252
290
  process = False
253
291
  processed_input = None
254
- if key in the_dict and key not in mapper:
255
- processed_input = the_dict[key]
292
+ if converted_key in input_dict and key not in mapper:
293
+ processed_input = input_dict[converted_key]
256
294
  process = True
257
295
  elif key in mapper:
258
- processed_input = get_processed_input(key, mapper, the_dict)
296
+ processed_input = get_processed_input(key, mapper, input_dict)
259
297
  process = True
260
298
  if process:
261
- sub_mapper = mapper.get(f"{key}._mapper", {})
299
+ sub_mapper = mapper.get(f"{converted_key}._mapper", {})
262
300
  result[key] = deserialize_single_field(
263
301
  field,
264
302
  processed_input,
265
303
  key,
266
304
  mapper=sub_mapper,
267
305
  keep_undefined=keep_undefined,
306
+ camel_case_convert=camel_case_convert,
307
+ ignore_none=ignore_none
268
308
  )
269
309
  return result
270
310
 
@@ -288,7 +328,10 @@ class FunctionCall(Structure):
288
328
 
289
329
 
290
330
  def deserialize_structure_internal(
291
- cls, the_dict, name=None, *, mapper=None, keep_undefined=True
331
+ cls, the_dict, name=None, *,
332
+ mapper=None,
333
+ keep_undefined=True,
334
+ camel_case_convert=False
292
335
  ):
293
336
  """
294
337
  Deserialize a dict to a Structure instance, Jackson style.
@@ -314,6 +357,7 @@ def deserialize_structure_internal(
314
357
  """
315
358
  if mapper is None:
316
359
  mapper = {}
360
+ ignore_none = getattr(cls, IGNORE_NONE_VALUES, False)
317
361
  if not isinstance(mapper, (collections.abc.Mapping,)):
318
362
  raise TypeError("Mapper must be a mapping")
319
363
  field_by_name = _get_all_fields_by_name(cls)
@@ -326,7 +370,7 @@ def deserialize_structure_internal(
326
370
  if len(fields) == 1 and required == fields and additional_props is False:
327
371
  field_name = fields[0]
328
372
  return cls(
329
- deserialize_single_field(getattr(cls, field_name), the_dict, field_name)
373
+ deserialize_single_field(getattr(cls, field_name), the_dict, field_name, ignore_none=ignore_none)
330
374
  )
331
375
  raise TypeError(
332
376
  "{}: Expected a dictionary; Got {}".format(name, wrap_val(the_dict))
@@ -339,12 +383,17 @@ def deserialize_structure_internal(
339
383
  if k not in field_by_name and keep_undefined
340
384
  ]
341
385
  )
342
- kwargs.update(construct_fields_map(field_by_name, keep_undefined, mapper, the_dict))
386
+ kwargs.update(construct_fields_map(field_by_name, keep_undefined, mapper, the_dict,
387
+ camel_case_convert=camel_case_convert,
388
+ ignore_none=ignore_none))
343
389
 
344
390
  return cls(**kwargs)
345
391
 
346
392
 
347
- def deserialize_structure(cls, the_dict, *, mapper=None, keep_undefined=True):
393
+ def deserialize_structure(cls, the_dict, *,
394
+ mapper=None,
395
+ keep_undefined=True,
396
+ camel_case_convert=False):
348
397
  """
349
398
  Deserialize a dict to a Structure instance, Jackson style.
350
399
  Note the top level must be a python dict - which implies that a JSON of
@@ -370,7 +419,9 @@ def deserialize_structure(cls, the_dict, *, mapper=None, keep_undefined=True):
370
419
  an instance of the provided :class:`Structure` deserialized
371
420
  """
372
421
  return deserialize_structure_internal(
373
- cls, the_dict, mapper=mapper, keep_undefined=keep_undefined
422
+ cls, the_dict, mapper=mapper,
423
+ keep_undefined=keep_undefined,
424
+ camel_case_convert=camel_case_convert
374
425
  )
375
426
 
376
427
 
@@ -399,7 +450,7 @@ def get_processed_input(key, mapper, the_dict):
399
450
  return processed_input
400
451
 
401
452
 
402
- def serialize_val(field_definition, name, val, mapper=None):
453
+ def serialize_val(field_definition, name, val, mapper=None, camel_case_convert=False):
403
454
  if isinstance(field_definition, SerializableField) and isinstance(
404
455
  field_definition, Field
405
456
  ):
@@ -416,32 +467,37 @@ def serialize_val(field_definition, name, val, mapper=None):
416
467
  ):
417
468
  key_type, value_type = field_definition.items
418
469
  return {
419
- serialize_val(key_type, name, k): serialize_val(value_type, name, v)
470
+ serialize_val(key_type, name, k, camel_case_convert=camel_case_convert):
471
+ serialize_val(value_type, name, v, camel_case_convert=camel_case_convert)
420
472
  for (k, v) in val.items()
421
473
  }
422
474
  else:
423
475
  return {
424
- serialize_val(Anything, name, k): serialize_val(Anything, name, v)
476
+ serialize_val(Anything, name, k, camel_case_convert=camel_case_convert):
477
+ serialize_val(Anything, name, v, camel_case_convert=camel_case_convert)
425
478
  for (k, v) in val.items()
426
479
  }
427
480
  if isinstance(field_definition.items, list):
428
481
  return [
429
- serialize_val(field_definition.items[ind], name, v, mapper=mapper)
482
+ serialize_val(field_definition.items[ind], name, v, mapper=mapper,
483
+ camel_case_convert=camel_case_convert)
430
484
  for ind, v in enumerate(val)
431
485
  ]
432
486
  elif isinstance(field_definition.items, Field):
433
487
  return [
434
- serialize_val(field_definition.items, name, i, mapper=mapper)
488
+ serialize_val(field_definition.items, name, i, mapper=mapper,
489
+ camel_case_convert=camel_case_convert)
435
490
  for i in val
436
491
  ]
437
492
  else:
438
- return [serialize_val(None, name, i, mapper=mapper) for i in val]
493
+ return [serialize_val(None, name, i, mapper=mapper, camel_case_convert=camel_case_convert)
494
+ for i in val]
439
495
  if isinstance(val, (list, set, tuple)):
440
- return [serialize_val(None, name, i) for i in val]
496
+ return [serialize_val(None, name, i, camel_case_convert=camel_case_convert) for i in val]
441
497
  if isinstance(field_definition, Anything) and isinstance(val, Structure):
442
- return serialize(val, mapper=mapper)
498
+ return serialize(val, mapper=mapper, camel_case_convert=camel_case_convert)
443
499
  if isinstance(val, Structure) or isinstance(field_definition, Field):
444
- return serialize_internal(val, mapper=mapper)
500
+ return serialize_internal(val, mapper=mapper, camel_case_convert=camel_case_convert)
445
501
 
446
502
  # nothing worked. Not a typedpy field. Last ditch effort.
447
503
  try:
@@ -450,7 +506,7 @@ def serialize_val(field_definition, name, val, mapper=None):
450
506
  raise ValueError(f"{name}: cannot serialize value: {ex}") from ex
451
507
 
452
508
 
453
- def serialize_field(field_definition: Field, value):
509
+ def serialize_field(field_definition: Field, value, camel_case_convert=False):
454
510
  """
455
511
  Serialize a specific :class:`Field` from a structure to a JSON-like dict.
456
512
  Example:
@@ -479,7 +535,8 @@ def serialize_field(field_definition: Field, value):
479
535
  Returns:
480
536
  a serialized Python object that can be directly converted to JSON
481
537
  """
482
- return serialize_val(field_definition, field_definition._name, value)
538
+ return serialize_val(field_definition, field_definition._name, value,
539
+ camel_case_convert=camel_case_convert)
483
540
 
484
541
 
485
542
  def _get_mapped_value(mapper, key, items):
@@ -498,7 +555,23 @@ def _get_mapped_value(mapper, key, items):
498
555
  return None
499
556
 
500
557
 
501
- def serialize_internal(structure, mapper=None, compact=False):
558
+ def _convert_to_camel_case_if_required(key, camel_case_convert):
559
+ if camel_case_convert:
560
+ words = key.split('_')
561
+ return words[0] + ''.join(w.title() for w in words[1:])
562
+ else:
563
+ return key
564
+
565
+ def _convert_to_snake_case_if_required(key, camel_case_convert):
566
+ if camel_case_convert:
567
+ return ''.join(['_' + char.lower() if char.isupper()
568
+ else char for char in key]).lstrip('_')
569
+ else:
570
+ return key
571
+
572
+
573
+
574
+ def serialize_internal(structure, mapper=None, compact=False, camel_case_convert=False):
502
575
  if mapper is None:
503
576
  mapper = {}
504
577
  field_by_name = _get_all_fields_by_name(structure.__class__)
@@ -520,7 +593,8 @@ def serialize_internal(structure, mapper=None, compact=False):
520
593
  ):
521
594
  key = fields[0]
522
595
  result = serialize_val(
523
- field_by_name.get(key, None), key, getattr(structure, key)
596
+ field_by_name.get(key, None), key, getattr(structure, key),
597
+ camel_case_convert=camel_case_convert
524
598
  )
525
599
  else:
526
600
  result = {}
@@ -531,7 +605,7 @@ def serialize_internal(structure, mapper=None, compact=False):
531
605
  mapped_key = (
532
606
  mapper[key]
533
607
  if key in mapper and isinstance(mapper[key], (str,))
534
- else key
608
+ else _convert_to_camel_case_if_required(key, camel_case_convert)
535
609
  )
536
610
  mapped_value = _get_mapped_value(mapper, key, items_map)
537
611
  the_field_definition = (
@@ -539,12 +613,14 @@ def serialize_internal(structure, mapper=None, compact=False):
539
613
  )
540
614
  sub_mapper = mapper.get(f"{key}._mapper", {})
541
615
  result[mapped_key] = serialize_val(
542
- the_field_definition, key, mapped_value or val, mapper=sub_mapper
616
+ the_field_definition, key, mapped_value or val,
617
+ mapper=sub_mapper,
618
+ camel_case_convert=camel_case_convert
543
619
  )
544
620
  return result
545
621
 
546
622
 
547
- def serialize(value, *, mapper: Dict = None, compact=False):
623
+ def serialize(value, *, mapper: Dict = None, compact=False, camel_case_convert=False):
548
624
  """
549
625
  Serialize an instance of :class:`Structure` to a JSON-like dict.
550
626
  `See working examples in test. <https://github.com/loyada/typedpy/tree/master/tests/test_serialization.py>`_
@@ -580,11 +656,12 @@ def serialize(value, *, mapper: Dict = None, compact=False):
580
656
  return value
581
657
  if isinstance(value, (_ListStruct, _DictStruct)):
582
658
  field_definition = value._field_definition
583
- return serialize_val(field_definition, field_definition._name, value)
659
+ return serialize_val(field_definition, field_definition._name, value,
660
+ camel_case_convert=camel_case_convert)
584
661
  if isinstance(value, (enum.Enum,)):
585
662
  return value.name
586
663
  raise TypeError(
587
664
  "serialize: Not a Structure or Field that with an obvious serialization. Got: {}."
588
665
  " Maybe try serialize_field() instead?".format(value)
589
666
  )
590
- return serialize_internal(value, mapper=mapper, compact=compact)
667
+ return serialize_internal(value, mapper=mapper, compact=compact, camel_case_convert=camel_case_convert)
@@ -1,5 +1,5 @@
1
1
  from typedpy import Structure
2
- from typedpy.fields import StructureClass, Map, String, OneOf
2
+ from typedpy.fields import StructureClass, Map, String, OneOf, Boolean
3
3
  from typedpy.serialization import FunctionCall, deserialize_structure, serialize
4
4
  from typedpy.structures import _get_all_fields_by_name
5
5
 
@@ -49,11 +49,15 @@ class Deserializer(Structure):
49
49
  }
50
50
  Deserializer(target_class=Foo, mapper = mapper).deserializer(the_input_dict)
51
51
 
52
+ camel_case_convert(bool): Optional
53
+ If true, will convert any camelCase key that does not have explicit mapping to a snake_case attribute
54
+ name. Default is False.
52
55
 
53
56
  """
54
57
 
55
58
  target_class = StructureClass
56
59
  mapper = Map[String, OneOf[String, FunctionCall, Map]]
60
+ camel_case_convert = Boolean(default = False)
57
61
 
58
62
  _required = ["target_class"]
59
63
 
@@ -74,6 +78,7 @@ class Deserializer(Structure):
74
78
  input_data,
75
79
  mapper=self.mapper,
76
80
  keep_undefined=keep_undefined,
81
+ camel_case_convert=self.camel_case_convert
77
82
  )
78
83
 
79
84
 
@@ -155,5 +160,20 @@ class Serializer(Structure):
155
160
  for key in self.mapper:
156
161
  verify_key_in_mapper(key, valid_keys, source_class)
157
162
 
158
- def serialize(self, compact=True):
159
- return serialize(self.source, mapper=self.mapper, compact=compact)
163
+ def serialize(self, compact=True, camel_case_convert=False):
164
+ """
165
+
166
+ Arguments:
167
+ compact(boolean): optional
168
+ whether to use a compact form for Structure that is a simple wrapper of a field.
169
+ for example: if a Structure has only one field of an int, if compact is True
170
+ it will serialize the structure as an int instead of a dictionary.
171
+ Default is False.
172
+
173
+ camel_case_convert(dict): optional
174
+ If True, convert any camel-case key that does not have a mapping in the mapper to a snake-case
175
+ attribute.
176
+ Default is False.
177
+ """
178
+ return serialize(self.source, mapper=self.mapper, compact=compact,
179
+ camel_case_convert=camel_case_convert)
@@ -145,7 +145,7 @@ def _check_for_final_violations(classes):
145
145
  )
146
146
  )
147
147
  if "ImmutableStructure" in globals() and is_sub_class(
148
- c, ImmutableStructure
148
+ c, ImmutableStructure
149
149
  ):
150
150
  raise TypeError(
151
151
  "Tried to extend {}, which is an ImmutableStructure. This is forbidden".format(
@@ -210,9 +210,9 @@ class UniqueMixin:
210
210
  def __manage_uniqueness__(self):
211
211
  myclass = self.__class__
212
212
  if (
213
- getattr(myclass, MUST_BE_UNIQUE, False)
214
- and len(getattr(myclass, "_ALL_INSTANCES", set()))
215
- < MAX_NUMBER_OF_INSTANCES_TO_VERIFY_UNIQUENESS
213
+ getattr(myclass, MUST_BE_UNIQUE, False)
214
+ and len(getattr(myclass, "_ALL_INSTANCES", set()))
215
+ < MAX_NUMBER_OF_INSTANCES_TO_VERIFY_UNIQUENESS
216
216
  ):
217
217
  hash_of_instance = self.__hash__()
218
218
  if hash_of_instance in getattr(myclass, "_ALL_INSTANCES", set()):
@@ -236,14 +236,14 @@ class UniqueMixin:
236
236
  structure_class_name
237
237
  ]
238
238
  if (
239
- getattr(self, MUST_BE_UNIQUE, False)
240
- and len(instance_by_value_for_current_struct)
241
- < MAX_NUMBER_OF_INSTANCES_TO_VERIFY_UNIQUENESS
239
+ getattr(self, MUST_BE_UNIQUE, False)
240
+ and len(instance_by_value_for_current_struct)
241
+ < MAX_NUMBER_OF_INSTANCES_TO_VERIFY_UNIQUENESS
242
242
  ):
243
243
  hash_of_field_val = value.__hash__()
244
244
  if (
245
- instance_by_value_for_current_struct.get(hash_of_field_val, instance)
246
- != instance
245
+ instance_by_value_for_current_struct.get(hash_of_field_val, instance)
246
+ != instance
247
247
  ):
248
248
  raise ValueError(
249
249
  "Instance copy of field {} in {}, which is defined as unique. Instance is {}".format(
@@ -320,13 +320,13 @@ class Field(UniqueMixin, metaclass=_FieldMeta):
320
320
  else owner.__dict__[self._name]
321
321
  )
322
322
  is_immutable = (
323
- instance is not None
324
- and getattr(instance, IS_IMMUTABLE, False)
325
- or getattr(self, IS_IMMUTABLE, False)
323
+ instance is not None
324
+ and getattr(instance, IS_IMMUTABLE, False)
325
+ or getattr(self, IS_IMMUTABLE, False)
326
326
  )
327
327
  needs_defensive_copy = (
328
- not isinstance(res, (ImmutableMixin, int, float, str, bool, enum.Enum))
329
- or res is None
328
+ not isinstance(res, (ImmutableMixin, int, float, str, bool, enum.Enum))
329
+ or res is None
330
330
  )
331
331
  return deepcopy(res) if (is_immutable and needs_defensive_copy) else res
332
332
 
@@ -334,7 +334,7 @@ class Field(UniqueMixin, metaclass=_FieldMeta):
334
334
  if getattr(self, IS_IMMUTABLE, False) and self._name in instance.__dict__:
335
335
  raise ValueError("{}: Field is immutable".format(self._name))
336
336
  if getattr(self, IS_IMMUTABLE, False) and not getattr(
337
- self, "_custom_deep_copy_implementation", False
337
+ self, "_custom_deep_copy_implementation", False
338
338
  ):
339
339
  try:
340
340
  instance.__dict__[self._name] = deepcopy(value)
@@ -349,7 +349,7 @@ class Field(UniqueMixin, metaclass=_FieldMeta):
349
349
  instance.__dict__[self._name] = value
350
350
  instance.__manage__uniqueness_of_all_fields__()
351
351
  if getattr(instance, "_instantiated", False) and not getattr(
352
- instance, "_skip_validation", False
352
+ instance, "_skip_validation", False
353
353
  ):
354
354
  instance.__validate__()
355
355
 
@@ -413,34 +413,34 @@ def _get_all_fields_by_name(cls):
413
413
  def _instantiate_fields_if_needed(cls_dict: dict, defaults: dict):
414
414
  for key, val in cls_dict.items():
415
415
  if (
416
- key
417
- not in {
418
- REQUIRED_FIELDS,
419
- ADDITIONAL_PROPERTIES,
420
- IS_IMMUTABLE,
421
- DEFAULTS,
422
- OPTIONAL_FIELDS,
423
- IGNORE_NONE_VALUES,
424
- }
425
- and not isinstance(val, Field)
426
- and not key.startswith("__")
427
- and (
416
+ key
417
+ not in {
418
+ REQUIRED_FIELDS,
419
+ ADDITIONAL_PROPERTIES,
420
+ IS_IMMUTABLE,
421
+ DEFAULTS,
422
+ OPTIONAL_FIELDS,
423
+ IGNORE_NONE_VALUES,
424
+ }
425
+ and not isinstance(val, Field)
426
+ and not key.startswith("__")
427
+ and (
428
428
  Field in getattr(val, "__mro__", []) or is_function_returning_field(val)
429
- )
429
+ )
430
430
  ):
431
431
  new_val = val(default=defaults[key]) if key in defaults else val()
432
432
  cls_dict[key] = new_val
433
433
 
434
434
 
435
435
  def _apply_default_and_update_required_not_to_include_fields_with_defaults(
436
- cls_dict: dict, defaults: dict, fields: list
436
+ cls_dict: dict, defaults: dict, fields: list
437
437
  ):
438
438
  required_fields = set(cls_dict.get(REQUIRED_FIELDS, []))
439
439
  optional_fields = set(cls_dict.get(OPTIONAL_FIELDS, []))
440
440
  required_fields_predefined = REQUIRED_FIELDS in cls_dict
441
441
  for field_name in fields:
442
442
  if field_name in defaults and not getattr(
443
- cls_dict[field_name], "_default", None
443
+ cls_dict[field_name], "_default", None
444
444
  ):
445
445
  cls_dict[field_name]._try_default_value(defaults[field_name])
446
446
  cls_dict[field_name]._default = defaults[field_name]
@@ -547,15 +547,15 @@ def _type_is_generic(v):
547
547
  generic_alias = getattr(typing, "_GenericAlias", Foo)
548
548
  special_generic_alias = getattr(typing, "_SpecialGenericAlias", Foo)
549
549
  return (
550
- (python_ver_36 and isinstance(v, typing_base))
551
- or (
552
- python_ver_atleast_than_37
553
- and isinstance(v, (generic_alias, special_generic_alias))
554
- )
555
- or (
556
- python_ver_atleast_39
557
- and origin in {list, dict, tuple, set, frozenset, deque, typing.Union}
558
- )
550
+ (python_ver_36 and isinstance(v, typing_base))
551
+ or (
552
+ python_ver_atleast_than_37
553
+ and isinstance(v, (generic_alias, special_generic_alias))
554
+ )
555
+ or (
556
+ python_ver_atleast_39
557
+ and origin in {list, dict, tuple, set, frozenset, deque, typing.Union}
558
+ )
559
559
  )
560
560
 
561
561
 
@@ -614,7 +614,7 @@ def add_annotations_to_class_dict(cls_dict):
614
614
  from .fields import AnyOf
615
615
 
616
616
  if isinstance(the_type, AnyOf) and getattr(
617
- the_type, "_is_optional", False
617
+ the_type, "_is_optional", False
618
618
  ):
619
619
  optional_fields.add(k)
620
620
  if k in cls_dict:
@@ -691,6 +691,7 @@ class Structure(UniqueMixin, metaclass=StructMeta):
691
691
  _ignore_none(bool): optional
692
692
  Ignore assignment to None for any field value.
693
693
  Default is False.
694
+ Required fields never ignore None (since they are required)
694
695
 
695
696
  Decorating it with @unique ensures that no all instances of this structure will be unique. It
696
697
  will raise an exception otherwise (see "Uniqueness" section).
@@ -723,14 +724,18 @@ class Structure(UniqueMixin, metaclass=StructMeta):
723
724
  if key in self.__dict__:
724
725
  raise ValueError("Structure is immutable")
725
726
  value = deepcopy(value)
726
- if getattr(self, IGNORE_NONE_VALUES, False) and value is None:
727
+ if all([getattr(self, IGNORE_NONE_VALUES, False),
728
+ value is None,
729
+ key not in getattr(self.__class__, REQUIRED_FIELDS, [])]
730
+ ):
727
731
  return
732
+
728
733
  super().__setattr__(key, value)
729
734
 
730
735
  if (
731
- getattr(self, "_instantiated", False)
732
- and not _is_dunder(key)
733
- and not _is_sunder(key)
736
+ getattr(self, "_instantiated", False)
737
+ and not _is_dunder(key)
738
+ and not _is_sunder(key)
734
739
  ):
735
740
  self.__manage_uniqueness__()
736
741
 
@@ -766,7 +771,7 @@ class Structure(UniqueMixin, metaclass=StructMeta):
766
771
 
767
772
  name = self.__class__.__name__
768
773
  if name.startswith("StructureReference_") and self.__class__.__bases__ == (
769
- Structure,
774
+ Structure,
770
775
  ):
771
776
  name = "Structure"
772
777
  props = []
@@ -797,7 +802,7 @@ class Structure(UniqueMixin, metaclass=StructMeta):
797
802
 
798
803
  def __delitem__(self, key):
799
804
  if isinstance(getattr(self, REQUIRED_FIELDS), list) and key in getattr(
800
- self, REQUIRED_FIELDS
805
+ self, REQUIRED_FIELDS
801
806
  ):
802
807
  raise ValueError("{} is mandatory".format(key))
803
808
  del self.__dict__[key]
@@ -842,9 +847,9 @@ class Structure(UniqueMixin, metaclass=StructMeta):
842
847
  required = props.get(REQUIRED_FIELDS, field_names)
843
848
  additional_props = props.get(ADDITIONAL_PROPERTIES, True)
844
849
  if (
845
- len(field_names) == 1
846
- and required == field_names
847
- and additional_props is False
850
+ len(field_names) == 1
851
+ and required == field_names
852
+ and additional_props is False
848
853
  ):
849
854
  return item in getattr(self, field_names[0], {})
850
855
 
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: typedpy
3
- Version: 2.2.2
3
+ Version: 2.2.4
4
4
  Summary: Type-safe Python
5
5
  Home-page: http://github.com/loyada/typedpy
6
6
  Author: Danny Loya
7
7
  Author-email: dan.loya@gmail.com
8
8
  License: MIT
9
- Download-URL: https://github.com/loyada/typedpy/archive/v2.2.2.tar.gz
9
+ Download-URL: https://github.com/loyada/typedpy/archive/v2.2.4.tar.gz
10
10
  Description: [![][travis img]][travis]
11
11
  [![][docs img]][docs]
12
12