Skip to content

Commit 194e398

Browse files
authored
Add failed to load error logging (#27)
1 parent 2a4f395 commit 194e398

File tree

5 files changed

+62
-2
lines changed

5 files changed

+62
-2
lines changed

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 1.2.4
2+
3+
- [IMPROVED] Fetchers and checks that failed to load appear as errors in STDERR now.
4+
15
# 1.2.3
26

37
- [IMPROVED] Github service `get_commit_details` now take `path` as an optional argument.

compliance/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@
1414
# limitations under the License.
1515
"""Compliance automation package."""
1616

17-
__version__ = '1.2.3'
17+
__version__ = '1.2.4'

compliance/controls.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import itertools
1919
import json
2020
import os
21+
from collections import defaultdict
2122

2223

2324
class ControlDescriptor(object):
@@ -49,6 +50,18 @@ def as_dict(self):
4950
"""Provide control descriptor content as a modifiable dictionary."""
5051
return copy.deepcopy(self._controls)
5152

53+
@property
54+
def accred_checks(self):
55+
"""Provide all checks by accreditation (key) as a dictionary."""
56+
if not hasattr(self, '_accred_checks'):
57+
self._accred_checks = defaultdict(list)
58+
for check, evidence in self._controls.items():
59+
for control in evidence.values():
60+
for accreds in control.values():
61+
for accred in accreds:
62+
self._accred_checks[accred].append(check)
63+
return self._accred_checks
64+
5265
def get_accreditations(self, test_path):
5366
"""
5467
Provide the accreditation list for a given test_path.

compliance/runners.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import inspect
1818
import os
19+
import re
1920
import sys
2021
import time
2122
import unittest
@@ -40,6 +41,7 @@ class _BaseRunner(object):
4041
def __init__(self, opts, extra_opts):
4142
self.opts = opts
4243
self.extra_opts = extra_opts
44+
self.load_errors = set()
4345

4446
def __enter__(self):
4547
self.init_config()
@@ -168,6 +170,15 @@ def get_fetchers(self):
168170
for candidate in candidates:
169171
if issubclass(candidate.__class__, ComplianceFetcher):
170172
fetchers.add(candidate.__class__)
173+
for load_err in tl.errors:
174+
try:
175+
locate = re.search(
176+
'^Failed to import test module: (.+?)\n.*?', load_err
177+
)
178+
if locate.group(1).split('.')[-1].startswith(FETCH_PREFIX):
179+
self.load_errors.add(load_err)
180+
except AttributeError:
181+
pass
171182
return fetchers
172183

173184
def run_fetchers(self, reruns=None):
@@ -243,6 +254,7 @@ def init_config(self):
243254
def get_checks(self):
244255
"""Provide the appropriate compliance framework check classes."""
245256
checks = set()
257+
tests_found = set()
246258
for loc in self.dirs:
247259
tl = unittest.TestLoader()
248260
tl.testMethodPrefix = CHECK_PREFIX
@@ -251,6 +263,7 @@ def get_checks(self):
251263
)
252264
for test in [c.__class__ for c in candidates]:
253265
path = f'{test.__module__}.{test.__name__}'
266+
tests_found.add(path)
254267
in_accred_grouping = self.controls.is_test_included(
255268
path, self.accreds
256269
)
@@ -264,6 +277,32 @@ def get_checks(self):
264277
)
265278
]
266279
checks.add(test)
280+
for load_err in tl.errors:
281+
try:
282+
locate = re.search(
283+
'^Failed to import test module: (.+?)\n.*?', load_err
284+
)
285+
for accred in self.accreds:
286+
for check in self.controls.accred_checks[accred]:
287+
if check.startswith(locate.group(1)):
288+
self.load_errors.add(
289+
f'Unable to load {check}\n\n{load_err}'
290+
)
291+
tests_found.add(check)
292+
except AttributeError:
293+
pass
294+
expected_checks = set()
295+
for accred, checks_in_accred in self.controls.accred_checks.items():
296+
if accred in self.accreds:
297+
expected_checks.update(checks_in_accred)
298+
for check_not_found in expected_checks - tests_found:
299+
self.load_errors.add(
300+
(
301+
f'Unable to load {check_not_found}\n\n'
302+
f'The check {check_not_found} was not found. '
303+
'Please validate that the path provided is correct.'
304+
)
305+
)
267306
return checks
268307

269308
def run_checks(self):

compliance/scripts/compliance_cli.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,15 @@ def _run(self, args):
157157
'\nUnable to resolve dependency issues with %s.\n',
158158
', '.join(reruns)
159159
)
160+
for fetch_load_error in fetch.load_errors:
161+
self.err(f'\nERROR: {fetch_load_error}\n')
160162
if args.check:
161163
with CheckMode(args, self.extra_args) as check:
162164
accreds = ', '.join(check.accreds)
163-
self.out(f'\nCheck Run - Accreditations: {accreds} \n')
165+
self.out(f'\nCheck Run - Accreditations: {accreds}\n')
164166
success = check.run_checks() and success
167+
for check_load_error in check.load_errors:
168+
self.err(f'\nERROR: {check_load_error}\n')
165169
return 0 if success else 1
166170

167171

0 commit comments

Comments
 (0)