This is the sixth day of my participation in the First Challenge 2022.

Hello ~ I’m Milo! I am building an open source platform from 0 to 1, and also writing a complete set of interface test platform series tutorials, I hope you can support more.

Welcome to follow my public account Milo test diary, get the latest article tutorial!

This article describes some application scenarios of decorators, interested students can understand.

Before we do that, let’s check the answer to the previous assignment. If you got it right, you can scroll down:

Requires the implementation of a custom decorator

A) Callable B) callable C) able D) able

def run(times) :
    if callable(times):
        def wrapper(*args, **kwargs) :
            for i in range(5):
                times(*args, **kwargs)
                print(F "runs the first{i}Time. "")

        return wrapper
    else:
        def decorator(func) :
            def wrapper(*args, **kwargs) :
                for i in range(times):
                    func(*args, **kwargs)
                    print(F "runs the first{i}Time. "")

            return wrapper

        return decorator
Copy the code

One core method is used here: Callable

Callable means whether the variable is callable or not. If our run accepts a method, it follows the if logic, and if it does not, it wraps another layer of decorators.

How do you tell the difference

@run
def wqrf() :
    print("haha")
Copy the code

If that’s the case, and run is not followed by any arguments, not even parentheses, just at sign run, then what’s The Times variable going to be?

You can guess:

So we are going to call the run decorator five times if there is no argument after it. (There are some changes here, because there is no point in adding this decorator if I don’t loop, so I changed it to 5 times with no arguments.)

Let’s look at what times looks like with an argument:

23 does not trigger the callable condition.

Figure out when callable is true, and this problem will be solved.

If this is true, then it can be abbreviated as:

def run(times) :
    def wrapper(*args, **kwargs) :
        for i in range(5):
            times(*args, **kwargs)
            print(F "runs the first{i}Time. "")

    return wrapper
Copy the code

If not, it is another decorator:

def run(times) :
    def decorator(func) :
        def wrapper(*args, **kwargs) :
            for i in range(times):
                func(*args, **kwargs)
                print(F "runs the first{i}Time. "")

        return wrapper

    return decorator
Copy the code

So, seemingly complicated, it’s actually a matter of choosing what decorator to become based on whether the received parameter is callable

If in doubt, think back to the way you wrote decorators in the last article. The full code will be attached at the end of the article.

Back to the topic

Now that we’ve talked so much, it’s time to talk about the use of decorators. The following are all examples of requirements + for everyone to see and cherish

  1. The timer

When we need to count the execution time of some method, we can write this.

def time_dec(func) :
    def wrapper(*arg) :
        t = time.perf_counter()
        res = func(*arg)
        print(func.__name__, time.perf_counter() - t)
        return res
    return wrapper
Copy the code

But you will notice that when the decorator name is printed, func.name becomes a wrapper, which is normal because we did define a wrapper.

Just add a decorator to the def wrapper decorator:

@functools.wraps(func)

  1. Exception catcher (From test development ideas)

Bored with try and except? You can play it like this:

def exceptions(func) :
    def wrapper(*args, **kwargs) :
        try:
            return func(*args, **kwargs)
        except Exception as e:
            raise Exception(e)

    return wrapper
Copy the code
  • Determine whether a user is logged in in Web development
def login_required(func) :
        @wraps(func)
        def wrapper(*args, **kwargs) :
            try:
                headers = request.headers
                token = headers.get('token')
                if token is None:
                    return jsonify(dict(code=401, msg="User information authentication failed, please check"))
                Parse user information
                user_info = UserToken.parse_token(token)
                Write user information to kwargs
                kwargs["user_info"] = user_info
            except Exception as e:
                return jsonify(dict(code=401, msg=str(e)))
            return func(*args, **kwargs)

        return wrapper
Copy the code
  1. Data driven
@parameters(
   (2.4.6),
   (5.6.11),
)
def test_add(a, b, expected) :
    assert a + b == expected
Copy the code

A data-driven approach similar to that provided by the DDT library for test cases.

  1. logging

  2. Use case to run

  3. Automatic screenshot of UI case execution exception

This is similar to bamboo shoots in that additional processing is done after the exception is caught.

  1. Lock variables
import functools

def synchronized(lock) :
    """ Synchronization decorator """
    def wrap(f) :
        @functools.wraps(f)
        def newFunction(*args, **kw) :
            with lock:
                return f(*args, **kw)
        return newFunction
    return wrap
Copy the code

Today’s content is briefly introduced here, the end is obviously a little sloppy, because a colleague urged me to leave work, ha ha ~

See you next time