alkali package

alkali.database module

from alkali import Database, JSONStorage, Model, fields

class MyModel( Model ):
    id = fields.IntField(primary_key=True)
    title = fields.StringField()

db = Database(models=[MyModel], storage=JSONStorage, root_dir='/tmp', save_on_exit=True)

m = MyModel(id=1,title='old title')                      # adds model instance to MyModel.objects                    # creates /tmp/MyModel.json

db.load()                     # read /tmp/MyModel.json
m = MyModel.objects.get(pk=1) # do a search on primary key
m.title = "my new title"      # change the title
# don't need to call since the database "knows" about m
# is automatically called as db goes out of scope, save_on_exit==True
class alkali.database.Database(models=[], **kw)[source]

Bases: object

This is the parent object that owns and coordinates all the different classes and objects defined in this module.

  • _storage_type – default storage type for all models, defaults to
  • _root_dir – directory where all models are stored, defaults to current working directory
  • _save_on_exit – automatically save all models before Database object is destroyed. call explicitly if _save_on_exit is false.
  • models – a list of alkali.model.Model classes
  • kw
    • root_dir: default save path directory
    • save_on_exit: save all models to disk on exit
    • storage: default storage class for all models

property: return list of models in the database

Parameters:model_name – the name of the model, note: all model names are converted to lowercase
Return type:alkali.model.Model
get_filename(model, storage=None)[source]

get the filename for the specified model. allow models to specify their own filename or generate one based on storage class. prepend Database._root_dir.

eg. <_root_dir>/<model name>.<storage.extension>

  • model – the model name or model class
  • storage – the storage class, uses the database default if None

returns a filename path

Return type:


set_storage(model, storage=None)[source]

set the storage instance for the specified model

precedence is:

  1. passed in storage class
  2. model defined storage class
  3. default storage class of database (JSONStorage)
  • model – the model name or model class
  • storage (IStorage) – override model storage class
Return type: instance or None


get the storage instance for the specified model

Parameters:model – the model name or model class
Return instance or None

persistantly store all model data

Parameters:force (bool) – force store even if alkali.manager.Manager thinks data is clean

load all model data from disk

Parameters:storage (IStorage) – override model storage class

alkali.fields module

class alkali.fields.Field(field_type, **kw)[source]

Bases: object

Base class for all field types. it tries to hold all the functionality so derived classes only need to override methods in special circumstances.

Field objects are instantiated during model creation. i = IntField()

All Model instances share the same instantiated Field objects in their Meta class. ie: id(MyModel().Meta.fields['i']) == id(MyModel().Meta.fields['i'])

Fields are python descriptors (@property is also a descriptor). So when a field is get/set the actual value is stored in the parent model instance.

The actual Field() object is accessable via model().Meta.fields[field_name] or via dynamic lookup of <field_name>__field. eg. m.email__field.

  • field_type (str/int/float/etc) – the type this field should hold
  • kw
    • primary_key: is this field a primary key of parent model
    • indexed: is this field indexed (not implemented yet)

property: return type of this field (int, str, etc)


property: return list of possible Field properties


property: what value does this Field default to during model instantiation


Whenever a field value is set, the given value passes through this (or derived class) function. This allows validation plus helpful conversion.

int_field = “1” # converts to int(“1”) date_field = “Jan 1 2017” # coverted to datetime()

called during json serialization, if json module is unable to deal with given Field.field_type, convert to a known type here.


called during json serialization, if json module is unable to deal with given Field.field_type, convert to a known type here.

class alkali.fields.IntField(**kw)[source]

Bases: alkali.fields.Field


IntField implements auto_increment, useful for a primary_key. The value is incremented during model instantiation.

class alkali.fields.BoolField(**kw)[source]

Bases: alkali.fields.Field

class alkali.fields.FloatField(**kw)[source]

Bases: alkali.fields.Field

class alkali.fields.StringField(**kw)[source]

Bases: alkali.fields.Field

holds a unicode string

class alkali.fields.DateTimeField(**kw)[source]

Bases: alkali.fields.Field


make sure date always has a time zone

class alkali.fields.SetField(**kw)[source]

Bases: alkali.fields.Field

class alkali.fields.ForeignKey(foreign_model, **kw)[source]

Bases: alkali.fields.Field

A ForeignKey is a special type of field. It stores the same value as a primary key in another field. When the model gets/sets a ForeignKey the appropriate lookup is done in the remote manager to return the remote instance.

  • foreign_model (alkali.model.Model) – the Model that this field is referencing
  • kw
    • primary_key: is this field a primary key of parent model
Return type:IField.field_type(), eg: IntField

given a pk, return foreign_model instance


return the primary_key value of the foreign model

class alkali.fields.OneToOneField(foreign_model, **kw)[source]

Bases: alkali.fields.ForeignKey

  • foreign_model (alkali.model.Model) – the Model that this field is referencing
  • kw
    • primary_key: is this field a primary key of parent model

alkali.manager module

interface alkali.manager.IManager[source]
class alkali.manager.Manager(model_class)[source]

Bases: object

the Manager class is the parent/owner of all the alkali.model.Model instances. Each Model has it’s own manager. Manager could rightly be called Table.

Parameters:model_class (Model) – the model that we should store (not an instance)

property: number of model instances we’re holding


property: return all primary keys

Return type:list

property: return all model instances

Return type:list

property: return True if any model instances are dirty

Return type:bool
static sorter(elements, reverse=False)[source]

yield model instances in primary key order

  • elements (Manager.instances) – our instances
  • kw
    • reverse: return in reverse order
Return type:


save(instance, dirty=True, copy_instance=True)[source]

Copy instance into our collection. We make a copy so that caller can’t change its object and affect our version without calling save() again.

  • instance (Model) –
  • dirty – don’t mark us as dirty if False, used during loading

remove all instances of our models. we’ll be marked as dirty if we previously had model instances.

Note: this does not affect on-disk files until is called.


remove an instance from our models by calling del on it

Parameters:instance (Model) –
cb_delete_foreign(sender, instance)[source]

called when our foreign parent is about to be deleted

cb_create_foreign(sender, instance)[source]

called when our foreign parent (likely OneToOneField) is created

store(storage, force=False)[source]

save all our instances to storage

  • storage (Storage) – an instance
  • force (bool) – force save even if we’re not dirty

load all our instances from storage

Parameters:storage (Storage) – an instance
Raises:KeyError – if there are duplicate primary keys
get(*pk, **kw)[source]

perform a query that returns a single instance of a model

  • pk (value or tuple if multi-pk) – optional primary key
  • kw – optional field_name=value
Return type:

single alkali.model.Model instance

  • DoesNotExist – if 0 instances returned
  • MultipleObjectsReturned – if more than 1 instance returned
m = MyModel.objects.get(1)      # equiv to
m = MyModel.objects.get(pk=1)

m = MyModel.objects.get(some_field='a unique value')
m = MyModel.objects.get(field1='a unique', field2='value')

alkali.metamodel module

class alkali.metamodel.MetaModel[source]

Bases: type

do not use this class directly

code reviews of this class are very welcome

base class for alkali.model.Model.

this complicated metaclass is required to convert a stylized class into a useful concrete one. it converts alkali.fields.Field variables into their base types as attributes on the instantiated class.

Meta: adds a Meta class if not already defined in Model derived class

objects: alkali.manager.Manager

alkali.model module

exception alkali.model.ObjectDoesNotExist[source]

Bases: exceptions.Exception

base class for a model specific exception (eg. MyModel.DoesNotExist) raised when a query yields no results

class alkali.model.Model(*args, **kw)[source]

Bases: object

main class for the database.

the definition of this class defines a table schema but instances of this class hold a row.

model fields are available as attributes. eg. m.my_field = 'foo'

the Django docs at will be fairly relevant to alkali

see alkali.database for some example code

set_field(field, value)[source]

set a field value, this method is automatically called when setting a field value. safe to call externally.

fires alkali.signals.field_update for any listeners

  • field (alkali.fields.Field) – instance of Field
  • value (Field.field_type) – the already-cast value to store

property: return True if our fields have changed since creation


property: a string that quickly shows the fields and types


property: returns this models primary key value. If the model is comprised of serveral primary keys then return a tuple of them.

Return type:Field.field_type or tuple-of-Field.field_type

property: returns a dict of all the fields, the fields are json consumable

Return type:OrderedDict

property: returns json that holds all the fields

Return type:str

add ourselves to our alkali.manager.Manager and mark ourselves as no longer dirty.

it’s up to our Manager to persistently save us

alkali.peekorator module

from, slightly modified by Kurt Neufeld

Generic Peekorator, modeled after next(), for “looking into the future” of a generator/iterator.


class alkali.peekorator.PeekoratorDefault[source]

Bases: object

alkali.peekorator.peek(peekorator, n=0, default=<alkali.peekorator.PeekoratorDefault object>)[source]

next()-like function to be used with a Peekorator

  • peekorator – Peekorator to use
  • n (int) – Number of items to look ahead
  • default – If the iterator is exhausted then a default is given, raise StopIteration if not given
class alkali.peekorator.Peekorator(generator)[source]

Bases: object

Wrap a generator (or iterator) and allow the ability to peek at the next element in a lazy fashion. If the user never uses peek(), then the only cost over a regular generator is the proxied function call.

Parameters:generator – a generator or iterator that will be iterated over

Return the peeked element for the generator

Parameters:n (int) – how many iterations into the future to peek

Get the next result from the generator


if you just got the first element then return True

Return type:bool

if you’re about to get the last element then return True

Return type:bool

alkali.query module

from alkali import Database, Model

class MyModel( Model ):
    id = fields.IntField(primary_key=True)
    title = fields.StringField()

db = Database( models=[MyModel] )

# create 10 instances and save them
for i in range(10):
    MyModel(id=i, title='number %d' % i).save()

assert MyModel.objects.count == 10
assert MyModel.objects.filter(id__gt=5).count == 4
assert MyModel.objects.filter(id__gt=5, id__le=7).count == 2
assert MyModel.objects.get(pk=1).title == 'number 1'
assert MyModel.objects.order_by('id')[0].id == 0
assert MyModel.objects.order_by('-id')[0].id == 9
class alkali.query.Aggregate(field)[source]

Bases: object

A reducing function that returns a single value

Parameters:str (field) –
class alkali.query.Count(field)[source]

Bases: alkali.query.Aggregate

number of objects in query

Parameters:str (field) –
class alkali.query.Sum(field)[source]

Bases: alkali.query.Aggregate

sum of given field (numeric field required)

Parameters:str (field) –
class alkali.query.Max(field)[source]

Bases: alkali.query.Aggregate

largest field (numeric field required)

Parameters:str (field) –
class alkali.query.Min(field)[source]

Bases: alkali.query.Aggregate

smallest field (numeric field required)

Parameters:str (field) –
class alkali.query.Query(manager)[source]

Bases: object

this class performs queries on manager instances returns lists of model instances

this class is one of the main reasons to use alkali

the Django docs at will be fairly relevant to alkali, except for anything related to foreign or many2many fields.

this is an internal class so you shouldn’t have to create it directly. create via Manager. MyModel.objects

Parameters:manager (Manager) –

property: number of model instances we are currently tracking


property: helper function to get dict of model fields

Return type:dict

property: return our managers model class


property: return our model field names

Return type:list of str
Parameters:kwfield_name__op=value, note: field_name can be a property
Return type:Query

perform a query, keeping model instances that pass the criteria specified in the kw parameter.

see example code above. see Django page for very thorough docs on this functionality. basically, its field_name ‘__’ operation = value.

# field/property f is 'foo' or 'bar'
MyModel.objects.filter( f__in=['foo','bar'] )

# 'foo' is in field/property myset
MyModel.objects.filter( myset__rin='foo' )

change order of self.instances

Parameters:fields (str) – field names, prefixed with optional ‘-‘ to indicate reverse order
Return type:Query

warning: because this isn’t a real database and we don’t have grouping, passing in multiple fields will very possibly sort on the last field only. python sorting is stable however, so a multiple field sort may work as intended.


returns a dict of distinct values and Query objects

Parameters:field – field name
Return type:dict

{ 's1': <Query MyModel(1), MyModel(3)>
  's2': <Query MyModel(2)> }

return first(+) or last(-) n elements

this has to be the last call during a query since it returns a list of instances and not a Query. passing in 0 is a no-op and returns all instances

Parameters:n (int) – non-zero integer
Return type:list

return first object from query, depends on ordering raise if query is empty


returns list of dicts, each sub-list contains (field_name, field_value)

Return type:list of OrderedDict
# [ OrderedDict([('int_type', 10), ('str_type', u'hi')]),
#   OrderedDict([('int_type', 11), ('str_type', u'hi')]) ]
values_list(*fields, **kw)[source]

returns nested list of values in given fields order

if flat=True, returns single list

  • fields (str) – field names or all fields if empty
  • kw (bool) – flat


see alkali.query.Query.annotate() for example


does the current query hold any elements

Return type:int
aggregate(*args, **kw)[source]

Aggregate (aka reduce) the query via the given function. Each callable Aggregate object takes a field/property name as a parameter.

The returned dictionary has key <field_name>__<agg function> unless keyword is given.

  • args (Aggregate) – Count Sum Max Min
  • kwkey_value=Aggregate, note: field_name can be a property
Return type:


MyModel.objects.aggregate( the_count=Count('id'), Sum('size') )
# { 'the_count': 12, 'size__sum': 24957 }

add a variable to each model instance currently in the query

each element in query is passed into function.

Parameters:kw – variable=function
Return type:Query
c = itertools.count()
Counter = lambda elem, c=c:
# [[10, 1], [11, 2]]
# [[10, 3], [11, 4]]

returns a list of lists, each sub-list contains distinct values of the given field

Parameters:fields (str) – field names
Return type:list
MyModel(int_type=10, str_type='hi').save()
MyModel(int_type=11, str_type='hi').save()
MyModel(int_type=12, str_type='there').save()

# [[10, 11, 12], [u'there', u'hi']]

# [[u'there', u'hi']]

alkali.relmanager module

interface alkali.relmanager.IRelManager[source]
class alkali.relmanager.RelManager(foreign, child_class, child_field)[source]

Bases: object

This is an internal class that a user of alkali unlikely to use directly.

The RelManager class manages queries/connections between two models that have a alkali.fields.ForeignKey (or equivalent) field.

  • foreign (Model) – instance of the model that is pointed at
  • child_class (Model) – the model class that contains the ForeignKey
  • child_field (str) – the field name that points to ForeignKey

get all objects that point to this instance see alkali.manager.Manager.all() for syntax

Return type:alkali.query.Query

get a single object that refers to this instance see alkali.manager.Manager.get() for syntax

Return type:alkali.model.Model

alkali.signals module module


Bases: exceptions.Exception

the exception that is thrown when a storage instance tries and fails to lock its data file


class level attr of desired filename extension. eg. json


yield (or return a list) of instantiated model_class objects or dicts up to implementer but likely you want to read filename


accept an iterator that yields elements up to implementer but likely you want to write out to filename

class*args, **kw)[source]

Bases: object

helper base class for the Storage object hierarchy

class, *args, **kw)[source]


this helper class determines the on-disk representation of the database. it could write out objects as json or plain txt or binary, that’s up to the implementation and should be transparent to any models/database.

extension = 'raw'

helper function that just reads a file

class, *args, **kw)[source]


save models in json format

extension = 'json'
class, *args, **kw)[source]


load models in csv format

first line assumed to be column headers (aka: field names)

use remap_fieldnames to change column headers into model field names

extension = 'csv'
remap_fieldnames(model_class, row)[source]

example of remap_fieldnames that could be defined in derived class or as a stand-alone function.

warning: make sure your header row that contains field names has no spaces in it

def remap_fieldnames(self, model_class, row):
    fields = model_class.Meta.fields.keys()

    for k in row.keys():
        results_key = k.lower().replace(' ', '_')

        if results_key not in fields:
            if k == 'Some Wierd Name':
                results_key = 'good_name'
                raise RuntimeError( "unknown field: {}".format(k) )

        row[results_key] = row.pop(k)

    return row

warning: if remap_fieldnames changes names then saved file will have a different header line than original file