why fastapi do not resolve my dependency? - fastapi

handler
#router.post("/profile/{user_id}/grade", response_model=UserGradeResponseSchema)
#access_control("create", user_grade_acl)
async def new_grade(
user_id: int,
grade: UserGradeCreateSchema,
db: Session = Depends(get_db),
):
try:
created_grade = create_grade(db, grade, user_id, reviewer.id)
except IntegrityError:
raise ModelNotFoundError("User")
return created_grade
access_control decorator:
def get_user_principals(user: User = Depends(verify_token)):
return [
f"user:{user.id}",
f"user:{user.is_staff}",
f"user:{user.is_superuser}",
Authenticated,
Everyone
]
def access_control(action: str, acl: Union[Callable, List[Tuple[str, str, str]]]):
def intermediate_wrapper(func: Callable):
#wraps(func)
async def wrapper(access=Depends(get_user_principals), *args, **kwargs):
return await func(*args, **kwargs)
return wrapper
return intermediate_wrapper
It doesn't call the get_user_principals dependency (I tried outputting the access value, outputs Depends(get_user_principals)
)

Related

Can't access my router in fastapi with postman

If I try to access my router(/users/signup)
with postman(send to http://127.0.0.1:8000/users/signup), it keeps the response "detail": "Not Found"
and fastapi is showing
api | INFO: 172.19.0.1:43736 - "GET /users/signup HTTP/1.1" 404 Not Found
Originally I tried to use post instead of get but it doesn't work anyway
Here is the code in my main.py in the app folder and user.py in the routers folder
main.py:
import os
import sys
from .database import init_db
sys.path.append(os.path.dirname(os.path.abspath(os.path.dirname(__file__))))
baseurl = os.path.dirname(os.path.abspath(__file__))
from fastapi import FastAPI, APIRouter
from .routers.user import router as user_router
from .routers.article import router as article_router
from fastapi.middleware.cors import CORSMiddleware
router = APIRouter()
router.include_router(user_router, prefix="/users",tags=["users"])
router.include_router(article_router, prefix="/articles",tags=["articles"])
app = FastAPI()
origins = ["http://localhost:3000"]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
#app.on_event("startup")
async def on_startup():
await init_db()
#app.get("/")
async def root():
return {"message ": " Welcome Fastapi"}
#app.get("/hello/{name}")
async def say_hello(name: str):
return {"message": f"Hello {name}"}
user.py
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
import app.repository.user as dao
from app.admin.utils import currentTime
from app.database import get_db
from app.schemas.user import UserDTO
from app.admin.utils import currentTime
router = APIRouter()
#router.get("/signup")
async def signup(user: UserDTO, db: Session = Depends(get_db)):
print(f" 회원가입에 진입한 시간: {currentTime()} ")
print(f"SignUp Inform : {user}")
result = dao.signup(user, db)
if result == "":
result = "failure"
return {"data": result}
#router.post("/login")
async def login(user: UserDTO, db: Session = Depends(get_db)):
return_user = dao.login(user, db)
print(f"로그인 정보 : {return_user}")
return {"data": return_user}
#router.put("/modify/{id}")
async def update(id: str, item: UserDTO, db: Session = Depends(get_db)):
dao.update(id, item, db)
return {"data": "success"}
#router.delete("/delete/{id}", tags=['age'])
async def delete(id: str, item: UserDTO, db: Session = Depends(get_db)):
dao.delete(id, item, db)
return {"data": "success"}
#router.get("/page/{page}")
async def get_users(page: int, db: Session = Depends(get_db)):
ls = dao.find_users(page, db)
return {"data": ls}
#router.get("/email/{id}")
async def get_user(id: str, db: Session = Depends(get_db)):
dao.find_user(id, db)
return {"data": "success"}
#router.get("/point/{search}/{page}")
async def get_users_by_point(search: int, page: int, db: Session = Depends(get_db)):
dao.find_users_by_point(search, page, db)
return {"data": "success"}
What I was expecting is send {"user_email": "hong#naver.com", "id": "hong1234", "username": "홍길동", "password": "hong1234", "cpassword": "hong1234"} by using postman and recieve data:success and enrollment the data to DB
I tried the post method and get method and change router address but it didn't work anyway
You need to include router in your app too:
router = APIRouter()
router.include_router(user_router, prefix="/users",tags=["users"])
router.include_router(article_router, prefix="/articles",tags=["articles"])
app = FastAPI()
app.include_router(router) # Add this.
Alternatively remove main router:
# Remove router = APIRouter()
app = FastAPI()
app.include_router(user_router, prefix="/users",tags=["users"])
app.include_router(article_router, prefix="/articles",tags=["articles"])

Handling TimeOut Exception in AsyncIO

import asyncio
import aiohttp
from time import perf_counter
import csv
path = "*******************"
domains = []
total_count=0
with open(path, 'r') as file:
csvreader = csv.reader(file)
for row in csvreader:
try:
website = row[4].split("//")[-1].split("www.")[-1].split('/')[0]
if website == "":
continue
domains.append(website)
except:
continue
sample = domains[0:50]
async def fetch(s, body):
async with s.post('https://****************', json=body) as r:
if r.status!= 200:
pass
enrich_response = await r.json()
#print(enrich_response)
employees = enrich_response['employees']
for employee in employees:
if(employee['job_title'] == "Owner"):
print(employee)
print("************************************************")
global total_count
total_count += 1
print("Total Count:", total_count)
continue
elif(employee['job_title'] == "CEO"):
print(employee)
print("***************************************************")
total_count+=1
print("Total Count:", total_count)
continue
else:
continue
async def fetch_all(s,bodies):
tasks = []
for body in bodies:
task = asyncio.create_task(fetch(s, body))
tasks.append(task)
res = await asyncio.gather(*tasks)
return res
async def main():
# apikeys = list(apikeysone.keys.values())
bodies = []
for domain in sample:
body = {
"api_key": "********************************",
"domain" : "{}".format(domain)
}
bodies.append(body)
async with aiohttp.ClientSession() as session:
data = await fetch_all(session, bodies)
print(data[0])
if __name__ == '__main__':
start = perf_counter()
try:
asyncio.run(main())
except Exception as e:
print(e)
pass
stop = perf_counter()
print("Time taken:", stop - start)
Hi!
I'm trying to connect to a scraping service provider using asyncio, instead of simple synchronous api calls.
But I get a TimeOut error. How could I use exception handling to wait a few seconds before retrying it once again? Or just skipping that task if it fails?
Thank you in advance fellow coder!
Tried adding to some places continue/pass
Try exploring asyncio.wait_for() function. It takes an awaitable and a timeout value. If task isn't completed before timeout value, it raises asyncio.exceptions.TimeoutError which you can handle in any way you want in except clause.
A typical example (from Python doc) is as follows:
async def eternity():
# Sleep for one hour
await asyncio.sleep(3600)
print('yay!')
async def main():
# Wait for at most 1 second
try:
await asyncio.wait_for(eternity(), timeout=1.0)
except TimeoutError:
print('timeout!')
asyncio.run(main())
# Expected output:
#
# timeout!

fastapi's class as dependency override in unit testing

Using the parameter supplied in the request body, I wanted to instantiate an object. For instance, if the request body contains type=push I desired to instantiate in this manner.
if channel_type.upper() == "PUSH":
return NotificationService(Push())
I am able to achieve this through classes as dependency of fastapi as following:
def factory(channel_type):
if channel_type.upper() == "PUSH":
return NotificationService(Push())
elif channel_type.upper() == "EMAIL":
return NotificationService(Email())
else:
return NotificationService(Sms())
class NotificationRequest:
def __init__(self, type: str = Body("push")):
self.service = factory(type)
class MessageRequest(BaseModel):
title: str
message: str
payload: Dict = {}
#router.post("/", response_model=dict)
def notify(message: MessageRequest, request: NotificationRequest = Depends()) -> Any:
request.service.send(users=get_users(), message=Message(
title=message.title, message=message.message, payload=message.payload
))
return {"success": True}
However, I wanted to override the NotificationRequest dependency in the unit test so that I could provide fake instances of real classes:
def get_factory(channel_type):
if channel_type.upper() == "PUSH":
return NotificationService(FakePush())
elif channel_type.upper() == "EMAIL":
return NotificationService(FakeEmail())
else:
return NotificationService(FakeSms())
I have tried following:
app.dependency_overrides[factory] = lambda: get_factory
which fails because the factory is not injected through the dependency.
How can we give a parameter to the body and inject a dependency into the function to produce a fake instance for unit testing?

Customized asyncContextManger not working concurrently on http requests

I am using asyncio to send a bunch of requests to a web service concurrently. I cannot use aiohttp.ClientSession becuase the target service provides a client package with a lot of proprietary functions. Even if I can hack the authentication process to establish a session, I still have to implement those client's functions. I implemented my own Async Context Manager as in the following code snippet. It seems fine, except apparently the requests are executed sequentially. It takes 22 seconds where a concurrent run costs less than 3 seconds.
import aiohttp, asyncio, requests, time, json
start_time = time.time()
class MyRequests:
def __init__(self, requests):
self.requests = requests
async def __aenter__(self) -> "MyRequests":
return self
async def __aexit__(
self, exc_type, exc, tb
) -> None:
pass
def get(self, url) -> "MyResponse":
return MyResponse(self.requests.get(url))
class MyResponse:
def __init__(self, resp) -> None:
self.response = resp
self.url = self.response.url
async def __aenter__(self) -> "MyResponse":
return self
async def __aexit__(self, exc_type, exc, tb) -> None:
pass
async def get_pokemon(session, url):
async with session.get(url) as resp:
return resp.url
async def main():
async with MyRequests(requests) as session:
tasks = []
for number in range(1, 50):
url = f'https://pokeapi.co/api/v2/pokemon/{number}'
tasks.append(asyncio.create_task(get_pokemon(session, url)))
original_pokemon = await asyncio.gather(*tasks)
for pokemon in original_pokemon:
print(pokemon)
asyncio.run(main())
print("--- %s seconds ---" % (time.time() - start_time))

Query graphite index.json for a specific sub-tree

I'm querying Graphite's index.json to get all the metrics. Is there an option to pass a root metric and get only a sub-tree? Something like:
http://<my.graphite>/metrics/index.json?query="my.metric.subtree"
That is not supported.
What you can do however is call /metrics/find recursively (call it again for each branch encountered)
Something like this:
#!/usr/bin/python
from __future__ import print_function
import requests
import json
import argparse
try:
from Queue import Queue
except:
from queue import Queue
from threading import Thread, Lock
import sys
import unicodedata
outLock = Lock()
def output(msg):
with outLock:
print(msg)
sys.stdout.flush()
class Walker(Thread):
def __init__(self, queue, url, user=None, password=None, seriesFrom=None, depth=None):
Thread.__init__(self)
self.queue = queue
self.url = url
self.user = user
self.password = password
self.seriesFrom = seriesFrom
self.depth = depth
def run(self):
while True:
branch = self.queue.get()
try:
branch[0].encode('ascii')
except Exception as e:
with outLock:
sys.stderr.write('found branch with invalid characters: ')
sys.stderr.write(unicodedata.normalize('NFKD', branch[0]).encode('utf-8','xmlcharrefreplace'))
sys.stderr.write('\n')
else:
if self.depth is not None and branch[1] == self.depth:
output(branch[0])
else:
self.walk(branch[0], branch[1])
self.queue.task_done()
def walk(self, prefix, depth):
payload = {
"query": (prefix + ".*") if prefix else '*',
"format": "treejson"
}
if self.seriesFrom:
payload['from']=self.seriesFrom
auth = None
if self.user is not None:
auth = (self.user, self.password)
r = requests.get(
self.url + '/metrics/find',
params=payload,
auth=auth,
)
if r.status_code != 200:
sys.stderr.write(r.text+'\n')
raise Exception(
'Error walking finding series: branch={branch} reason={reason}'
.format(branch=unicodedata.normalize('NFKD', prefix).encode('ascii','replace'), reason=r.reason)
)
metrics = r.json()
for metric in metrics:
try:
if metric['leaf']:
output(metric['id'])
else:
self.queue.put((metric['id'], depth+1))
except Exception as e:
output(metric)
raise e
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("--url", help="Graphite URL", required=True)
parser.add_argument("--prefix", help="Metrics prefix", required=False, default='')
parser.add_argument("--user", help="Basic Auth username", required=False)
parser.add_argument("--password", help="Basic Auth password", required=False)
parser.add_argument("--concurrency", help="concurrency", default=8, required=False, type=int)
parser.add_argument("--from", dest='seriesFrom', help="only get series that have been active since this time", required=False)
parser.add_argument("--depth", type=int, help="maximum depth to traverse. If set, the branches at the depth will be printed", required=False)
args = parser.parse_args()
url = args.url
prefix = args.prefix
user = args.user
password = args.password
concurrency = args.concurrency
seriesFrom = args.seriesFrom
depth = args.depth
queue = Queue()
for x in range(concurrency):
worker = Walker(queue, url, user, password, seriesFrom, depth)
worker.daemon = True
worker.start()
queue.put((prefix, 0))
queue.join()
Note: this code comes from: https://github.com/grafana/cloud-graphite-scripts/blob/master/query/walk_metrics.py

Resources