'FastAPI' object has no attribute 'default_response_class' - python

I am trying to include a router in the main FastAPI router:
from fastapi import FastAPI
from test.main.app.google_calendar_wrapper import app as calendar_manager_router
app = FastAPI()
#
app.include_router(calendar_manager_router, prefix="/calendar_manager", tags=["calendar_manager"])
#app.get("/")
def root():
return {"message": "Home Page"}
However, when running
uvicorn test.main.app.webhook.router:app --port 8050 --reload
I get an error:
AttributeError: 'FastAPI' object has no attribute 'default_response_class'
My file structure is:
test
| main
| app
| google_calendar_wrapper
| endpoints.py
| __init__.py
| webhooks
| router.py
So far I have tried:
Not including the router, in this case the application starts normally
google_calendar_wrapper with and without __init__.py. If with an __init__.py, I tried exporting the google_calendar_wrapper and it still raises the same error
Both routers work independently of each other but nothing has helped so far and I have not found any solutions.
Here is the calendar_manager_router definition:
from fastapi import FastAPI
app = FastAPI()
#app.get("/")
def root():
return {"message": "Hello World"}
#app.get("/health")
def health():
"""Api health endpoint."""
return {"Api is up and running"}

FastAPI's include_router accepts an APIRouter, but the object you imported in the main file, calendar_manager_router, is another FastAPI object. In your google_calendar_wrapper, you should be defining an APIRouter and that's what you import and include in your main app.
In google_calendar_wrapper, change it to:
from fastapi import APIRouter
router = APIRouter() # <---------
#router.get("/")
def root():
return {"message": "Hello World"}
#router.get("/health")
def health():
"""Api health endpoint."""
return {"Api is up and running"}
Notice the change to use APIRouter.
Then in your main app:
from test.main.app.google_calendar_wrapper import router as calendar_manager_router
...
app = FastAPI()
app.include_router(
calendar_manager_router,
prefix="/calendar_manager",
tags=["calendar_manager"]
)
See the FastAPI tutorials on Bigger Applications - Multiple Files:
You want to have the path operations related to your users separated from the rest of the code, to keep it organized.
But it's still part of the same FastAPI application/web API (it's part of the same "Python Package").
You can create the path operations for that module using APIRouter.
...
You can think of APIRouter as a "mini FastAPI" class.

Related

SqlAlchemy and Celery structure Issue in python

I have two files:
app.py
from flask import Flask
from flask_restful import Api
from celery import Celery
from resources.item import Item, ItemList, ItemInsert
from db import db
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = ""
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config['CELERY_BROKER_URL'] = ''
app.config['CELERY_RESULT_BACKEND'] = ''
celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])
app.secret_key = ""
api = Api(app)
#app.before_first_request
def create_tables():
db.create_all()
api.add_resource(ItemList,"/items")
api.add_resource(Item,"/item/<int:id>")
api.add_resource(ItemInsert,"/items")
#celery.task
def update_row(data):
pass
if __name__ == "__main__":
db.init_app(app)
app.run(debug=True,port=5000)
item.py
from flask_restful import Resource, reqparse
from flask import request
from models.item import ItemModel
class ItemInsert(Resource):
def post(self):
file_task = update_row.apply_async((data,), countdown=3)
return item.json(), 201
As you can see in app.py I have imported classes from item.py, however now my celery (task) function call i.e update_row from item.py is left hanging, since I cannot import from app.py as it will result in a cyclic import. Is there any solution?
With simple project, you could implement the tasks inside app.py as you're doing for now. But with more complicated project, it's better to move the tasks definition into a separated package so that it could mitigate the cyclic import.
Like so:
App and celery configutation
**# app.py**
# App & celery
# ...
Tasks definitions
**# tasks.py**
from project_name.app import celery
#celery.task
def update_row(data):
pass
API
**# resources/item.py**
from project_name.tasks import update_row
# ...
Separate the tasks into another package (tasks package, which is auto discovered by Celery) could help you to prevent cyclic import and also good to maintain the code.
But if you're still want to use the current approach, to prevent cyclic import, you could import it dynamically when calling API:
**# resources/item.py**
# ...
class ItemInsert(Resource):
def post(self):
from project_name.app import update_row
file_task = update_row.apply_async((data,), countdown=3)
return item.json(), 201
celery_app.task is a decorator, so just a regular Python function. It works in the following way: it takes your function, registers it in the celery app and returns a wrapper object with methods delay, apply_async etc. And you can always get a registered task from celery_app.tasks dictionary by its name. Another trick to avoid circular imports is in storing celery_app reference as an attribute of flask_app, and inside request context you can always get current flask app from flask.current_app
app.py
from tasks import register_tasks
...
app = Flask(__name__)
...
app.celery = Celery(app.name, ...)
register_tasks(app.celery)
tasks.py
def update_row(data):
pass
def register_tasks(celery_app):
celery_app.task(update_row, name="update_row")
views.py
from flask import current_app
class ItemInsert(Resource):
def post(self):
update_row = current_app.celery.tasks["update_row"]
file_task = update_row.apply_async((data,), countdown=3)
return item.json(), 201
UPD: indeed the most canonical way is to use autodiscovery of tasks:
myapp/tasks.py
from celery import shared_task
#shared_task
def update_row(data):
pass
myapp/app.py
celery_app = Celery(...)
celery_app.set_default()
celery_app.autodiscover_tasks(["myapp"], force=True)
myapp/views.py
from .tasks import update_row
def index_view():
update_row.delay(...)

FastApi middleware on different folder not working

I,m getting this error when I try to run my FastApi api.
app = cls(app=app, **options)
TypeError: 'module' object is not callable
I'm trying to add a middleware on other folder separeted from main.py and don't know why isn't working. Otherwise when I add the middleware code into main.py works without problems. Here is my code, thank you for your help and excuse my english.
main.py
from fastapi import FastAPI
from fastapi import Depends, FastAPI, HTTPException
from fastapi import Request
from routers import rutas
from utils import CheckApiKey
from utils.CheckApiKey import check_api_key
app = FastAPI()
app.add_middleware(CheckApiKey, dispatch=check_api_key) <--- Here calling middleware
app.include_router(rutas.router)
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)
Middleware
from fastapi import Request
async def check_api_key(request: Request, call_next):
print("ok")
response = await call_next(request)
return response
I found the solution...I have to create a class that inherite from starlette basehttpmiddleare, like this.
from starlette.middleware.base import BaseHTTPMiddleware
class CheckApiKey(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
print("ok")
response = await call_next(request)
return response
The CheckApiKey seems like a python module in your case and check_api_key is the middleware function.
The issue was, the add_middleware() method expects the first argument as a callable function or callable class. But in your case, you were given a module.
So,
Change your statement as,
app.add_middleware(check_api_key)

Flask python app failing to get app object after initializing

app_instance.py
from app import FlaskApp
app = None
def init_instance(env):
global app
app = FlaskApp(env)
def get_instance():
assert app is not None
return app
FlaskApp class is pretty much like this
class FlaskApp(object):
def __init__(self, env):
self.oauth_manager = .... bla bla ..
self.clients_manager = .. bla bla ..
app = Flask(__name__)
app.config.from_object(env)
app = app_wrapper.wrap(app, app.config['NUM_PROXY_SERVERS'])
self.app = app
self.api = Api(self.app, prefix='/v3', default_mediatype='application/json')
self.define_routes()
# Initialize the DB
self.db = Database(self.app)
fmt = "%(asctime)s - %(request_id)s - %(name)s - %(levelname)s - %(message)s"
logging.basicConfig(format=fmt, level=self.app.config.get('LOG_LEVEL'))
request_id.init(app, prefix='MY_API_', internal=False)
def run_server(self):
self.app.run(host=self.app.config['HOST'], port=self.app.config['PORT'], debug=self.app.config['DEBUG'])
def define_routes(self):
# Configure Api Resources
self.api.add_resource(VersionListController, '/my/route', endpoint='versions')
more routes here
self.api.init_app(self.app)
in my app controller
def is_valid_oauth_token(request):
from mobile_module import app_instance
app = app_instance.get_instance()
# more code here
I'm running the app on localhost and getting
assert app is not None
AssertionError
How can "fix" this code? should I be importing from mobile_module import app_instance in every route access? suggestions please
I should state that this app works in production well behind a Nginx
I guess my question is more about python (how to make this work) and less in flask.
The problem is not related to get_instance or init_instance(create_app etc.).
Flask has different states. App will work in out of request context when you initialize app instance(FlaskApp(env)).
As I see in your example, you try to get a application in context of request(def is_valid_oauth_token(request)). It means that is not initialization of application. This is processing while a request is active. This is other state of application - app was created and work in context of some request. In this case you can get application instance using from flask import current_app.
To better understanding how it works/use I recommend to read about flask._app_ctx_stack, app_context() and flask.g.
Hope this helps.
I think the best way to devellop a flask app simple is to follow the official documentation about simple flask project structure here
you need to organize your floder like this :
/yourapplication
/yourapplication
__init__.py
/static
style.css
/templates
layout.html
index.html
login.html
...
and then create your application in init.py file like as follow:
from flask import Flask
def create_app():
"""this method will initialise the flask Ap instance """
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello, World!'
return app
in your yourapplication directory add the run.py to run the app with the following code :
from yourapplication import create_app
app = create_app()
if __name__ == '__main__':
app.run()
and if you want to use your controller you can do this:
from yourapplication import create_app
def is_valid_oauth_token(request):
app = create_app()
# more code here
this is called application factory design-pattern.
And in addition if you want to put it in production you will need to use WSGI configuration find more here

Flask returns 404 in views

I am running unittests in a Flask app and I keep getting 404 when views.py file is not imported even though it is not used. I have such tests.py package:
import unittest
from presence_analyzer import main, utils
from presence_analyzer import views
class PresenceAnalyzerViewsTestCase(unittest.TestCase):
def setUp(self):
self.client = main.app.test_client()
def test_mainpage(self):
resp = self.client.get('/')
self.assertEqual(resp.status_code, 302)
When I delete views import the described problem occurs. Views are organized in a similar way to this:
from presence_analyzer.main import app
#app.route('/')
def mainpage():
return redirect('/static/presence_weekday.html')
And the main.py file:
import os.path
from flask import Flask
app = Flask(__name__) # pylint: disable=invalid-name
app.config.update(
DEBUG=True,
)
I guess it's something similar to what happened in this case, so I'm trying to change the application so that I don't have to make this dumb imports while testing. I've been trying to make use of the answer from above, but still can't make it work and these docs don't seem helpful. What am I doing wrong? main.py:
from flask.blueprints import Blueprint
PROJECT_NAME = 'presence_analyzer'
blue_print = Blueprint(PROJECT_NAME, __name__)
def create_app():
app_to_create = Flask(__name__) # pylint: disable=invalid-name
app_to_create.register_blueprint(blue_print)
return app_to_create
app = create_app()
views.py:
from presence_analyzer.main import app, blue_print
#blue_print.route('/')
def mainpage():
return redirect('/static/presence_weekday.html')
And tests.py has remained unchanged.
You must import views, or the route will not be registered. No, you are not executing the views directly, but importing executes code all module-level code. Executing code calls route. route registers the view function. You cannot get around needing to import a module in order to use the module.

How to use Flasgger with Flask applications using Blueprints?

I am adding Swagger UI to my Python Flask application using Flasgger. Most common examples on the Internet are for the basic Flask style using #app.route:
from flasgger.utils import swag_from
#app.route('/api/<string:username>')
#swag_from('path/to/external_file.yml')
def get(username):
return jsonify({'username': username})
That works.
In my application however, I am not using #app.route decorators to define the endpoints. I am using flask Blueprints. Like following:
from flask import Flask, Blueprint
from flask_restful import Api, Resource
from flasgger.utils import swag_from
...
class TestResourceClass(Resource):
#swag_from('docs_test_get.yml', endpoint='test')
def get() :
print "This is the get method for GET /1.0/myapi/test endpoint"
app = Flask(__name__)
my_api_blueprint = Blueprint('my_api', __name__)
my_api = Api(my_api_blueprint)
app.register_blueprint(my_api_blueprint, url_prefix='/1.0/myapi/')
my_api.add_resource(TestResourceClass, '/test/'
endpoint='test',
methods=['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])
....
As seen above, I used #swag_from decorator on the TestResourceClass.get() method which is bound to the GET method endpoint. I also have the endpoint=test matching in the two places.
But I am not getting anything on the Swagger UI, it is all blank. The docs_test_get.yml file does contain the valid yaml markup to define the swagger spec.
What am I missing? How can I get Flasgger Swagger UI working with Flask Blueprint based setup?
Now there is an example of blueprint app in https://github.com/rochacbruno/flasgger/blob/master/examples/example_blueprint.py
"""
A test to ensure routes from Blueprints are swagged as expected.
"""
from flask import Blueprint, Flask, jsonify
from flasgger import Swagger
from flasgger.utils import swag_from
app = Flask(__name__)
example_blueprint = Blueprint("example_blueprint", __name__)
#example_blueprint.route('/usernames/<username>', methods=['GET', 'POST'])
#swag_from('username_specs.yml', methods=['GET'])
#swag_from('username_specs.yml', methods=['POST'])
def usernames(username):
return jsonify({'username': username})
#example_blueprint.route('/usernames2/<username>', methods=['GET', 'POST'])
def usernames2(username):
"""
This is the summary defined in yaml file
First line is the summary
All following lines until the hyphens is added to description
the format of the first lines until 3 hyphens will be not yaml compliant
but everything below the 3 hyphens should be.
---
tags:
- users
parameters:
- in: path
name: username
type: string
required: true
responses:
200:
description: A single user item
schema:
id: rec_username
properties:
username:
type: string
description: The name of the user
default: 'steve-harris'
"""
return jsonify({'username': username})
app.register_blueprint(example_blueprint)
swag = Swagger(app)
if __name__ == "__main__":
app.run(debug=True)
You just need to add your blueprint's name when you reference endpoint. Blueprints create namespaces. Example below. And useful tip: use app.logger.info(url_for('hello1')) for debugging problems with endpoint - it shows very useful error messages like this Could not build url for endpoint 'hello1'. Did you mean 'api_bp.hello1' instead?.
from flask import Flask, Blueprint, url_for
from flask_restful import Api, Resource
from flasgger import Swagger, swag_from
app = Flask(__name__)
api_blueprint = Blueprint('api_bp', __name__)
api = Api(api_blueprint)
class Hello(Resource):
#swag_from('hello1.yml', endpoint='api_bp.hello1')
#swag_from('hello2.yml', endpoint='api_bp.hello2')
def get(self, user=''):
name = user or 'stranger'
resp = {'message': 'Hello %s!' % name}
return resp
api.add_resource(Hello, '/hello', endpoint='hello1')
api.add_resource(Hello, '/hello/<string:user>', endpoint='hello2')
app.register_blueprint(api_blueprint)
swagger = Swagger(app)
app.run(debug=True)
swag_from function have some error to parse file path.you can use doc string to define api in get() first.
flasgger will parse MethodView() method like get,post.
Seems flasgger does not work or has no proper support for blue print style Flask definitions (yet). I used https://github.com/rantav/flask-restful-swagger which worked great!

Categories

Resources