Breakpoints inside Flask-RESTful class methods aren't hit on PTVS - python

I'm using Python Tools for Visual Studio, and I've set up a project with a virtual environment and installed Flask-RESTful there.
Then, I just copied their hello world example
from flask import Flask
from flask.ext.restful import reqparse, abort, Api, Resource
app = Flask(__name__)
app.debug = True
api = Api(app)
TODOS = {
'todo1': {'task': 'build an API'},
'todo2': {'task': '?????'},
'todo3': {'task': 'profit!'},
}
def abort_if_todo_doesnt_exist(todo_id):
if todo_id not in TODOS:
abort(404, message="Todo {} doesn't exist".format(todo_id))
parser = reqparse.RequestParser()
parser.add_argument('task', type=str)
# Todo
# show a single todo item and lets you delete them
class Todo(Resource):
def get(self, todo_id):
abort_if_todo_doesnt_exist(todo_id)
return TODOS[todo_id]
def delete(self, todo_id):
abort_if_todo_doesnt_exist(todo_id)
del TODOS[todo_id]
return '', 204
def put(self, todo_id):
args = parser.parse_args()
task = {'task': args['task']}
TODOS[todo_id] = task
return task, 201
# TodoList
# shows a list of all todos, and lets you POST to add new tasks
class TodoList(Resource):
def get(self):
return TODOS
def post(self):
args = parser.parse_args()
todo_id = 'todo%d' % (len(TODOS) + 1)
TODOS[todo_id] = {'task': args['task']}
return TODOS[todo_id], 201
##
## Actually setup the Api resource routing here
##
api.add_resource(TodoList, '/todos')
api.add_resource(Todo, '/todos/<string:todo_id>')
if __name__ == '__main__':
app.run(debug=True)
Everything works fine, and if I put breakpoints on the lines that are executed before starting the app.run(debug=True) they're hit (with F10 and F11 working great, and local variables being updated as expected)
However, I'd love to debug what happens when a request is processed, but if I add breakpoints to the methods of the Todo or TodoList classes, they're never hit. I added code (like print('here')) to see if they're being processed and they are... also, they're returning what I expect when opened from a browser.
Is there something I'm missing from the setup?
Thanks!
UPDATE: I found out that if I attach VS to the python.exe process that's running my code, I'm able to debug those methods... so I guess that the question is now: can I force VS to attach to the process once it's launched as it does with regular .NET apps?

I had the same exact problem (but with PyCharm on Mac).
I believe this has something to do with the way Flask reloads itself when debug=True. Setting debug to False allowed me to break inside the view methods.

Disabling Debug resolves the issue with the breakpoint hit, but has the disadvantage that you are unable to read exception trace output.
One way to bypass this limitation is to add:
DEBUG = False
PROPAGATE_EXCEPTIONS = True
to your config.
When an exception occurs the browser still displays "Internal Server Error" message, but the console Window will receive the exception trace normally.

Related

cant run function, because signal only works in main thread of the main interpreter?

I'm trying to build an API using Flask. If the endpoint /register/string:token is retrieved and the token is in a defined list, the function register_user(username) should be executed. This function sends a command to another server using the rcon protocol. However, as soon as the function is called, I get the following error: ValueError: signal only works in main thread of the main interpreter. How can I fix the problem? Or, how can I run the function in the main thread?
My code looks something like this (Note: The script runs fine on Windows):
from flask import Flask, send_file
from mcrcon import MCRcon as rcon
import configparser
api = Flask(__name__)
# #api.route('/register/<string:token>', methods=['GET'])
def register(token):
cache = utils.load_cache()
if token in list(cache['register_tokens'].keys()):
utils.register_user(username=cache['register_tokens'][token]) # problem!
del cache['register_tokens'][token]
utils.update_cache(obj=cache)
return {'status':'registration successful'}
else:
return {'status':'registration failed'}
class utils:
#staticmethod
def load_cache(file='../cache.pkl'):
...
#staticmethod
def update_cache(obj, file='../cache.pkl'):
...
#staticmethod
def clear_cache():
...
#staticmethod
def register_user(username):
with rcon(config['global']['server_ip'], config['global']['server_rcon_secret']) as console:
r = console.command(...)
console.disconnect()
if __name__ == '__main__':
config = configparser.ConfigParser()
config.read('../config.conf')
if bool(config['discordbot.py']['enable'])==True:
api.add_url_rule('/register/<string:token>', 'register', register)
api.run(host=config['global']['server_ip'], debug=False, use_reloader=False)

How do I get the request id from RequestIDLogFilter in python flask?

So, in my main app.py, I have :
app = create_app()
...
app.run()
In __init__.py I have:
def create_app():
...
app = Flask(__name__)
setup_logging(app)
return app
def setup_logging(app):
RequestID(app)
handler = logging.FileHandler(app.container.config.get("app.LOG_FILE"))
handler.setFormatter(logging.Formatter("%(asctime)s : %(levelname)s : %(request_id)s - %(message)s"))
handler.addFilter(RequestIDLogFilter()) # << Add request id contextual filter
logging.getLogger().addHandler(handler)
logging.getLogger().setLevel(level="DEBUG")
And in my routes.py, I have: (This wiring/linking is done with the help of dependency injection)
def configure(app):
app.add_url_rule("/", "check", check)
...
def check():
logging.info("You hit /")
# Here I want to return the Request UUID that's generated by RequestLogIDFilter
return make_response(jsonify(dict(ok=True)), 200)
How do I access the Request UUID generated by the RequestLogIDFilter? In my log file I correctly see the log messages as:
2022-08-15 07:00:18,030 : INFO : 27b437fd-98be-4bc9-a609-912043e3a38e - Log message test memberId=9876
I want to take this value 27b437fd-98be-4bc9-a609-912043e3a38e and include it in the response headers as X-REQUEST-UUID: 27b437fd-98be-4bc9-a609-912043e3a38e
An alternative was to rip out RequestLogIDFilter and only work with flask-request-id-header except, there's no way to write it to log file (logging.FileHandler does not have a write() method so it fails)
This is something trivial I'm sure but the official docs nowhere mention how to access these request ids for logging to file or sending back as a response header.
If you are using flask_log_request_id then you probably want this as given in their github repo
Code copied here in case link does not work
from flask_log_request_id import current_request_id
#app.after_request
def append_request_id(response):
response.headers.add('X-REQUEST-ID', current_request_id())
return response

Testing a POST method unit test which inserts data to mongodb database

I want to know how am I supposed to test my code and see whether it works properly. I want to make sure that it stores the received data to the database. Can you please tell me how am I supposed to do that? While I was searching the forum I found this post but I did not really understand what is going on. here is the code I want to test.
client = MongoClient(os.environ.get("MONGODB_URI"))
app.db = client.securify
app.secret_key = str(os.environ.get("APP_SECRET"))
#app.route("/", methods=["GET", "POST"])
def home():
if request.method == "POST":
ip_address = request.remote_addr
entry_content = request.form.get("content")
formatted_date = datetime.datetime.today().strftime("%Y-%m-%d/%H:%M")
app.db.entries.insert({"content": entry_content, "date": formatted_date, "IP": ip_address})
return render_template("home.html")
and here is the mock test I wrote:
import os
from unittest import TestCase
from app import app
class AppTest(TestCase):
# executed prior to each test
def setUp(self):
# you can change your application configuration
app.config['TESTING'] = True
# you can recover a "test cient" of your defined application
self.app = app.test_client()
# then in your test method you can use self.app.[get, post, etc.] to make the request
def test_home(self):
url_path = '/'
response = self.app.get(url_path)
self.assertEqual(response.status_code, 200)
def test_post(self):
url_path = '/'
response = self.app.post(url_path,data={"content": "this is a test"})
self.assertEqual(response.status_code, 200)
The test_post gets stuck and after some seconds gives an error when reaches app.db.entries.insert({"content": entry_content, "date": formatted_date, "IP": ip_address}) part. Please tell me also how can I retrieve the saved data in order to make sure it is saved in the expected way
This is what I do using NodeJS, not tested at all in python but the idea is the same.
First of all, find a in-memory DB, there are options like pymongo-inmemory or mongomock
Then in your code you have to do the connection according to you environment (production/development/whatever)
Something like this:
env = os.environ.get("ENV")
if env == "TESTING":
# connect to mock db
elif env == "DEVELOMPENT":
# for example if you want to test against a real DB but not the production one
# then do the connection here
else:
# connect to production DB
I don't know if it is the proper way to do it but I found a solution. After creating a test client self.app = app.test_client() the db gets set to localhost:27017 so I changed it manually as follows and it worked:
self.app = app.test_client()
client = MongoClient(os.environ.get("MONGODB_URI"))

Routing Cherrypy web apps(fail to road resource)

I have spent more than 4 days trying to figure out this, but my every attempt failed, so I thought I could really use some of your helps.
In the code below, Storm is an app with function submit_data() which fetches data from databases according to pagename parameter passed at instantiation and idx parameter passed from xmlhttp GET request in Javascript.
Basically I want to have an index page with Root class, and a bunch of similar Storm pages with different pagename parameters read from an XML file.
I am using RoutesDispatcher because I am parametrizing page name(address) using the XML file too.
When I used /storm as the address it didn't even work probably because the name clashes, so I set the address as /st/.
Now it displays the header, but it does not find submit_data function at all. The Chrome console throws the following error.
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:8080/st/submit_data?idx=0
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:8080/st/submit_data?idx=2
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:8080/st/submit_data?idx=1
How can I structure this so that when I type localhost:8080/st on my browser, the html/javascript template storm.html looks up submit_data function and call it and display it on the webpage? Before I started routing, it worked totally fine.
class Storm(object):
pagename = ""
def __init__(self, pagename):
self.pagename = pagename
#cherrypy.expose
#cherrypy.tools.mako(filename="storm.html", directories=MEDIA_DIR)
def index(self, name=None):
return {'size': len(params["dashboards"][self.pagename])}
#cherrypy.expose
def submit_data(self, idx):
idx = int(idx)
chart = params["dashboards"][self.pagename][idx]
# Sample page that displays the number of records in "table"
# Open a cursor, using the DB connection for the current thread
c = cherrypy.thread_data.dbdict[chart["dbname"]].cursor()
print 'query being executed......'
c.execute(chart["command"])
print 'query being fetched......'
res = c.fetchall()
c.close()
# construct a JSON object from query result
jsres = []
jsres.append(chart["selected"])
.
.
.
return json.dumps(jsres)
class Root(object):
#storm = Storm("first")
#cherrypy.expose
#cherrypy.tools.mako(filename="index.html", directories=MEDIA_DIR)
def index(self, name=None):
return {}
config = {'/media':
{'tools.staticdir.on': True,
'tools.staticdir.dir': MEDIA_DIR,
}
}
root = Root()
storm = Storm("first")
mapper = cherrypy.dispatch.RoutesDispatcher()
mapper.connect('home','/',controller=root, action='index')
mapper.connect('st','/st/',controller=storm, action='index')
conf = {'/': {'request.dispatch': mapper}}
app=cherrypy.tree.mount(root=None,config=conf)
cherrypy.quickstart(app)
mapper.connect('st','/st/submit_data',controller=storm, action='submit_data')
Routes explicitly list all their handlers, and because of that also don't need to be exposed. Based on this example, you might not want the routes dispatcher. The default dispatcher with multiple roots mounted would work fine.

Bottle.py error routing

Bottle.py ships with an import to handle throwing HTTPErrors and route to a function.
Firstly, the documentation claims I can (and so do several examples):
from bottle import error
#error(500)
def custom500(error):
return 'my custom message'
however, when importing this statement error is unresolved but on running the application ignores this and just directs me to the generic error page.
I found a way to get around this by:
from bottle import Bottle
main = Bottle()
#Bottle.error(main, 500)
def custom500(error):
return 'my custom message'
But this code prevents me from embedding my errors all in a separate module to control the nastiness that would ensue if I kept them in my main.py module because the first argument has to be a bottle instance.
So my questions:
Has anyone else experienced this?
why doesn't error seem to resolve in only my case (I installed from pip install bottle)?
Is there a seamless way to import my error routing from a separate python module into the main application?
If you want to embed your errors in another module, you could do something like this:
error.py
def custom500(error):
return 'my custom message'
handler = {
500: custom500,
}
app.py
from bottle import *
import error
app = Bottle()
app.error_handler = error.handler
#app.route('/')
def divzero():
return 1/0
run(app)
This works for me:
from bottle import error, run, route, abort
#error(500)
def custom500(error):
return 'my custom message'
#route("/")
def index():
abort("Boo!")
run()
In some cases I find it's better to subclass Bottle. Here's an example of doing that and adding a custom error handler.
#!/usr/bin/env python3
from bottle import Bottle, response, Route
class MyBottle(Bottle):
def __init__(self, *args, **kwargs):
Bottle.__init__(self, *args, **kwargs)
self.error_handler[404] = self.four04
self.add_route(Route(self, "/helloworld", "GET", self.helloworld))
def helloworld(self):
response.content_type = "text/plain"
yield "Hello, world."
def four04(self, httperror):
response.content_type = "text/plain"
yield "You're 404."
if __name__ == '__main__':
mybottle = MyBottle()
mybottle.run(host='localhost', port=8080, quiet=True, debug=True)

Categories

Resources