Failed Conditions
Pull Request — master (#1152)
by Lasse
03:36
created

coalib.collecting._iimport_objects()   F

Complexity

Conditions 18

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 18
dl 0
loc 33
rs 2.7088

How to fix   Complexity   

Complexity

Complex classes like coalib.collecting._iimport_objects() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
import inspect
2
import os
3
import platform
4
import sys
5
from coalib.misc.Decorators import arguments_to_lists, yield_once
6
from coalib.misc.ContextManagers import suppress_stdout
7
8
9
def _import_module(file_path):
10
    if not os.path.exists(file_path):
11
        raise ImportError
12
13
    module_name = os.path.splitext(os.path.basename(file_path))[0]
14
    module_dir = os.path.dirname(file_path)
15
16
    if module_dir not in sys.path:
17
        sys.path.insert(0, module_dir)
18
19
    # Ugly inconsistency: Python will insist on correctly cased module names
20
    # independent of whether the OS is case-sensitive or not.
21
    # We want all cases to match though.
22
    if platform.system() == 'Windows':  # pragma: nocover
23
        for cased_file_path in os.listdir(module_dir):
24
            cased_module_name = os.path.splitext(cased_file_path)[0]
25
            if cased_module_name.lower() == module_name.lower():
26
                module_name = cased_module_name
27
                break
28
29
    return __import__(module_name)
30
31
32
def _is_subclass(test_class, superclasses):
33
    for superclass in superclasses:
34
        try:
35
            if issubclass(test_class, superclass):
36
                return True
37
        except TypeError:
38
            pass
39
    return False
40
41
42
def _has_all(obj, attribute_names):
43
    for attribute_name in attribute_names:
44
        if not hasattr(obj, attribute_name):
45
            return False
46
    return True
47
48
49
def _is_defined_in(obj, file_path):
50
    try:
51
        source = inspect.getfile(obj)
52
        if (platform.system() == 'Windows' and
53
                source.lower() == file_path.lower() or
54
                source == file_path):
55
            return True
56
    except TypeError:  # Bool values and others
57
        pass
58
    return False
59
60
61
@arguments_to_lists
62
@yield_once
63
def _iimport_objects(file_paths, names, types, supers, attributes, local):
64
    """
65
    Import all objects from the given modules that fulfill the requirements
66
67
    :param file_paths: File path(s) from which objects will be imported
68
    :param names:      Name(s) an objects need to have one of
69
    :param types:      Type(s) an objects need to be out of
70
    :param supers:     Class(es) objects need to be a subclass of
71
    :param attributes: Attribute(s) an object needs to (all) have
72
    :param local:      if True: Objects need to be defined in the file they
73
                       appear in to be collected
74
    :return:           iterator that yields all matching python objects
75
    :raises Exception: Any exception that is thrown in module code or an
76
                       ImportError if paths are erroneous.
77
    """
78
    if (file_paths == [] or
79
            (names == [] and
80
             types == [] and
81
             supers == [] and
82
             attributes == [])):
83
        raise StopIteration
84
85
    for file_path in file_paths:
86
        module = _import_module(file_path)
87
        for obj_name, obj in inspect.getmembers(module):
88
            if ((names == [] or obj_name in names) and
89
                    (types == [] or isinstance(obj, tuple(types))) and
90
                    (supers == [] or _is_subclass(obj, supers)) and
91
                    (attributes == [] or _has_all(obj, attributes)) and
92
                    (local[0] is False or _is_defined_in(obj, file_path))):
93
                yield obj
94
95
96
def iimport_objects(file_paths, names=None, types=None, supers=None,
97
                    attributes=None, local=False, verbose=False):
98
    """
99
    Import all objects from the given modules that fulfill the requirements
100
101
    :param file_paths: File path(s) from which objects will be imported
102
    :param names:      Name(s) an objects need to have one of
103
    :param types:      Type(s) an objects need to be out of
104
    :param supers:     Class(es) objects need to be a subclass of
105
    :param attributes: Attribute(s) an object needs to (all) have
106
    :param local:      if True: Objects need to be defined in the file they
107
                       appear in to be collected
108
    :return:           iterator that yields all matching python objects
109
    :raises Exception: Any exception that is thrown in module code or an
110
                       ImportError if paths are erroneous.
111
    """
112
    if not verbose:
113
        with suppress_stdout():
114
            for obj in _iimport_objects(file_paths, names, types, supers,
115
                                        attributes, local):
116
                yield obj
117
    else:
118
        for obj in _iimport_objects(file_paths, names, types, supers,
119
                                    attributes, local):
120
            yield obj
121
122
123
def import_objects(file_paths, names=None, types=None, supers=None,
124
                   attributes=None, local=False, verbose=False):
125
    """
126
    Import all objects from the given modules that fulfill the requirements
127
128
    :param file_paths: File path(s) from which objects will be imported
129
    :param names:      Name(s) an objects need to have one of
130
    :param types:      Type(s) an objects need to be out of
131
    :param supers:     Class(es) objects need to be a subclass of
132
    :param attributes: Attribute(s) an object needs to (all) have
133
    :param local:      if True: Objects need to be defined in the file they
134
                       appear in to be collected
135
    :return:           list of all matching python objects
136
    :raises Exception: Any exception that is thrown in module code or an
137
                       ImportError if paths are erroneous.
138
    """
139
    return list(iimport_objects(file_paths, names, types, supers, attributes,
140
                                local, verbose))
141