Bottle framework and OOP, using method instead of function - python

I've done some coding with Bottle. It's really simple and fits my needs. However, I got stick when I tried to wrap the application into a class :
import bottle
app = bottle
class App():
def __init__(self,param):
self.param = param
# Doesn't work
#app.route("/1")
def index1(self):
return("I'm 1 | self.param = %s" % self.param)
# Doesn't work
#app.route("/2")
def index2(self):
return("I'm 2")
# Works fine
#app.route("/3")
def index3():
return("I'm 3")
Is it possible to use methods instead of functions in Bottle?

Your code does not work because you are trying to route to non-bound methods. Non-bound methods do not have a reference to self, how could they, if instance of App has not been created?
If you want to route to class methods, you first have to initialize your class and then bottle.route() to methods on that object like so:
import bottle
class App(object):
def __init__(self,param):
self.param = param
def index1(self):
return("I'm 1 | self.param = %s" % self.param)
myapp = App(param='some param')
bottle.route("/1")(myapp.index1)
If you want to stick routes definitions near the handlers, you can do something like this:
def routeapp(obj):
for kw in dir(app):
attr = getattr(app, kw)
if hasattr(attr, 'route'):
bottle.route(attr.route)(attr)
class App(object):
def __init__(self, config):
self.config = config
def index(self):
pass
index.route = '/index/'
app = App({'config':1})
routeapp(app)
Don't do the bottle.route() part in App.__init__(), because you won't be able to create two instances of App class.
If you like the syntax of decorators more than setting attribute index.route=, you can write a simple decorator:
def methodroute(route):
def decorator(f):
f.route = route
return f
return decorator
class App(object):
#methodroute('/index/')
def index(self):
pass

You have to extend the Bottle class. It's instances are WSGI web applications.
from bottle import Bottle
class MyApp(Bottle):
def __init__(self, name):
super(MyApp, self).__init__()
self.name = name
self.route('/', callback=self.index)
def index(self):
return "Hello, my name is " + self.name
app = MyApp('OOBottle')
app.run(host='localhost', port=8080)
What most examples out there are doing, including the answers previously provided to this question, are all reusing the "default app", not creating their own, and not using the convenience of object orientation and inheritance.

Below works nicely for me :)
Quite object orientated and easy to follow.
from bottle import Bottle, template
class Server:
def __init__(self, host, port):
self._host = host
self._port = port
self._app = Bottle()
self._route()
def _route(self):
self._app.route('/', method="GET", callback=self._index)
self._app.route('/hello/<name>', callback=self._hello)
def start(self):
self._app.run(host=self._host, port=self._port)
def _index(self):
return 'Welcome'
def _hello(self, name="Guest"):
return template('Hello {{name}}, how are you?', name=name)
server = Server(host='localhost', port=8090)
server.start()

I took #Skirmantas answer and modified it a bit to allow for keyword arguments in the decorator, like method, skip, etc:
def routemethod(route, **kwargs):
def decorator(f):
f.route = route
for arg in kwargs:
setattr(f, arg, kwargs[arg])
return f
return decorator
def routeapp(obj):
for kw in dir(obj):
attr = getattr(obj, kw)
if hasattr(attr, "route"):
if hasattr(attr, "method"):
method = getattr(attr, "method")
else:
method = "GET"
if hasattr(attr, "callback"):
callback = getattr(attr, "callback")
else:
callback = None
if hasattr(attr, "name"):
name = getattr(attr, "name")
else:
name = None
if hasattr(attr, "apply"):
aply = getattr(attr, "apply")
else:
aply = None
if hasattr(attr, "skip"):
skip = getattr(attr, "skip")
else:
skip = None
bottle.route(attr.route, method, callback, name, aply, skip)(attr)

try this, worked for me, documentation is also pretty decent to get started with ...
https://github.com/techchunks/bottleCBV

I know this question is quite old, but it is useful.
I am currently refactoring a Bottle application code into class. I am thinking about using something like:
import bottle
class App:
def __init__(self, port=80):
self.app = bottle.Bottle()
self.port = port
def setup_routes(self):
#self.app.route("/foo")
def foo():
return("foo")
#self.app.route("/bar")
def bar():
return("bar")
def start(self):
self.app.run(port=self.port)
a = App()
a.setup_routes()
a.start()
Does it work for your applications too? Feel free to comment, I am interested about this.
Notes from the creator of Bottle: https://github.com/bottlepy/bottle/issues/1224#issuecomment-619344849

Related

How to wrap a base class method in python

Working with python3, I have following classes
class RESTApi:
def __init__(self):
self.connection = # Create connection
def hello_world(self):
print("Hello World !!")
... # More methods
class BaseVM:
def __init__(self):
self.restapi = RESTApi()
def _refresh_restapi_connection(self):
self.restapi = # Reconnect
class MyClass(BaseVM):
def __init__(self, *args):
BaseVM.__init__(self, *args)
def create_user(self, usr, pwd):
self.restapi.create_user(usr, pwd)
MyClass is where I interact with the existing classes and I can change the code in MyClass but not in the other classes.
The issue I am having is that the REST connection is being cleaned out, when left idle. So I need to implement a call to refresh the REST connection(_refresh_restapi_connection), whenever 'self.restapi.XYZ' fails.
And I want this to be automatically done in try...except. The methods like 'create_user' etc. need not know that there was a reconnect.
Since I can't change the other classes, I am trying to create a wrapper over self.restapi calls
Something of the type:
class RestRetry:
'''
Wrapper over RESTApi class instance with retries
'''
def __init__(self, vm):
self._restapi = vm.restapi
def __getattr__(self, attrb):
return self.rest_retry(attrb)
def rest_retry(self, attrb):
def _retry():
try:
# class REST API with retries
result = getattr(self._restapi, attrb)
except:
self._refresh_restapi_connection()
return getattr(self._restapi, attrb)
else:
return result
return _retry
And then MyClass will have
def __init__(self, *args):
BaseVM.__init__(self, *args)
self.restapi = RestRetry()
It doesn't work though
Maybe I am missing something very basic, or maybe trying to solve this the wrong way.
This is what I eventually ended up writing. Had to change the attribute name in MyClass which I didn't want to but could not get it working without that
class RestRetry:
'''
Wrapper over RESTApi class instance with retries
'''
def __init__(self, vm):
self._vm= vm
def __getattr__(self, attrb):
return self.rest_retry(attrb)
def rest_retry(self, attrb):
def _retry(*args, **kwargs):
try:
# class REST API with retries though
result = getattr(self._vm.restapi, attrb)(*args, **kwargs)
except:
self._vm._refresh_restapi_connection()
return getattr(self._vm.restapi, attrb)(*args, **kwargs)
else:
return result
return _retry
And then in MyClass
def __init__(self):
BaseVM.__init__(self)
self._restapi = self.RestRetry(self)
def any_method(self):
self._restapi.hello_world()

Set methods with decorators dynamically

I have this class:
class SomeMixin:
permissions = (SomePermission,)
methods = ('GET',)
#list_route(methods=methods, url_path='en', permission_classes=permissions)
def en_list(self):
return get_translated_objects(self, 'en')
#list_route(methods=methods, url_path='ru', permission_classes=permissions)
def ru_list(self):
return get_translated_objects(self, 'ru')
#detail_route(methods=methods, url_path='en', permission_classes=permissions)
def en_detail(self):
return get_translated_object(self.get_object(), 'en')
#detail_route(methods=methods, url_path='ru', permission_classes=permissions)
def ru_detail(self):
return get_translated_object(self.get_object(), 'ru')
I can have more languages in the future and it's not a good solution.
I thought to create loop of languages list and add methods to the class with setattr(self, func_name, func), like:
langs = ('en', 'ru')
for lang in langs:
setattr(self, func.__name__, func)
But I should add decorators to every method, how can I do it?
I believe you should be able to patch the contents of this example into your code. That being said, it seems like a better approach to detect the language from the http header and return an appropriate response.
This approach though functional is not the cleanest.
# This decorator is meant to simulate the decorator exposed by django
def route(url, language, method):
def wrapper(func):
def inner(*args, **kwargs):
print('url => {0}'.format(url))
print('language => {0}'.format(language))
print('method => {0}'.format(method))
func(*args, **kwargs)
return inner
return wrapper
# This class is analogous to your SomeMixin class
class foo(object):
def __init__(self):
method = 'GET'
# fields holds the parameters that will change for each method like url
# and language
fields = (('en', '/en', 'en_list'), ('ru', '/ru', 'ru_list'))
for lang, url, func_name in fields:
setattr(self, func_name, route(url=url, language=lang, method=method)(self.func_creator(lang)))
def func_creator(self, language):
def inner():
print('hello in {0}'.format(language))
return inner
def main():
foo_instance = foo()
print('Calling foo.en_list()')
foo_instance.en_list()
print()
print('Calling foo.ru_list()')
foo_instance.ru_list()
if __name__ == '__main__':
main()

Can't grasp : making a simple singleton in python (for a mongodb manager)

I have read the various main examples of singletons but I have trouble making what I want. My main inspiration here is this example :
class OnlyOne:
class __OnlyOne:
def __init__(self, arg):
self.val = arg
def __str__(self):
return repr(self) + self.val
instance = None
def __init__(self, arg):
if not OnlyOne.instance:
OnlyOne.instance = OnlyOne.__OnlyOne(arg)
else:
OnlyOne.instance.val = arg
def __getattr__(self, name):
return getattr(self.instance, name)
But I don't need anything like getattr, here is my code so far:
class MongoManager:
class __MongoManager:
def __init__(self):
# Initialise client
self.client = pymongo.MongoClient('localhost', 27017)
__instance = None
def __init__(self):
if not MongoManager.__instance:
MongoManager.__instance = MongoManager.__MongoManager()
All I want is to get the variable client when I type MongoManager.client, but I get the error AttributeError: 'MongoManager' object has no attribute 'client'.
I guess I could try with getattr and check something like if arg == "client" but it feels dirty, I'm sure there's a proper way to do that.
Try this:
class MongoManager:
__instance = None
#staticmethod
def getInstance():
if MongoManager.__instance == None:
MongoManager()
return MongoManager.__instance
def __init__(self):
if MongoManager.__instance != None:
raise Exception("This class is a singleton!")
else:
MongoManager.__instance = pymongo.MongoClient('localhost', 27017)
You can get instance of this class by calling MongoManager.getInstance().
What we are doing here? We have a class variable __instance and when object of the class is created, we are initializing it. Now, when we call getInstance() this instance which is stored in __instance will get passed instead of creating new instance.
I managed to do exactly what I wanted. I didn't completely understand how __getattr__ worked so a bit more reading was necessary.
class MongoManager:
class __MongoManager:
def __init__(self):
# Initialise mongo client
self.client = pymongo.MongoClient('localhost', 27017)
__instance = None
def __init__(self):
if not MongoManager.__instance:
MongoManager.__instance = MongoManager.__MongoManager()
def __getattr__(self, item):
return getattr(self.__instance, item)
Now whenever I call MongoManager().client, I get the desired client.

Pythonic way to access Flask request arguements

In my Flask app I am setting a number of local variables that have come in via an API call, using the
from flask import request
.
.
submission_id = request.args.get('submission_id')
grader = request.args.get('grader')
grading_factor = float(request.args.get('grading_factor'))
answer_key = request.args.get('answer_key')
submission_key = request.args.get('submission_key')
What is a less repetitive or otherwise more Pythonic way of setting these 5 variables?
As I suggested in the comments, you could come up with a decorator that would map the request arguments to the corresponding function parameters, such as:
def map_args(func):
#functools.wraps(func)
def wrapper(**kwargs):
all_args = dict(request.args, **kwargs)
return func(**all_args)
return wrapper
Then:
#app.route('/mypath')
#map_args
def handler(submission_id, grader, grading_factor, ...):
"""the remaining logic"""
That's similar to what Flask does with the view_args, except that it does not do any type conversions, which this margin is too narrow to contain.
from flask import Flask, request
app = Flask(__name__)
class DotDict(object):
def __init__(self, inner):
self._inner = inner
def __getattr__(self, item):
return self._inner.get(item)
def get(self, item, default=None):
return self._inner.get(item, default)
class LazyAttribute(object):
def __init__(self, obj, attr):
self.obj = obj
self.attr = attr
def __getattribute__(self, item):
return getattr(getattr(object.__getattribute__(self, 'obj'),
object.__getattribute__(self, 'attr')),
item)
rargs = DotDict(LazyAttribute(request, 'args'))
#app.route("/")
def hello():
print rargs.a, rargs.c, rargs.get('d', 3)
return "Hello World!"
if __name__ == "__main__":
app.run(debug=True)
Accessing http://localhost:5000/?a=1 prints 1 None 3 in the terminal.
The LazyAttribute class is because calling just DotDict(request.args) outside of a request context throws an error. The alternative is to make a function:
def rargs():
return DotDict(request.args)
but I wanted to make usage as smooth as possible.
Firstly, I don't think there's anything wrong with the way you're doing it. But, there are a couple of different approaches you could take; the first being to the call to get the argument from request:
from flask import request
# ...
def getRequestArg(name, *args):
return request.args.get(name, *args)
submission_id = getRequestArg('submission_id')
grader = getRequestArg('grader')
grading_factor = float(getRequestArg('grading_factor'))
answer_key = getRequestArg('answer_key')
submission_key = getRequestArg('submission_key')
If you don't need each of these to be separate local variables, you could store them all in a dict:
from flask import request
# ...
args = {}
arg_names = ('submission_id', 'grader', 'grading_factor', 'answer_key', 'submission_key')
for arg in arg_names:
args[arg] = request.args.get(arg)

Python. Put bottle.py routes in class and use self. [duplicate]

I've done some coding with Bottle. It's really simple and fits my needs. However, I got stick when I tried to wrap the application into a class :
import bottle
app = bottle
class App():
def __init__(self,param):
self.param = param
# Doesn't work
#app.route("/1")
def index1(self):
return("I'm 1 | self.param = %s" % self.param)
# Doesn't work
#app.route("/2")
def index2(self):
return("I'm 2")
# Works fine
#app.route("/3")
def index3():
return("I'm 3")
Is it possible to use methods instead of functions in Bottle?
Your code does not work because you are trying to route to non-bound methods. Non-bound methods do not have a reference to self, how could they, if instance of App has not been created?
If you want to route to class methods, you first have to initialize your class and then bottle.route() to methods on that object like so:
import bottle
class App(object):
def __init__(self,param):
self.param = param
def index1(self):
return("I'm 1 | self.param = %s" % self.param)
myapp = App(param='some param')
bottle.route("/1")(myapp.index1)
If you want to stick routes definitions near the handlers, you can do something like this:
def routeapp(obj):
for kw in dir(app):
attr = getattr(app, kw)
if hasattr(attr, 'route'):
bottle.route(attr.route)(attr)
class App(object):
def __init__(self, config):
self.config = config
def index(self):
pass
index.route = '/index/'
app = App({'config':1})
routeapp(app)
Don't do the bottle.route() part in App.__init__(), because you won't be able to create two instances of App class.
If you like the syntax of decorators more than setting attribute index.route=, you can write a simple decorator:
def methodroute(route):
def decorator(f):
f.route = route
return f
return decorator
class App(object):
#methodroute('/index/')
def index(self):
pass
You have to extend the Bottle class. It's instances are WSGI web applications.
from bottle import Bottle
class MyApp(Bottle):
def __init__(self, name):
super(MyApp, self).__init__()
self.name = name
self.route('/', callback=self.index)
def index(self):
return "Hello, my name is " + self.name
app = MyApp('OOBottle')
app.run(host='localhost', port=8080)
What most examples out there are doing, including the answers previously provided to this question, are all reusing the "default app", not creating their own, and not using the convenience of object orientation and inheritance.
Below works nicely for me :)
Quite object orientated and easy to follow.
from bottle import Bottle, template
class Server:
def __init__(self, host, port):
self._host = host
self._port = port
self._app = Bottle()
self._route()
def _route(self):
self._app.route('/', method="GET", callback=self._index)
self._app.route('/hello/<name>', callback=self._hello)
def start(self):
self._app.run(host=self._host, port=self._port)
def _index(self):
return 'Welcome'
def _hello(self, name="Guest"):
return template('Hello {{name}}, how are you?', name=name)
server = Server(host='localhost', port=8090)
server.start()
I took #Skirmantas answer and modified it a bit to allow for keyword arguments in the decorator, like method, skip, etc:
def routemethod(route, **kwargs):
def decorator(f):
f.route = route
for arg in kwargs:
setattr(f, arg, kwargs[arg])
return f
return decorator
def routeapp(obj):
for kw in dir(obj):
attr = getattr(obj, kw)
if hasattr(attr, "route"):
if hasattr(attr, "method"):
method = getattr(attr, "method")
else:
method = "GET"
if hasattr(attr, "callback"):
callback = getattr(attr, "callback")
else:
callback = None
if hasattr(attr, "name"):
name = getattr(attr, "name")
else:
name = None
if hasattr(attr, "apply"):
aply = getattr(attr, "apply")
else:
aply = None
if hasattr(attr, "skip"):
skip = getattr(attr, "skip")
else:
skip = None
bottle.route(attr.route, method, callback, name, aply, skip)(attr)
try this, worked for me, documentation is also pretty decent to get started with ...
https://github.com/techchunks/bottleCBV
I know this question is quite old, but it is useful.
I am currently refactoring a Bottle application code into class. I am thinking about using something like:
import bottle
class App:
def __init__(self, port=80):
self.app = bottle.Bottle()
self.port = port
def setup_routes(self):
#self.app.route("/foo")
def foo():
return("foo")
#self.app.route("/bar")
def bar():
return("bar")
def start(self):
self.app.run(port=self.port)
a = App()
a.setup_routes()
a.start()
Does it work for your applications too? Feel free to comment, I am interested about this.
Notes from the creator of Bottle: https://github.com/bottlepy/bottle/issues/1224#issuecomment-619344849

Categories