Failed Conditions
Pull Request — master (#1522)
by Abdeali
02:00
created

coalib.collecting.icollect_bears()   C

Complexity

Conditions 7

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 7
dl 0
loc 28
rs 5.5
1
import functools
2
import os
3
4
from coalib.bears.BEAR_KIND import BEAR_KIND
5
from coalib.collecting.Importers import iimport_objects
6
from coalib.misc.Decorators import yield_once
7
from coalib.output.printers.LOG_LEVEL import LOG_LEVEL
8
from coalib.parsing.Globbing import fnmatch, iglob
9
10
11
def _get_kind(bear_class):
12
    try:
13
        return bear_class.kind()
14
    except NotImplementedError:
15
        return None
16
17
18
def _import_bears(file_path, kinds):
19
    # recursive imports:
20
    for bear_list in iimport_objects(file_path,
21
                                     names='__additional_bears__',
22
                                     types=list):
23
        for bear_class in bear_list:
24
            if _get_kind(bear_class) in kinds:
25
                yield bear_class
26
    # normal import
27
    for bear_class in iimport_objects(file_path,
28
                                      attributes='kind',
29
                                      local=True):
30
        if _get_kind(bear_class) in kinds:
31
            yield bear_class
32
33
34
@yield_once
35
def icollect(file_paths, ignored_globs=None):
36
    """
37
    Evaluate globs in file paths and return all matching files.
38
39
    :param file_paths:    file path or list of such that can include globs
40
    :param ignored_globs: list of globs to ignore when matching files
41
    :return:              iterator that yields tuple of path of a matching
42
                          file, the glob where it was found
43
    """
44
    if isinstance(file_paths, str):
45
        file_paths = [file_paths]
46
47
    for file_path in file_paths:
48
        for match in iglob(file_path):
49
            if not ignored_globs or not fnmatch(match, ignored_globs):
50
                yield match, file_path
51
52
53
def collect_files(file_paths, log_printer, ignored_file_paths=None,
54
                  limit_file_paths=None):
55
    """
56
    Evaluate globs in file paths and return all matching files
57
58
    :param file_paths:         file path or list of such that can include globs
59
    :param ignored_file_paths: list of globs that match to-be-ignored files
60
    :param limit_file_paths:   list of globs that the files are limited to
61
    :return:                   list of paths of all matching files
62
    """
63
    if limit_file_paths:
64
        limit_fnmatch = functools.partial(fnmatch, patterns=limit_file_paths)
65
    else:
66
        limit_fnmatch = lambda fname: True  # Always in the limit_files
67
68
    valid_files = list(filter(lambda fname: os.path.isfile(fname[0]),
69
                              icollect(file_paths, ignored_file_paths)))
70
71
    # Find globs that gave no files and warn the user
72
    if valid_files:
73
        collected_files, file_globs_with_files = zip(*valid_files)
74
    else:
75
        collected_files, file_globs_with_files = [], []
76
77
    _warn_if_unused_glob(log_printer, file_paths, file_globs_with_files,
78
                         "No files matching '{}' were found.")
79
    limited_files = list(filter(limit_fnmatch, collected_files))
80
    return limited_files
81
82
83
def collect_dirs(dir_paths, ignored_dir_paths=None):
84
    """
85
    Evaluate globs in directory paths and return all matching directories
86
87
    :param dir_paths:         file path or list of such that can include globs
88
    :param ignored_dir_paths: list of globs that match to-be-ignored dirs
89
    :return:                  list of paths of all matching directories
90
    """
91
    print(ignored_dir_paths)
92
    valid_dirs = list(filter(lambda fname: os.path.isdir(fname[0]),
93
                             icollect(dir_paths, ignored_dir_paths)))
94
    if valid_dirs:
95
        collected_dirs, dummy = zip(*valid_dirs)
96
        return list(collected_dirs)
97
    else:
98
        return []
99
100
101
@yield_once
102
def icollect_bears(bear_dirs, bear_globs, kinds, log_printer):
103
    """
104
    Collect all bears from bear directories that have a matching kind.
105
106
    :param bear_dirs:   directory name or list of such that can contain bears
107
    :param bear_globs:  globs of bears to collect
108
    :param kinds:       list of bear kinds to be collected
109
    :param log_printer: log_printer to handle logging
110
    :return:            iterator that yields a tuple with bear class and
111
                        which bear_glob was used to find that bear class.
112
    """
113
    for bear_dir, dir_glob in filter(lambda x: os.path.isdir(x[0]),
114
                                     icollect(bear_dirs)):
115
        for bear_glob in bear_globs:
116
            for matching_file in iglob(
117
                    os.path.join(bear_dir, bear_glob + '.py')):
118
119
                try:
120
                    for bear in _import_bears(matching_file, kinds):
121
                        yield bear, bear_glob
122
                except BaseException as exception:
123
                    log_printer.log_exception(
124
                        "Unable to collect bears from {file}. Probably the "
125
                        "file is malformed or the module code raises an "
126
                        "exception.".format(file=matching_file),
127
                        exception,
128
                        log_level=LOG_LEVEL.WARNING)
129
130
131
def collect_bears(bear_dirs, bear_globs, kinds, log_printer):
132
    """
133
    Collect all bears from bear directories that have a matching kind
134
    matching the given globs.
135
136
    :param bear_dirs:   directory name or list of such that can contain bears
137
    :param bear_globs:  globs of bears to collect
138
    :param kinds:       list of bear kinds to be collected
139
    :param log_printer: log_printer to handle logging
140
    :return:            tuple of list of matching bear classes based on kind.
141
                        The lists are in the same order as `kinds`
142
    """
143
    bears_found = tuple([] for i in range(len(kinds)))
144
    bear_globs_with_bears = set()
145
    for bear, glob in icollect_bears(bear_dirs, bear_globs, kinds, log_printer):
146
        index = kinds.index(_get_kind(bear))
147
        bears_found[index].append(bear)
148
        bear_globs_with_bears.add(glob)
149
150
    _warn_if_unused_glob(log_printer, bear_globs, bear_globs_with_bears,
151
                         "No bears were found matching '{}'.")
152
    return bears_found
153
154
155
def collect_all_bears_from_sections(sections, log_printer):
156
    """
157
    Collect all kinds of bears from bear directories given in the sections.
158
159
    :param bear_dirs:   directory name or list of such that can contain bears
160
    :param log_printer: log_printer to handle logging
161
    """
162
    local_bears = {}
163
    global_bears = {}
164
    for section in sections:
165
        bear_dirs = sections[section].bear_dirs()
166
        local_bears[section], global_bears[section] = collect_bears(
167
            bear_dirs,
168
            ["**"],
169
            [BEAR_KIND.LOCAL, BEAR_KIND.GLOBAL],
170
            log_printer)
171
    return local_bears, global_bears
172
173
174
def _warn_if_unused_glob(log_printer, globs, used_globs, message):
175
    """
176
    Warn if a glob has not been used.
177
178
    :param log_printer: The log_printer to handle logging.
179
    :param globs:       List of globs that were expected to be used.
180
    :param used_globs:  List of globs that were actually used.
181
    :param message:     Warning message to display if a glob is unused.
182
                        The glob which was unused will be added using
183
                        .format()
184
    """
185
    unused_globs = set(globs) - set(used_globs)
186
    for glob in unused_globs:
187
        log_printer.warn(message.format(glob))
188