Completed
Pull Request — master (#1522)
by Abdeali
01:49
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):
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
    :return:            iterator that yields tuple of path of a matching file,
41
                        the glob where it was found
42
    """
43
    if isinstance(file_paths, str):
44
        file_paths = [file_paths]
45
46
    for file_path in file_paths:
47
        for match in iglob(file_path):
48
            yield match, file_path
49
50
51
def collect_files(file_paths, log_printer, ignored_file_paths=None,
52
                  limit_file_paths=None):
53
    """
54
    Evaluate globs in file paths and return all matching files
55
56
    :param file_paths:         file path or list of such that can include globs
57
    :param ignored_file_paths: list of globs that match to-be-ignored files
58
    :param limit_file_paths:   list of globs that the files are limited to
59
    :return:                   list of paths of all matching files
60
    """
61
    if ignored_file_paths:
62
        ignore_fnmatch = functools.partial(fnmatch, patterns=ignored_file_paths)
63
    else:
64
        ignore_fnmatch = lambda x: False  # Never ignored
65
    if limit_file_paths:
66
        limit_fnmatch = functools.partial(fnmatch, patterns=limit_file_paths)
67
    else:
68
        limit_fnmatch = lambda x: True  # Always in the limit_files
69
    valid_files = list(filter(
70
        lambda x: (os.path.isfile(x[0]) and not ignore_fnmatch(x[0]) and
71
                   limit_fnmatch(x[0])),
72
        icollect(file_paths)))
73
74
    if valid_files:
75
        collected_files, file_globs_with_files = zip(*valid_files)
76
    else:
77
        collected_files, file_globs_with_files = [], []
78
    empty_file_globs = set(file_paths) - set(file_globs_with_files)
79
    for glob in empty_file_globs:
80
        log_printer.warn("No files matching '{}' were found.".format(glob))
81
82
    return list(collected_files)
83
84
85
def collect_dirs(dir_paths, ignored_dir_paths=None):
86
    """
87
    Evaluate globs in directory paths and return all matching directories
88
89
    :param dir_paths:         file path or list of such that can include globs
90
    :param ignored_dir_paths: list of globs that match to-be-ignored dirs
91
    :return:                  list of paths of all matching directories
92
    """
93
    if ignored_dir_paths:
94
        ignore_fnmatch = functools.partial(fnmatch, patterns=ignored_dir_paths)
95
    else:
96
        ignore_fnmatch = lambda x: False  # Never ignored
97
    valid_dirs = list(filter(
98
        lambda x: os.path.isdir(x[0]) and not ignore_fnmatch(x[0]),
99
        icollect(dir_paths)))
100
    if valid_dirs:
101
        collected_dirs, dummy = zip(*valid_dirs)
102
        return list(collected_dirs)
103
    else:
104
        return []
105
106
107
@yield_once
108
def icollect_bears(bear_dirs, bear_globs, kinds, log_printer):
109
    """
110
    Collect all bears from bear directories that have a matching kind.
111
112
    :param bear_dirs:   directory name or list of such that can contain bears
113
    :param bear_globs:  globs of bears to collect
114
    :param kinds:       list of bear kinds to be collected
115
    :param log_printer: log_printer to handle logging
116
    :return:            iterator that yields a tuple with bear class and
117
                        which bear_glob was used to find that bear class.
118
    """
119
    for bear_dir, dir_glob in filter(lambda x: os.path.isdir(x[0]),
120
                                     icollect(bear_dirs)):
121
        for bear_glob in bear_globs:
122
            for matching_file in iglob(
123
                    os.path.join(bear_dir, bear_glob + '.py')):
124
125
                try:
126
                    for bear in _import_bears(matching_file, kinds):
127
                        yield bear, bear_glob
128
                except BaseException as exception:
129
                    log_printer.log_exception(
130
                        "Unable to collect bears from {file}. Probably the "
131
                        "file is malformed or the module code raises an "
132
                        "exception.".format(file=matching_file),
133
                        exception,
134
                        log_level=LOG_LEVEL.WARNING)
135
136
137
def collect_bears(bear_dirs, bear_globs, kinds, log_printer):
138
    """
139
    Collect all bears from bear directories that have a matching kind
140
    matching the given globs.
141
142
    :param bear_dirs:   directory name or list of such that can contain bears
143
    :param bear_globs:  globs of bears to collect
144
    :param kinds:       list of bear kinds to be collected
145
    :param log_printer: log_printer to handle logging
146
    :return:            tuple of list of matching bear classes based on kind.
147
                        The lists are in the same order as `kinds`
148
    """
149
    bears_found = tuple([] for i in range(len(kinds)))
150
    bear_globs_with_bears = set()
151
    for bear, glob in icollect_bears(bear_dirs, bear_globs, kinds, log_printer):
152
        index = kinds.index(_get_kind(bear))
153
        bears_found[index].append(bear)
154
        bear_globs_with_bears.add(glob)
155
156
    empty_bear_globs = set(bear_globs) - set(bear_globs_with_bears)
157
    for glob in empty_bear_globs:
158
        log_printer.warn("No bears matching '{}' were found.".format(glob))
159
160
    return bears_found
161
162
163
def collect_all_bears_from_sections(sections, log_printer):
164
    """
165
    Collect all kinds of bears from bear directories given in the sections.
166
167
    :param bear_dirs:   directory name or list of such that can contain bears
168
    :param log_printer: log_printer to handle logging
169
    """
170
    local_bears = {}
171
    global_bears = {}
172
    for section in sections:
173
        bear_dirs = sections[section].bear_dirs()
174
        local_bears[section], global_bears[section] = collect_bears(
175
            bear_dirs,
176
            ["**"],
177
            [BEAR_KIND.LOCAL, BEAR_KIND.GLOBAL],
178
            log_printer)
179
    return local_bears, global_bears
180