Failed Conditions
Pull Request — master (#1522)
by Abdeali
01:30
created

coalib.collecting.limit_paths()   A

Complexity

Conditions 4

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 4
dl 0
loc 18
rs 9.2
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]),
71
        icollect(file_paths)))
72
73
    # Find globs that gave no files and warn the user
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
    limited_files = list(filter(limit_fnmatch, collected_files))
83
    return limited_files
84
85
86
def collect_dirs(dir_paths, ignored_dir_paths=None):
87
    """
88
    Evaluate globs in directory paths and return all matching directories
89
90
    :param dir_paths:         file path or list of such that can include globs
91
    :param ignored_dir_paths: list of globs that match to-be-ignored dirs
92
    :return:                  list of paths of all matching directories
93
    """
94
    if ignored_dir_paths:
95
        ignore_fnmatch = functools.partial(fnmatch, patterns=ignored_dir_paths)
96
    else:
97
        ignore_fnmatch = lambda x: False  # Never ignored
98
    valid_dirs = list(filter(
99
        lambda x: os.path.isdir(x[0]) and not ignore_fnmatch(x[0]),
100
        icollect(dir_paths)))
101
    if valid_dirs:
102
        collected_dirs, dummy = zip(*valid_dirs)
103
        return list(collected_dirs)
104
    else:
105
        return []
106
107
108
@yield_once
109
def icollect_bears(bear_dirs, bear_globs, kinds, log_printer):
110
    """
111
    Collect all bears from bear directories that have a matching kind.
112
113
    :param bear_dirs:   directory name or list of such that can contain bears
114
    :param bear_globs:  globs of bears to collect
115
    :param kinds:       list of bear kinds to be collected
116
    :param log_printer: log_printer to handle logging
117
    :return:            iterator that yields a tuple with bear class and
118
                        which bear_glob was used to find that bear class.
119
    """
120
    for bear_dir, dir_glob in filter(lambda x: os.path.isdir(x[0]),
121
                                     icollect(bear_dirs)):
122
        for bear_glob in bear_globs:
123
            for matching_file in iglob(
124
                    os.path.join(bear_dir, bear_glob + '.py')):
125
126
                try:
127
                    for bear in _import_bears(matching_file, kinds):
128
                        yield bear, bear_glob
129
                except BaseException as exception:
130
                    log_printer.log_exception(
131
                        "Unable to collect bears from {file}. Probably the "
132
                        "file is malformed or the module code raises an "
133
                        "exception.".format(file=matching_file),
134
                        exception,
135
                        log_level=LOG_LEVEL.WARNING)
136
137
138
def collect_bears(bear_dirs, bear_globs, kinds, log_printer):
139
    """
140
    Collect all bears from bear directories that have a matching kind
141
    matching the given globs.
142
143
    :param bear_dirs:   directory name or list of such that can contain bears
144
    :param bear_globs:  globs of bears to collect
145
    :param kinds:       list of bear kinds to be collected
146
    :param log_printer: log_printer to handle logging
147
    :return:            tuple of list of matching bear classes based on kind.
148
                        The lists are in the same order as `kinds`
149
    """
150
    bears_found = tuple([] for i in range(len(kinds)))
151
    bear_globs_with_bears = set()
152
    for bear, glob in icollect_bears(bear_dirs, bear_globs, kinds, log_printer):
153
        index = kinds.index(_get_kind(bear))
154
        bears_found[index].append(bear)
155
        bear_globs_with_bears.add(glob)
156
157
    empty_bear_globs = set(bear_globs) - set(bear_globs_with_bears)
158
    for glob in empty_bear_globs:
159
        log_printer.warn("No bears were found matching '{}'.".format(glob))
160
161
    return bears_found
162
163
164
def collect_all_bears_from_sections(sections, log_printer):
165
    """
166
    Collect all kinds of bears from bear directories given in the sections.
167
168
    :param bear_dirs:   directory name or list of such that can contain bears
169
    :param log_printer: log_printer to handle logging
170
    """
171
    local_bears = {}
172
    global_bears = {}
173
    for section in sections:
174
        bear_dirs = sections[section].bear_dirs()
175
        local_bears[section], global_bears[section] = collect_bears(
176
            bear_dirs,
177
            ["**"],
178
            [BEAR_KIND.LOCAL, BEAR_KIND.GLOBAL],
179
            log_printer)
180
    return local_bears, global_bears
181