Bottle.py error routing - python

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)

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)

Using Pytest to monkeypatch an initialized redis connection in a flask application

I've been struggling with this for awhile now. I Have a flask app that is executed in my app.py file. In this file I have a bunch of endpoints that call different functions from other files. In another file, extensions.py, I've instantiated a class that contains a redis connection. See the file structure below.
#app.py
from flask import Flask
from extensions import redis_obj
app = Flask(__name__)
#app.route('/flush-cache', methods=['POST'])
def flush_redis():
result = redis_obj.flush_redis_cache()
return result
# extensions.py
from redis_class import CloudRedis
redis_obj = CloudRedis()
# redis_class
import redis
class CloudRedis:
def __init__(self):
self.conn = redis.Redis(connection_pool=redis.ConnectionPool.from_url('REDIS_URL',
ssl_cert_reqs=None))
def flush_redis_cache(self):
try:
self.conn.flushdb()
return 'OK'
except:
return 'redis flush failed'
I've been attempting to use monkeypatching in a test patch flush_redis_cache, so when I run flush_redis() the call to redis_obj.flush_redis_cache() will just return "Ok", since I've already tested the CloudRedis class in other pytests. However, no matter what I've tried I haven't been able to successfully patch this. This is what I have below.
from extensions import redis_obj
from app import app
#pytest.fixture()
def client():
yield app.test_client()
def test_flush_redis_when_redis_flushed(client, monkeypatch):
# setup
def get_mock_flush_redis_cache():
return 'OK'
monkeypatch.setattr(cloud_reids, 'flush_redis_cache', get_mock_flush_redis_cache)
cloud_redis.flush_redis = get_mock_flush_redis_cache
# act
res = client.post('/flush-cache')
result = flush_redis()
Does anyone have any ideas on how this can be done?

How to log redirects in Pyramid?

I'm trying to set it up so that my Pyramid app logs a message whenever it does a redirect - e.g. When one of my views raises HTTPFound.
Creating a custom subclass of HTTPFound worked but it sucks to have to make sure that class is used everywhere in the app.
I then had the idea of creating a custom exception view with context=HTTPFound but that view doesn't seem to get called.
Is there a standard way to set up special handling for a redirect that is global to the app?
To log out redirects you would just have a tween that checks the return status, something like:
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.httpexceptions import HTTPFound
def hello1(request):
raise HTTPFound(request.route_url('hello2'))
def hello2(request):
return {'boom': 'shaka'}
def redirect_logger(handler, registry):
def redirect_handler(request):
response = handler(request)
if response.status_int == 302:
print("OMGZ ITS NOT GOING WHERE YOU THINK")
return response
return redirect_handler
def main():
config = Configurator()
config.add_route('hello1', '/', view=hello1)
config.add_route('hello2', '/hi', view=hello2, renderer='json')
config.add_tween('__main__.redirect_logger')
app = config.make_wsgi_app()
return app
if __name__ == '__main__':
app = main()
server = make_server('0.0.0.0', 8080, app)
print("http://0.0.0.0:8080")
server.serve_forever()

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

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.

How to use beaker for multiple bottle apps

The documentation of Bottle show how to use beaker for session management, like the following
import bottle
from beaker.middleware import SessionMiddleware
session_opts = {
'session.type': 'file',
'session.cookie_expires': 300,
'session.data_dir': './data',
'session.auto': True
}
app = SessionMiddleware(bottle.app(), session_opts)
#bottle.route('/test')
def test():
s = bottle.request.environ.get('beaker.session')
s['test'] = s.get('test',0) + 1
s.save()
return 'Test counter: %d' % s['test']
bottle.run(app=app)
My problem is, I have multiple bottle applications, and each of them serves a virtual host (that is powered by cherrypy). So I can not use decorate "#bottle.route", instead, I need to use decorate like "app1.route('/test')" , "app2.route('/test')" .
But if I warp app with Beaker middleware, like the following,
app1= Bottle()
app2= Bottle()
app1 = SessionMiddleware(app1, session_opts)
app2 = SessionMiddleware(app2, session_opts)
when python run to the following ,
#app1.route('/test')
def test():
return 'OK'
it will report error, AttributeError: 'SessionMiddleware' object has no attribute 'route'
That's for sure, because now app1 is actually a 'SessionMiddleware' not a Bottle app.
How to solve that issue?
After digging into the beaker source code a little, finally I found the way.
Use the decorator this way:
#app1.wrap_app.route('/test')
def test():
return 'OK'

Categories

Resources