Skip to content

Commit

Permalink
Add option to avoid sorting namespaces.
Browse files Browse the repository at this point in the history
This is backwards compatible by keeping the default, but should be switched in the next major release. Closes #3
  • Loading branch information
phillbaker committed Jun 22, 2019
1 parent 2664774 commit 41a6f13
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Release notes
=============

version 0.8.2 (Unreleased)
------------------------
* Add option to disable the sorting of namespaces.

version 0.8.1 (2019-02-06)
------------------------
* Fix bug introduced in 0.8.0, initialize optional arrays with empty lists (@guifran001)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ They are as follows:
* autoblend:: Flag that ensures that the schema(s) defined
within the WSDL import each other.
* nosend:: Flag that causes suds to generate the soap envelope but not send it. Instead, a `RequestContext` is returned Default: False.
* sortNamespaces:: Flag that causes suds to sort namespaces alphabetically on storing them. Default: True.

## Enumerations

Expand Down
8 changes: 7 additions & 1 deletion suds/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ class Options(Skin):
ever automatically unwrapped.
- type: I{bool}
- default: True
- B{sortNamespaces} - Namespaces are sorted alphabetically. If disabled,
namespaces are left in the order they are received from the source.
Enabled by default for historical purposes.
- type: I{bool}
- default: True
"""
def __init__(self, **kwargs):
domain = __name__
Expand All @@ -146,5 +151,6 @@ def __init__(self, **kwargs):
Definition('cachingpolicy', int, 0),
Definition('plugins', (list, tuple), []),
Definition('nosend', bool, False),
Definition('unwrap', bool, True)]
Definition('unwrap', bool, True),
Definition('sortNamespaces', bool, True)]
Skin.__init__(self, domain, definitions, kwargs)
8 changes: 7 additions & 1 deletion suds/servicedefinition.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class ServiceDefinition(UnicodeMixin):
of a service.
@ivar wsdl: A wsdl.
@type wsdl: L{wsdl.Definitions}
@ivar sort_namespaces: Whether to sort namespaces on storing them.
@ivar service: The service object.
@type service: L{suds.wsdl.Service}
@ivar ports: A list of port-tuple: (port, [(method-name, pdef)])
Expand All @@ -48,6 +49,8 @@ def __init__(self, wsdl, service):
@type wsdl: L{Definitions}
@param service: A service B{name}.
@type service: str
@param service: A service B{name}.
@param sort_namespaces: Whether to sort namespaces on storing them.
"""
self.wsdl = wsdl
self.service = service
Expand Down Expand Up @@ -121,7 +124,10 @@ def getprefixes(self):
if ns[1] in namespaces: continue
namespaces.append(ns[1])
i = 0
namespaces.sort()

if self.wsdl.options.sortNamespaces:
namespaces.sort()

for u in namespaces:
p = self.nextprefix()
ns = (p, u)
Expand Down
220 changes: 220 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,226 @@ def test_resolving_references_to_later_entities_in_XML():
assert input_element == ('Lollypop', 'xsd-ns')


def test_sortnamespaces_default():
"""
Option to not sort namespaces.
"""
wsdl = b("""\
<?xml version='1.0' encoding='UTF-8'?>
<definitions targetNamespace="urn:wsdl" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:tns="urn:wsdl" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<types>
<xs:schema elementFormDefault="qualified" targetNamespace="urn:wsdl" xmlns:ns1="urn:wsdl:common" xmlns:ns2="urn:wsdl:account" xmlns:tns="urn:wsdl" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="urn:wsdl:common"/>
<xs:import namespace="urn:wsdl:account"/>
<xs:element name="PingRequest">
<xs:complexType>
<xs:complexContent>
<xs:extension base="ns1:RequestMessage"/>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element name="PingResponse">
<xs:complexType>
<xs:complexContent>
<xs:extension base="ns1:ResponseMessage"/>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:complexType name="CustomField">
<xs:sequence>
<xs:element name="FieldId" type="xs:long"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
<xs:schema elementFormDefault="qualified" targetNamespace="urn:wsdl:common" xmlns:tns="urn:wsdl:common" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="RequestMessage">
<xs:sequence>
<xs:element minOccurs="0" name="MessageHeader" type="tns:MessageHeader"/>
<xs:element minOccurs="0" name="SessionId" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ResponseMessage">
<xs:sequence>
<xs:element minOccurs="0" name="MessageHeader" type="tns:MessageHeader"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="ErrorList" type="tns:ErrorResponse"/>
<xs:element name="hasErrors" type="xs:boolean"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="MessageHeader">
<xs:sequence>
<xs:element minOccurs="0" name="Trace" type="xs:int"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ErrorResponse">
<xs:sequence>
<xs:element name="ErrorCode" type="xs:int"/>
<xs:element name="ErrorMessage" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Email">
<xs:sequence>
<xs:element name="EmailAddress" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
<xs:schema elementFormDefault="qualified" targetNamespace="urn:wsdl:account" xmlns:msg="urn:wsdl:common" xmlns:tns="urn:wsdl:account" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="urn:wsdl:common" />
<xs:simpleType name="UserAccess">
<xs:restriction base="xs:string">
<xs:enumeration value="NO_ACCESS"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
</types>
<message name="PingRequest">
<part element="tns:PingRequest" name="body"/>
</message>
<message name="PingResponse">
<part element="tns:PingResponse" name="body"/>
</message>
<portType name="Service">
<operation name="Ping">
<input message="tns:PingRequest"/>
<output message="tns:PingResponse"/>
</operation>
</portType>
<binding name="ServiceBinding" type="tns:Service">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="Ping">
<soap:operation soapAction="" style="document"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="Service">
<port binding="tns:ServiceBinding" name="ServicePort">
<soap:address location="/services/Service/"/>
</port>
</service>
</definitions>
""")
store = MockDocumentStore(wsdl=wsdl)
client = suds.client.Client("suds://wsdl", cache=None, documentStore=store)
prefixes = client.sd[0].prefixes
assert str(prefixes[0][1]) == 'urn:wsdl'
assert str(prefixes[1][1]) == 'urn:wsdl:account'
assert str(prefixes[2][1]) == 'urn:wsdl:common'


def test_sortnamespaces_turned_off():
"""
Option to not sort namespaces.
"""
wsdl = b("""\
<?xml version='1.0' encoding='UTF-8'?>
<definitions targetNamespace="urn:wsdl" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:tns="urn:wsdl" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<types>
<xs:schema elementFormDefault="qualified" targetNamespace="urn:wsdl" xmlns:ns1="urn:wsdl:common" xmlns:ns2="urn:wsdl:account" xmlns:tns="urn:wsdl" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="urn:wsdl:common"/>
<xs:import namespace="urn:wsdl:account"/>
<xs:element name="PingRequest">
<xs:complexType>
<xs:complexContent>
<xs:extension base="ns1:RequestMessage"/>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element name="PingResponse">
<xs:complexType>
<xs:complexContent>
<xs:extension base="ns1:ResponseMessage"/>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:complexType name="CustomField">
<xs:sequence>
<xs:element name="FieldId" type="xs:long"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
<xs:schema elementFormDefault="qualified" targetNamespace="urn:wsdl:common" xmlns:tns="urn:wsdl:common" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="RequestMessage">
<xs:sequence>
<xs:element minOccurs="0" name="MessageHeader" type="tns:MessageHeader"/>
<xs:element minOccurs="0" name="SessionId" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ResponseMessage">
<xs:sequence>
<xs:element minOccurs="0" name="MessageHeader" type="tns:MessageHeader"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="ErrorList" type="tns:ErrorResponse"/>
<xs:element name="hasErrors" type="xs:boolean"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="MessageHeader">
<xs:sequence>
<xs:element minOccurs="0" name="Trace" type="xs:int"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ErrorResponse">
<xs:sequence>
<xs:element name="ErrorCode" type="xs:int"/>
<xs:element name="ErrorMessage" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Email">
<xs:sequence>
<xs:element name="EmailAddress" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
<xs:schema elementFormDefault="qualified" targetNamespace="urn:wsdl:account" xmlns:msg="urn:wsdl:common" xmlns:tns="urn:wsdl:account" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="urn:wsdl:common" />
<xs:simpleType name="UserAccess">
<xs:restriction base="xs:string">
<xs:enumeration value="NO_ACCESS"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
</types>
<message name="PingRequest">
<part element="tns:PingRequest" name="body"/>
</message>
<message name="PingResponse">
<part element="tns:PingResponse" name="body"/>
</message>
<portType name="Service">
<operation name="Ping">
<input message="tns:PingRequest"/>
<output message="tns:PingResponse"/>
</operation>
</portType>
<binding name="ServiceBinding" type="tns:Service">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="Ping">
<soap:operation soapAction="" style="document"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="Service">
<port binding="tns:ServiceBinding" name="ServicePort">
<soap:address location="/services/Service/"/>
</port>
</service>
</definitions>
""")
store = MockDocumentStore(wsdl=wsdl)
client = suds.client.Client("suds://wsdl", cache=None, documentStore=store, sortNamespaces=False)
prefixes = client.sd[0].prefixes
assert str(prefixes[0][1]) == 'urn:wsdl:common'
assert str(prefixes[1][1]) == 'urn:wsdl'
assert str(prefixes[2][1]) == 'urn:wsdl:account'


class TestRecursiveWSDLImport:
"""
Test different recursive WSDL import variations.
Expand Down

0 comments on commit 41a6f13

Please sign in to comment.