non-iterable ellipsis in Flask_Restful parser.parse_args() - python

I've come back to a project I worked on previously, utilising flask RESTful, though whenever I attempt a post request the line args = parser.parse_args() breaks the code, giving an error reading:
File "/usr/local/lib/python3.9/site-packages/werkzeug/datastructures.py", line 554, in update
for key, value in iter_multi_items(mapping):
TypeError: cannot unpack non-iterable ellipsis object
For some reason the parser seems to be having issues with its own arguments, the post function is:
def post(self):
from flask_restful import reqparse
parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, help='Rate cannot be converted')
parser.add_argument('name')
args = parser.parse_args()
Any help or direction would be greatly appreciated.
-- Edit
To give more context, my init file looks like this, the error occurring when the POST function is called in postman.
from flask import Flask, g
from flask_restful import Resource, Api, reqparse
import os
import shelve
app = Flask(__name__)
api = Api(app)
def get_db():
db = getattr(g, '_database', None)
if db is None:
db = g._database = shelve.open("rates.db")
return db
#app.teardown_appcontext
def teardown_db(exception):
db = getattr(g, '_database', None)
if db is not None:
db.close()
#app.route("/")
def index():
return "Hello World"
class RateList(Resource):
def get(self):
shelf = get_db()
keys = list(shelf.keys())
devices = []
for key in keys:
devices.append(shelf[key])
return {'message' : 'Success', 'data' : devices}, 200
def post(self):
parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, help='Rate cannot be converted')
parser.add_argument('name')
args = parser.parse_args()
shelf = get_db()
shelf[args['rate']] = args
return {'message' : 'Rate registered', 'data' : args}, 201
class Rate(Resource):
def get(self, rate):
shelf = get_db()
if not(rate in shelf):
return {'message' : 'Rate not found', 'data' : {}}, 404
return {'message' : 'Rate found', 'data' : shelf[rate]}, 200
def delete(self, rate):
shelf = get_db()
if not(rate in shelf):
return {'message' : 'Rate not found', 'data' : {}}, 404
del shelf[rate]
return {'message' : 'Rate deleted', 'data' : {}}, 200
api.add_resource(RateList, '/Rate')
api.add_resource(Rate, '/Rate/<string:rate>')
Docker-compose file:
version: '3.4'
services:
stock-registry:
build: .
volumes:
- .:/usr/src/app
ports:
- 5001:80
Dockerfile:
FROM python:3
WORKDIR /usr/src/app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "./run.py"]
Run.py:
from stock_registry import app
app.run(host='0.0.0.0', port=80, debug=True)
-- Edit 2
The JSON body sent on the post request, using postman:
{
"rate" : 1,
"name" : "Test"
}
The complete error log that shows when this is attempted:
172.22.0.1 - - [30/Jun/2021 12:37:12] "POST /shares HTTP/1.1" 500 -
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1997, in __call__
return self.wsgi_app(environ, start_response)
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1985, in wsgi_app
response = self.handle_exception(e)
File "/usr/local/lib/python3.9/site-packages/flask_restful/__init__.py", line 265, in error_router
return original_handler(e)
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1540, in handle_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python3.9/site-packages/flask/_compat.py", line 32, in reraise
raise value.with_traceback(tb)
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1982, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1614, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python3.9/site-packages/flask_restful/__init__.py", line 265, in error_router
return original_handler(e)
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1517, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python3.9/site-packages/flask/_compat.py", line 32, in reraise
raise value.with_traceback(tb)
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/usr/local/lib/python3.9/site-packages/flask_restful/__init__.py", line 446, in wrapper
resp = resource(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/flask/views.py", line 84, in view
return self.dispatch_request(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/flask_restful/__init__.py", line 550, in dispatch_request
resp = meth(*args, **kwargs)
File "/usr/src/app/stock_registry/__init__.py", line 65, in post
args = parser.parse_args()
File "/usr/local/lib/python3.9/site-packages/flask_restful/reqparse.py", line 261, in parse_args
value, found = arg.parse(req)
File "/usr/local/lib/python3.9/site-packages/flask_restful/reqparse.py", line 143, in parse
source = self.source(request)
File "/usr/local/lib/python3.9/site-packages/flask_restful/reqparse.py", line 101, in source
values.update(value)
File "/usr/local/lib/python3.9/site-packages/werkzeug/datastructures.py", line 554, in update
for key, value in iter_multi_items(mapping):
TypeError: cannot unpack non-iterable ellipsis object

I had the same issue. Can you share your version numbers of flask and of flask-restful?
What worked for me is updating both packages, since somehow I apparently got 2 versions that don't work together in my environment. I upgraded them to the following and now it's working:
Try: pip install flask==2.0.1 flask-restful==0.3.9
Hope this works!

It is a version compatibility error in flask or flask-restful or werkzeug.
What worked for me:
Werkzeug==1.0.1
Flask==0.11.1
Flask-RESTful==0.3.5

Related

Oauth2 in Python with Flask gets 302 from API

I am trying to access MeisterTask's API with Python and Flask, and no matter what I do, I seem to always get a 302 code in return from the API, although I can get an access token (or so I think). Here is the code I have so far (I tried reducing it, this is the smallest snippet I could get that replicates the error):
from flask import Flask, redirect, url_for, session, request, jsonify
from flask_oauthlib.client import OAuth
app = Flask(__name__)
app.debug = True
app.secret_key = "development"
oauth = OAuth(app)
meistertask = oauth.remote_app(
'meistertask',
consumer_key= "XXXXXX",
consumer_secret= "XXXXXX",
request_token_params={"scope" : "meistertask"},
base_url='https://www.meistertask.com/api',
request_token_url=None,
access_token_method='GET',
access_token_url='https://www.mindmeister.com/login/oauth2/token',
authorize_url='https://www.mindmeister.com/oauth2/authorize'
)
#app.route('/')
def index():
if 'meistertask_token' in session:
me = meistertask.get('user')
return jsonify(me.data)
return redirect(url_for('login'))
#app.route('/login')
def login():
return meistertask.authorize(callback=url_for('authorized', _external=True))
#app.route('/logout')
def logout():
session.pop('meistertask_token', None)
return redirect(url_for('index'))
#app.route('/login/authorized')
def authorized():
resp = meistertask.authorized_response()
print(resp.get('code'))
if resp is None or resp.get('code') is None:
return 'Access denied: reason=%s error=%s resp=%s' % (
request.args['error'],
request.args['error_description'],
resp
)
session['meistertask_token'] = (resp['code'], '')
return "Hello"
#meistertask.tokengetter
def get_meistertask_oauth_token():
return session.get('meistertask_token')
if __name__ == "__main__":
app.run()
And here is the traceback:
flask_oauthlib.client.OAuthException: Invalid response from meistertask
Traceback (most recent call last):
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 2309, in __call__ return self.wsgi_app(environ, start_response)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 2295, in wsgi_app response = self.handle_exception(e)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 1741, in handle_exception reraise(exc_type, exc_value, tb)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\_compat.py", line 35, in reraise raise value
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 2292, in wsgi_app response = self.full_dispatch_request()
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 1815, in full_dispatch_request rv = self.handle_user_exception(e)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 1718, in handle_user_exception reraise(exc_type, exc_value, tb)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\_compat.py", line 35, in reraise raise value
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 1813, in full_dispatch_request rv = self.dispatch_request()
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 1799, in dispatch_request return self.view_functions[rule.endpoint](**req.view_args)
File "~\Documents\MeisterTaskServer\hello.py", line 49, in authorized resp = meistertask.authorized_response()
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask_oauthlib\client.py", line 707, in authorized_response data = self.handle_oauth2_response(args)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask_oauthlib\client.py", line 692, in handle_oauth2_response
Things I have tried
Tried to modify the method to get the access token from GET to POST. The API clearly states that I should use GET, yet every other example I have seen on flask_oauthlib's GitHub uses POST (the examples are 3 years old, but some still work, namely the GitHub one). Either give the same result.
Tried doing it barebones, without any library. The resulting code was thrice as long and also had more problems.
Used Django instead of Flask. Never even managed to get the hello world example going, it was too much work, and also I have discovered the library flask_oauthlib.
Things worth mentioning
I derived this code from this here GitHub example
There is also code there I omitted in order to keep the snippet short, that establishes that the server should use SSL (as per the request from the API that the redirect_uri should use HTTPS)
The app manages to redirect me over at MeisterTask and asks for my permission. Once I grant it, it redirects to "https://127.0.0.1:5000/login/authorized?code=some_token" where I get the traceback. If I look with Chrome's debugging tools to the requests made and what I receive, I see that I get an 302 from the API, but I also get an access token.
I run Windows 10 with Python 3.7.0
So what's the deal? What's the next step here? I've run out of things to try. Thank you for taking the time to solve this!

TypeError: Parse() argument 1 must be string or read-only buffer, not tuple

I am trying to create a web service for an Alexa skill using the Flask-Ask extension in python. This web service would allow users to query a local MySQL database to find out parking information and make parking reservations.
I have been following the guide at https://medium.com/spawn-ai/chat-bots-and-how-to-build-one-on-alexa-35772e429631
I am using the following python extensions: flask, flask_ask, MySQL.
Here is the code for my web service:
'''This is a Web Service for the Logan Parking skill'''
author__ = '*****'
import logging
from flaskext.mysql import MySQL
from flask import Flask, render_template
from flask_ask import Ask, statement, question
mysql = MySQL()
app = Flask(__name__)
app.config.from_object(__name__)
app.config['MYSQL_DATABASE_USER'] = '*****'
app.config['MYSQL_DATABASE_PASSWORD'] = '*****'
app.config['MYSQL_DATABASE_DB'] = '*****'
app.config['MYSQL_DATABASE_HOST'] = '*****'
mysql.init_app(app)
ask = Ask(app, "/")
logging.getLogger("flask_ask").setLevel(logging.DEBUG)
#on launch of the parking skill, alexa tells the user "Welcome to the prototype Parking skill!"
#ask.launch
def launch():
welcome_message = 'Welcome to the prototype Parking Skill ! What can I do for you ?'
return question(welcome_message) \
.standard_card(title='Parking',
text='Welcome to the prototype Parking Skill ! ',
large_image_url='IMG')
#logic for the AvailableParking intent, which at this point tells the user which garages are closed/opened
#ask.intent('AvailableParking')
def available(occupancy):
#converts the inputted occupancy variable into all caps, to match the DB schema
occupancy = occupancy.upper()
#creates cursor variable that connects to DB
cursor = mysql.connect().cursor()
#connects to db and executes SQL query that displays all garages where the OCCUPANCY field matches the users slot input (OPEN OR CLOSED)
cursor.execute("SELECT GARAGE_NAME FROM GARAGES WHERE OCCUPANCY = %s", (occupancy,))
#stores all result rows from the query in the data variable
data = cursor.fetchall()
return statement(data)
#opens a server on localhost5000
if __name__ == '__main__':
app.run(debug=True, port=5000)
And here is the error message:
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 2309, in __call__
return self.wsgi_app(environ, start_response)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 2295, in wsgi_app
response = self.handle_exception(e)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1741, in handle_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1718, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/usr/local/lib/python2.7/dist-packages/flask_ask/core.py", line 767, in _flask_view_func
result = self._map_intent_to_view_func(self.request.intent)()
File "/home/ngrok/ngrok/dbcon9", line 46, in available
return statement(data)
File "/usr/local/lib/python2.7/dist-packages/flask_ask/models.py", line 188, in __init__
super(statement, self).__init__(speech)
File "/usr/local/lib/python2.7/dist-packages/flask_ask/models.py", line 51, in __init__
'outputSpeech': _output_speech(speech)
File "/usr/local/lib/python2.7/dist-packages/flask_ask/models.py", line 402, in _output_speech
xmldoc = ElementTree.fromstring(speech)
File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 1311, in XML
parser.feed(text)
File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 1651, in feed
self._parser.Parse(data, 0)
TypeError: Parse() argument 1 must be string or read-only buffer, not tuple
{"context": {"System": {"apiAccessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJhdWQiOiJodHRwczovL2FwaS5hbWF6b25hbGV4YS5jb20iLCJpc3MiOiJBbGV4YVNraWxsS2l0Iiwic3ViIjoiYW16bjEuYXNrLnNraWxsLmY2NDViYTgxLTZmM2QtNDZlNi04MDM1LTI1MTBlYTg5ODNkYiIsImV4cCI6MTUzMDU1NzAyNSwiaWF0IjoxNTMwNTUzNDI1LCJuYmYiOjE1MzA1NTM0MjUsInByaXZhdGVDbGFpbXMiOnsiY29uc2VudFRva2VuIjpudWxsLCJkZXZpY2VJZCI6ImFtem4xLmFzay5kZXZpY2UuQUhFRzdPQ0RLN01TNkdFWkhYTUlTUlFXRkNNTktKVUxHTk1GTkdSVFlWSFk0WFZMSkVWUzRZTFFGSjVFSDVRR0YyREVYQTVVVjI0UzJHNFJNU0tYWFpHVjZFRDY0RktJWkpHVVQyWURVRlZZTzVLVUZLSENDUzVPWUdPTDI3NUU1QjRGRU1XTk5VRVAzUkxPSENFNzRUVTdLSDZBIiwidXNlcklkIjoiYW16bjEuYXNrLmFjY291bnQuQUg0T1FZSVBSQ1NRVEZaR1pIWkVRSFpCU1RISDI1VUE2MjYyNFY1WTdDWTdCSzZDWU9RWjdJN1BMV0s0Tzc0RFpVSzRBMkw0MlpQUjRQV1lFRlJIVzY2MlRPVlVEM1BCT1c0UkxRSjNFSFpVWllTTzM2VkM0NkMzRVdHT1ZPRFlVSVNWNzNPRVRHUkJEUkFTRjc0NEg3VFcyR0pYM01FN0kyWVNGQkFTNjNOUUFGNzNCVUJHUlRSSU5ZQ0tIM0dPWjZZVjNVS0tRM0xZMlFJIn19.J6mfOthVmALtgGPeYJLKSyq0EtrohslTxIWgB4g-jMTD5WHOlI869QWz7VRORHrGiLYJSM6kh980z-kTQ_LPLmPeLiQZrFKnMgMAnhKbJooa0xlsh_R4ILUb4Rgur-PHN1IX5SWBP1kfy0oEU95YtYcXJ0KlFD1O8zl9hZGxkL61-_1CuU9l8HGBVhkrO544PdgirSqmpfc7o1NMzEgkAfKmQRC-MpSe02KaWlNTj_ryzwQLYCLPFs39iOYWJzot56Fz3YrNhftIbCnG5c36I86nd0j8T1N0rTKyELg0mgs28wQmJSdQiPOLbhzFFcBtT6-7le7iUhG4bI1UBfHI0A", "apiEndpoint": "https://api.amazonalexa.com", "application": {"applicationId": "amzn1.ask.skill.f645ba81-6f3d-46e6-8035-2510ea8983db"}, "device": {"deviceId": "amzn1.ask.device.AHEG7OCDK7MS6GEZHXMISRQWFCMNKJULGNMFNGRTYVHY4XVLJEVS4YLQFJ5EH5QGF2DEXA5UV24S2G4RMSKXXZGV6ED64FKIZJGUT2YDUFVYO5KUFKHCCS5OYGOL275E5B4FEMWNNUEP3RLOHCE74TU7KH6A", "supportedInterfaces": {}}, "user": {"userId": "amzn1.ask.account.AH4OQYIPRCSQTFZGZHZEQHZBSTHH25UA62624V5Y7CY7BK6CYOQZ7I7PLWK4O74DZUK4A2L42ZPR4PWYEFRHW662TOVUD3PBOW4RLQJ3EHZUZYSO36VC46C3EWGOVODYUISV73OETGRBDRASF744H7TW2GJX3ME7I2YSFBAS63NQAF73BUBGRTRINYCKH3GOZ6YV3UKKQ3LY2QI"}}}, "request": {"error": {"message": "An exception occurred while dispatching the request to the skill.", "type": "INVALID_RESPONSE"}, "locale": "en-US", "reason": "ERROR", "requestId": "amzn1.echo-api.request.864a9333-cee5-409a-abbe-9bda1cbed895", "timestamp": "2018-07-02T17:43:45Z", "type": "SessionEndedRequest"}, "session": {"application": {"applicationId": "amzn1.ask.skill.f645ba81-6f3d-46e6-8035-2510ea8983db"}, "new": false, "sessionId": "amzn1.echo-api.session.462ff667-b8cf-4ede-b838-4834c767e66e", "user": {"userId": "amzn1.ask.account.AH4OQYIPRCSQTFZGZHZEQHZBSTHH25UA62624V5Y7CY7BK6CYOQZ7I7PLWK4O74DZUK4A2L42ZPR4PWYEFRHW662TOVUD3PBOW4RLQJ3EHZUZYSO36VC46C3EWGOVODYUISV73OETGRBDRASF744H7TW2GJX3ME7I2YSFBAS63NQAF73BUBGRTRINYCKH3GOZ6YV3UKKQ3LY2QI"}}, "version": "1.0"}
{}
127.0.0.1 - - [02/Jul/2018 17:43:45] "POST / HTTP/1.1" 200 -
In particular, these two lines are used to return the results of the query to Alexa.
#stores all result rows from the query in the data variable
data = cursor.fetchall()
return statement(data)
I've been looking through other questions and have found out that cursor.fetchall() returns query results as a tuple.
Does anyone know how I would convert the results of the query into a string, so that I could return the query results to Alexa ?
Try printing data. If data is a singleton (a tuple with one element), then you can simply use data = data[0] and then return statement(data). If not, you may just want to use cursor.fetchone() to guarantee that you are returned a singleton.

Key Error when passing a variable using session in Flask

I want to pass variables from a function to a class in a Flask app using session. This is my code:
#app.route('/login', methods=['POST'])
def login():
if not request.is_json:
return jsonify({"msg": "Missing JSON in request"}), 400
username = request.json.get('username', None)
password = request.json.get('password', None)
session['client_fname'] = request.json.get('Client First Name', None)
session['client_lname'] = request.json.get('Client Last Name', None)
... ...
access_token = create_access_token(identity=username)
return jsonify(access_token=access_token), 200
class PrivateResource(Resource):
#app.route('/protected', methods=['GET'])
#jwt_required
def sendData():
return mysqldb.addUser("{}".format(session['client_fname']),"{}".format(session['client_lname']))
The variables I want to pass are session['client_fname'] and session['client_lname']. However, when I try to trigger sendData() using curl -X GET http://localhost:5000/protected -H "Authorization: Bearer JWTGOESHERE" I get:
File "/Users/open/venv/FlaskMiddleware/lib/python2.7/site-packages/flask/app.py", line 2309, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/open/venv/FlaskMiddleware/lib/python2.7/site-packages/flask/app.py", line 2295, in wsgi_app
response = self.handle_exception(e)
File "/Users/open/venv/FlaskMiddleware/lib/python2.7/site-packages/flask_restful/__init__.py", line 273, in error_router
return original_handler(e)
File "/Users/open/venv/FlaskMiddleware/lib/python2.7/site-packages/flask/app.py", line 1741, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/open/venv/FlaskMiddleware/lib/python2.7/site-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
File "/Users/open/venv/FlaskMiddleware/lib/python2.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/open/venv/FlaskMiddleware/lib/python2.7/site-packages/flask_restful/__init__.py", line 273, in error_router
return original_handler(e)
File "/Users/open/venv/FlaskMiddleware/lib/python2.7/site-packages/flask/app.py", line 1718, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/open/venv/FlaskMiddleware/lib/python2.7/site-packages/flask/app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/open/venv/FlaskMiddleware/lib/python2.7/site-packages/flask/app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/open/venv/FlaskMiddleware/lib/python2.7/site-packages/flask_jwt_extended/view_decorators.py", line 101, in wrapper
return fn(*args, **kwargs)
File "/Users/open/PycharmProjects/FlaskMiddleware/Flask-API-Middleware-V1/authenticate.py", line 48, in sendData
return mysqldb.addUser("{}".format(session['client_fname'],None),"{}".format(session['client_lname']))
File "/Users/open/venv/FlaskMiddleware/lib/python2.7/site-packages/werkzeug/local.py", line 377, in <lambda>
__getitem__ = lambda x, i: x._get_current_object()[i]
File "/Users/open/venv/FlaskMiddleware/lib/python2.7/site-packages/flask/sessions.py", line 83, in __getitem__
return super(SecureCookieSession, self).__getitem__(key)
KeyError: 'client_fname'
Whats wrong with the way I'm using session to pass the variables? How can I fix this?
UPDATE
If this is a cookie issue having to do with preserving the session, how could I achieve this? Now I'm creating a cookie file in curl -H "Content-Type: application/json" -X POST -d '{"username":"user1","password":"abc123","Client First Name":"SAUR","Client Last Name":"KRIS"}' http://localhost:5000/login -c cookies.txt and then trying curl -X GET http://localhost:5000/protected -H "Authorization: Bearer JWTGOESHERE" -b cookies.txt. Now I'm getting TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement. and I'm not sure if this is progress or just plain wrong.
It seems that you're using flask_jwt_extended so you could bypass the problem by storing that data directly in the token.
# ...
access_token = create_access_token({
'first_name': request.json.get('Client First Name', None),
'last_name': request.json.get('Client Last Name', None)
})
And then retrieving it in the protected route:
# ...
user_adata = get_jwt_identity()
You are almost definitely not preserving state in between your requests. If you are using postman or curl make sure to save the session id cookie. If you are using requests use requests.session().
Your update looks like it's likely to be progress, but perhaps the mysqldb.addUser function is returning None.
The easiest way to determine if you're making progress would be to debug with a breakpoint at the line return mysqldb.addUser("{}".format(session['client_fname']),"{}".format(session['client_lname'])) (I usually use PyCharm for this, but most IDEs will have something for debug, or you can use pdb). A faster but messier option, add a print statement before the call to mysqldb.addUser, and another print statement afterwards printing the return value.

Unit testing Flask app - mocking global variables in the app

I've got a Flask app module (app.py) which looks like this
# imports
...
from flask import Flask, request, Response
...
# module-level vars, including `logger` and `APP`
...
logger = None
APP = None
...
def init():
"""
Initialisation of app resources, including `logger`
"""
...
APP = Flask(__name__)
...
logger = logging.getLogger()
...
...
try:
init()
except Exception as e:
logger.error(str(e))
#APP.route('/healthcheck', methods=['GET'])
def healthcheck():
"""
Healthcheck endpoint - just returns OK if the app
initialised OK.
"""
return 'OK'
#APP.route('/get_keys', method=['POST'])
def get_keys():
"""
Main endpoint - accepts a POST request from a client
containing either a CSV or JSON payload defining a set
of geographic locations, and then returns some "keys"
for these.
"""
try:
logger.info('Extracting payload')
# extract payload
logger.info('Processing for keys')
# do some stuff
...
...
except Exception as e:
logger.error("Error: {}.".format(str(e)))
# return response
I've got unit tests for the Flask app defined in a module AppTests in the tests subpackage.
# general imports including `unittest` etc.
# import app module as `app`
class AppTests(unittest.TestCase):
"""
Flask app tests
"""
#classmethod
def setUpClass(self):
app.APP.config['TESTING'] = True
app.APP.config['DEBUG'] = False
self.app = app.APP.test_client()
# define other resources needed for `self.app`
def test_healthcheck(self):
res = self.app.get(path='/healthcheck')
self.assertEqual(res.status_code, 200)
def test_get_keys__csv(self):
# define sample csv data in `data` variable
headers = {
'Accept-Encoding': 'identity,deflate,gzip,compress',
'Content-Type': 'text/csv; charset=utf-8',
'Content-Length': len(data)
}
res = self.app.post(path='/get_keys', headers=headers.items(), data=data)
self.assertEqual(res.status_code, 200)
The test for the healthcheck endpoint passes but the test for the get_keys endpoint fails.
$ python -m unittest -v AppTests.AppTests.test_get_keys__csv
test_get_keys__csv (AppTests.AppTests) ...
ERROR
======================================================================
ERROR: test_get_keys__csv (AppTests.AppTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "AppTests.py", line 105, in test_get_keys__csv
res = self.app.post(path='/get_keys', headers=headers.items(), data=data)
File "/path/to/venv/lib/python2.7/site-packages/werkzeug/test.py", line 801, in post
return self.open(*args, **kw)
File "/path/to/venv/lib/python2.7/site-packages/flask/testing.py", line 127, in open
follow_redirects=follow_redirects)
File "/path/to/venv/lib/python2.7/site-packages/werkzeug/test.py", line 764, in open
response = self.run_wsgi_app(environ, buffered=buffered)
File "/path/to/venv/lib/python2.7/site-packages/werkzeug/test.py", line 677, in run_wsgi_app
rv = run_wsgi_app(self.application, environ, buffered=buffered)
File "/path/to/venv/lib/python2.7/site-packages/werkzeug/test.py", line 884, in run_wsgi_app
app_rv = app(environ, start_response)
File "/path/to/venv/lib/python2.7/site-packages/flask/app.py", line 1994, in __call__
return self.wsgi_app(environ, start_response)
File "/path/to/venv/lib/python2.7/site-packages/flask/app.py", line 1985, in wsgi_app
response = self.handle_exception(e)
File "/path/to/venv/lib/python2.7/site-packages/flask/app.py", line 1540, in handle_exception
reraise(exc_type, exc_value, tb)
File "/path/to/venv/lib/python2.7/site-packages/flask/app.py", line 1982, in wsgi_app
response = self.full_dispatch_request()
File "/path/to/venv/lib/python2.7/site-packages/flask/app.py", line 1614, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/path/to/venv/lib/python2.7/site-packages/flask/app.py", line 1517, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/path/to/venv/lib/python2.7/site-packages/flask/app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "/path/to/venv/lib/python2.7/site-packages/flask/app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/path/to/app.py", line 205, in get_keys
logger.error("Error: {}.".format(str(e)))
AttributeError: 'NoneType' object has no attribute 'error'
----------------------------------------------------------------------
Ran 1 test in 0.036s
FAILED (errors=1)
It looks like the reference to the logger object in the get_keys endpoint in the app is null when I make the call to self.app.post('/get_keys, headers=headers.items(), data=data). Every call to logger.info is generating an exception in the endpoint, which is caught and logged, and that's what I am seeing when I run the endpoint test.
Is there a way to mock this, or some how bypass the use of logger from the tests module itself? I would rather not modify the endpoint method itself.
You could potentially mock out the logging import when you run test_get_keys__csv().
from unittest.mock import patch
#patch('path.to.app.logging') # Mock the logging import
def test_get_keys__csv(self, mock_logging):
# define sample csv data in `data` variable
headers = {
'Accept-Encoding': 'identity,deflate,gzip,compress',
'Content-Type': 'text/csv; charset=utf-8',
'Content-Length': len(data)
}
res = self.app.post(path='/get_keys', headers=headers.items(), data=data)
self.assertEqual(res.status_code, 200)
If you're using Python 2, mock is a separate install.
pip install mock
and then import with
from mock import patch
More info on mock: https://docs.python.org/3/library/unittest.mock.html

'NoneType' object is not subscriptable [duplicate]

This question already has answers here:
How to get POSTed JSON in Flask?
(13 answers)
Closed 6 years ago.
I am creating an ios app that uses a server written in flask + python, and when I make a connection to the server to register a user I keep getting a 'NoneType' object is not subscriptable error in my server.py file. Basically my question is what is causing this error and how am I able to fix this. Also if anyone can point me in the right direction of different or easier ways to do this I would appreciate it thanks!
Here is the server.py file:
import bcrypt
from flask import Flask, request, make_response,jsonify
from flask_restful import Resource, Api
from pymongo import MongoClient
from json import JSONEncoder
from bson.objectid import ObjectId
from functools import wraps
app = Flask(__name__)
mongo = MongoClient('localhost', 27017)
app.db = mongo.eventure_db
app.bcrypt_rounds = 12
api = Api(app)
# Authentication code.
def check_auth(username, password):
# check_auth should access the database and check if the username + password are correct.
# create a collection to hold the users.
user_collection = app.db.users
user = user_collection.find_one({'username': username})
if user is None:
return False
else:
# check if hash generated matches stored hash
encodedPassword = password.encode('utf-8')
if bcrypt.hashpw(encodedPassword, user['password']) == user['password']:
return True
else:
return False
# User resource
class User(Resource):
def post(self):
if (request.json['username'] == None
or request.json['password'] == None):
return ({'error': 'Request requires username and password'},
400,
None)
user_collection = app.db.users
user = user_collection.find_one({'username': request.json['username']})
if user is not None:
return ({'error': 'Username already in use'}, 400, None)
else:
encodedPassword = request.json['password'].encode('utf-8')
hashed = bcrypt.hashpw(
encodedPassword, bcrypt.gensalt(app.bcrypt_rounds))
request.json['password'] = hashed
user_collection.insert_one(request.json)
#requires_auth
def get(self):
return (None, 200, None)
api.add_resource(User, '/eventure/api/v1.1/user/')
# Must define a custom JSON Serializer for flask_restful
# this is because ObjectId is not a string, and therefore,
# Flask's default serializer cannot serialize it.
#api.representation('application/json')
def output_json(data, code, headers=None):
resp = make_response(JSONEncoder().encode(data), code)
resp.headers.extend(headers or {})
return resp
if __name__ == '__main__':
app.config['TRAP_BAD_REQUEST_ERRORS'] = True
app.run(host='localhost', port=8789, debug=True)
And this is my register function in swift:
#IBAction func register(_ sender: AnyObject) {
let url = URL(string: "http://localhost:8789/eventure/api/v1.1/user/")
var request = URLRequest(url: url!)
request.httpMethod = "POST"
request.setValue(generateBasicAuthHeader(username: username.text!, password: password.text!), forHTTPHeaderField: "Authorization")
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error in
if let response = response, let data = data {
print(String(data: data, encoding: String.Encoding.utf8))
}
}
task.resume()
self.username.text = ""
self.password.text = ""
}
traceback:
[28/Oct/2016 19:22:33] "POST /eventure/api/v1.1/user/ HTTP/1.1" 500 -
Traceback (most recent call last):
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1836, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1820, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask_restful/__init__.py", line 270, in error_router
return original_handler(e)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1403, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/_compat.py", line 32, in reraise
raise value.with_traceback(tb)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1477, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask_restful/__init__.py", line 270, in error_router
return original_handler(e)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1381, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/_compat.py", line 32, in reraise
raise value.with_traceback(tb)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1475, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1461, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask_restful/__init__.py", line 471, in wrapper
resp = resource(*args, **kwargs)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/views.py", line 84, in view
return self.dispatch_request(*args, **kwargs)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask_restful/__init__.py", line 581, in dispatch_request
resp = meth(*args, **kwargs)
File "/Users/Dynee/eventure-backend-api/server.py", line 128, in post
if (request.json['username'] == None
TypeError: 'NoneType' object is not subscriptable
Also here is the generateBasicAuthHeader function:
func generateBasicAuthHeader(username: String, password: String) -> String {
let loginString = String(format: "%#:%#", username, password)
let loginData = loginString.data(using: String.Encoding.utf8)!
let base64LoginString = loginData.base64EncodedString()
let basicAuthHeader = "Basic \(base64LoginString)"
return basicAuthHeader
}
You need to explicitly set the content-type to application/json for request.json to work properly in flask. If the header isn't set, request.json would return None.
But the recommended to get json data in flask from a post request is to use request.get_json()
I'll also urge you to test your api with the nifty requests module before using your ios application.
>>> import requests
>>> requests.post(url, json={'name': 'hello world'})
It already sets the appropriate headers required to make a json request
If it works with the requests module, then you can be sure that it's going to work with your ios application. you just need to make sure you're setting the correct content-type.
You can forcefully tell flask to ignore the content-type header with
request.get_json(force=True)

Categories