Skip to content

Commit 9910d2c

Browse files
committed
Merge remote-tracking branch 'plq/master'
2 parents 1af271b + 9a0b607 commit 9910d2c

File tree

9 files changed

+302
-102
lines changed

9 files changed

+302
-102
lines changed

spyne/protocol/dictobj.py

Lines changed: 70 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -91,35 +91,36 @@ def decompose_incoming_envelope(self, ctx, message):
9191
logger.debug('\theader : %r' % (ctx.in_header_doc))
9292
logger.debug('\tbody : %r' % (ctx.in_body_doc))
9393

94-
def _doc_to_object(self, cls, doc):
94+
@classmethod
95+
def _doc_to_object(cls, class_, doc, validator=None):
9596
if doc is None:
9697
return []
9798

98-
if issubclass(cls, Array):
99+
if issubclass(class_, Array):
99100
retval = [ ]
100-
(serializer,) = cls._type_info.values()
101+
(serializer,) = class_._type_info.values()
101102

102103
for child in doc:
103-
retval.append(self._from_dict_value(serializer, child))
104+
retval.append(cls._from_dict_value(serializer, child, validator))
104105

105106
return retval
106107

107-
inst = cls.get_deserialization_instance()
108+
inst = class_.get_deserialization_instance()
108109

109110
# get all class attributes, including the ones coming from parent classes.
110-
flat_type_info = cls.get_flat_type_info(cls)
111+
flat_type_info = class_.get_flat_type_info(class_)
111112

112113
# initialize instance
113114
for k in flat_type_info:
114115
setattr(inst, k, None)
115116

116-
# this is for validating cls.Attributes.{min,max}_occurs
117+
# this is for validating class_.Attributes.{min,max}_occurs
117118
frequencies = {}
118119

119120
try:
120121
items = doc.items()
121122
except AttributeError:
122-
items = zip(cls._type_info.keys(), doc)
123+
items = zip(class_._type_info.keys(), doc)
123124

124125
# parse input to set incoming data to related attributes.
125126
for k,v in items:
@@ -138,14 +139,14 @@ def _doc_to_object(self, cls, doc):
138139
value = []
139140

140141
for a in v:
141-
value.append(self._from_dict_value(member, a))
142+
value.append(cls._from_dict_value(member, a, validator))
142143

143144
else:
144-
value = self._from_dict_value(member, v)
145+
value = cls._from_dict_value(member, v, validator)
145146

146147
setattr(inst, k, value)
147148

148-
if self.validator is self.SOFT_VALIDATION:
149+
if validator is cls.SOFT_VALIDATION:
149150
for k, v in flat_type_info.items():
150151
val = frequencies.get(k, 0)
151152
if (val < v.Attributes.min_occurs or val > v.Attributes.max_occurs):
@@ -174,7 +175,7 @@ def deserialize(self, ctx, message):
174175
# assign raw result to its wrapper, result_message
175176
result_message_class = ctx.descriptor.in_message
176177
value = ctx.in_body_doc.get(result_message_class.get_type_name(), None)
177-
result_message = self._doc_to_object(result_message_class, value)
178+
result_message = self._doc_to_object(result_message_class, value, self.validator)
178179

179180
ctx.in_object = result_message
180181

@@ -214,59 +215,63 @@ def serialize(self, ctx, message):
214215
out_type, out_instance = unwrap_instance(out_type, out_instance,
215216
self.skip_depth)
216217

217-
# arrays get wrapped in [], whereas other objects get wrapped in
218-
# {object_name: ...}
219-
wrapper_name = None
220-
if not issubclass(out_type, Array):
221-
wrapper_name = out_type.get_type_name()
218+
ctx.out_document = self._object_to_doc(out_type, out_instance)
219+
self.event_manager.fire_event('after_serialize', ctx)
222220

223-
# transform the results into a dict:
224-
if out_type.Attributes.max_occurs > 1:
225-
ctx.out_document = (self._to_value(out_type, inst, wrapper_name)
226-
for inst in out_instance)
227-
else:
228-
ctx.out_document = [self._to_value(out_type, out_instance, wrapper_name)]
221+
@classmethod
222+
def _object_to_doc(cls, class_, value, wrapper_name=None):
223+
# arrays get wrapped in [], whereas other objects get wrapped in
224+
# {object_name: ...}
225+
if wrapper_name is None and not issubclass(class_, Array):
226+
wrapper_name = class_.get_type_name()
227+
228+
# transform the results into a dict:
229+
if class_.Attributes.max_occurs > 1:
230+
return (cls._to_value(class_, inst, wrapper_name) for inst in value)
231+
else:
232+
return [cls._to_value(class_, value, wrapper_name)]
229233

230-
self.event_manager.fire_event('after_serialize', ctx)
231234

232-
def _from_dict_value(self, cls, value):
235+
@classmethod
236+
def _from_dict_value(cls, class_, value, validator):
233237
# validate raw input
234-
if self.validator is self.SOFT_VALIDATION:
235-
if issubclass(cls, Unicode) and not isinstance(value, unicode):
236-
if not (issubclass(cls, String) and isinstance(value, str)):
238+
if validator is cls.SOFT_VALIDATION:
239+
if issubclass(class_, Unicode) and not isinstance(value, unicode):
240+
if not (issubclass(class_, String) and isinstance(value, str)):
237241
raise ValidationError(value)
238242

239-
elif issubclass(cls, Decimal) and not isinstance(value, (int, long, float)):
243+
elif issubclass(class_, Decimal) and not isinstance(value, (int, long, float)):
240244
raise ValidationError(value)
241245

242-
elif issubclass(cls, DateTime) and not (isinstance(value, unicode) and
243-
cls.validate_string(cls, value)):
246+
elif issubclass(class_, DateTime) and not (isinstance(value, unicode) and
247+
class_.validate_string(class_, value)):
244248
raise ValidationError(value)
245249

246250
# get native type
247-
if issubclass(cls, ComplexModelBase):
248-
retval = self._doc_to_object(cls, value)
251+
if issubclass(class_, ComplexModelBase):
252+
retval = cls._doc_to_object(class_, value, validator)
249253

250-
elif issubclass(cls, DateTime):
251-
retval = cls.from_string(value)
254+
elif issubclass(class_, DateTime):
255+
retval = class_.from_string(value)
252256

253257
else:
254258
retval = value
255259

256260
# validate native type
257-
if self.validator is self.SOFT_VALIDATION and \
258-
not cls.validate_native(cls, retval):
261+
if validator is cls.SOFT_VALIDATION and \
262+
not class_.validate_native(class_, retval):
259263
raise ValidationError(retval)
260264

261265
return retval
262266

263-
def _get_member_pairs(self, cls, inst):
264-
parent_cls = getattr(cls, '__extends__', None)
267+
@classmethod
268+
def _get_member_pairs(cls, class_, inst):
269+
parent_cls = getattr(class_, '__extends__', None)
265270
if not (parent_cls is None):
266-
for r in self._get_member_pairs(parent_cls, inst):
271+
for r in cls._get_member_pairs(parent_cls, inst):
267272
yield r
268273

269-
for k, v in cls._type_info.items():
274+
for k, v in class_._type_info.items():
270275
try:
271276
sub_value = getattr(inst, k, None)
272277
except Exception, e: # to guard against e.g. sqlalchemy throwing NoSuchColumnError
@@ -275,36 +280,39 @@ def _get_member_pairs(self, cls, inst):
275280

276281
if v.Attributes.max_occurs > 1:
277282
if sub_value != None:
278-
yield (k, [self._to_value(v,sv) for sv in sub_value])
283+
yield (k, [cls._to_value(v,sv) for sv in sub_value])
279284

280285
else:
281-
yield (k, self._to_value(v, sub_value))
286+
yield (k, cls._to_value(v, sub_value))
282287

283-
def _to_value(self, cls, value, k=None):
284-
if issubclass(cls, ComplexModelBase):
285-
return self._to_dict(cls, value, k)
288+
@classmethod
289+
def _to_value(cls, class_, value, k=None):
290+
if issubclass(class_, ComplexModelBase):
291+
return cls._to_dict(class_, value, k)
286292

287-
if issubclass(cls, DateTime):
288-
return cls.to_string(value)
293+
if issubclass(class_, DateTime):
294+
return class_.to_string(value)
289295

290-
if issubclass(cls, Decimal):
291-
if cls.Attributes.format is None:
296+
if issubclass(class_, Decimal):
297+
if class_.Attributes.format is None:
292298
return value
293299
else:
294-
return cls.to_string(value)
300+
return class_.to_string(value)
295301

296302
return value
297303

298-
def _to_dict(self, cls, inst, field_name=None):
299-
inst = cls.get_serialization_instance(inst)
304+
@classmethod
305+
def _to_dict(cls, class_, inst, field_name=None):
306+
inst = class_.get_serialization_instance(inst)
300307

301-
retval = dict(self._get_member_pairs(cls, inst))
308+
retval = dict(cls._get_member_pairs(class_, inst))
302309
if field_name is None:
303310
return retval
304311
else:
305312
return {field_name: retval}
306313

307-
def flat_dict_to_object(self, doc, inst_class):
314+
@classmethod
315+
def flat_dict_to_object(cls, doc, inst_class, validator=None):
308316
"""Converts a flat dict to a native python object.
309317
310318
See :func:`spyne.model.complex.ComplexModelBase.get_flat_type_info`.
@@ -327,10 +335,10 @@ def flat_dict_to_object(self, doc, inst_class):
327335
if value is None:
328336
value = []
329337

330-
# extract native values from the list of strings that comes from the
338+
# extract native values from the list of strings that come from the
331339
# http dict.
332340
for v2 in v:
333-
if (self.validator is self.SOFT_VALIDATION and not
341+
if (validator is cls.SOFT_VALIDATION and not
334342
member.type.validate_string(member.type, v2)):
335343
raise ValidationError(v2)
336344

@@ -342,7 +350,7 @@ def flat_dict_to_object(self, doc, inst_class):
342350
else:
343351
native_v2 = member.type.from_string(v2)
344352

345-
if (self.validator is self.SOFT_VALIDATION and not
353+
if (validator is cls.SOFT_VALIDATION and not
346354
member.type.validate_native(member.type, native_v2)):
347355
raise ValidationError(v2)
348356

@@ -390,7 +398,7 @@ def flat_dict_to_object(self, doc, inst_class):
390398
logger.debug("\tset default %r(%r) = %r" %
391399
(member.path, pkey, value))
392400

393-
if self.validator is self.SOFT_VALIDATION:
401+
if validator is cls.SOFT_VALIDATION:
394402
sti = simple_type_info.values()
395403
sti.sort(key=lambda x: (len(x.path), x.path))
396404
pfrag = None
@@ -432,7 +440,8 @@ def flat_dict_to_object(self, doc, inst_class):
432440
return inst
433441

434442

435-
def object_to_flat_dict(self, inst_cls, value, hier_delim="_", retval=None,
443+
@classmethod
444+
def object_to_flat_dict(cls, inst_cls, value, hier_delim="_", retval=None,
436445
prefix=None, parent=None):
437446
"""Converts a native python object to a flat dict.
438447
@@ -462,7 +471,7 @@ def object_to_flat_dict(self, inst_cls, value, hier_delim="_", retval=None,
462471
retval[key] = None
463472

464473
else:
465-
self.object_to_flat_dict(fti[k], subvalue, hier_delim,
474+
cls.object_to_flat_dict(fti[k], subvalue, hier_delim,
466475
retval, new_prefix, parent=inst_cls)
467476

468477
return retval

spyne/protocol/http.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,10 @@ def deserialize(self, ctx, message):
110110

111111
if ctx.descriptor.in_header:
112112
ctx.in_header = self.flat_dict_to_object(ctx.in_header_doc,
113-
ctx.descriptor.in_header)
113+
ctx.descriptor.in_header, self.validator)
114114
if ctx.descriptor.in_message:
115115
ctx.in_object = self.flat_dict_to_object(ctx.in_body_doc,
116-
ctx.descriptor.in_message)
116+
ctx.descriptor.in_message, self.validator)
117117

118118
self.event_manager.fire_event('after_deserialize', ctx)
119119

spyne/test/model/test_complex.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ class CCM(ComplexModel):
414414

415415
val = CCM(i=5, s='a', c=CM(i=7, s='b'))
416416

417-
d = DictObject().object_to_flat_dict(CCM, val)
417+
d = DictObject.object_to_flat_dict(CCM, val)
418418

419419
assert d['i'] == 5
420420
assert d['s'] == 'a'

spyne/test/protocol/test_soap.py

100644100755
File mode changed.

spyne/test/protocol/test_xml.py

100644100755
File mode changed.

spyne/test/test_soft_validation.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ def __get_ctx(self, mn, qs):
123123
return ctx
124124

125125
def test_http_rpc(self):
126-
127126
ctx = self.__get_ctx('some_method', 's=1')
128127
self.assertEquals(ctx.in_error.faultcode, 'Client.ValidationError')
129128

spyne/test/test_sqlalchemy.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,5 +493,81 @@ class SomeClass(TableModel):
493493

494494
session.close()
495495

496+
def test_nested_sql_array_as_xml(self):
497+
from spyne.model.complex import TableModel
498+
499+
engine = create_engine('sqlite:///:memory:')
500+
session = sessionmaker(bind=engine)()
501+
metadata = TableModel.Attributes.sqla_metadata = MetaData()
502+
metadata.bind = engine
503+
504+
class SomeOtherClass(ComplexModel):
505+
id = Integer32(primary_key=True)
506+
s = Unicode(64)
507+
508+
class SomeClass(TableModel):
509+
__tablename__ = 'some_class'
510+
__table_args__ = {"sqlite_autoincrement": True}
511+
512+
id = Integer32
513+
others = Array(SomeOtherClass, store_as='xml')
514+
515+
get_sqlalchemy_table(SomeClass)
516+
517+
metadata.create_all()
518+
519+
soc1 = SomeOtherClass(s='ehe1')
520+
soc2 = SomeOtherClass(s='ehe2')
521+
sc = SomeClass(others=[soc1, soc2])
522+
523+
session.add(sc)
524+
session.commit()
525+
session.close()
526+
527+
sc_db = session.query(SomeClass).get(1)
528+
529+
assert sc_db.others[0].s == 'ehe1'
530+
assert sc_db.others[1].s == 'ehe2'
531+
532+
session.close()
533+
534+
def test_nested_sql_array_as_json(self):
535+
from spyne.model.complex import TableModel
536+
537+
engine = create_engine('sqlite:///:memory:')
538+
session = sessionmaker(bind=engine)()
539+
metadata = TableModel.Attributes.sqla_metadata = MetaData()
540+
metadata.bind = engine
541+
542+
class SomeOtherClass(ComplexModel):
543+
id = Integer32
544+
s = Unicode(64)
545+
546+
class SomeClass(TableModel):
547+
__tablename__ = 'some_class'
548+
__table_args__ = {"sqlite_autoincrement": True}
549+
550+
id = Integer32(primary_key=True)
551+
others = Array(SomeOtherClass, store_as='json')
552+
553+
get_sqlalchemy_table(SomeClass)
554+
555+
metadata.create_all()
556+
557+
soc1 = SomeOtherClass(s='ehe1')
558+
soc2 = SomeOtherClass(s='ehe2')
559+
sc = SomeClass(others=[soc1, soc2])
560+
561+
session.add(sc)
562+
session.commit()
563+
session.close()
564+
565+
sc_db = session.query(SomeClass).get(1)
566+
567+
assert sc_db.others[0].s == 'ehe1'
568+
assert sc_db.others[1].s == 'ehe2'
569+
570+
session.close()
571+
496572
if __name__ == '__main__':
497573
unittest.main()

0 commit comments

Comments
 (0)