Source code for surf.session

# Copyright (c) 2009, Digital Enterprise Research Institute (DERI),
# NUI Galway
# All rights reserved.

# author: Cosmin Basca
# email: cosmin.basca@gmail.com

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#    * Redistributions of source code must retain the above copyright
#      notice, this list of conditions and the following disclaimer.
#    * Redistributions in binary form must reproduce the above copyright
#      notice, this list of conditions and the following disclaimer
#      in the documentation and/or other materials provided with
#      the distribution.
#    * Neither the name of DERI nor the
#      names of its contributors may be used to endorse or promote
#      products derived from this software without specific prior
#      written permission.

# THIS SOFTWARE IS PROVIDED BY DERI ''AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DERI BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
# OF THE POSSIBILITY OF SUCH DAMAGE.

# -*- coding: utf-8 -*-
__author__ = 'Cosmin Basca'

import new

from surf.rdf import BNode, URIRef
from surf.resource import Resource
from surf.store import Store, NO_CONTEXT
from surf.util import DE_CAMEL_CASE_DEFAULT
from surf.util import attr2rdf, de_camel_case, is_uri, uri_to_classname

'''
TODO:
    Come to a resolution regarding the metaclass conflict
    for now classes that extend the Resource must have no metaclasses of 
    their own.
    
    Q: is it a good idea the generate a sublclass of all meta?
    or should the only meta to be used be ResourceMeta ?
    what are the implications ?

    from noconflict import classmaker
'''

__all__ = ['Session']

DEFAULT_RESOURCE_EXPIRE_TIME = 60 * 60
DEFAULT_STORE_KEY = 'default'

[docs]class Session(object): """ The `Session` will manage the rest of the components in **SuRF**, it also acts as the type factory for surf, the resources will walk the graph in a lazy manner based on the session that they are bound to (the last created session). """ # TODO: add cache def __init__(self, default_store = None, mapping = {}, auto_persist = False, auto_load = False): """ Create a new `session` object that handles the creation of types and instances, also the session binds itself to the `Resource` objects to allow the Resources to access the data `store` and perform `lazy loading` of results. .. note:: The `session` object *behaves* like a `dict` when it comes to managing the registered `stores`. """ self.mapping = mapping self.__auto_persist = auto_persist self.__auto_load = auto_load #self.__use_cached = use_cached #self.__cache_expire = cache_expire self.__stores = {} if default_store: if type(default_store) is not Store: raise Exception('the arguments is not a valid Store instance') self.default_store = default_store # Emulate a dict for the sessions stores. def __len__(self): """ Total number of `stores` managed by the session. """ return len(self.__stores) def __getitem__(self, key): """ Return the `store` associated with the key. """ return self.__stores[key] def __setitem__(self, key, value): """ Set the `store` for the specified key, if value not a `Store` instance ignored. """ if type(value) is Store : self.__stores[key] = value def __delitem__(self, key): """ Remove the specified `store` from the management `session`. """ del self.__stores[key] def __iter__(self): """ `iterator` over the managed `stores`. """ return self.__stores.__iter__() def __reversed__(self): return self.__stores.__reversed__() def __contains__(self, item): """ True if the `item` is contained within the managed `stores`. """ return self.__stores.__contains__(item)
[docs] def keys(self): """ The `keys` that are assigned to the managed `stores`. """ return self.__stores.keys()
def set_auto_persist(self, val): """ Setter function for the `auto_persist` property. Do not use this, use the `auto_persist` property instead. """ if not isinstance(val, bool): val = False self.__auto_persist = val auto_persist = property(fget = lambda self: self.__auto_persist, fset = set_auto_persist) """ Toggle `auto_persistence` (no need to explicitly call `commit`, `resources` are persisted to the `store` each time a modification occurs) on or off. Accepts boolean values. """ def set_auto_load(self, val): """ Setter function for the `auto_load` property. Do not use it, use the `auto_load` property instead """ if not isinstance(val, bool): val = False self.__auto_load = val auto_load = property(fget = lambda self: self.__auto_load, fset = set_auto_load) """Toggle `auto_load` (no need to explicitly call `load`, `resources` are loaded from the `store` automatically on creation) on or off. Accepts boolean values. """ def get_enable_logging(self): """ Getter function for the `enable_logging` property. Do not use this, use the `enable_logging` property instead. """ for store in self.__stores: if not self.__stores[store].is_enable_logging(): return False return True def set_enable_logging(self, enable): """ Setter function for the `enable_logging` property. Do not use this, use the `enable_logging` property instead. """ for store in self.__stores: self.__stores[store].enable_logging(enable) enable_logging = property(fget = get_enable_logging, fset = set_enable_logging) """ Toggle `logging` on or off. Accepts boolean values. """ # TODO: add caching ... need strategies '''def set_use_cached(self,val): self.__use_cached = val if type(val) is bool else False use_cached = property(fget = lambda self: self.__use_cached, fset = set_use_cached) def set_cache_expire(self,val): try: self.__cache_expire = int(val) except TypeError: self.__cache_expire = DEFAULT_RESOURCE_EXPIRE_TIME cache_expire = property(fget = lambda self: self.__cache_expire, fset = set_cache_expire) ''' def get_default_store_key(self): """ Getter function for the `default_store_key` property. Do not use this, use the `default_store_key` property instead. """ if DEFAULT_STORE_KEY in self.__stores: return DEFAULT_STORE_KEY elif len(self.__stores) > 0: return self.__stores.keys()[0] return None default_store_key = property(fget = get_default_store_key) """ The `default store key` of the session. If it is set explicitly on `session` creation it is returned, else the first `store key` is returned. If no `stores` are in the session None is returned. """ def set_default_store(self, store): """ Setter function for the `default_store` property. Do not use this, use the `default_store` property instead. """ self.__setitem__(DEFAULT_STORE_KEY, store) def get_default_store(self): """ Getter function for the `default_store` property. Do not use this, use the `default_store` property instead. """ ds_key = self.default_store_key if ds_key: return self.__stores[ds_key] return None default_store = property(fget = get_default_store, fset = set_default_store) """ The `default store` of the session. See `default_store_key` to see how the `default store` is selected. """ def __uri(self, uri): """ For **internal** use only, convert the `uri` to a `URIRef`. """ if not uri: return None if type(uri) is URIRef: return uri else: if not is_uri(uri): attrname = de_camel_case(uri, '_', DE_CAMEL_CASE_DEFAULT) uri, _ = attr2rdf(attrname) return URIRef(uri)
[docs] def close(self): """ Close the `session`. .. note:: It is good practice to close the `session` when it's no longer needed. Remember: upon closing session all resources will lose the ability to reference the session thus the store and the mapping. """ for store in self.__stores: self.__stores[store].close() del self.__stores[store] self.mapping = None
[docs] def map_type(self, uri, store = None, *classes): """ Create and return a `class` based on the `uri` given. Also will add the `classes` to the inheritance list. """ if store is None: store = self.default_store_key uri = self.__uri(uri) if not uri: return None name = uri_to_classname(uri) base_classes = [Resource] base_classes.extend(classes) # Also take classes from session.mapping session_classes = self.mapping.get(uri, []) if type(session_classes) not in [list, tuple, set]: session_classes = [session_classes] base_classes.extend(session_classes) return new.classobj(str(name), tuple(base_classes), {'uri' : uri, 'store_key' : store, 'session' : self})
[docs] def get_class(self, uri, store = None, *classes): """ See :func:`surf.session.Session.map_type`. The `uri` parameter can be any of the following: - a `URIRef` - a `Resource` - a `string` of the form - a URI - a Resource class name eg: `SiocPost` - a namespace_symbol type string eg: `sioc_post` """ return self.map_type(uri, store, *classes)
[docs] def map_instance(self, concept, subject, store = None, classes = [], block_auto_load = False, context = None): """Create a `instance` of the `class` specified by `uri` and `classes` to be inherited, see `map_type` for more information. """ if not type(subject) in [URIRef, BNode]: subject = URIRef(unicode(subject)) if not store: store = self.default_store_key if not (isinstance(concept, type) and issubclass(concept, Resource)): concept = self.map_type(concept, store, *classes) return concept(subject, block_auto_load = block_auto_load, context = context)
[docs] def get_resource(self, subject, uri = None, store = None, graph = None, block_auto_load = False, context = None, *classes): """ Same as `map_type` but `set` the resource from the `graph`. """ if not isinstance(subject, URIRef): subject = URIRef(unicode(subject)) if uri is None: uri = Resource.concept(subject) resource = self.map_instance(uri, subject, store, classes, block_auto_load = block_auto_load, context = context) if graph: resource.set(graph) return resource
[docs] def load_resource(self, uri, subject, store = None, data = None, file = None, location = None, format = None, *classes): """ Create a `instance` of the `class` specified by `uri`. Also set the internal properties according to the ones by the specified source. """ resource = self.map_type(uri, store, *classes)(subject) resource.load_from_source(data = data, file = file, location = location, format = format) return resource
[docs] def commit(self): """ Commit all the changes, update all the `dirty` `resources`. """ # Copy set into list because it will shrink as we go through it for resource in list(Resource.get_dirty_instances()): resource.update()