Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing the xs:all tag #584

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions spyne/interface/xml_schema/defn.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ class Choice(SchemaBase):
elements = Element.customize(max_occurs="unbounded", sub_name="element")


class All(SchemaBase):
elements = Element.customize(max_occurs="unbounded", sub_name="element")


class Sequence(SchemaBase):
elements = Element.customize(max_occurs="unbounded", sub_name="element")
choices = Choice.customize(max_occurs="unbounded", sub_name="choice")
Expand All @@ -121,6 +125,7 @@ class ComplexType(SchemaBase):
attributes = Attribute.customize(max_occurs="unbounded",
sub_name="attribute")
choice = Choice
all = All


class Include(SchemaBase):
Expand Down
22 changes: 18 additions & 4 deletions spyne/interface/xml_schema/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ def complex_add(document, cls, tags):

deferred = deque()
choice_tags = defaultdict(lambda: etree.Element(XSD('choice')))
all_tags = defaultdict(lambda: etree.Element(XSD('all')))

for k, v in type_info.items():
assert isinstance(k, string_types)
Expand Down Expand Up @@ -253,14 +254,27 @@ def complex_add(document, cls, tags):
doc = etree.SubElement(annotation, XSD('documentation'))
doc.text = v_doc_text

if a.xml_choice_group is None:
sequence.append(member)
else:
if all([a.xml_choice_group, a.xml_all_group]):
raise ValueError("Cannot specify xml_choice_group and xml_all_group"
" at the same time. Class: %r. Groups: %r"
% (cls.__name__,
[a.xml_choice_group, a.xml_all_group]))
elif a.xml_choice_group is not None:
choice_tags[a.xml_choice_group].append(member)
elif a.xml_all_group is not None:
all_tags[a.xml_all_group].append(member)
else:
sequence.append(member)

sequence.extend(choice_tags.values())

if len(sequence) > 0:
if len(sequence) == 0:
if len(all_tags) > 1:
raise ValueError("Cannot specify more than one xml_all_group"
" at the same time. Class: %r. Groups: %r"
% (cls.__name__, [key for key in all_tags.keys()]))
sequence_parent.extend(all_tags.values())
else:
sequence_parent.append(sequence)

_ext_elements = dict()
Expand Down
5 changes: 5 additions & 0 deletions spyne/interface/xml_schema/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,11 @@ def process_element(e):
for e in ch.elements:
process_element(e)

if c.all is not None:
if c.all.elements is not None:
for e in c.all.elements:
process_element(e)

if c.choice is not None:
if c.choice.elements is not None:
for e in c.choice.elements:
Expand Down
7 changes: 6 additions & 1 deletion spyne/model/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,12 @@ class Attributes(object):

xml_choice_group = None
"""When not None, shares the same <choice> tag with fields with the same
xml_choice_group value.
xml_choice_group value. Cannot be used in conjunction with xml_all_group.
"""

xml_all_group = None
"""When not None, shares the same <all> tag with fields with the same
xml_all_group value. Cannot be used in conjunction with xml_choice_group.
"""

index = None
Expand Down
83 changes: 83 additions & 0 deletions spyne/test/interface/test_xml_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,89 @@ def wooo(ctx):
'/xs:sequence/xs:choice/xs:element[@name="one"]',
namespaces={'xs': NS_XSD})) > 0

def test_all_tag(self):
class SomeObject(ComplexModel):
__namespace__ = "flag_ns"

usa = Boolean(xml_all_group="flags")
can = Boolean(xml_all_group="flags")
ident = Unicode

class SomeService(Service):
@rpc(_returns=SomeObject)
def wooo(ctx):
return SomeObject()

Application([SomeService],
tns='flag.ns',
in_protocol=Soap11(validator='lxml'),
out_protocol=Soap11()
)

docs = get_schema_documents([SomeObject])
doc = docs['tns']
print(etree.tostring(doc, pretty_print=True))
assert len(doc.xpath('/xs:schema/xs:complexType[@name="SomeObject"]'
'/xs:sequence/xs:element[@name="ident"]',
namespaces={'xs': NS_XSD})) > 0
assert len(doc.xpath('/xs:schema/xs:complexType[@name="SomeObject"]'
'/xs:any/xs:element[@name="usa"]',
namespaces={'xs': NS_XSD})) == 0

def test_all_tag2(self):
class SomeObject(ComplexModel):
__namespace__ = "flag_ns"

ident = Unicode
usa = Boolean(xml_all_group="flags")
can = Boolean(xml_all_group="flags")

class SomeService(Service):
@rpc(_returns=SomeObject)
def wooo(ctx):
return SomeObject()

Application([SomeService],
tns='flag.ns',
in_protocol=Soap11(validator='lxml'),
out_protocol=Soap11()
)

docs = get_schema_documents([SomeObject])
doc = docs['tns']
print(etree.tostring(doc, pretty_print=True))
assert len(doc.xpath('/xs:schema/xs:complexType[@name="SomeObject"]'
'/xs:sequence/xs:element[@name="ident"]',
namespaces={'xs': NS_XSD})) > 0
assert len(doc.xpath('/xs:schema/xs:complexType[@name="SomeObject"]'
'/xs:any/xs:element[@name="usa"]',
namespaces={'xs': NS_XSD})) == 0

def test_all_tag3(self):
class SomeObject(ComplexModel):
__namespace__ = "flag_ns"

usa = Boolean(xml_all_group="flags")
can = Boolean(xml_all_group="flags")

class SomeService(Service):
@rpc(_returns=SomeObject)
def wooo(ctx):
return SomeObject()

Application([SomeService],
tns='flag.ns',
in_protocol=Soap11(validator='lxml'),
out_protocol=Soap11()
)

docs = get_schema_documents([SomeObject])
doc = docs['tns']
print(etree.tostring(doc, pretty_print=True))
assert len(doc.xpath('/xs:schema/xs:complexType[@name="SomeObject"]'
'/xs:any/xs:element[@name="usa"]',
namespaces={'xs': NS_XSD})) > 0

def test_customized_class_with_empty_subclass(self):
class SummaryStatsOfDouble(ComplexModel):
_type_info = [('Min', XmlAttribute(Integer, use='required')),
Expand Down