How to group decorators in Python - python

In Flask I'm using a set of decorators for each route, but the code is "ugly":
#app.route("/first")
#auth.login_required
#crossdomain(origin='*')
#nocache
def first_page:
....
#app.route("/second")
#auth.login_required
#crossdomain(origin='*')
#nocache
def second_page:
....
I would prefer to have a declaration that groups all of them with a single decorator:
#nice_decorator("/first")
def first_page:
....
#nice_decorator("/second")
def second_page:
....
I tried to follow the answer at Can I combine two decorators into a single one in Python? but I cannot make it working:
def composed(*decs):
def deco(f):
for dec in reversed(decs):
f = dec(f)
return f
return deco
def nice_decorator(route):
composed(app.route(route),
auth.login_required,
crossdomain(origin="*"),
nocache)
#nice_decorator("/first")
def first_page:
....
because of this error that I don't understand:
#nice_decorator("/first")
TypeError: 'NoneType' object is not callable
Following one of the comments I defined it with another form that works but without the possibility to specify the route parameter:
new_decorator2 = composed(app.route("/first"),
auth.login_required,
crossdomain(origin="*"),
nocache)
Is it possible to define a single decorator with parameters?

You're not defining the composition correctly. You need to change the definition of nice_decorator to something like this:
def nice_decorator(route):
return composed(
app.route(route),
auth.login_required,
crossdomain(origin="*"),
nocache
)
Your previous version never actually returned anything. Python isn't like Ruby or Lisp where the last expression is the return value.

Related

How can I use the same parameters in a decorator with that in the functions in Python?

I want use a same param in decorater and function like:
def outer(deco_param):
def decorator(func):
print("param:%s" % deco_param)
return func
return decorator
#outer(deco_param=s) # problems here: 's' is unreferenced
def test(s):
print("test:%s" % s)
I got this idea when using Flask but I didn't know how did they make it, which supports using a param of view function in its decorater like:
app.route(rule="/user/<int:uid>")
def access_user(uid): # if I use any other name except from 'uid', IDE would raise a tip
...
What's more, Flask seems to make a static check. If I miss uid argument or use any other name, IDE(pycharm) would raise a tip of "Function 'access_user` doesn't have a parameter 'int:uid'". That's the effect I want.
Can anyone please give me some advice? Thanks
You don't need to pass the same parameter to both the outer function and the inner function. To call the parameters via decorator, you should add a wrapper function to pass the parameters (*args) from inner one to outer one.
You can write like this:
def outer():
def decorator(func):
def wrapper(*args):
print("param:%s" % func.__param__)
return func(*args)
return wrapper
return decorator
#outer
def test(s):
print("test:%s" % s)

Flask list of last used pages with sessions TypeError

I am trying to make a list of pages that a user has visited recently, but I keep getting TypeError: store_visited_urls() takes no arguments (1 given).
I don't know how an argument is given with it.
Python/Flask code:
app.secret_key = '/r/xd8}q/xde/x13/xe5F0/xe5/x8b/x96A64/xf2/xf8MK/xb1/xfdA7x8c'
def recentsites():
session['urls'] = []
#app.after_request
def store_visited_urls():
session['urls'].append(request.url)
if(len[session['urls']]) > 3:
session['urls'].pop(0)
session.modified = True
#app.route('/')
def index():
data = []
if 'urls' in session:
data = session['urls']
return render_template('index.html', data=data)
I think that as a function, it automatically includes self as an argument. This argument is included as part of the call when you create a class.
make the definition def store_visited_urls(self): but continue to call it without an argument.
From the way it looks session must be defined in the class. Thus, you would reference self.session so that it will be picked up when the class is instantiated.
See What is the purpose of self? for an explanation.
I see that you are using a decorator See Decorators I: Introduction to Python Decorators and A guide to Python's function decorators or Python Decorators or Primer on Python Decorators

Python decorator access argument by name

I want to make a Python decorator that can take an argument (the name of an argument in the function it is decorating) and use that argument to find the value of the argument in the function. I know that sounds super confusing, ha! But what I want to do would look like this (it's for a Flask web application):
This is what I have right now:
#app.route('/admin/courses/<int:course_id>')
def view_course(course_id):
... view code here
And I want to do something like this:
#app.route('/admin/courses/<int:course_id>')
#access_course_permission(course_id)
def view_course(course_id):
... view code here
Right now I know I can do something like:
def access_course_permission(f):
#wraps(f)
def decorated_function(*args, **kwargs):
# check args[0] right here to find the course ID
return f(*args, **kwargs)
return decorated_function
which works great as long as course_id is always the first parameter. However, it isn't. That's why I want to be able to specify the name.
If this is not possible, I guess I could just pass the index of the parameter to the decorator, but that isn't very nice...
You can use the inspect.getfullargspec() function to access the names used in a function:
try:
# Python 3
from inspect import getfullargspec
except ImportError:
# Python 2, use inspect.getargspec instead
# this is the same function really, without support for annotations
# and keyword-only arguments
from inspect import getargspec as getfullargspec
from functools import wraps
def access_course_permission(argument_name):
def decorator(f):
argspec = getfullargspec(f)
argument_index = argspec.args.index(argument_name)
#wraps(f)
def wrapper(*args, **kwargs):
try:
value = args[argument_index]
except IndexError:
value = kwargs[argument_name]
# do something with value
return f(*args, **kwargs)
return wrapper
return decorator
The above finds out at what index your specific argument is positioned; this covers both positional and keyword arguments (because in Python, you can pass in a value for a keyword argument by position too).
Note however that for your specific example, Flask will call view_course with course_id as a keyword argument, so using kwargs[argument_name] would suffice.
You'll have to pass in a string to name that argument:
#app.route('/admin/courses/<int:course_id>')
#access_course_permission('course_id')
def view_course(course_id):
# ... view code here
Note however, that in Flask you could just access request.view_args, without the need to parse this information out of the function arguments:
course_id = requests.view_args[argument_name]

How to use class methods instead of functions in Bottle microframework?

I am trying to use methods of my own class instead of funcons.
But ths code fails due to "syntax error". What am i doing wrong?
from bottle import route, run, template
class controller():
def test(self):
return ("<h1>Its a main page!</h1>")
def hello(self,name):
return "Hello {0}".format(name)
sc = controller()
#route('/test')
sc.test()
#route('/hello/<name>')
def index(name):
return template('<b>Hello {{name}}</b>!', name=name)
#route('/')
def indexFunc():
return ('<h1>Hello on first App!!</h1>!')
run(host='localhost', port=8080)
sc.test() returns a string. You can't decorate a string. So the first problem is, you are calling that method (and therefore trying to decorate its result) instead of decorating the method itself.
The second problem is that the # decorator syntax must be followed by a function definition, i.e., a def keyword. You could write a function that does nothing but call sc.test(), as shown by llyas. Or you can take advantage of the fact that the # is simply syntactic sugar for a function call, and just write:
route('/test')(sc.test)
You cannot decorate a function call, you decorate a function definition.
Try replacing this line:
sc.test()
wiith this:
#route('/test')
def view():
return sc.test()

How to use decorators (bottle.py)

I am trying to use bottle.py to build some webpages. It seems like a major part of using bottle is learning to use decorators but I have read the python docs explanation of what decorators are but I am still not sure I understand them.
The docs say:
"A Python decorator is a specific change to the Python syntax that allows us to more conveniently alter functions and methods (and possibly classes in a future version)."
It sounds like you are calling a function with some changes made but I am not sure why you would do it this way or how to read the decorator.
Looking at some bottle code:
if __name__ == '__main__':
PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static').replace('\\', '/')
HOST = os.environ.get('SERVER_HOST', 'localhost')
try:
PORT = int(os.environ.get('SERVER_PORT', '5555'))
except ValueError:
PORT = 5555
#bottle.route('/static/<filepath:path>')
def server_static(filepath):
"""Handler for static files, used with the development server.
When running under a production server such as IIS or Apache,
the server should be configured to serve the static files."""
return bottle.static_file(filepath, root=STATIC_ROOT)
# Starts a local test server.
bottle.run(server='wsgiref', host=HOST, port=PORT)
What does this line do #bottle.route('/static/<filepath:path>')?
If its a fancy function call then why do it this way rather than just calling the function?
Thanks for your help! :D
Check out this code:
def my_decorator(func):
return lambda: print("goodbye")
def greet():
print('hello')
result = my_decorator(greet)
result()
--output:--
goodbye
The following is a shortcut to accomplish the same thing:
def my_decorator(func):
return lambda: print("goodbye")
#my_decorator
def greet():
print('hello')
greet()
--output:--
goodbye
The #my_decorator syntax takes the function below it, greet, and makes this call:
greet = my_decorator(greet)
The my_decorator() function has to be defined so that:
It takes a function as an argument.
Returns a function.
A Python decorator is a specific change to the Python syntax that
allows us to more conveniently alter functions and methods (and
possibly classes in a future version).
Okay, so let's say that you want to add to whatever the greet() function does:
def my_decorator(func): # func = greet
def add_to_greet():
func() #<*********This is greet()
print('world') #<***This is additional stuff.
return add_to_greet
#my_decorator
def greet():
print('hello')
greet()
--output:--
hello
world
What does this line do #bottle.route('/static/<filepath:path>')
Okay, are you ready? If the #some_name syntax specifies an argument, for instance:
#wrapper('world')
def do_stuff():
First python will execute the following call:
#wrapper('world')
def do_stuff():
...
#****HERE:
decorator = wrapper('world') #decorator is a newly created variable
The wrapper() function must be defined to:
Take any old argument, e.g. 'world'
Return a function that:
Takes a function as an argument.
Returns a function.
Secondly, python will execute the call:
#wrapper('world')
def do_stuff():
...
decorator = wrapper('world')
#*****HERE:
do_stuff = decorator(do_stuff)
Whew! Here is an example:
def wrapper(extra_greeting):
def my_decorator(func):
def add_to_greet():
func()
print(extra_greeting)
return add_to_greet
return my_decorator
#wrapper('world')
def greet():
print('hello')
greet()
--output:--
hello
world
Now, let's analyze this decorator:
#bottle.route('/static/<filepath:path>')
def server_static(filepath):
bottle -- a module
route -- a function(or other callable) defined in the bottle module
'/static/<filepath:path>' -- a route
So the bottle module might look like this:
#bottle.py
def route(your_route): #your_route <= '/static/<filepath:path>'
def my_decorator(func): #The decorator syntax will cause python to call this function with server_static as the argument
def do_stuff(filepath):
func(filepath) #Call the server_static() function with the part of the url that matched filepath
return do_stuff #This function will be called when your code calls server_static(...)
return my_decorator
If its a fancy function call then why do it this way rather than just
calling the function?
Advanced stuff.
Comment: Perhaps you forgot to explain what specifically that route decorator does?
#route('/hello')
def hello():
return "Hello World!"
The route() decorator binds a piece of code to an URL path. In this
case, we link the /hello path to the hello() function. This is called
a route (hence the decorator name) and is the most important concept
of this framework. You can define as many routes as you want. Whenever
a browser requests a URL, the associated function is called and the
return value is sent back to the browser. It’s as simple as that.
http://bottlepy.org/docs/dev/tutorial.html
A path can include wild cards:
The simplest form of a wildcard consists of a name enclosed in angle
brackets (e.g. <name>)....Each wildcard matches one or more
characters, but stops at the first slash (/).
The rule /<action>/<item> matches as follows:
Path Result
/save/123 {'action': 'save', 'item': '123'}
/save/123/ No Match
/save/ No Match
//123 No Match
Filters are used to define more specific wildcards, and/or transform
the matched part of the URL before it is passed to the callback. A
filtered wildcard is declared as <name:filter>
The following standard filters are implemented:
:path matches all characters including the slash character in a non-greedy way and may be used to match more than one path
segment.
http://bottlepy.org/docs/dev/routing.html
A decorator is just a function wrapper, it takes some computable and surround it with more computables, technically a decorators is a function that returns a function(or an object, in fact there are decorator classes).
Lets say for example you want to make a logger decorator, this logger decorator will execute something and log who is executing it:
def loggger(name):
def wrapper(f):
def retF(*args, **kwargs):
print name, "is executing"
f(*args, **kwargs)
return retF
return wrapper
So, we have our decorator that will print "Daniel is executing" before call our desired function, for example
#logger("Daniel")
def add2Nums(a,b):
return a+b
>>> add2Nums(1,2)
>>> Daniel is executing
>>> 3
Bottle just works the same way, in the
#bottle.route('/static/<filepath:path>')
it just wrapps your server_static call so whenever some acces that route your function is called.

Categories