Source code for bob.db.base.database

#!/usr/bin/env python
# vim: set fileencoding=utf-8 :

import os

from . import utils

from .file import File


class Database(object):
    """Low-level Database API to be used within bob."""


[docs] def check_parameters_for_validity(self, parameters, parameter_description, valid_parameters, default_parameters=None): """Checks the given parameters for validity. Checks a given parameter is in the set of valid parameters. It also assures that the parameters form a tuple or a list. If parameters is 'None' or empty, the default_parameters will be returned (if default_parameters is omitted, all valid_parameters are returned). This function will return a tuple or list of parameters, or raise a ValueError. Parameters: parameters : str, [str] or None The parameters to be checked. Might be a string, a list/tuple of strings, or None. parameter_description : str A short description of the parameter. This will be used to raise an exception in case the parameter is not valid. valid_parameters : [str] A list/tuple of valid values for the parameters. default_parameters : [str] or None The list/tuple of default parameters that will be returned in case parameters is None or empty. If omitted, all valid_parameters are used. """ if parameters is None: # parameters are not specified, i.e., 'None' or empty lists parameters = default_parameters if default_parameters is not None else valid_parameters if not isinstance(parameters, (list, tuple, set)): # parameter is just a single element, not a tuple or list -> transform it into a tuple parameters = (parameters,) # perform the checks for parameter in parameters: if parameter not in valid_parameters: raise ValueError("Invalid %s '%s'. Valid values are %s, or lists/tuples of those" % (parameter_description, parameter, valid_parameters)) # check passed, now return the list/tuple of parameters return parameters
[docs] def check_parameter_for_validity(self, parameter, parameter_description, valid_parameters, default_parameter=None): """Checks the given parameter for validity Ensures a given parameter is in the set of valid parameters. If the parameter is ``None`` or empty, the value in ``default_parameter`` will be returned, in case it is specified, otherwise a :py:exc:`ValueError` will be raised. This function will return the parameter after the check tuple or list of parameters, or raise a :py:exc:`ValueError`. Parameters: parameter : str The single parameter to be checked. Might be a string or None. parameter_description : str A short description of the parameter. This will be used to raise an exception in case the parameter is not valid. valid_parameters : [str] A list/tuple of valid values for the parameters. default_parameters : [str] or None The default parameter that will be returned in case parameter is None or empty. If omitted and parameter is empty, a ValueError is raised. """ if parameter is None: # parameter not specified ... if default_parameter is not None: # ... -> use default parameter parameter = default_parameter else: # ... -> raise an exception raise ValueError("The %s has to be one of %s, it might not be 'None'." % (parameter_description, valid_parameters)) if isinstance(parameter, (list, tuple, set)): # the parameter is in a list/tuple ... if len(parameter) > 1: raise ValueError("The %s has to be one of %s, it might not be more than one (%s was given)." % (parameter_description, valid_parameters, parameter)) # ... -> we take the first one parameter = parameter[0] # perform the check if parameter not in valid_parameters: raise ValueError("The given %s '%s' is not allowed. Please choose one of %s." % (parameter_description, parameter, valid_parameters)) # tests passed -> return the parameter return parameter
[docs] def convert_names_to_highlevel(self, names, low_level_names, high_level_names): """ Converts group names from a low level to high level API This is useful for example when you want to return ``db.groups()`` for the :py:mod:`bob.bio.base`. Your instance of the database should already have ``low_level_names`` and ``high_level_names`` initialized. """ if names is None: return None mapping = dict(zip(low_level_names, high_level_names)) if isinstance(names, str): return mapping.get(names) return [mapping[g] for g in names]
[docs] def convert_names_to_lowlevel(self, names, low_level_names, high_level_names): """ Same as convert_names_to_highlevel but on reverse """ if names is None: return None mapping = dict(zip(high_level_names, low_level_names)) if isinstance(names, str): return mapping.get(names) return [mapping[g] for g in names]
class SQLiteDatabase(Database): """This class can be used for handling SQL databases. It opens an SQL database in a read-only mode and keeps it opened during the whole session. Parameters: sqlite_file : str The file name (including full path) of the SQLite file to read or generate. file_class : a class instance The ``File`` class, which needs to be derived from :py:class:`bob.db.base.File`. This is required to be able to :py:meth:`query` the databases later on. """ def __init__(self, sqlite_file, file_class): self.m_sqlite_file = sqlite_file if not os.path.exists(sqlite_file): self.m_session = None else: self.m_session = utils.session_try_readonly('sqlite', sqlite_file) # assert the given file class is derived from the File class assert issubclass(file_class, File) self.m_file_class = file_class def __del__(self): """Closes the connection to the database.""" if self.is_valid(): # do some magic to close the connection to the database file try: # Since the dispose function re-creates a pool # which might fail in some conditions, e.g., when this destructor is called during the exit of the python interpreter self.m_session.close() self.m_session.bind.dispose() except (TypeError, AttributeError, KeyError): # ... I can just ignore the according exception... pass
[docs] def is_valid(self): """Returns if a valid session has been opened for reading the database. """ return self.m_session is not None
[docs] def assert_validity(self): """Raise a RuntimeError if the database back-end is not available.""" if not self.is_valid(): raise IOError("Database of type 'sqlite' cannot be found at expected location '%s'." % self.m_sqlite_file)
[docs] def query(self, *args): """Creates a query to the database using the given arguments.""" self.assert_validity() return self.m_session.query(*args)
[docs] def files(self, ids, preserve_order=True): """Returns a list of ``File`` objects with the given file ids Parameters: ids : list, tuple The ids of the object in the database table "file". This object should be a python iterable (such as a tuple or list). preserve_order : bool If True (the default) the order of elements is preserved, but the execution time increases. Returns: list: a list (that may be empty) of ``File`` objects. """ file_objects = self.query(self.m_file_class).filter(self.m_file_class.id.in_(ids)) if not preserve_order: return list(file_objects) else: path_dict = {} for f in file_objects: path_dict[f.id] = f return [path_dict[id] for id in ids]
[docs] def paths(self, ids, prefix=None, suffix=None, preserve_order=True): """Returns a full file paths considering particular file ids Parameters: ids : list, tuple The ids of the object in the database table "file". This object should be a python iterable (such as a tuple or list). prefix : str or None The bit of path to be prepended to the filename stem suffix : str or None The extension determines the suffix that will be appended to the filename stem. preserve_order : bool If True (the default) the order of elements is preserved, but the execution time increases. Returns: list: A list (that may be empty) of the fully constructed paths given the file ids. """ file_objects = self.files(ids, preserve_order) return [f.make_path(prefix, suffix) for f in file_objects]
[docs] def reverse(self, paths, preserve_order=True): """Reverses the lookup from certain paths, returns a list of :py:class:`File`'s Parameters: paths : [str] The filename stems to query for. This object should be a python iterable (such as a tuple or list) preserve_order : True If True (the default) the order of elements is preserved, but the execution time increases. Returns: list: A list (that may be empty). """ file_objects = self.query(self.m_file_class).filter(self.m_file_class.path.in_(paths)) if not preserve_order: return file_objects else: # path_dict = {f.path : f for f in file_objects} <<-- works fine with python 2.7, but not in 2.6 path_dict = {} for f in file_objects: path_dict[f.path] = f return [path_dict[path] for path in paths]
[docs] def uniquify(self, file_list): """Sorts the given list of File objects and removes duplicates from it. Parameters: file_list: [:py:class:`File`] A list of File objects to be handled. Also other objects can be handled, as long as they are sortable. Returns: list: A sorted copy of the given ``file_list`` with the duplicates removed. """ return sorted(set(file_list))
[docs] def all_files(self, **kwargs): """Returns the list of all File objects that satisfy your query. For possible keyword arguments, please check the implemention's ``objects()`` method. """ return self.uniquify(self.objects(**kwargs))