Completed
Pull Request — master (#1127)
by Mischa
02:43
created

coalib.bears.Bear.check_prerequisites()   A

Complexity

Conditions 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 1
dl 0
loc 12
rs 9.4286
1
import traceback
2
from pyprint.Printer import Printer
3
4
from coalib.misc.Decorators import enforce_signature
5
from coalib.output.printers.LogPrinter import LogPrinter
6
from coalib.settings.FunctionMetadata import FunctionMetadata
7
from coalib.settings.Section import Section
8
9
10
class Bear(Printer, LogPrinter):
11
    """
12
    A bear contains the actual subroutine that is responsible for checking
13
    source code for certain specifications. However it can actually do
14
    whatever it wants with the files it gets. If you are missing some Result
15
    type, feel free to contact us and/or help us extending the coalib.
16
17
    This is the base class for every bear. If you want to write an bear, you
18
    will probably want to look at the GlobalBear and LocalBear classes that
19
    inherit from this class. In any case you'll want to overwrite at least the
20
    run method. You can send debug/warning/error messages through the
21
    debug(), warn(), err() functions. These will send the
22
    appropriate messages so that they are outputted. Be aware that if you use
23
    err(), you are expected to also terminate the bear run-through
24
    immediately.
25
26
    If you need some setup or teardown for your bear, feel free to overwrite
27
    the set_up() and tear_down() functions. They will be invoked
28
    before/after every run invocation.
29
30
    Settings are available at all times through self.section.
31
    """
32
33
    @enforce_signature
34
    def __init__(self,
35
                 section: Section,
36
                 message_queue,
37
                 timeout=0):
38
        """
39
        Constructs a new bear.
40
41
        :param section:       The section object where bear settings are
42
                              contained.
43
        :param message_queue: The queue object for messages. Can be `None`.
44
        :param timeout:       The time the bear is allowed to run. To set no
45
                              time limit, use 0.
46
        :raises TypeError:    Raised when `message_queue` is no queue.
47
        :raises RuntimeError: Raised when bear requirements are not fulfilled.
48
        """
49
        Printer.__init__(self)
50
        LogPrinter.__init__(self, self)
51
52
        if message_queue is not None and not hasattr(message_queue, "put"):
53
            raise TypeError("message_queue has to be a Queue or None.")
54
55
        cp = type(self).check_prerequisites()
56
        if cp is not True:
57
            error_string = ("The bear " + type(self).__name__ +
58
                            " does not fulfill all requirements.")
59
            if cp is not False:
60
                error_string += " " + cp
61
62
            raise RuntimeError(error_string)
63
64
        self.section = section
65
        self.message_queue = message_queue
66
        self.timeout = timeout
67
68
    def _print(self, output, **kwargs):
69
        self.debug(output)
70
71
    def log_message(self, log_message, timestamp=None, **kwargs):
72
        if self.message_queue is not None:
73
            self.message_queue.put(log_message)
74
75
    def run(self, *args, dependency_results=None, **kwargs):
76
        raise NotImplementedError
77
78
    def run_bear_from_section(self, args, kwargs):
79
        try:
80
            kwargs.update(
81
                self.get_metadata().create_params_from_section(self.section))
82
        except ValueError as err:
83
            self.warn("The bear {} cannot be executed.".format(
84
                type(self).__name__), str(err))
85
            return
86
87
        return self.run(*args, **kwargs)
88
89
    def execute(self, *args, **kwargs):
90
        name = type(self).__name__
91
        try:
92
            self.debug("Running bear {}...".format(name))
93
            # If it's already a list it won't change it
94
            return list(self.run_bear_from_section(args, kwargs) or [])
95
        except:
96
            self.warn(
97
                "Bear {} failed to run. Take a look at debug messages for "
98
                "further information.".format(name))
99
            self.debug(
100
                "The bear {bear} raised an exception. If you are the writer "
101
                "of this bear, please make sure to catch all exceptions. If "
102
                "not and this error annoys you, you might want to get in "
103
                "contact with the writer of this bear.\n\nTraceback "
104
                "information is provided below:\n\n{traceback}"
105
                "\n".format(bear=name, traceback=traceback.format_exc()))
106
107
    @staticmethod
108
    def kind():
109
        """
110
        :return: The kind of the bear
111
        """
112
        raise NotImplementedError
113
114
    @classmethod
115
    def get_metadata(cls):
116
        """
117
        :return: Metadata for the run function. However parameters like `self`
118
                 or parameters implicitly used by coala (e.g. filename for
119
                 local bears) are already removed.
120
        """
121
        return FunctionMetadata.from_function(
122
            cls.run,
123
            omit={"self", "dependency_results"})
124
125
    @classmethod
126
    def missing_dependencies(cls, lst):
127
        """
128
        Checks if the given list contains all dependencies.
129
130
        :param lst: A list of all already resolved bear classes (not
131
                    instances).
132
        :return:    A list of missing dependencies.
133
        """
134
        dep_classes = cls.get_dependencies()
135
136
        for item in lst:
137
            if item in dep_classes:
138
                dep_classes.remove(item)
139
140
        return dep_classes
141
142
    @staticmethod
143
    def get_dependencies():
144
        """
145
        Retrieves bear classes that are to be executed before this bear gets
146
        executed. The results of these bears will then be passed to the
147
        run method as a dict via the dependency_results argument. The dict
148
        will have the name of the Bear as key and the list of its results as
149
        results.
150
151
        :return: A list of bear classes.
152
        """
153
        return []
154
155
    @classmethod
156
    def get_non_optional_settings(cls):
157
        """
158
        This method has to determine which settings are needed by this bear.
159
        The user will be prompted for needed settings that are not available
160
        in the settings file so don't include settings where a default value
161
        would do.
162
163
        :return: A dictionary of needed settings as keys and a tuple of help
164
                 text and annotation as values
165
        """
166
        return cls.get_metadata().non_optional_params
167
168
    @staticmethod
169
    def check_prerequisites():
170
        """
171
        Checks whether needed runtime prerequisites of the bear are satisfied.
172
173
        This function gets executed at construction and returns True by
174
        default.
175
176
        :return: True if prerequisites are satisfied, else False or a string
177
                 that serves a more detailed description of what's missing.
178
        """
179
        return True
180