# 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
if sys.version_info < (3, 10):
from importlib_metadata import version
else:
from importlib.metadata import version
from typing import List, Sequence
__version__ = version("logilab-common")
# 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
[docs]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)
[docs]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)
[docs]class nullobject:
def __repr__(self):
return "<nullobject>"
def __bool__(self):
return False
__nonzero__ = __bool__
[docs]class tempattr:
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
[docs]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 ?
[docs]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)