pyopenapi / pyswagger

An OpenAPI (fka Swagger) client & converter in python, which is type-safe, dynamic, spec-compliant.
MIT License
384 stars 88 forks source link

AttributeError: 'unicode' object has no attribute 'iteritems' #69

Closed WillaLuo closed 8 years ago

WillaLuo commented 8 years ago

Hi, Mission My project define two kinds of 'Accept' tpye, That meas the "produces": [ "applicationcompute-v3+json", "application/compute-v3+directory+json" ] If produces is applicationcompute-v3+json and send request, the response Body should be like this a dic: { "result": [ { "enabled": true, "uri": "http://xxxxxx/sshkey/benguela/willa.luo/sshkey", "key": "ssh-xxxxxxDPZQrSdUCD2e5MMf/SNWw", "name": "/benguela/willa.luo/sshkey" } ] }' And if produce is application/compute-v3+directory+json and send request, the response Body should be like this a list: { "result": [ "/benguela/willa.luo/" ] }

When I use pyswagger, it seems like the response body don't support a list ? When I set produces to "application/compute-v3+directory+json" calling reps = client.request((req,reps), ) it Occur errors:

WillaLuo commented 8 years ago

Traceback (most recent call last): File "pyClientSample.py", line 51, in <module> abc = client.request((req, resp), ) File "/scratch/shuluo/swagger/swagger_sdk/compute-controlplane/virtualenv/lib/python2.6/site-packages/pyswagger/contrib/client/reques ts.py", line 55, in request raw=six.StringIO(rs.content).getvalue() File "/scratch/shuluo/swagger/swagger_sdk/compute-controlplane/virtualenv/lib/python2.6/site-packages/pyswagger/io.py", line 361, in apply_with self.__data = r.schema._prim_(data, self.__op._prim_factory) File "/scratch/shuluo/swagger/swagger_sdk/compute-controlplane/virtualenv/lib/python2.6/site-packages/pyswagger/spec/v2_0/objects.py" , line 92, in _prim_ return prim_factory.produce(self, v) File "/scratch/shuluo/swagger/swagger_sdk/compute-controlplane/virtualenv/lib/python2.6/site-packages/pyswagger/primitives/__init__.p y", line 184, in produce val = ret.apply_with(obj, val, ctx) File "/scratch/shuluo/swagger/swagger_sdk/compute-controlplane/virtualenv/lib/python2.6/site-packages/pyswagger/primitives/_model.py" , line 26, in apply_with self[k] = ctx['factory'].produce(pobj, v) File "/scratch/shuluo/swagger/swagger_sdk/compute-controlplane/virtualenv/lib/python2.6/site-packages/pyswagger/primitives/__init__.p y", line 180, in produce val = _2nd(obj, ret, val, ctx) File "/scratch/shuluo/swagger/swagger_sdk/compute-controlplane/virtualenv/lib/python2.6/site-packages/pyswagger/primitives/comm.py", line 40, in _2nd_pass_obj return ret.apply_with(obj, val, ctx) File "/scratch/shuluo/swagger/swagger_sdk/compute-controlplane/virtualenv/lib/python2.6/site-packages/pyswagger/primitives/_array.py" , line 37, in apply_with self.extend(map(functools.partial(ctx['factory'].produce, obj.items), val)) File "/scratch/shuluo/swagger/swagger_sdk/compute-controlplane/virtualenv/lib/python2.6/site-packages/pyswagger/primitives/__init__.p y", line 184, in produce val = ret.apply_with(obj, val, ctx) File "/scratch/shuluo/swagger/swagger_sdk/compute-controlplane/virtualenv/lib/python2.6/site-packages/pyswagger/primitives/_model.py" , line 23, in apply_with for k, v in six.iteritems(val): File "/scratch/shuluo/swagger/swagger_sdk/compute-controlplane/virtualenv/lib/python2.6/site-packages/six.py", line 599, in iteritems return d.iteritems(**kw) AttributeError: 'unicode' object has no attribute 'iteritems'

mission-liao commented 8 years ago

Array should be supported when the Schema object of your body is array. I guess somewhere else is failed, could you help to provide a minimum reproducible swagger spec to me to try?

WillaLuo commented 8 years ago

Ok, this is an example, If this is not enough, please let me know. Thanks very much!

{
   "swagger":"2.0",
   "info":{
      "version":"1.0.0",
      "description":"",
      "title":""
   },
   "host":"xx.xx.xx.xx",
   "basePath":"",
   "schemes":[
      "http"
   ],
   "consumes":[
      "application/compute-v3+json",
      "application/compute-v3+directory+json"
   ],
   "produces":[
      "application/compute-v3+json",
      "application/compute-v3+directory+json"
   ],
   "paths":{
      "/authenticate/":{
         "post":{
            "tags":[
               "Authenticate"
            ],
            "summary":"summary",
            "description":"description",
            "operationId":"addAuthenticate",
            "responses":{
               "204":{
                  "description":"description"
               }
            },
            "parameters":[
               {
                  "name":"body",
                  "in":"body",
                  "description":"description",
                  "required":true,
                  "schema":{
                     "$ref":"#/definitions/Authenticate"
                  }
               }
            ]
         }
      },
      "/imagelist/{container}/":{
         "get":{
            "tags":[
               "Image Lists"
            ],
            "summary":"summary",
            "description":"description",
            "operationId":"discoverImageList",
            "responses":{
               "200":{
                  "description":"description",
                  "schema":{
                     "$ref":"#/definitions/ImageList-container-response"
                  }
               }
            },
            "parameters":[
               {
                  "name":"container",
                  "in":"path",
                  "description":"description",
                  "required":true,
                  "type":"string"
               }
            ]
         }
      }
   },
   "definitions":{
      "Authenticate":{
         "description":"",
         "properties":{
            "password":{
               "type":"string",
               "description":""
            },
            "user":{
               "type":"string",
               "description":""
            }
         }
      },
      "ImageList-container-response":{
         "description":"",
         "properties":{
            "result":{
               "items":{
                  "$ref":"#/definitions/ImageList-response"
               },
               "type":"array",
               "description":"description"
            }
         }
      }
   },
   "tags":[
      {
         "name":"Authenticate"
      },
      {
         "name":"Image Lists"
      }
   ]
}
mission-liao commented 8 years ago

I didn't get into the code, but this definition:

"ImageList-container-response": {
  "description":"",
  "properties":{
       "result":{
            "items":{
                  "$ref":"#/definitions/ImageList-response"
             },
             "type":"array",
             "description":"description"
        }
   }
}

suggests that there should be an array of #/definitions/ImageList-response. I assume the type of ImageList-response is a model, then

{
   "result":[
      {
         "enabled":true,
         "uri":"http://xxxxxx/sshkey/benguela/willa.luo/sshkey",
         "key":"ssh-xxxxxxDPZQrSdUCD2e5MMf/SNWw",
         "name":"/benguela/willa.luo/sshkey"
      }
   ]
}

would be a correct response body, and the later one is not

{
   "result":[
      "/benguela/willa.luo/"
   ]
}

pyswagger would try to generate a model based on the string in response body, and it won't work.

If the type of ImageList-Response is a string, then the later one would work, and the previous one would fail (but not the exception).

WillaLuo commented 8 years ago

Hi, mission, is there possible let pyswagger to work with all the response body ? the previous and later one? Thanks Willa

mission-liao commented 8 years ago

No, it's no t supported by Swagger Spec, please refer to this issue for details.

WillaLuo commented 8 years ago

Ok, but in the Swagger ui, this too kind of Accept type can be both correctly used. Can I have a workaround to change the type of the response body when the accept is the second one when using pyswagger? Thanks!

mission-liao commented 8 years ago

The only way I know is to return everything in string, and parse it by yourself.

WillaLuo commented 8 years ago

you means, I should write code to parse the different response body according to the two different kinds of 'Accept' type? Thanks!

WillaLuo commented 8 years ago

Can we firstly distinguish the type of response body , not according to the 'type' parameters in the definition, and then give the correct result ?

Can our pyswagger regardless of the tpye of ImageList-response, here, "result":{ "items":{ "$ref":"#/definitions/ImageList-response" }, "type":"array", "description":"description" } Just give back the response body, I look into the code, when the type of response data is 'string',
request.py 51 rs = self.__s.send(rq, stream=True, **self.__send_opt) 52 resp.apply_with( 53 status=rs.status_code, 54 header=rs.headers, 55 raw=six.StringIO(rs.content).getvalue() # rs.content here is correctly although 56

primitives/_str.py def create_str(obj, v, ctx=None): 18 r = str(v) 19 validate_str(obj, r, v, ctx) 20 return r 21 22 def validate_email_(obj, ret, val, ctx): 23 if not validate_email(ret): 24 raise ValidationError('{0} is not a valid email for {1}'.format(ret, obj)) 25 26 return val Can I don't use this data parse raw=six.StringIO(rs.content).getvalue() , or close it in my code ? Thanks!

.

mission-liao commented 8 years ago

Currently, pyswagger would just try to parse everything based on the loaded swagger-definition. There is no option for "do nothing", I think I can add some option to skip those action and let you handle response body by yourself.

What do you think?

WillaLuo commented 8 years ago

OK, Thanks very much!

mission-liao commented 8 years ago

To bypass parsing of byte-stream of body response, just do this:

req, resp = op(...)
resp.raw_body_only = True

... # perform the request

resp.data == None # nothing parsed