How to generate a List<String> with zeep? - python

I'm using the Python library zeep to talk to a SOAP service. One of the required arguments in the documentation is of type List<String> and in the WSDL I found this:
<xs:element minOccurs="0" maxOccurs="1" name="IncludedLenders" type="tns:ArrayOfString"/>
And I believe AraryOfString is defined as:
<xs:complexType name="ArrayOfString">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="string" nillable="true" type="xs:string"/>
</xs:sequence>
</xs:complexType>
How do I make zeep generate the values for that? I tried with:
"IncludedLenders": [
"BMS",
"BME"
]
but that generates:
<ns0:IncludedLenders>
<ns0:string>BMS</ns0:string>
</ns0:IncludedLenders>
instead of:
<ns0:IncludedLenders>
<ns0:string>BMS</ns0:string>
<ns0:string>BME</ns0:string>
</ns0:IncludedLenders>
Any ideas how to generate the later?

I figured out. First I needed to extract the ArrayOfString type:
array_of_string_type = client.get_type("ns1:ArrayOfString")
and then create it this way:
"IncludedLenders": array_of_string_type(["BMS","BME"])

Related

How to make a complexType that extends a simpleType

I have to implement a service with spyne that exposes a specific wsdl
right now I'm unable to replicate this definition:
<xs:complexType name="MyType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="version" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
So far the best I was able to achieve is
class MyType(ComplexModel):
__namespace__ = "uri:my-ns"
__extends__ = primitive.Unicode
version = XmlAttribute(primitive.Unicode)
that raise an AttributeError but if I "patch" models/complex.py[1] I obtain:
<xs:complexType name="MyType">
<xs:complexContent>
<xs:extension base="xs:string">
<xs:attribute name="version " type="xs:string"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
almost there! but my complexType has a complexContent instead of a simpleContent
[1] I'm using the master branch of spyne (0f587b2d606b54e41fc5cc3d33b51cc3b324a2ca) and if I add __extends__ = primitive.Unicode I need to change line 602 of model/complex.py from:
if eattr._subclasses is None
to
if getattr(eattr, "_subclasses", None) is None

PyXB : Creating More Rigid Instances of Anonymous Types

Is it possible to use PyXB to generate a module from an XSD that has anonymous complex types that can have some basic type- and constraints-checking ?
Here's what I've managed so far:
Using a 3rd-Party (Anonymised) XSD:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://www.foo.com" xmlns="http://www.foo.com" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="ResultSet">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="Bar">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="1" name="code" type="xs:string"/>
<xs:element minOccurs="0" name="description" type="xs:string"/>
<xs:element minOccurs="0" name="name" type="xs:string"/>
</xs:sequence>
<xs:attribute name="href" type="xs:string"/>
<xs:attribute name="id" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
I generated a python module:
> pyxbgen -u Foo.xsd -m Foo
Python for http://www.foo.com requires 1 modules
> ls
Foo.py Foo.xsd
Then in the python interpreter, I was able to create a ResultSet instance and create entries under Bar:
> python
Python 2.7.6 (default, Mar 22 2014, 22:59:56)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyxb
>>> import Foo
>>> Foo
<module 'Foo' from 'Foo.py'>
>>> rs = Foo.ResultSet()
>>> rs
<Foo.CTD_ANON object at 0x7f7360834950>
>>> rs.Bar
<pyxb.binding.content._PluralBinding object at 0x7f7360834a90>
>>> rs.Bar.append( pyxb.BIND( name='a', code='b', description ='c' ) )
>>> rs.Bar
<pyxb.binding.content._PluralBinding object at 0x7f39f1018a90>
>>> [x for x in rs.Bar ]
[<Foo.CTD_ANON_ object at 0x7f39f1018b90>]
And I'm able to interact with the members of rs.Bar[0].
BUT what bothers me is that I can also:
>>> rs.Bar.append( pyxb.BIND( name='a' ) )
and it'll accept it, even though the code element in the anonymous complexType has an attribute of minOccurs="1". I gather from reading the docs and some other questions on StackOverflow that PyXB is trying to reverse-engineer the data types on the fly, so that might explain the permissiveness.
I'd like if possible to overlay something on the whole process that allows me to basically say:
>>> rs.Bar.append( Something( name='a', code='b' ) )
and have it complain about any missing parameters. So far I have mulled over just writing my own functions that return the result of pyxb.BIND() with the appropriate parameters, but this implies quite a degree of manual intervention.
Is there a way with PyXB to create such smarts automatically ?
PyXB isn't being permissive because of the reverse-engineering, but because it doesn't validate elements at the time they are constructed, nor does it validate complex types when they are assigned to elements. This is because it may require multiple Python statements to assign the necessary content to the element: it can't always be reasonably provided through parameters to pyxb.BIND().
For example, your code can be completed by doing:
rs.Bar.append( pyxb.BIND( name='a' ) )
rs.Bar[-1].code = '42'
at which point rs will be valid.
If you want the validation performed at the point the object is appended, you should use an intermediary function that validates the element that was appended. The following shows how to do this in a situation where the incoming parameter may be an unresolved pyxb.BIND structure:
import pyxb
import Foo
def checkedAppendBar(rs, bar):
rs.Bar.append(bar)
bar = rs.Bar[-1]
try:
bar.validateBinding()
except pyxb.ValidationError as e:
print e.details()
return bar
rs = Foo.ResultSet()
# Fails:
#bar = checkedAppendBar(rs, pyxb.BIND(name='a'))
bar = checkedAppendBar(rs, pyxb.BIND(name='a', code='42'))

pyxb UnrecognizedDOMRootNodeError

i've got the following xml schema:
<xsd:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="DataPackage">
<xsd:sequence>
<xsd:element name="timestamp" type="xsd:float" default="0.0"/>
<xsd:element name="type" type="xsd:string" default="None"/>
<xsd:element name="host" type="xsd:string" default="None"/>
<xsd:element name="data" type="Data" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Data">
<xsd:sequence>
<xsd:element name="item" type="Item" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Item">
<xsd:sequence>
<xsd:element name="key" type="xsd:string"/>
<xsd:element name="val" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
I used pyxbgen -u DataPackage.xsd -m DataPackage to generate the corresponding python classes and used these to generate the following xml code:
<?xml version="1.0" encoding="utf-8"?>
<DataPackage>
<timestamp>1378970933.29</timestamp>
<type>None</type>
<host>Client 1</host>
<data>
<item>
<key>KEY1</key>
<val>value1</val>
</item>
</data>
</DataPackage>
If i try to read this using the following in python interpreter:
import DataPackage
xml = file("dataPackage-Test.xml").read()
data = DataPackage.CreateFromDocument(xml)
I get the exception:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "DataPackage.py", line 54, in CreateFromDocument
instance = handler.rootObject()
File "/usr/local/lib/python2.7/dist-packages/pyxb/binding/saxer.py", line 274, in rootObject
raise pyxb.UnrecognizedDOMRootNodeError(self.__rootObject)
pyxb.exceptions_.UnrecognizedDOMRootNodeError: <pyxb.utils.saxdom.Element object at 0x9c7c76c>
Anyone an idea what's wrong?
Your schema defines a top-level complex type named DataPackage, but does not define any top-level elements. Thus the DOM element DataPackage has no corresponding element that PyXB can use to process it.
You need to add something like:
<element name="DataPackage" type="DataPackage"/>
Note that in XML Schema the namespaces for elements and types are distinct, but in Python they are not, so PyXB will rename one of them (the complex type in this case) to avoid the conflict. See http://pyxb.sourceforge.net/arch_binding.html?highlight=conflict#deconflicting-names

spyne generates bad WSDL/XSD schema for ComplexModels with ComplexModel children

I'm trying to use spyne to implement a SOAP service in Python. My client sends SOAP requests like this:
<ns1:loadServices xmlns:ns1="dummy">
<serviceParams xmlns="dummy">
<header>
<user>foo</user>
<password>secret</password>
</header>
</serviceParams>
</ns1:loadServices>
But I have difficulties putting that structure into a spyne model.
So far I came up with this code:
class Header(ComplexModel):
__type_name__ = 'header'
user = Unicode
password = Unicode
class serviceParams(ComplexModel):
__type_name__ = 'serviceParams'
header = Header()
class DummyService(ServiceBase):
#rpc(serviceParams, _returns=Unicode)
def loadServices(ctx, serviceParams):
return '42'
The problem is that spyne generates and XSD like this:
...
<xs:complexType name="loadServices">
<xs:sequence>
<xs:element name="serviceParams" type="tns:serviceParams" minOccurs="0" nillable="true"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="serviceParams"/>
...
which is not what I want because essentially it says that "serviceParams" is just an empty tag without children.
Is that a bug in spyne? Or am I missing something?
It turned out that this line was the culprit:
header = Header()
that should be:
header = Header
Very nasty behavior and really easy to overlook.

xml validation: validating a URI type

I'm using python's lxml to validate xmls against a schema. I have a schema with an element:
<xs:element name="link-url" type="xs:anyURL"/>
and I test, for example, this (part of an) xml:
<a link-url="server/path"/>
I would like this test to FAIL because the link-url doesn't start with http://. I tried switching anyURI to anyURL but this results in an exception - it's not a valid tag.
Is this possible with lxml? is it possible at all with schema validation?
(I'm pretty sure xs:anyURL is not valid. The XML Schema standard calls it anyURI. And since link-url is an attribute, shouldn't you be using xs:attribute instead of xs:element?)
You could restrict the URIs by creating a new simpleType based on it, and put a restriction on the pattern. For example,
#!/usr/bin/env python2.6
from lxml import etree
from StringIO import StringIO
schema_doc = etree.parse(StringIO('''
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="httpURL">
<xs:restriction base="xs:anyURI">
<xs:pattern value='https?://.+'/>
<!-- accepts only http:// or https:// URIs. -->
</xs:restriction>
</xs:simpleType>
<xs:element name="a">
<xs:complexType>
<xs:attribute name="link-url" type="httpURL"/>
</xs:complexType>
</xs:element>
</xs:schema>
''')) #/
schema = etree.XMLSchema(schema_doc)
schema.assertValid(etree.parse(StringIO('<a link-url="http://sd" />')))
assert not schema(etree.parse(StringIO('<a link-url="server/path" />')))

Categories