0

UPDATE 05-09-2019: I still haven't been able to fix this, but I least I think I figured out where the problem is coming from. Apparently Zeep isn't able to determine the type for some elements, I imagine that's what's causing the error I described originally:

DEBUG:zeep.xsd.schema:register_element('{http://eur-lex.europa.eu/search}WORK_PART_OF_WORK', <Element(name='WORK_PART_OF_WORK', type=<UnresolvedType(qname='{http://eur-lex.europa.eu/search}Relation')>)>)

This is happening for multiple elements, so my problem now is how to handle these cases. Is there a way to manually define the type for unrecognized elements?

Original post:

I'm working on a web application in Python 3.6 and Flask, where I need to get some data from a web service using a SOAP API. I've installed the Zeep module to do so, but since this is my first time using a SOAP API and Zeep I'm quite lost. As far as I can tell the request is being sent correctly and I am receiving information from the web service in the terminal window, but then I get the following error:

AttributeError: 'str' object has no attribute 'keys'

The below is WSDL I need to use: https://eur-lex.europa.eu/eurlex-ws?wsdl and XML query I'm trying to run, I've tested it in SOAPUI and it works fine:

   <soap:Body>
      <sear:searchRequest>
         <sear:expertQuery>Title~"Decision (EU) 2016/342"</sear:expertQuery>
         <sear:page>1</sear:page>
         <sear:pageSize>10</sear:pageSize>
         <sear:searchLanguage>en</sear:searchLanguage>
      </sear:searchRequest>
   </soap:Body>

My code in Python is:

history = HistoryPlugin()
client = Client("https://eur-lex.europa.eu/eurlex-ws?wsdl", wsse=UsernameToken("#######","###########"), plugins=[history])
request_data = {
    'expertQuery': 'Title~"Decision (EU) 2016/342"',
    'page': 1,
    'pageSize': 10,
    'searchLanguage': 'en'
}
response = client.service.doQuery(**request_data)

In the terminal window I see the request is being sent and data is being received:

DEBUG:zeep.transports:HTTP Post to https://eur-lex.europa.eu/EURLexWebService:
<?xml version='1.0' encoding='utf-8'?>
<soap-env:Envelope xmlns:soap-env="http://www.w3.org/2003/05/soap-envelope"><soap-env:Header><wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><wsse:UsernameToken><wsse:Username>########</wsse:Username><wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">###########</wsse:Password></wsse:UsernameToken></wsse:Security></soap-env:Header><soap-env:Body><ns0:searchRequest xmlns:ns0="http://eur-lex.europa.eu/search"><ns0:expertQuery>Title~"Decision (EU) 2016/342"</ns0:expertQuery><ns0:page>1</ns0:page><ns0:pageSize>10</ns0:pageSize><ns0:searchLanguage>en</ns0:searchLanguage></ns0:searchRequest></soap-env:Body></soap-env:Envelope>
DEBUG:urllib3.connectionpool:https://eur-lex.europa.eu:443 "POST /EURLexWebService HTTP/1.1" 200 None
DEBUG:zeep.transports:HTTP Response from https://eur-lex.europa.eu/EURLexWebService (status: 200):
<?xml version='1.0' encoding='UTF-8'?><S:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:S="http://www.w3.org/2003/05/soap-envelope"><env:Header/><S:Body><searchResults xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:searchLayerHelper="xalan//eu.europa.ec.op.searchlayer.domain.util.SearchLayerHelper" xmlns="http://eur-lex.europa.eu/search" xsi:schemaLocation="http://eur-lex.europa.eu/search https://eur-lex.europa.eu/eurlex-ws?xsd=3">
   <numhits>1</numhits>
   <totalhits>1</totalhits>
   <page>1</page>
   <language>en</language>
   <result>
      <reference>eng_cellar:9c53bb30-eb42-11e5-8a81-01aa75ed71a1_en</reference>
      <rank>1</rank>
      <content>
         <NOTICE>
            <EXPRESSION>
               <EXPRESSION_TITLE>
                  <VALUE>Council Decision (EU) 2016/342 of 12 February 2016 on the conclusion, on behalf of the Union, of the Stabilisation and Association Agreement between the European Union and the European Atomic Energy Community, of the one part, and Kosovo *, of the other part</VALUE>
               </EXPRESSION_TITLE>
               <EXPRESSION_USES_LANGUAGE>
                  <URI>
                     <IDENTIFIER>ENG</IDENTIFIER>
                  </URI>
               </EXPRESSION_USES_LANGUAGE>
            </EXPRESSION>
            <WORK>
               <ID_CELEX>
                  <VALUE>32016D0342</VALUE>
               </ID_CELEX>
               <RESOURCE_LEGAL_IN-FORCE>
                  <VALUE>true</VALUE>
               </RESOURCE_LEGAL_IN-FORCE>
               <RESOURCE_LEGAL_PUBLISHED_IN_OFFICIAL-JOURNAL>
                  <EMBEDDED_NOTICE>
                     <WORK>
(***SHORTENED BECAUSE THE RESULTS ARE REALLY LONG***)
                              </URI>
                           </SAMEAS>
                        </MANIFESTATION_PART_OF_MANIFESTATION>
                     </MANIFESTATION>
                  </EMBEDDED_NOTICE>
               </WORK_HAS_EXPRESSION>
            </WORK>
         </NOTICE>
      </content>
   </result>
</searchResults></S:Body></S:Envelope>

But then right after I get this list of errors:

127.0.0.1 - - [30/Aug/2019 16:59:31] "GET /test HTTP/1.1" 500 -
INFO:werkzeug:127.0.0.1 - - [30/Aug/2019 16:59:31] "GET /test HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/home/luiscosta/.local/lib/python3.6/site-packages/flask/app.py", line 2463, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/luiscosta/.local/lib/python3.6/site-packages/flask/app.py", line 2449, in wsgi_app
    response = self.handle_exception(e)
  File "/home/luiscosta/.local/lib/python3.6/site-packages/flask/app.py", line 1866, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/luiscosta/.local/lib/python3.6/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/home/luiscosta/.local/lib/python3.6/site-packages/flask/app.py", line 2446, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/luiscosta/.local/lib/python3.6/site-packages/flask/app.py", line 1951, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/luiscosta/.local/lib/python3.6/site-packages/flask/app.py", line 1820, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/luiscosta/.local/lib/python3.6/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/home/luiscosta/.local/lib/python3.6/site-packages/flask/app.py", line 1949, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/luiscosta/.local/lib/python3.6/site-packages/flask/app.py", line 1935, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/luiscosta/eur-lex/helpers.py", line 38, in decorated_function
    return f(*args, **kwargs)
  File "/home/luiscosta/eur-lex/application.py", line 128, in test
    response = client.service.doQuery(**request_data)
  File "/usr/local/lib/python3.6/dist-packages/zeep/proxy.py", line 45, in __call__
    kwargs,
  File "/usr/local/lib/python3.6/dist-packages/zeep/wsdl/bindings/soap.py", line 130, in send
    return self.process_reply(client, operation_obj, response)
  File "/usr/local/lib/python3.6/dist-packages/zeep/wsdl/bindings/soap.py", line 197, in process_reply
    result = operation.process_reply(doc)
  File "/usr/local/lib/python3.6/dist-packages/zeep/wsdl/bindings/soap.py", line 396, in process_reply
    return self.output.deserialize(envelope)
  File "/usr/local/lib/python3.6/dist-packages/zeep/wsdl/messages/soap.py", line 94, in deserialize
    body_result = self._deserialize_body(body)
  File "/usr/local/lib/python3.6/dist-packages/zeep/wsdl/messages/soap.py", line 425, in _deserialize_body
    result = self.body.parse(xmlelement, self.wsdl.types, context=context)
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/element.py", line 130, in parse
    schema_type=self.type,
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/types/complex.py", line 194, in parse_xmlelement
    elements, schema, name, context=context
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/indicators.py", line 609, in parse_xmlelements
    xmlelements, schema, name, context=context
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/element.py", line 176, in parse_xmlelements
    item = self.parse(xmlelement, schema, allow_none=True, context=context)
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/element.py", line 130, in parse
    schema_type=self.type,
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/types/complex.py", line 194, in parse_xmlelement
    elements, schema, name, context=context
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/indicators.py", line 609, in parse_xmlelements
    xmlelements, schema, name, context=context
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/element.py", line 176, in parse_xmlelements
    item = self.parse(xmlelement, schema, allow_none=True, context=context)
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/element.py", line 130, in parse
    schema_type=self.type,
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/types/complex.py", line 194, in parse_xmlelement
    elements, schema, name, context=context
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/indicators.py", line 609, in parse_xmlelements
    xmlelements, schema, name, context=context
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/element.py", line 176, in parse_xmlelements
    item = self.parse(xmlelement, schema, allow_none=True, context=context)
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/element.py", line 130, in parse
    schema_type=self.type,
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/types/complex.py", line 194, in parse_xmlelement
    elements, schema, name, context=context
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/indicators.py", line 362, in parse_xmlelements
    context=context,
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/indicators.py", line 609, in parse_xmlelements
    xmlelements, schema, name, context=context
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/element.py", line 176, in parse_xmlelements
    item = self.parse(xmlelement, schema, allow_none=True, context=context)
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/element.py", line 130, in parse
    schema_type=self.type,
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/types/complex.py", line 194, in parse_xmlelement
    elements, schema, name, context=context
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/indicators.py", line 609, in parse_xmlelements
    xmlelements, schema, name, context=context
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/element.py", line 176, in parse_xmlelements
    item = self.parse(xmlelement, schema, allow_none=True, context=context)
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/element.py", line 130, in parse
    schema_type=self.type,
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/types/complex.py", line 194, in parse_xmlelement
    elements, schema, name, context=context
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/indicators.py", line 307, in parse_xmlelements
    sub_elements, schema, context=context
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/element.py", line 176, in parse_xmlelements
    item = self.parse(xmlelement, schema, allow_none=True, context=context)
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/element.py", line 130, in parse
    schema_type=self.type,
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/types/complex.py", line 219, in parse_xmlelement
    value = self._value_class(**init_kwargs)
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/valueobjects.py", line 95, in __init__
    items = _process_signature(self._xsd_type, args, kwargs)
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/valueobjects.py", line 207, in _process_signature
    values = element.parse_kwargs(kwargs, element_name, available_kwargs)
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/indicators.py", line 181, in parse_kwargs
    value = element.parse_kwargs(item_value, item_name, item_kwargs)
  File "/usr/local/lib/python3.6/dist-packages/zeep/xsd/elements/indicators.py", line 686, in parse_kwargs
    available_sub_kwargs = set(sub_kwargs.keys())
AttributeError: 'str' object has no attribute 'keys'

I'm completely lost and don't know how to fix this because I don't understand what the error means and I couldn't find any information online for this specific error in Zeep.

Any ideas?

Luis Costa
  • 13
  • 5
  • I don't know `zeep`, but that error means python is trying to call a method reserved for dictionaries on a string object, i.e. `>>>x = 'a string' ---> x.keys() ---> AttributeError: 'str' object has no attribute 'keys'`. This only works for dicts, i.e. >>>`x = {'a key':'a value'} ---> x.keys() ---> dict_keys(['a key'])`. My guess is that your `json` `request_data` is being treated as `str` by python. – Cole Robertson Aug 30 '19 at 16:57
  • 1
    Ok, that's a start. From what I understood the Zeep object was supposed to return a dictionary, but maybe I'm not using it properly. I'll look into it, thanks for the tip! – Luis Costa Aug 31 '19 at 17:17
  • It might have been returning JSON. Have you solved this issue? – user1558604 Dec 03 '19 at 03:20
  • @user1558604 I did solve it, by using Zeep's raw response option and then parsing the results with beautiful soup. – Luis Costa Dec 04 '19 at 09:12

0 Answers0