FastAPI - module 'app.routers.test' has no attribute 'routes' - python

I am trying to setup an app using FastAPI but keep getting this error which I can't make sense of. My main.py file is as follows:
from fastapi import FastAPI
from app.routers import test
app = FastAPI()
app.include_router(test, prefix="/api/v1/test")
And in my routers/test.py file I have:
from fastapi import APIRouter, File, UploadFile
import app.schemas.myschema as my_schema
router = APIRouter()
Response = my_schema.Response
#router.get("/", response_model=Response)
def process(file: UploadFile = File(...)):
# Do work
But I keep getting the following error:
File
"/Users/Desktop/test-service/venv/lib/python3.8/site-packages/fastapi/routing.py",
line 566, in include_router
for route in router.routes: AttributeError: module 'app.routers.test' has no attribute 'routes'
python-BaseException
I cant make sense of this as I can see something similar being done in the sample app here.

I think you want:
app.include_router(test.router, prefix="/api/v1/test")
rather than:
app.include_router(test, prefix="/api/v1/test")

No, you can not directly access it from the app, because when you add an instance of APIRouter with include_router, FastAPI adds every router to the app.routes.
for route in router.routes:
if isinstance(route, APIRoute):
self.add_api_route(
...
)
It does not add the route to the application instead it adds the routes, but since your router is an instance of APIRouter, you can reach the routes from that.
class APIRouter(routing.Router):
def __init__(
self,
routes: Optional[List[routing.BaseRoute]] = None,
...
)

The problem lies in your import statement, your import should be like
from parentfolder.file import attribute
Confused? no worries let me make it simple
Any variable you use to define and assign to APIRouter, becomes your attribute.
in your example in test.py, you defined routes as your attribute routes = APIRouter()
that means that if you want to use it in any other place, you need to do as below
from routers.test import routes
GoodLuck

Related

FastAPI APIRouter prefix are showing twice. Why?

notecard_router = APIRouter(
prefix = "/notecard",
tags = ['notecard']
)
Hi, I am trying to include a router to my app, but for some reason, the prefix that I give in the API router comes twice in the API paths. I have no idea why this is happening.
This is happening because from what I'm able to tell, you're not structuring your endpoints the way you want them. Instead of creating the API router and adding your prefix to it in each endpoint,
from fastapi import APIRouter
app = APIRouter(tags=["notecard"], prefix="/notecard")
#app.get("/notecard/index")
async def root():
...
When you do something like this, your endpoint actually becomes /prefix/notecard/index, which is /notecard/notecard/index. It looks like that's what you're doing. Instead of that, you can do
from fastapi import APIRouter
app = APIRouter(tags=["notecard"], prefix="/notecard")
#app.get("/index")
async def root():
...
And FastAPI will handle the prefixing for you.
Also, relevant snippets of your code or anything that can be used to reproduce your issue would make answering easier :)

How to debug Fastapi openapi generation error

I spend some time going over this error but had no success.
File "C:\Users\ebara.conda\envs\asci\lib\site-packages\fastapi\openapi\utils.py", line 388, in get_openapi
flat_models=flat_models, model_name_map=model_name_map
File "C:\Users\ebara.conda\envs\asci\lib\site-packages\fastapi\utils.py", line 28, in get_model_definitions
model_name = model_name_map[model]
KeyError: <class 'pydantic.main.Body_login_access_token_api_v1_login_access_token_post'>
The problem is that I'm trying to build a project with user authentication from OpenAPI form to create new users in database.
I've used backend part of this template project https://github.com/tiangolo/full-stack-fastapi-postgresql
Everything works except for Authentication like here.
#router.post("/login/access-token", response_model=schemas.Token)
def login_access_token(
db: Session = Depends(deps.get_db), form_data: OAuth2PasswordRequestForm = Depends()) -> Any:
When I add this part form_data: OAuth2PasswordRequestForm = Depends() - and go to /docs page - this error appears (Failed to load API definition. Fetch error. Internal Server Error /openapi.json)
.
The server itself runs in normal mode, but it can't load the open API. If I remove the aforementioned formdata part - then everything works smoothly, but without Authorisation. I tried to debug it, but I have no success. I think it might be connected to a dependency graph or some start-up issues, but have no guess how to trace it back.
Here is the full working example which will reproduce the error. The link points out the code which causes the problem. If you will comment out lines 18-39 - the docs will open without any problems.
https://github.com/BEEugene/fastapi_error_demo/blob/master/fastapi_service/api/api_v1/endpoints/login.py
Any ideas on how to debug or why this error happens?
You are using Depends function without an argument. Maybe in the console, you were having the error provoked by a function. You must pass the OAuth2PasswordRequestForm function after importing it from fastapi.security to get the result you were expecting.
from fastapi.security import OAuth2PasswordRequestForm
form_data: OAuth2PasswordRequestForm = Depends(OAuth2PasswordRequestForm)
it might work.
It seems that in my case - the main error issue was that I was an idiot.
As said, if you will comment out lines 18-39 - the docs will open without any problems. But, you will notice this warning:
UserWarning: Duplicate Operation ID read_users_api_v1_users__get for
function read_users at
...\fastapi_error\fastapi_service\api\api_v1\endpoints\users.py
warnings. Warn(message)
I've started to compare all the files and it is appeared that I included router to the fastapi twice:
import logging
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware
from fastapi_service.api.api_v1.api import api_router
from fastapi_service.core.config import settings
from fastapi_service.core.event_handlers import (start_app_handler,
stop_app_handler)
log = logging.getLogger(__name__)
def get_app(mode="prod") -> FastAPI:
fast_app = FastAPI(title=settings.PROJECT_NAME,
version=settings.APP_VERSION,
debug=settings.IS_DEBUG)
# openapi_url=f"{settings.API_V1_STR}/openapi.json")
# first time when I included the router
fast_app.include_router(api_router, prefix=f"{settings.API_V1_STR}")
fast_app.mode = mode
logger = log.getChild("get_app")
logger.info("adding startup")
fast_app.add_event_handler("startup", start_app_handler(fast_app))
logger.info("adding shutdown")
fast_app.add_event_handler("shutdown", stop_app_handler(fast_app))
return fast_app
app = get_app()
# Set all CORS enabled origins
if settings.BACKEND_CORS_ORIGINS:
app.add_middleware(
CORSMiddleware,
allow_origins=[str(origin) for origin in settings.BACKEND_CORS_ORIGINS],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# second time when I included the router
app.include_router(api_router, prefix=settings.API_V1_STR)
So, if you comment out (or just delete) the second router introduction - the app will work normally.
It seems, that the answer to my question on how to debug this error - is to find the point where the bug appears in fastapi and compare the values in it to the version where there is no error. In my case the number of keys in different dictionaries differed in the function get_model_definitions.
I had the same problem. For me it was because I had code like this
from pydantic import BaseModel
class A(BaseModel):
b: B
class B(BaseModel):
c: int
but instead, Class B should have been defined above class A. This fixed it:
from pydantic import BaseModel
class B(BaseModel):
c: int
class A(BaseModel):
b: B
More info: https://stackoverflow.com/a/70384637/9439097
Regaring your original question on how to debug these or similar errors:
You probably have your routes defined somewhere. Comment all of your routers/routes out, then the openapi docs should generate (and they should show you have no routes. Then, enable the routes one by one and see which one causes the error. THis is how I debuged my situation.

Application context errors when locally testing a (Python) Google Cloud Function

I am trying to locally test a Python function that I hope to deploy as a Google Cloud Function. These functions seem to be essentially Flask based, and I have found that the best way to return JSON is to use Flask's jsonify function. This seems to work fine when deployed, but I want to set up some local unit tests, and here is where I got stuck. Simply adding the line to import jsonify, results in the following error:
RuntimeError: Working outside of application context.
There are several posts here on Stackoverflow that seem relevant to this issue, and yet Google Cloud Functions do not really follow the Flask pattern. There is no app context, as far as I can tell, and there are no decorators. All of the examples I've found have not been useful to this particular use case. Can anyone suggest a method for constructing a unit test that will respect the application context and still jibe with the GCF pattern here.
I have a unittest, which I can share, but you will see the same error when you run the following, with the method invocation inside of main.
import os
import json
from flask import jsonify
from unittest.mock import Mock
def dummy_request(request):
request_json = request.get_json()
if request_json and 'document' in request_json:
document = request_json['document']
else:
raise ValueError("JSON is invalid, or missing a 'docuemnt' property")
data = document
return jsonify(data)
if __name__ == '__main__':
data = {"document":"This is a test document"}
request = Mock(get_json=Mock(return_value=data), args=data)
result = dummy_request(request)
print(result)
You don't really need to test whether flask.jsonify works as expected, right? It's a third-party function.
What you're actually trying to test is that flask.jsonify was called with the right data, so instead you can just patch flask.jsonify, and make assertions on whether the mock was called:
import flask
from unittest.mock import Mock, patch
def dummy_request(request):
request_json = request.get_json()
if request_json and 'document' in request_json:
document = request_json['document']
else:
raise ValueError("JSON is invalid, or missing a 'docuemnt' property")
data = document
return flask.jsonify(data)
#patch('flask.jsonify')
def test(mock_jsonify):
data = {"document": "This is a test document"}
request = Mock(get_json=Mock(return_value=data), args=data)
dummy_request(request)
mock_jsonify.assert_called_once_with("This is a test document")
if __name__ == '__main__':
test()
I'd recommend you to take a look at Flask's documentation on how to test Flask apps, it's described pretty well how to setup a test and get an application context.
P.S. jsonify requires application context, but json.dumps is not. Maybe you can use the latter?
I came across the same issue. As you've said the flask testing doesn't seem to fit well with Cloud Functions and I was happy with how the code worked so didn't want to change that. Adding an application context in setUp() of testing then using it for the required calls worked for me. Something like this...
import unittest
import main
from flask import Flask
class TestSomething(unittest.TestCase):
def setUp(self):
self.app = Flask(__name__)
def test_something(self):
with self.app.app_context():
(body, code) = main.request_something()
self.assertEqual(200, code, "The request did not return a successful response")
if __name__ == '__main__':
unittest.main()

Aiohttp routing (aiohttp.web.RouteTableDef)

I have a problems with RouteTableDef.
There is some project where routing structure like this:
1) There is file route.py.
routes.py
from aiohttp import web
routes = web.RouteTableDef()
2) There are some handlers in different files, for example:
handler1.py
from aiohttp import web
from routes import routes
#routes.get('/get')
async def handle(request):
name = request.match_info.get('name', "Anonymous")
text = "Hello, " + name
return web.Response(text=text)
and so on.
3) and main file, where app runs:
from aiohttp import web
from routes import routes
if __name__ == '__main__':
app = web.Application()
app.router.add_routes(routes)
web.run_app(app, host='localhost', port=8877)
The idea is:
all routes store at routes variable, when we wanna create new handler we import that variable from routes.py and use it.
In order to register routes to app, we import routes from routes.py.
And how to make it working with aiohttp v3.3.2?
The problem is: it works with aiohttp version 2.3.10.
But there is the real project with this way of routing and it works.
How to create a one place that will store routes?
I suppose that problem is in lib version, because that project doesn't work with the latest version of aiohttp.
The problem here is order of the imports, as already answered. This is one of the reasons why I create one web.RouteTableDef() in each file and then I put them together for app.router:
from .dashboard import routes as dashboard_routes
from .posts import routes as posts_routes
app.router.add_routes([
*dashboard_routes,
*posts_routes,
])
Import request handler handle to file where web.Application creates and runs. Import it before importing routes from routes.py

Access flask app endpoints in another python file?

I have a python file which defines some endpoints using flask each doing some computation and return a JSON (POST method). I want to do unit testing on this in order to do this I want to be able to access the app I created in one python file in another file so I can test my endpoints.
I see a lot of this on the internet :
from source.api import app
from unittest import TestCase
class TestIntegrations(TestCase):
def setUp(self):
self.app = app.test_client()
def test_thing(self):
response = self.app.get('/')
assert <make your assertion here>
It doesn't explain how I can define and access my app in another file. This might be a stupid question but I really don't see how.
My app is defined as follows:
from flasgger import Swagger
from flask import Flask, jsonify, request
from flask_cors import CORS
import os
def init_deserializer_restful_api():
# Initiate the Flask app
app = Flask(__name__)
Swagger(app)
CORS(app)
# Handler for deserializer
#app.route("/deserialize", methods=['POST'])
def handle_deserialization_request():
pass
I have many other end points in this fashion. Should i just do:
import my_file_name
Thanks!!
Check out this question: What does if __name__ == "__main__": do?
As long as you have that in your python program, you can both treat it as a module and you can call it directly.

Categories

Resources