2

So, im writing a library for appium tests. I have a main class that look like this:

class APP():
    def __init__(self):
        self.variable1 = 1
        self.current_view = "main_screen"

    def do_operation_A(self):
        self.open_side_menu()
        do_something
        self.current_view = "side_menu"
    
    def do_operation_B(self):
        self.open_side_menu()
        do_something_else
        self.current_view = "side_menu"

    def set_landscape(self):
        self.open_settings_menu()
        configure_landscape
        self.current_view = "settings_menu"

The class has a lot of operations so i can do things like app.do_operation_A() or app.set_landscape() without having to first go to each menu manually (resolved inside the class)

To reduce this i want to implement a decorator to do something like this if possible:

class APP():
    def __init__(self):
        self.variable1 = 1
        self.current_view = "main_screen"

    #DEFINE_DECORATOR_HERE

    @side_menu
    def do_operation_A(self):
        do_something
    
    @side_menu
    def do_operation_B(self):
        do_something_else

    @settings_menu
    def set_landscape(self):
        configure_landscape

So i want to implement this decorators to navigate to the corresponding view and also change that variable that i use to check some things in other functions. I have seen some examples with functools.wraps but is not clear to me of how to implement the decorator inside the class to be able to modify this self variables.

Any help?

2 Answers 2

4

Using a decorator means that you "wrap" your other function, i.e. you call the decorator and then call the function from inside the decorator.

E.g.:

import functools

def outer(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        return func(*args, **kwargs)
    return inner

Upon defining the function, the decorator will be called, returning the inner function.

Whenever you call func, you will in reality call inner, which runs it's own code, including calling the original func function.

So for your use case, you should be able to create decorators similar to:

def settings_menu(func):
    @functools.wraps(func)
    def inner(self, *args, **kwargs):
        self.open_settings_menu()
        self.current_view = "settings_menu"
        return func(self, *args, **kwargs)
    return inner
Sign up to request clarification or add additional context in comments.

2 Comments

Nice response! More succinct than mine. But I do think it gets a bit more clear when explicitly having self as first argument in the inner method. Then you can avoid having this args[0] business.
Makes a lot of sense. I was just trying to make a quick example and didn't think about that.
2

So a decorator is basically a function that returns another function, right?

def side_menu(func):
    def wrapper():
        return func()
    
    return wrapper

The wrapper, returned by side_menu, will be called whenever App().do_operationA is called. And whenever that method is called, self is always the first argument. Or rather, the first argument is the instance of App, but whatever. So we could do:

def side_menu(func):
    def wrapper(self, *args, **kwargs):
        self.open_menu()
        func(self, *args, **kwargs)

    return wrapper

Now, you don't want the method to present itself as wrapper - you like the name do_operationA. That's where @functools.wraps comes in, it makes things look and work right when decorating.

def side_menu(func):
    @functools.wraps
    def wrapper(self, *args, **kwargs):
        self.open_menu()
        func(self, *args, **kwargs)

    return wrapper

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.