Uploading images in FastAPI post request causes 400 Bad Request - image

I would like to get image via post request and then read it. I'm trying to do is this way:
import numpy as np
from PIL import Image
from fastapi import FastAPI, File, UploadFile, HTTPException, Depends
app = FastAPI()
#app.post("/predict_image")
#logger.catch
def predict_image(predict_image: UploadFile = File(...)):
logger.info('predict_image POST request performed')
try:
pil_image = np.array(Image.open(predict_image.file))
except:
raise HTTPException(
status_code=HTTP_422_UNPROCESSABLE_ENTITY, detail="Unable to process file"
)
pred = pil_image.shape
logger.info('predict_image POST request performed, shape {}'.format(pred))
return {'input_shape': pred}
Calling post request returns INFO: 127.0.0.1:59364 - "POST /predict_image HTTP/1.1" 400 Bad Request
How to fix it?
UPD:
Example from official tutorial return same exception:
#app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...)):
return {"filename": file.filename}

You probably need to install python-multipart.
Just:
pip install python-multipart

Fixed this way:
#app.post("/predict_image/")
#logger.catch
def make_inference(file: bytes = File(...)):
try:
pil_image = np.array(Image.open(BytesIO(file)))
except:
raise HTTPException(
status_code=HTTP_422_UNPROCESSABLE_ENTITY, detail="Unable to process file"
)

Related

call DjangoRestFramework DetailAPIView() from another view

solution from #brian-destura below
The DRF test client does not work, but the django.test.client does. Odd (?) because it's a DRF APIView being called.
from django.test import Client
client = Client()
result = client.get('/api/place/6873947')
print(result.json)
I have a DRF DetailAPIView() that returns a complex serializer json response to external API queries, so in the browser, and via curl etc. http://localhost:8000/api/place/6873947/ returns a big JSON object. All good. The url entry in the 'api' app looks like this
path('place/<int:pk>/', views.PlaceDetailAPIView.as_view(), name='place-detail'),
I need to use that in another, function-based view, so first I tried using both django.test.Client and rest_framework.test.APIClient, e.g.
from rest_framework.test import APIClient
from django.urls import reverse
client = APIClient()
url = '/api/place/6873947/'
res = client.get(url)
That gets an empty result. With django Client:
from django.test import Client
c=Client()
Then
res = c.get('/api/place?pk=6873947')
and
res = c.get('/api/place/', {'pk': 6873947})
Both return "as_view() takes 1 positional argument but 2 were given"
I've tried other approaches in my IDE, picked up in StackOverflow, starting with
from api.views import PlaceDetailAPIView
pid = 6873947
from django.test import Client
from django.http import HttpRequest
from places.models import Place
request = HttpRequest()
request.method='GET'
request.GET = {"pk": pid}
Then
res = PlaceDetailAPIView.as_view({"pk": pid})
"as_view() takes 1 positional argument but 2 were given"
res = PlaceDetailAPIView.as_view()(request=request)
"Expected view PlaceDetailAPIView to be called with a URL keyword argument named "pk". Fix your URL conf, or set the .lookup_field attribute on the view correctly"
res = PlaceDetailAPIView.as_view()(request=request._request)
"HttpRequest' object has no attribute '_request"
I must be missing something basic, but hours of thrashing has gotten me nowhere - ideas?

DJANGO-STORAGES, PARAMIKO: connection failure for global connection

I have a strange problem using the SFTP-API from django-storages(https://github.com/jschneier/django-storages). I am trying to use it in order to fetch media-files, which are stored on a different server and thus needed to create a Proxy for SFTP Downloads, since plain Django just sends GET-requests to the MEDIA_ROOT. I figured that Middleware provides a good hook:
import mimetypes
from storages.backends.sftpstorage import SFTPStorage
from django.http import HttpResponse
from storages.backends.sftpstorage import SFTPStorage
class SFTPMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
try:
path = request.get_full_path()
SFTP = SFTPStorage() # <- this is where the magic happens
if SFTP.exists(path):
file = SFTP._read(path)
type, encoding = mimetypes.guess_type(path)
response = HttpResponse(file, content_type=type)
response['Content-Disposition'] = u'attachment; filename="{filename}"'.format(filename=path)
except PermissionError:
pass
return response
which works fine, but obviously it opens a new connection every time a website call is issued which I don't want (it also crashes after 3 reloads or something, I think it has to many parallel connections by then). So I tried just opening one connection to the Server via SFTP by moving the SFTP = SFTPStorage()-initialization into the __init__()-method which is just called once:
import mimetypes
from storages.backends.sftpstorage import SFTPStorage
from django.http import HttpResponse
from storages.backends.sftpstorage import SFTPStorage
class SFTPMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.SFTP = SFTPStorage() # <- this is where the magic happens
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
try:
path = request.get_full_path()
if self.SFTP.exists(path):
file = self.SFTP._read(path)
type, encoding = mimetypes.guess_type(path)
response = HttpResponse(file, content_type=type)
response['Content-Disposition'] = u'attachment; filename="{filename}"'.format(filename=path)
except PermissionError:
pass
return response
But this implementation doesn't seem to work, the program is stuck either before the SFTP.exists() or after the SFTP._read() methods.
Can anybody tell me how to fix this problem? Or does anybody even have a better idea as to how to tackle this problem?
Thanks in advance,
Kingrimursel

Attempting to rewrite a Discord.py GIF Bot from an old outdated guide

Ok, so I'm attempting to rebuild a GIF bot in Discord.py.
It is based off of some code found on a website that gave a guide on making a discord.py gif bot.
The problem is, the code is old and severely outdated with over 37 errors on it's own.
How would I go about converting this to discord.py-rewrite and basically making it a gif searching bot that can pick up random gifs by tag from GIPHY?
Here is the code I currently have:
import discord.utils
import os
import giphy_client
from giphy_client.rest import ApiException
from pprint import pprint
from discord.ext import commands
import random
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv("GIPHY_API_KEY")
API_TOKEN = os.getenv("GIPHY_API_TOKEN")
api_instance = giphy_client.DefaultApi()
config = {
'api_key': API_KEY,
'token': API_TOKEN,
'limit': 1,
'rating': 'g'
}
try:
api_response = api_instance.gifs_trending_get(
config['token'], limit=config['limit'], rating=config['rating'])
pprint(api_response)
except ApiException as e:
print("Exception when calling DefaultApi->gifs_trending_get: %s\n" % e)
Client = commands.Bot(command_prefix=os.getenv("CLIENT_PREFIX"))
class DiscordClient(discord.Client):
#Client.event
async def on_ready(self):
print("Connected to Discord Client as")
print(f'{self.user.name}#{self.user.discriminator}')
print("-------")
#Client.command(name="gif")
async def search_gifs(query):
try:
giphy_token = API_TOKEN
response = api_instance.gifs_search_get(giphy_token, query, limit=3, rating='g')
lst = list(response.data)
gif = random.choices(lst)
return gif[0].url
except ApiException as e:
return "Exception when calling DefaultApi->gifs_search_get: %s\n" % e
async def on_ready(self):
print("Connected to Discord Client as")
print(Client.user.name)
print("-------")
#Client.command(name='gif')
async def search_gifs(query):
try:
response = api_instance.gifs_search_get(
API_TOKEN, query, limit=3, rating='g')
lst = list(response.data)
gif = random.choices(lst)
return gif[0].url
except ApiException as e:\
return "Exception when calling DefaultApi->gifs_search_get: %s\n" % e\
#Client.command(name='8ball')
async def magic_eight_ball(ctx):
response = [
'Without a doubt.',
'Outlook good.',
'Better not tell you now.',
'Cannot predict now.',
'My reply is no.',
'Outlook not so good.',
]
gif = await search_gifs('cheese')
await ctx.send(random.choice(response))
await ctx.send('Gif URL : ' + gif)
Client.run(os.getenv("CLIENT_TOKEN"))
Here is the website I got the code I'm building off of from:
https://www.maxongzb.com/serving-gifs-with-discord-bot-reading-time-12-mins/
Here is what I currently have for the requirements.txt file:
discord.py
giphy_client
python-dotenv
Any help would be much appreciated.
I host via Heroku due to my VPS taking a dump, so that's why I have the os.getenv("...") lines in there.
I've gotten it down to around 7 errors, 8 warnings and 8 weak warnings. But that's the furthest I was able to get to.

python: Force aiohttp to not normalize url

I use aiohttp to send a request like so:
async with ClientSession() as session:
res = await session.get("http://0.0.0.0:8000/./")
When I start up a http server using python like so:
python3 -m http.server
I see that the path is normalized, i.e., the server gets the following request:
GET / HTTP/1.1" 200
How do I disable this normalization, to enforce a behavior like in urrlib, for example, where urllib.request.urlopen("http://0.0.0.0:8000/./") results in the following request:
GET /./ HTTP/1.1.
aiohttp uses yarl for URL processing.
session.get('http://example.com') works as well as session.get(yarl.URL('http://example.com'))
You can disable URL encoding for yarl.URL with encoded=True, but you have to take care of URL correctness.
e.g.
import asyncio
import yarl
import aiohttp
async def test():
url = yarl.URL('https://stackoverflow.com/./', encoded=True)
async with aiohttp.ClientSession() as session:
async with session.get(url, allow_redirects=False) as resp:
print(resp.url)
asyncio.run(test())

Downloaded images from the Metropolitan Museum collection are empty

I'm trying to download random public domain images from the Metropolitan Museum collection using their API (more info here : https://metmuseum.github.io/) and Python, unfortunatly the images I get are empty. Here is a minimal code :
import urllib
from urllib2 import urlopen
import json
from random import randint
url = "https://collectionapi.metmuseum.org/public/collection/v1/objects"
objectID_list = json.loads(urlopen(url).read())['objectIDs']
objectID = objectID_list[randint(0,len(objectID_list)-1)]
url_request = url+"/"+str(objectID)
fetched_data = json.loads(urlopen(url_request).read())
if fetched_data['isPublicDomain']:
name = str(fetched_data['title'])
ID = str(fetched_data['objectID'])
url_image = str(fetched_data['primaryImage'])
urllib.urlretrieve(url_image, 'path/'+name+'_'+ID+'.jpg')
If I print url_image and copy/paste it in a browser I get to the desired image, but the code retrieves an image that weights 1ko and can't be opened.
Any idea what I'm doing wrong ?
Your way of downloading is correct, however, it seems as the domain is validating request headers to prevent scraping (probably unintended as they have an API to pull images).
One way of solving this problem is by changing your headers to something realistic, or utilizing fake_useragent and requests.
import requests
from fake_useragent import UserAgent
def save_image(link, file_path):
ua = UserAgent(verify_ssl=False)
headers = {"User-Agent": ua.random}
r = requests.get(link, stream=True, headers=headers)
if r.status_code == 200:
with open(file_path, 'wb') as f:
f.write(r.content)
else:
raise Exception("Error code {}.".format(r.status_code))

Resources