Failed Conditions
Pull Request — master (#1990)
by Mischa
01:34
created

_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.7087

How to fix   Complexity   

Complexity

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