Source code for bibolamazi.core.butils

################################################################################
#                                                                              #
#   This file is part of the Bibolamazi Project.                               #
#   Copyright (C) 2013 by Philippe Faist                                       #
#   philippe.faist@bluewin.ch                                                  #
#                                                                              #
#   Bibolamazi is free software: you can redistribute it and/or modify         #
#   it under the terms of the GNU General Public License as published by       #
#   the Free Software Foundation, either version 3 of the License, or          #
#   (at your option) any later version.                                        #
#                                                                              #
#   Bibolamazi 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 General Public License for more details.                               #
#                                                                              #
#   You should have received a copy of the GNU General Public License          #
#   along with Bibolamazi.  If not, see <http://www.gnu.org/licenses/>.        #
#                                                                              #
################################################################################

"""
Various utilities for use within all of the Bibolamazi Project.
"""


import re
import types
import math
import datetime
import logging

import bibolamazi.init
from . import version

logger = logging.getLogger(__name__)



[docs]def get_version(): """ Return the version string :py:data:`~core.version.version_str`, unchanged. """ return version.version_str;
_theversionsplit = None
[docs]def get_version_split(): """ Return a 4-tuple `(maj, min, rel, suffix)` resulting from parsing the version obtained via :py:data:`version.version_str`. ............ TODO: FIXME: CURRENTLY, the elements are strings! why not integers? If not there, they will/should be empty or None? """ if (_theversionsplit is None): m = re.match(r'^(\d+)(?:\.(\d+)(?:\.(\d+)(.+)?)?)?', version.version_str); _theversionsplit = (m.group(1), m.group(2), m.group(3), m.group(4)); return _theversionsplit;
[docs]class BibolamaziError(Exception): """ Root bibolamazi error exception. See also :py:class:`~core.bibfilter.BibFilterError` and :py:class:`~core.bibusercache.BibUserCacheError`. """ def __init__(self, msg, where=None): self.where = where; fullmsg = msg if (where is not None): fullmsg += "\n\t@: "+where; super(BibolamaziError, self).__init__(fullmsg);
[docs]def getbool(x): """ Utility to parse a string representing a boolean value. If `x` is already of integer or boolean type (actually, anything castable to an integer), then the corresponding boolean convertion is returned. If it is a string-like type, then it is matched against something that looks like 't(rue)?', '1', 'y(es)?' or 'on' (ignoring case), or against something that looks like 'f(alse)?', '0', 'n(o)?' or 'off' (also ignoring case). Leading or trailing whitespace is ignored. If the string cannot be parsed, a :py:exc:`ValueError` is raised. """ try: return (int(x) != 0) except (TypeError, ValueError): pass if (isinstance(x, basestring)): m = re.match(r'^\s*(t(?:rue)?|1|y(?:es)?|on)\s*$', x, re.IGNORECASE); if m: return True m = re.match(r'^\s*(f(?:alse)?|0|n(?:o)?|off)\s*$', x, re.IGNORECASE); if m: return False raise ValueError("Can't parse boolean value: %r" % x);
[docs]def resolve_type(typename, in_module=None): """ Returns a type object corresponding to the given type name `typename`, given as a string. ..... TODO: MORE DOC ......... """ if (in_module is not None): logger.longdebug("Resolving type %s in module %s", typename, in_module) if (typename in in_module.__dict__): return in_module.__dict__.get(typename) logger.longdebug("Resolving type %s (no module)", typename) if (typename == 'str'): return types.StringType if (typename == 'bool'): return types.BooleanType typestypename = (typename.title()+'Type') # e.g. "int" -> "IntType" return types.__dict__.get(typestypename, None)
[docs]def quotearg(x): if (re.match(r'^[-\w./:~%#]+$', x)): # only very sympathetic chars return x return '"' + re.sub(r'("|\\)', lambda m: '\\'+m.group(), x) + '"';
[docs]def guess_encoding_decode(dat, encoding=None): if encoding: return dat.decode(encoding); try: return dat.decode('utf-8'); except UnicodeDecodeError: pass # this should always succeed return dat.decode('latin1');
[docs]def call_with_args(fn, *args, **kwargs): """ Utility to call a function `fn` with `*args` and `**kwargs`. `fn(*args)` must be an acceptable function call; beyond that, additional keyword arguments which the function accepts will be provided from `**kwargs`. This function is meant to be essentially `fn(*args, **kwargs)`, but without raising an error if there are arguments in `kwargs` which the function doesn't accept (in which case, those arguments are ignored). """ args2 = args kwargs2 = kwargs if hasattr(fn, '__call__'): args2 = [fn] + args fn = fn.__call__ (fargs, varargs, keywords, defaults) = inspect.getargspec(fn) if keywords: return fn(*args2, **kwargs2) kwargs2 = dict([(k,v) for (k,v) in kwargs2 if k in fargs]) return fn(*args2, **kwargs2)
_rx_timedelta_part = re.compile(r'(?P<value>\d+(?:\.\d*)?|\d*\.\d+)(?P<unit>\w+)', flags=re.IGNORECASE)
[docs]def parse_timedelta(in_s): """ Note: only positive timedelta accepted. """ # all-lowercase, please keys = {"weeks": (7, 'days'), "days": (24, 'hours'), "hours": (60, 'minutes'), "minutes": (60, 'seconds'), "seconds": (1000, 'milliseconds'), } kwargs = {} for k in keys.keys(): kwargs[k] = 0.0 kwargs[keys[k][1]] = 0.0 for m in _rx_timedelta_part.finditer(in_s): unit = m.group('unit').lower() keyoks = [x for x in keys if x.startswith(unit)] if len(keyoks) < 1: raise ValueError("Unknown unit for timedelta: %s" %(unit)) if len(keyoks) > 1: raise ValueError("Ambiguous unit for timedelta: %s" %(unit)) # should never happen key = keyoks[0] value = float(m.group('value')) value_int = math.floor(value) kwargs[key] += value_int x = value - value_int while True: x *= keys[key][0] newkey = keys[key][1] v = math.floor(x) kwargs[newkey] += v x = (x - v) key = newkey if key not in keys: break #print 'kwargs: %r'%(kwargs) return datetime.timedelta(**kwargs)
[docs]def warn_deprecated(classname, oldname, newname, modulename=None, explanation=None): import traceback if modulename is not None: warnlogger = logging.getLogger(modulename) else: warnlogger = logger warnlogger.warning( ("%(modulenamecolon)s%(classnamedot)s%(oldname)s is deprecated. Please use " "%(modulenamecolon)s%(classnamedot)s%(newname)s instead. %(explanationspace)s" "at:\n" "%(stack)s") % { 'classnamedot': (classname+'.' if classname else ''), 'modulenamecolon': (modulename+':' if modulename else ''), 'oldname': oldname, 'newname': newname, 'explanationspace': (explanation+' ' if explanation else ''), 'stack': traceback.format_stack(limit=3)[0], } )