Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 389
TheBeardedBerry Avatar asked Dec 14 '22 10:12

TheBeardedBerry


1 Answers

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:

  1. It takes a function as an argument.

  2. 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:

  1. Take any old argument, e.g. 'world'
  2. Return a function that:
    1. Takes a function as an argument.
    2. 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

like image 179
7stud Avatar answered Dec 29 '22 20:12

7stud