Failed Conditions
Pull Request — master (#1127)
by Mischa
01:58 queued 21s
created

coalib.bears.Bear   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 159
Duplicated Lines 0 %
Metric Value
dl 0
loc 159
rs 10
wmc 21

12 Methods

Rating   Name   Duplication   Size   Complexity  
A run() 0 2 1
A _print() 0 2 1
A __init__() 0 14 3
A log_message() 0 3 2
A get_non_optional_settings() 0 12 1
A run_bear_from_section() 0 20 4
A kind() 0 6 1
A check_prerequisites() 0 11 1
A get_dependencies() 0 12 1
A get_metadata() 0 10 1
A execute() 0 17 2
A missing_dependencies() 0 16 3
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
        Printer.__init__(self)
39
        LogPrinter.__init__(self, self)
40
41
        if not hasattr(message_queue, "put") and message_queue is not None:
42
            raise TypeError("message_queue has to be a Queue or None.")
43
44
        self.section = section
45
        self.message_queue = message_queue
46
        self.timeout = timeout
47
48
    def _print(self, output, **kwargs):
49
        self.debug(output)
50
51
    def log_message(self, log_message, timestamp=None, **kwargs):
52
        if self.message_queue is not None:
53
            self.message_queue.put(log_message)
54
55
    def run(self, *args, dependency_results=None, **kwargs):
56
        raise NotImplementedError
57
58
    def run_bear_from_section(self, args, kwargs):
59
        try:
60
            kwargs.update(
61
                self.get_metadata().create_params_from_section(self.section))
62
        except ValueError as err:
63
            self.warn("The bear {} cannot be executed.".format(
64
                type(self).__name__), str(err))
65
            return
66
67
        cp = self.check_prerequisites()
68
        if cp is not True:
69
            error_string = ("The bear " + type(self).__name__ +
70
                            " does not fulfill all requirements.")
71
            if cp is not False:
72
                error_string += " " + cp
73
74
            self.warn(error_string)
75
            return
76
77
        return self.run(*args, **kwargs)
78
79
    def execute(self, *args, **kwargs):
80
        name = type(self).__name__
81
        try:
82
            self.debug("Running bear {}...".format(name))
83
            # If it's already a list it won't change it
84
            return list(self.run_bear_from_section(args, kwargs) or [])
85
        except:
86
            self.warn(
87
                "Bear {} failed to run. Take a look at debug messages for "
88
                "further information.".format(name))
89
            self.debug(
90
                "The bear {bear} raised an exception. If you are the writer "
91
                "of this bear, please make sure to catch all exceptions. If "
92
                "not and this error annoys you, you might want to get in "
93
                "contact with the writer of this bear.\n\nTraceback "
94
                "information is provided below:\n\n{traceback}"
95
                "\n".format(bear=name, traceback=traceback.format_exc()))
96
97
    @staticmethod
98
    def kind():
99
        """
100
        :return: The kind of the bear
101
        """
102
        raise NotImplementedError
103
104
    @classmethod
105
    def get_metadata(cls):
106
        """
107
        :return: Metadata for the run function. However parameters like `self`
108
                 or parameters implicitly used by coala (e.g. filename for
109
                 local bears) are already removed.
110
        """
111
        return FunctionMetadata.from_function(
112
            cls.run,
113
            omit={"self", "dependency_results"})
114
115
    @classmethod
116
    def missing_dependencies(cls, lst):
117
        """
118
        Checks if the given list contains all dependencies.
119
120
        :param lst: A list of all already resolved bear classes (not
121
                    instances).
122
        :return:    A list of missing dependencies.
123
        """
124
        dep_classes = cls.get_dependencies()
125
126
        for item in lst:
127
            if item in dep_classes:
128
                dep_classes.remove(item)
129
130
        return dep_classes
131
132
    @staticmethod
133
    def get_dependencies():
134
        """
135
        Retrieves bear classes that are to be executed before this bear gets
136
        executed. The results of these bears will then be passed to the
137
        run method as a dict via the dependency_results argument. The dict
138
        will have the name of the Bear as key and the list of its results as
139
        results.
140
141
        :return: A list of bear classes.
142
        """
143
        return []
144
145
    @classmethod
146
    def get_non_optional_settings(cls):
147
        """
148
        This method has to determine which settings are needed by this bear.
149
        The user will be prompted for needed settings that are not available
150
        in the settings file so don't include settings where a default value
151
        would do.
152
153
        :return: A dictionary of needed settings as keys and a tuple of help
154
                 text and annotation as values
155
        """
156
        return cls.get_metadata().non_optional_params
157
158
    def check_prerequisites(self):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
159
        """
160
        Checks whether needed prerequisites of the bear are satisfied.
161
162
        This function gets executed before the bear is run. Provided settings
163
        are also parsed before running this function.
164
165
        :return: True if prerequisites are satisfied, else False or a string
166
                 that serves a more detailed description of what's missing.
167
        """
168
        return True
169