gludb.config module
Provide gludb configuration.
This consists mainly of a mapping from Storable classes to a database configuration. It also includes a default mapping for classes not specifically mapped to a database.
We check for a mapping for a class in MRO (Method Resolution Order). Suppose a class Busy derives from the three classes X, Y, and Z - all of which derive from class Base:
class Base(object):
pass
class X(Base):
pass
class Y(Base):
pass
class Z(Base):
pass
class Busy(X, Y, Z):
pass
Then a check for mapping would first checking for class Busy, then (in order)
classes X, Y, Z, Base, and object. If this is the first time you've seen
multiple inheritance, you'll note that the order of Busy's super classes is
important. This is how Python resolves method calls (we didn't just make this
up). In fact, we depend on the results of the standard library call
inspect.getmro
.
If none of the classes mentioned have a mapping, then the default mapping will be used. If there is no default mapping, then the class can't be mapped to a database instance and an error will be thrown.
Astute readers will note that you could map the class object
to a database
as a kind of default mapping. We generally don't recommend this, because it
would work sort of. Some notes:
- Recall that we support Python 2 and 3. In Python 3.4, classes declared
without a base class get
object
as a base class automatically. In Python 2.7 they just don't have a base class. That means that using anobject
DB mapping won't work as a default in every case. - We always check for the default database mapping last, so mapping to object would be the last thing checked before the actual default.
"""Provide gludb configuration.
This consists mainly of a mapping from Storable classes to a database
configuration. It also includes a default mapping for classes not specifically
mapped to a database.
We check for a mapping for a class in MRO (Method Resolution Order). Suppose
a class Busy derives from the three classes X, Y, and Z - all of which derive
from class Base:
class Base(object):
pass
class X(Base):
pass
class Y(Base):
pass
class Z(Base):
pass
class Busy(X, Y, Z):
pass
Then a check for mapping would first checking for class Busy, then (in order)
classes X, Y, Z, Base, and object. If this is the first time you've seen
multiple inheritance, you'll note that the order of Busy's super classes is
important. This is how Python resolves method calls (we didn't just make this
up). In fact, we depend on the results of the standard library call
`inspect.getmro`.
If none of the classes mentioned have a mapping, then the default mapping will
be used. If there is no default mapping, then the class can't be mapped to a
database instance and an error will be thrown.
Astute readers will note that you could map the class `object` to a database
as a kind of default mapping. We generally don't recommend this, because it
would work *sort of*. Some notes:
* Recall that we support Python 2 and 3. In Python 3.4, classes declared
without a base class get `object` as a base class automatically. In Python
2.7 they just don't have a base class. That means that using an `object`
DB mapping won't work as a default in every case.
* We always check for the default database mapping last, so mapping to object
would be the last thing checked before the actual default.
"""
# pylama:ignore=D213
from inspect import getmro
from importlib import import_module
_APPLICATION_PREFIX = None
_APPLICATION_SEP = '_'
class Database(object):
"""Configuration class.
Represents a database instance supported by one of our backends.
"""
def __init__(self, db_driver, **kwrds):
"""Construct instance for a driver (and optionally parameters)."""
mod = import_module('.backends.'+db_driver, __package__)
Backend = getattr(mod, "Backend")
self.backend = Backend(**kwrds)
def ensure_table(self, cls):
"""Ensure the table exists - defer to backend."""
return self.backend.ensure_table(cls)
def find_one(self, cls, id):
"""Find one record - defer to backend."""
return self.backend.find_one(cls, id)
def find_all(self, cls):
"""Find all records - defer to backend."""
return self.backend.find_all(cls)
def find_by_index(self, cls, index_name, value):
"""Find records matching index query - defer to backend."""
return self.backend.find_by_index(cls, index_name, value)
def save(self, obj):
"""Save the object instance - defer to backend."""
self.backend.save(obj)
def delete(self, obj):
"""Delete the object instance - defer to backend."""
self.backend.delete(obj)
# Note our use of a class with a singleton instance - so configuration is
# process-wide. This makes things much simpler for some users (which is one
# of our main design drivers). One potential enhancement is to allow multiple
# database mappings - perhaps as a Flask Blueprint plugin/addon
class _DatabaseMapping(object):
def __init__(self):
self.clear_mappings()
def add_mapping(self, cls, db):
self.mapping[cls] = db
def get_mapping(self, cls, no_mapping_ok=False):
db = None
for candidate_cls in getmro(cls):
db = self.mapping.get(candidate_cls, None)
if db is not None:
break
if db is None:
db = self.default_database
if db is None:
if no_mapping_ok:
return None
raise ValueError("There is no database mapping for %s" % repr(cls))
return db
def clear_mappings(self):
self.default_database = None
self.mapping = {}
_database_mapping = _DatabaseMapping()
def default_database(db):
"""Set default database config for classes without a specific mapping."""
_database_mapping.default_database = db
def class_database(cls, db):
"""Map a class to a database configuration.
We assume issubclass(cls, Storable)==True).
"""
_database_mapping.add_mapping(cls, db)
def clear_database_config():
"""Reset all mappings to default state.
Note that any in-memory databases will be lost.
"""
_database_mapping.clear_mappings()
def get_mapping(cls, no_mapping_ok=False):
"""Return a database config object for the given class."""
return _database_mapping.get_mapping(cls, no_mapping_ok)
def apply_db_application_prefix(name):
"""Return name with app prefix if necessary."""
if _APPLICATION_PREFIX is not None:
return _APPLICATION_SEP.join([_APPLICATION_PREFIX, name])
else:
return name
def get_db_application_prefix():
"""Global application prefix accessor."""
return _APPLICATION_PREFIX
def get_db_application_sep():
"""Global application separator accessor."""
return _APPLICATION_SEP
def set_db_application_prefix(prefix, sep=None):
"""Set the global app prefix and separator."""
global _APPLICATION_PREFIX, _APPLICATION_SEP
_APPLICATION_PREFIX = prefix
if (sep is not None):
_APPLICATION_SEP = sep
Functions
def apply_db_application_prefix(
name)
Return name with app prefix if necessary.
def apply_db_application_prefix(name):
"""Return name with app prefix if necessary."""
if _APPLICATION_PREFIX is not None:
return _APPLICATION_SEP.join([_APPLICATION_PREFIX, name])
else:
return name
def class_database(
cls, db)
Map a class to a database configuration.
We assume issubclass(cls, Storable)==True).
def class_database(cls, db):
"""Map a class to a database configuration.
We assume issubclass(cls, Storable)==True).
"""
_database_mapping.add_mapping(cls, db)
def clear_database_config(
)
Reset all mappings to default state.
Note that any in-memory databases will be lost.
def clear_database_config():
"""Reset all mappings to default state.
Note that any in-memory databases will be lost.
"""
_database_mapping.clear_mappings()
def default_database(
db)
Set default database config for classes without a specific mapping.
def default_database(db):
"""Set default database config for classes without a specific mapping."""
_database_mapping.default_database = db
def get_db_application_prefix(
)
Global application prefix accessor.
def get_db_application_prefix():
"""Global application prefix accessor."""
return _APPLICATION_PREFIX
def get_db_application_sep(
)
Global application separator accessor.
def get_db_application_sep():
"""Global application separator accessor."""
return _APPLICATION_SEP
def get_mapping(
cls, no_mapping_ok=False)
Return a database config object for the given class.
def get_mapping(cls, no_mapping_ok=False):
"""Return a database config object for the given class."""
return _database_mapping.get_mapping(cls, no_mapping_ok)
def set_db_application_prefix(
prefix, sep=None)
Set the global app prefix and separator.
def set_db_application_prefix(prefix, sep=None):
"""Set the global app prefix and separator."""
global _APPLICATION_PREFIX, _APPLICATION_SEP
_APPLICATION_PREFIX = prefix
if (sep is not None):
_APPLICATION_SEP = sep
Classes
class Database
Configuration class.
Represents a database instance supported by one of our backends.
class Database(object):
"""Configuration class.
Represents a database instance supported by one of our backends.
"""
def __init__(self, db_driver, **kwrds):
"""Construct instance for a driver (and optionally parameters)."""
mod = import_module('.backends.'+db_driver, __package__)
Backend = getattr(mod, "Backend")
self.backend = Backend(**kwrds)
def ensure_table(self, cls):
"""Ensure the table exists - defer to backend."""
return self.backend.ensure_table(cls)
def find_one(self, cls, id):
"""Find one record - defer to backend."""
return self.backend.find_one(cls, id)
def find_all(self, cls):
"""Find all records - defer to backend."""
return self.backend.find_all(cls)
def find_by_index(self, cls, index_name, value):
"""Find records matching index query - defer to backend."""
return self.backend.find_by_index(cls, index_name, value)
def save(self, obj):
"""Save the object instance - defer to backend."""
self.backend.save(obj)
def delete(self, obj):
"""Delete the object instance - defer to backend."""
self.backend.delete(obj)
Ancestors (in MRO)
- Database
- __builtin__.object
Instance variables
var backend
Methods
def __init__(
self, db_driver, **kwrds)
Construct instance for a driver (and optionally parameters).
def __init__(self, db_driver, **kwrds):
"""Construct instance for a driver (and optionally parameters)."""
mod = import_module('.backends.'+db_driver, __package__)
Backend = getattr(mod, "Backend")
self.backend = Backend(**kwrds)
def delete(
self, obj)
Delete the object instance - defer to backend.
def delete(self, obj):
"""Delete the object instance - defer to backend."""
self.backend.delete(obj)
def ensure_table(
self, cls)
Ensure the table exists - defer to backend.
def ensure_table(self, cls):
"""Ensure the table exists - defer to backend."""
return self.backend.ensure_table(cls)
def find_all(
self, cls)
Find all records - defer to backend.
def find_all(self, cls):
"""Find all records - defer to backend."""
return self.backend.find_all(cls)
def find_by_index(
self, cls, index_name, value)
Find records matching index query - defer to backend.
def find_by_index(self, cls, index_name, value):
"""Find records matching index query - defer to backend."""
return self.backend.find_by_index(cls, index_name, value)
def find_one(
self, cls, id)
Find one record - defer to backend.
def find_one(self, cls, id):
"""Find one record - defer to backend."""
return self.backend.find_one(cls, id)
def save(
self, obj)
Save the object instance - defer to backend.
def save(self, obj):
"""Save the object instance - defer to backend."""
self.backend.save(obj)