decorators.py 5.27 KB
Newer Older
mathieui's avatar
mathieui committed
1
"""
2
Module containing various decorators
mathieui's avatar
mathieui committed
3
"""
4
from typing import Any, Callable, List, Optional
mathieui's avatar
mathieui committed
5

mathieui's avatar
mathieui committed
6
from poezio import common
7

mathieui's avatar
mathieui committed
8

9
class RefreshWrapper:
mathieui's avatar
mathieui committed
10 11 12
    def __init__(self):
        self.core = None

13
    def conditional(self, func: Callable) -> Callable:
mathieui's avatar
mathieui committed
14 15 16 17
        """
        Decorator to refresh the UI if the wrapped function
        returns True
        """
mathieui's avatar
mathieui committed
18

mathieui's avatar
mathieui committed
19 20 21 22 23
        def wrap(*args, **kwargs):
            ret = func(*args, **kwargs)
            if self.core and ret:
                self.core.refresh_window()
            return ret
mathieui's avatar
mathieui committed
24

mathieui's avatar
mathieui committed
25 26
        return wrap

27
    def always(self, func: Callable) -> Callable:
mathieui's avatar
mathieui committed
28 29 30
        """
        Decorator that refreshs the UI no matter what after the function
        """
mathieui's avatar
mathieui committed
31

mathieui's avatar
mathieui committed
32 33 34 35 36
        def wrap(*args, **kwargs):
            ret = func(*args, **kwargs)
            if self.core:
                self.core.refresh_window()
            return ret
mathieui's avatar
mathieui committed
37

mathieui's avatar
mathieui committed
38 39
        return wrap

40
    def update(self, func: Callable) -> Callable:
mathieui's avatar
mathieui committed
41 42 43
        """
        Decorator that only updates the screen
        """
mathieui's avatar
mathieui committed
44

mathieui's avatar
mathieui committed
45 46 47 48 49
        def wrap(*args, **kwargs):
            ret = func(*args, **kwargs)
            if self.core:
                self.core.doupdate()
            return ret
mathieui's avatar
mathieui committed
50

mathieui's avatar
mathieui committed
51 52
        return wrap

mathieui's avatar
mathieui committed
53

mathieui's avatar
mathieui committed
54
refresh_wrapper = RefreshWrapper()
55

mathieui's avatar
mathieui committed
56

57
class CommandArgParser:
58 59 60 61
    """Modify the string argument of the function into a list of strings
    containing the right number of extracted arguments, or None if we don’t
    have enough.
    """
mathieui's avatar
mathieui committed
62

63
    @staticmethod
64
    def raw(func: Callable) -> Callable:
65 66 67
        """Just call the function with a single string, which is the original string
        untouched
        """
mathieui's avatar
mathieui committed
68

69 70
        def wrap(self, args, *a, **kw):
            return func(self, args, *a, **kw)
mathieui's avatar
mathieui committed
71

72 73 74
        return wrap

    @staticmethod
75
    def ignored(func: Callable) -> Callable:
76 77 78
        """
        Call the function without any argument
        """
mathieui's avatar
mathieui committed
79

80
        def wrap(self, args=None, *a, **kw):
81
            return func(self, *a, **kw)
mathieui's avatar
mathieui committed
82

83 84 85
        return wrap

    @staticmethod
86
    def quoted(mandatory: int,
mathieui's avatar
mathieui committed
87
               optional=0,
88
               defaults: Optional[List[Any]] = None,
89 90 91 92 93
               ignore_trailing_arguments=False):
        """The function receives a list with a number of arguments that is between
        the numbers `mandatory` and `optional`.

        If the string doesn’t contain at least `mandatory` arguments, we return
94
        None because the given arguments are invalid.
95 96 97

        If there are any remaining arguments after `mandatory` and `optional`
        arguments have been found (and “ignore_trailing_arguments" is not True),
98
        we append them to the last argument of the list.
99

100
        An argument is a string (with or without whitespaces) between two quotes
101 102 103 104
        ("), or a whitespace separated word (if not inside quotes).

        The argument `defaults` is a list of strings that are used when an
        optional argument is missing.  For example if we accept one optional
105
        argument and none is provided, but we have one value in the `defaults`
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
        list, we use that string inplace. The `defaults` list can only
        replace missing optional arguments, not mandatory ones. And it
        should not contain more than `mandatory` values. Also you cannot

        Example:
        This method needs at least one argument, and accepts up to 3
        arguments

        >> @command_args_parser.quoted(1, 2, ['default for first arg'], False)
        >> def f(args):
        >>     print(args)

        >> f('coucou les amis') # We have one mandatory and two optional
        ['coucou', 'les', 'amis']
        >> f('"coucou les amis" "PROUT PROUT"') # One mandator and only one optional,
                                                # no default for the second
        ['coucou les amis', 'PROUT PROUT']
        >> f('')                # Not enough args for mandatory number
        None
        >> f('"coucou les potes"') # One mandatory, and use the default value
                                   # for the first optional
        ['coucou les potes, 'default for first arg']
        >> f('"un et demi" deux trois quatre cinq six') # We have three trailing arguments
        ['un et demi', 'deux', 'trois quatre cinq six']

        """
132
        default_args_outer = defaults or []
mathieui's avatar
mathieui committed
133

134 135 136
        def first(func: Callable):
            def second(self, args: str, *a, **kw):
                default_args = default_args_outer
137
                if args and args.strip():
138
                    split_args = common.shell_split(args)
139
                else:
140 141
                    split_args = []
                if len(split_args) < mandatory:
142
                    return func(self, None, *a, **kw)
143 144
                res, split_args = split_args[:mandatory], split_args[
                    mandatory:]
145
                if optional == -1:
146
                    opt_args = split_args[:]
147
                else:
148
                    opt_args = split_args[:optional]
149

150 151
                if opt_args:
                    res += opt_args
152
                    split_args = split_args[len(opt_args):]
153 154
                    default_args = default_args[len(opt_args):]
                res += default_args
155 156
                if split_args and res and not ignore_trailing_arguments:
                    res[-1] += " " + " ".join(split_args)
157
                return func(self, res, *a, **kw)
mathieui's avatar
mathieui committed
158

159
            return second
mathieui's avatar
mathieui committed
160

161 162
        return first

mathieui's avatar
mathieui committed
163

164
command_args_parser = CommandArgParser()