Completed
Pull Request — master (#1127)
by Mischa
01:42
created

coalib.bears.Bear._print()   A

Complexity

Conditions 1

Size

Total Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 1
dl 0
loc 2
rs 10
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
        self.section = section
56
        self.message_queue = message_queue
57
        self.timeout = timeout
58
59
        cp = self.check_prerequisites()
60
        if cp is not True:
61
            error_string = ("The bear " + type(self).__name__ +
62
                            " does not fulfill all requirements.")
63
            if cp is not False:
64
                error_string += " " + cp
65
66
            self.warn(error_string)
67
            raise RuntimeError(error_string)
68
69
    def _print(self, output, **kwargs):
70
        self.debug(output)
71
72
    def log_message(self, log_message, timestamp=None, **kwargs):
73
        if self.message_queue is not None:
74
            self.message_queue.put(log_message)
75
76
    def run(self, *args, dependency_results=None, **kwargs):
77
        raise NotImplementedError
78
79
    def run_bear_from_section(self, args, kwargs):
80
        try:
81
            kwargs.update(
82
                self.get_metadata().create_params_from_section(self.section))
83
        except ValueError as err:
84
            self.warn("The bear {} cannot be executed.".format(
85
                type(self).__name__), str(err))
86
            return
87
88
        return self.run(*args, **kwargs)
89
90
    def execute(self, *args, **kwargs):
91
        name = type(self).__name__
92
        try:
93
            self.debug("Running bear {}...".format(name))
94
            # If it's already a list it won't change it
95
            return list(self.run_bear_from_section(args, kwargs) or [])
96
        except:
97
            self.warn(
98
                "Bear {} failed to run. Take a look at debug messages for "
99
                "further information.".format(name))
100
            self.debug(
101
                "The bear {bear} raised an exception. If you are the writer "
102
                "of this bear, please make sure to catch all exceptions. If "
103
                "not and this error annoys you, you might want to get in "
104
                "contact with the writer of this bear.\n\nTraceback "
105
                "information is provided below:\n\n{traceback}"
106
                "\n".format(bear=name, traceback=traceback.format_exc()))
107
108
    @staticmethod
109
    def kind():
110
        """
111
        :return: The kind of the bear
112
        """
113
        raise NotImplementedError
114
115
    @classmethod
116
    def get_metadata(cls):
117
        """
118
        :return: Metadata for the run function. However parameters like `self`
119
                 or parameters implicitly used by coala (e.g. filename for
120
                 local bears) are already removed.
121
        """
122
        return FunctionMetadata.from_function(
123
            cls.run,
124
            omit={"self", "dependency_results"})
125
126
    @classmethod
127
    def missing_dependencies(cls, lst):
128
        """
129
        Checks if the given list contains all dependencies.
130
131
        :param lst: A list of all already resolved bear classes (not
132
                    instances).
133
        :return:    A list of missing dependencies.
134
        """
135
        dep_classes = cls.get_dependencies()
136
137
        for item in lst:
138
            if item in dep_classes:
139
                dep_classes.remove(item)
140
141
        return dep_classes
142
143
    @staticmethod
144
    def get_dependencies():
145
        """
146
        Retrieves bear classes that are to be executed before this bear gets
147
        executed. The results of these bears will then be passed to the
148
        run method as a dict via the dependency_results argument. The dict
149
        will have the name of the Bear as key and the list of its results as
150
        results.
151
152
        :return: A list of bear classes.
153
        """
154
        return []
155
156
    @classmethod
157
    def get_non_optional_settings(cls):
158
        """
159
        This method has to determine which settings are needed by this bear.
160
        The user will be prompted for needed settings that are not available
161
        in the settings file so don't include settings where a default value
162
        would do.
163
164
        :return: A dictionary of needed settings as keys and a tuple of help
165
                 text and annotation as values
166
        """
167
        return cls.get_metadata().non_optional_params
168
169
    @staticmethod
170
    def check_prerequisites():
171
        """
172
        Checks whether needed runtime prerequisites of the bear are satisfied.
173
174
        This function gets executed at construction and returns True by
175
        default.
176
177
        :return: True if prerequisites are satisfied, else False or a string
178
                 that serves a more detailed description of what's missing.
179
        """
180
        return True
181