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