# Source code for logilab.common

    # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
    # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
    #
    # This file is part of logilab-common.
    #
    # logilab-common is free software: you can redistribute it and/or modify it under
    # the terms of the GNU Lesser General Public License as published by the Free
    # Software Foundation, either version 2.1 of the License, or (at your option) any
    # later version.
    #
    # logilab-common is distributed in the hope that it will be useful, but WITHOUT
    # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
    # details.
    #
    # You should have received a copy of the GNU Lesser General Public License along
    # with logilab-common.  If not, see <http://www.gnu.org/licenses/>.
    """Logilab common library (aka Logilab's extension to the standard library).

    :type STD_BLACKLIST: tuple
    :var STD_BLACKLIST: directories ignored by default by the functions in
      this package which have to recurse into directories

    :type IGNORED_EXTENSIONS: tuple
    :var IGNORED_EXTENSIONS: file extensions that may usually be ignored
    """
    __docformat__ = "restructuredtext en"

    import sys
    import types
    import pkg_resources
    from typing import List, Sequence

    __version__ = pkg_resources.get_distribution("logilab-common").version

    # deprecated, but keep compatibility with pylint < 1.4.4
    __pkginfo__ = types.ModuleType("__pkginfo__")
    __pkginfo__.__package__ = __name__
    # mypy output: Module has no attribute "version"
    # logilab's magic
    __pkginfo__.version = __version__  # type: ignore
    sys.modules["logilab.common.__pkginfo__"] = __pkginfo__

    STD_BLACKLIST = ("CVS", ".svn", ".hg", ".git", ".tox", "debian", "dist", "build")

    IGNORED_EXTENSIONS = (".pyc", ".pyo", ".elc", "~", ".swp", ".orig")

    # set this to False if you've mx DateTime installed but you don't want your db
    # adapter to use it (should be set before you got a connection)
    USE_MX_DATETIME = True

    class attrdict(dict):
        """A dictionary for which keys are also accessible as attributes."""

        def __getattr__(self, attr: str) -> str:
            try:
                return self[attr]
            except KeyError:
                raise AttributeError(attr)

    class dictattr(dict):
        def __init__(self, proxy):
            self.__proxy = proxy

        def __getitem__(self, attr):
            try:
                return getattr(self.__proxy, attr)
            except AttributeError:
                raise KeyError(attr)

    class nullobject(object):
        def __repr__(self):
            return "<nullobject>"

        def __bool__(self):
            return False

        __nonzero__ = __bool__

    class tempattr(object):
        def __init__(self, obj, attr, value):
            self.obj = obj
            self.attr = attr
            self.value = value

        def __enter__(self):
            self.oldvalue = getattr(self.obj, self.attr)
            setattr(self.obj, self.attr, self.value)
            return self.obj

        def __exit__(self, exctype, value, traceback):
            setattr(self.obj, self.attr, self.oldvalue)

    # flatten -----
    # XXX move in a specific module and use yield instead
    # do not mix flatten and translate
    #
    # def iterable(obj):
    #    try: iter(obj)
    #    except: return False
    #    return True
    #
    # def is_string_like(obj):
    #    try: obj +''
    #    except (TypeError, ValueError): return False
    #    return True
    #
    # def is_scalar(obj):
    #    return is_string_like(obj) or not iterable(obj)
    #
    # def flatten(seq):
    #    for item in seq:
    #        if is_scalar(item):
    #            yield item
    #        else:
    #            for subitem in flatten(item):
    #               yield subitem

    def flatten(iterable, tr_func=None, results=None):
        """Flatten a list of list with any level.

        If tr_func is not None, it should be a one argument function that'll be called
        on each final element.

        :rtype: list

        >>> flatten([1, [2, 3]])
        [1, 2, 3]
        """
        if results is None:
            results = []
        for val in iterable:
            if isinstance(val, (list, tuple)):
                flatten(val, tr_func, results)
            elif tr_func is None:
                results.append(val)
            else:
                results.append(tr_func(val))
        return results

    # XXX is function below still used ?

    def make_domains(lists):
        """
        Given a list of lists, return a list of domain for each list to produce all
        combinations of possibles values.

        :rtype: list

        Example:

        >>> make_domains(['a', 'b'], ['c','d', 'e'])
        [['a', 'b', 'a', 'b', 'a', 'b'], ['c', 'c', 'd', 'd', 'e', 'e']]
        """
        domains = []
        for iterable in lists:
            new_domain = iterable[:]
            for i in range(len(domains)):
                domains[i] = domains[i] * len(iterable)
            if domains:
                missing = (len(domains[0]) - len(iterable)) / len(iterable)
                i = 0
                for j in range(len(iterable)):
                    value = iterable[j]
                    for dummy in range(missing):
                        new_domain.insert(i, value)
                        i += 1
                    i += 1
            domains.append(new_domain)
        return domains

    # private stuff ################################################################

    def _handle_blacklist(blacklist: Sequence[str], dirnames: List[str], filenames: List[str]) -> None:
        """remove files/directories in the black list

        dirnames/filenames are usually from os.walk
        """
        for norecurs in blacklist:
            if norecurs in dirnames:
                dirnames.remove(norecurs)
            elif norecurs in filenames:
                filenames.remove(norecurs)