Return text/html on exceptions - python

I was trying to modify the code example here but it seems that jsonify is making it hard... I did the following without jsonify:
#app.errorhandler(InvalidUsage)
def handle_invalid_usage(error):
response = error.to_dict()
response.status_code = error.status_code
return response
Originally, the third line was like:
response = jsonify(error.to_dict())
How can I make this work? I don't want to use JSON. Only text/html

Well, I presume that error.to_dict() returns a dict, which wouldn't have a status code attribute (it's just a regular old dict). You might try this instead:
#app.errorhandler(InvalidUsage)
def handle_invalid_usage(error):
response = error.to_dict()
response["status_code"] = error.status_code
return response
That said, it seems odd that the dictionary wouldn't already have the error code in it. Maybe a bit more detail on what what you are trying to accomplish would help? If you don't call the second-to-last line, and just return response, what do you see?

Related

How to create a unit test in Python for specific code in pytest

I have the following function:
def update_installation_register(
remote: RemoteRegisterData, install_id: str, data: dict, verify: bool
) -> None:
"""Updates an Installation register"""
credentials = (remote.auth_user, remote.auth_pass)
url = f"https://{remote.hostname}/api/instalaciones/{install_id}/"
resp = requests.put(url=url, auth=credentials, json=data, verify=verify)
if resp.status_code != 200:
raise RemoteRegisterRequestError()
It's part of a existing codebase, and I'm trying to add unit test to it.
I am not figuring out how to create a simple test for it, maybe a test that checks return None if all goes well and to catch an exception if it does not?
I'm not sure how to mock that request, if I write a test_update_installation_register for pytest
I am willing to refactor the code if necessary, although I'd prefer to leave it the way it is,
I'm puzzled by how this simple function managed to confuse me this much, but there it is.
The thing is, and maybe this is the fundamental question... How do I test a function that is merely a simple wrapper to a http request, without actually testing the request?
This is the best I could come up with, but I'm not entirely sure this is the right approach
def test_update_installation_register_valid():
with requests_mock.Mocker() as mock:
remote_register = remote_register_data()
mock.put(
f"https://{remote_register.hostname}/api/instalaciones/{req_test_id()}/",
status_code=200,
)
resp = update_installation_register(
remote_register, req_test_id(), req_test_data(), verify=True
)
assert resp == None
where
remote_register_data()
req_test_id()
req_test_id()
req_test_data()
are just returning a hardcoded "test" value
Is this a good solution?

Are there any empty values to trick Python hashlib?

So I am working on Python FastAPI project.
My current objective is to correctly authenticate password so correct password should trigger HTTP 204 response code, in every other case (also empty params) HTTP 401 should be triggered.
The examples look like that:
Here the response should be: 204
/auth?password=haslo&password_hash=013c6889f799cd986a735118e1888727d1435f7f623d05d58c61bf2cd8b49ac90105e5786ceaabd62bbc27336153d0d316b2d13b36804080c44aa6198c533215
And here: 401
/auth?password=haslo&password_hash=f34ad4b3ae1e2cf33092e2abb60dc0444781c15d0e2e9ecdb37e4b14176a0164027b05900e09fa0f61a1882e0b89fbfa5dcfcc9765dd2ca4377e2c794837e091
I am correctly handling RequestValidationError, and my hashed password validation looks like this:
class AuthResponse(BaseModel):
status_code: int
#app.get("/auth", response_model=AuthResponse)
async def auth(password: str, password_hash: str, response: Response):
try:
m = hashlib.sha512(bytes(password, encoding="ASCII"))
if str(m.hexdigest()) == password_hash:
response.status_code = 204
else:
response.status_code = 401
except Exception:
response.status_code = 401
return AuthResponse(status_code=response.status_code)
Now I cannot think of any passed parameter that would trigger incorrect validation (passing 204 code instead of 401), but it seems that it is possible, as external assertion shows. I know it must be something about empty values other than None, but I can't figure it out by myself.
I have already tried patterns like:
/auth?password=&password_hash=
/auth?password=%00&password_hash=%00
http://127.0.0.1:8000/auth?password=%0&password_hash=%0
I would really appreciate some kind of help, hint or suggestion.
Thank you all, it is only a coding exercise project, so many things could be a little bit off. But as it happens I didn't check the empty password parameter which creates valid hash, but is not correct according to task.

How to parse jsonp returned from api using python

I'm very new to coding, and I'm building my first web application using open REST api with python flask.
I think the api is returning jsonp which looks like this - callbackfunction{ json }; and I get from other posts that all I need to do is getting rid of this padding. However, I can't figure out at which point I should implement the stripping.
This is my code. 5th line is throwing an error "the JSON object must be str, bytes or bytearray, not HTTPResponse"
def lookup(title):
try:
url = "http://www.aladin.co.kr/ttb/api/ItemSearch.aspx?ttbkey=foo&Query=bar"
result = urllib.request.urlopen(url)
data = json.loads(result)
data_json = data.split("{", 1)[1].strip("}")
return data_json
except requests.RequestException:
return None
I'm sure it's working well until 4th line. When I tried the code below, at least it returned result, though cryptic, like this.
b'{ "version" : "20070901", "title" :
"\xec\x95\x8c\xeb\x9d\xbc\xeb\x94\x98 \xea\xb2\x80 ...
"customerReviewRank":9 } ] };'
Judging by the keys, I'm pretty sure this is the information I requested. So what can I do to fix this? Thanks in advance!
def lookup(title):
try:
url = "http://www.aladin.co.kr/ttb/api/ItemSearch.aspx?ttbkey=foo&Query=bar"
result = urllib.request.urlopen(url)
res = result.readline()
return res

Problems with the Bitly API v4 and the Python (2.7) request module, i keep getting response code 422: UNPROCESSABLE_ENTITY

I've been trying to get this to work for the past 4 hours with no luck, so here's the problem:
I have this class:
class Bitly:
def __init__(self, api_token):
self.apiToken = api_token
self.header = {'Content-Type':'application/json',
'Authorization' : 'Bearer {}'.format(self.apiToken)
}
def shorten(self, longURL):
payload = {"long_url": longURL}
url = "https://api-ssl.bitly.com/v4/shorten"
return requests.post(url, headers=self.header, data=payload)
that, after being initialized with a valid token, is supposed to return the response JSON with the shortened link inside when calling the shorten method.
Instead i keep getting this response:
{"message":"UNPROCESSABLE_ENTITY","resource":"bitlinks","description":"The JSON value provided is invalid."}
And i can't figure out what i'm doing wrong with the payload for it to give me this message.
I'm sure it's a stupid error but i'm pretty new to this, so have mercy.
Nevermind, i solved it, here's the solution for future reference:
instead of using
data=payload
use
json=payload
Yes, it was that simple.

Werkzeug response too slow

I have the following Werkzeug application for returning a file to the client:
from werkzeug.wrappers import Request, Response
#Request.application
def application(request):
fileObj = file(r'C:\test.pdf','rb')
response = Response( response=fileObj.read() )
response.headers['content-type'] = 'application/pdf'
return response
The part I want to focus on is this one:
response = Response( response=fileObj.read() )
In this case the response takes about 500 ms (C:\test.pdf is a 4 MB file. Web server is in my local machine).
But if I rewrite that line to this:
response = Response()
response.response = fileObj
Now the response takes about 1500 ms. (3 times slower)
And if write it like this:
response = Response()
response.response = fileObj.read()
Now the response takes about 80 seconds (that's right, 80 SECONDS).
Why is there that much difference between the 3 methods?
And why is the third method sooooo slow?
The answer to that is pretty simple:
x.read() <- reads the whole file into memory, inefficient
setting response to a file: very inefficient as the protocol for that object is an iterator. So you will send the file line by line. If it's binary you will send it with random chunk sizes even.
setting response to a string: bad idea. It's an iterator as mentioned before, so you are now sending each character in the string as a separate packet.
The correct solution is to wrap the file in the file wrapper provided by the WSGI server:
from werkzeug.wsgi import wrap_file
return Response(wrap_file(environ, yourfile), direct_passthrough=True)
The direct_passthrough flag is required so that the response object does not attempt to iterate over the file wrapper but leaves it untouched for the WSGI server.
After some testing I think I've figure out the mistery.
#Armin already explained why this...
response = Response()
response.response = fileObj.read()
...is so slow. But that doesn't explain why this...
response = Response( response=fileObj.read() )
...is so fast. They appear to be the same thing, but obviously they are not. Otherwise there wouldn't be that tremendous difference is speed.
The key here is in this part of the docs: http://werkzeug.pocoo.org/docs/wrappers/
Response can be any kind of iterable or string. If it’s a string it’s considered being an iterable with one item which is the string passed.
i.e. when you give a string to the constructor, it's converted to an iterable with the string being it's only element. But when you do this: response.response = fileObj.read(), the string is treated as is.
So to make it behave like the constructor, you have to do this:
response.response = [ fileObj.read() ]
and now the file is sent as fast as possible.
I can't give you a precise answer as to why this occurs, however http://werkzeug.pocoo.org/docs/wsgi/#werkzeug.wsgi.wrap_file may help address your underling problem.

Categories

Resources