Completed
Pull Request — master (#1522)
by Abdeali
02:07
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 Method

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