Search code examples
pythonflaskswaggerflasgger

Flasgger AttributeError: 'NoneType' object has no attribute 'get'?


When I try to access http://localhost:8000/apidocs/index.html I get the following error:

127.0.0.1 - - [31/Jan/2018 16:38:09] "GET /apidocs/index.html HTTP/1.1" 302 -
127.0.0.1 - - [31/Jan/2018 16:38:10] "GET /apidocs/ HTTP/1.1" 200 -
127.0.0.1 - - [31/Jan/2018 16:38:10] "GET /apispec_1.json HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1997, in __call__
    return self.wsgi_app(environ, start_response)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)
  File "/usr/local/lib/python2.7/dist-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1540, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python2.7/dist-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/usr/local/lib/python2.7/dist-packages/flask/views.py", line 84, in view
    return self.dispatch_request(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/flask/views.py", line 149, in dispatch_request
    return meth(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/flasgger/base.py", line 205, in get
    prefix_ids=prefix_ids)
  File "/usr/local/lib/python2.7/dist-packages/flasgger/utils.py", line 648, in extract_definitions
    properties.values(), level + 1, endpoint, verb, prefix_ids)
  File "/usr/local/lib/python2.7/dist-packages/flasgger/utils.py", line 617, in extract_definitions
    if not getattr(item, 'get'):
AttributeError: 'NoneType' object has no attribute 'get'

This is my flask application:

def init_deserializer_restful_api():
    # Get port number for the web app.
    PORT = 8000

    # Initiate the Flask app
    app = Flask(__name__)
    Swagger(app)
    CORS(app)

    # Handler for deserializer
    @app.route("/deserialize", methods=['POST'])
    def handle_deserialization_request():
        # here swagger documentation and content of method


  """
    Deserialize a bag file to json
    ---
    tags:
      - deserializer
    consumes:
      - application/json
    produces:
      - application/json
    parameters:
      - name: body
        in: body
        description: request body
        required: true
        schema:
          type: object
          required:
            - name
            - es_addr
            - json
          properties:
            name:
              description: the file path to the log file / name of the file e.g log.tar.lz4
              type: string
            json:
              description: local json file name
              type: string
            es_addr:
            description: elastic search address
            type: string
    responses:
      200:
        description: returns the status of deserialization
        schema:
          type: object
          properties:
            status:
              description: success / error
              type: string
            response:
              description: message about the status
              type: object
              properties:
                data:
                  description: the data returned by API upon success
                  type: string
                error:
                  description: the error message returned by API upon failure
                  type: string
    """
    # Check if input is valid json
    if not request.json:
        return jsonify(status="error",
                       response={"error": "Input is not JSON",
                                 "data": ""})
    else:
        print("[INFO] Received log deserialization request")

        parameters = request.get_json()

        file_name = parameters.get("name")
        es_addr = parameters.get("es_addr")

        #es_index = parameters.get("es_index",file_name)
        #buffer_size = parameters.get("buffer", 5000)
        json_output = parameters.get("json")
        #es_type = parameters.get("es_type","rosmsg")

        # Run deserializer
        ru = RosbagUploader(file_name, json_output, es_addr)
        status = ru.start()
        print("status = {}".format(status))
        if status == ReturnCodes.SUCCESS:
            return jsonify(status="success",
                           response={"error": "",
                                     "data": ""})
        else:
            return jsonify(status="error",
                           response={"error": "Deserialization Failed",
                                     "data": ""})

I don't understand the error, I tried adding GET next to POST above in my code did not solve the problem. I am not sure if the error is even from my code? Any suggestions?


Solution

  • Be sure that your API specification is formatted properly when writing it as a docstring. You can write your specs to it's own file and use the file shortcut.

    specs/deserialize.yml:

    Deserialize a bag file to json
    ---
    tags:
      - deserializer
    consumes:
      - application/json
    produces:
      - application/json
    parameters:
      - name: body
        in: body
        description: request body
        required: true
        schema:
          type: object
          required:
            - name
            - es_addr
            - json
          properties:
            name:
              description: the file path to the log file / name of the file e.g log.tar.lz4
              type: string
            json:
              description: local json file name
              type: string
            es_addr:
              description: elastic search address
              type: string
    responses:
      200:
        description: returns the status of deserialization
        schema:
          type: object
          properties:
            status:
              description: success / error
              type: string
            response:
              description: message about the status
              type: object
              properties:
                data:
                  description: the data returned by API upon success
                  type: string
                error:
                  description: the error message returned by API upon failure
                  type: string
    

    api.py:

    def create_api():
        # Get port number for the web app.
        PORT = 8000
    
        # Initiate the Flask app
        app = Flask(__name__)
        Swagger(app)
        ...
    
        # Handler for deserializer
        @app.route("/deserialize", methods=['POST'])
        def handle_deserialization_request():
            """
            file: specs/deserialize.yml
            """
            ...
            return jsonify(
                status="error",
                response={"error": "Input is not JSON", "data": ""}
            )
    
        return app
    
    
    api = init_deserializer_restful_api()
    api.run()