coalib.collecting.Importers — coala 0.11.0.rc1 documentation

# Source code for coalib.collecting.Importers

    from contextlib import ExitStack
    import inspect
    import os
    import platform
    import sys

    from coala_utils.ContextManagers import suppress_stdout
    from coala_utils.decorators import arguments_to_lists, yield_once

    def _import_module(file_path):
        if not os.path.exists(file_path):
            raise ImportError

        module_name = os.path.splitext(os.path.basename(file_path))[0]
        module_dir = os.path.dirname(file_path)

        if module_dir not in sys.path:
            sys.path.insert(0, module_dir)

        # Ugly inconsistency: Python will insist on correctly cased module names
        # independent of whether the OS is case-sensitive or not.
        # We want all cases to match though.
        if platform.system() == 'Windows':  # pragma: nocover
            for cased_file_path in os.listdir(module_dir):
                cased_module_name = os.path.splitext(cased_file_path)[0]
                if cased_module_name.lower() == module_name.lower():
                    module_name = cased_module_name
                    break

        return __import__(module_name)

    def _is_subclass(test_class, superclasses):
        for superclass in superclasses:
            try:
                if issubclass(test_class, superclass):
                    return True
            except TypeError:
                pass
        return False

    def _has_all(obj, attribute_names):
        for attribute_name in attribute_names:
            if not hasattr(obj, attribute_name):
                return False
        return True

    def object_defined_in(obj, file_path):
        """
        Check if the object is defined in the given file.

        >>> object_defined_in(object_defined_in, __file__)
        True
        >>> object_defined_in(object_defined_in, "somewhere else")
        False

        Builtins are always defined outside any given file:

        >>> object_defined_in(False, __file__)
        False

        :param obj:       The object to check.
        :param file_path: The path it might be defined in.
        :return:          True if the object is defined in the file.
        """
        try:
            source = inspect.getfile(obj)
            if (platform.system() == 'Windows' and
                    source.lower() == file_path.lower() or
                    source == file_path):
                return True
        except TypeError:  # Builtin values don't have a source location
            pass

        return False

    def _is_defined_in(obj, file_path):
        """
        Check if a class is defined in the given file.

        Any class is considered to be defined in the given file if any of it's
        parent classes or the class itself is defined in it.
        """
        if not inspect.isclass(obj):
            return object_defined_in(obj, file_path)

        for base in inspect.getmro(obj):
            if object_defined_in(base, file_path):
                return True

        return False

    @arguments_to_lists
    @yield_once
    def _iimport_objects(file_paths, names, types, supers, attributes, local):
        """
        Import all objects from the given modules that fulfill the requirements

        :param file_paths: File path(s) from which objects will be imported
        :param names:      Name(s) an objects need to have one of
        :param types:      Type(s) an objects need to be out of
        :param supers:     Class(es) objects need to be a subclass of
        :param attributes: Attribute(s) an object needs to (all) have
        :param local:      if True: Objects need to be defined in the file they
                           appear in to be collected
        :return:           iterator that yields all matching python objects
        :raises Exception: Any exception that is thrown in module code or an
                           ImportError if paths are erroneous.
        """
        if not file_paths:
            return

        for file_path in file_paths:
            module = _import_module(file_path)
            for obj_name, obj in inspect.getmembers(module):
                if ((not names or obj_name in names) and
                        (not types or isinstance(obj, tuple(types))) and
                        (not supers or _is_subclass(obj, supers)) and
                        (not attributes or _has_all(obj, attributes)) and
                        (local[0] is False or _is_defined_in(obj, file_path))):
                    yield obj

    def iimport_objects(file_paths, names=None, types=None, supers=None,
                        attributes=None, local=False, suppress_output=False):
        """
        Import all objects from the given modules that fulfill the requirements

        :param file_paths:
            File path(s) from which objects will be imported.
        :param names:
            Name(s) an objects need to have one of.
        :param types:
            Type(s) an objects need to be out of.
        :param supers:
            Class(es) objects need to be a subclass of.
        :param attributes:
            Attribute(s) an object needs to (all) have.
        :param local:
            If True: Objects need to be defined in the file they appear in to be
            collected.
        :param suppress_output:
            Whether console output from stdout shall be suppressed or not.
        :return:
            An iterator that yields all matching python objects.
        :raises Exception:
            Any exception that is thrown in module code or an ImportError if paths
            are erroneous.
        """
        with ExitStack() as stack:
            if not suppress_output:
                stack.enter_context(suppress_stdout())

            yield from _iimport_objects(file_paths, names, types, supers,
                                        attributes, local)

    def import_objects(file_paths, names=None, types=None, supers=None,
                       attributes=None, local=False, verbose=False):
        """
        Import all objects from the given modules that fulfill the requirements

        :param file_paths: File path(s) from which objects will be imported
        :param names:      Name(s) an objects need to have one of
        :param types:      Type(s) an objects need to be out of
        :param supers:     Class(es) objects need to be a subclass of
        :param attributes: Attribute(s) an object needs to (all) have
        :param local:      if True: Objects need to be defined in the file they
                           appear in to be collected
        :return:           list of all matching python objects
        :raises Exception: Any exception that is thrown in module code or an
                           ImportError if paths are erroneous.
        """
        return list(iimport_objects(file_paths, names, types, supers, attributes,
                                    local, verbose))