Source code for rdfrest.cores.factory
# This file is part of RDF-REST <http://champin.net/2012/rdfrest>
# Copyright (C) 2011-2012 Pierre-Antoine Champin <pchampin@liris.cnrs.fr> /
# Universite de Lyon <http://www.universite-lyon.fr>
#
# RDF-REST is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RDF-REST 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RDF-REST. If not, see <http://www.gnu.org/licenses/>.
"""I provide a general core factory.
While :meth:`ICore.factory <rdfrest.interface.ICore.factory>` aims at
producing a resource of the *same kind* as the target, it may be necessary, in
some cases, to navigate a link to a resource of another kind:
* from a local resource to a remote one
* from a remote resource to a local one
* from a local resource to a local resource handled by another service
* from a remote resource to a remote one using a different protocol
For this, this module provides its own :func:`factory` function.
.. autofunction:: factory
But this function needs to know all the implementations of
:class:`.interface.ICore` and all implemented
`services <.local.Service>`:class:. This is what :func:`register_implementation`
and :func:`register_service` are about, respectively.
.. autofunction:: register_implementation
.. autofunction:: register_service
.. autofunction:: unregister_service
Note that this module automatically registers all the implementations shipped
with rdfrest; furthermore, :class:`.local.Service` automatically registers all
its instances. However, you should check before you call :func:`factory` that:
* all the external implementations have been registered (this is usually done
by simply importing them, as :meth:`register_implementation` is meant to be
used as a class decorator);
* all the services you rely on have been instanciated.
"""
# implementation and services are stored in lexicographic order of the URI
# prefix they handle; by using a bisect search, we find the most specific
# implementation/service for a given URI.
from bisect import bisect, insort
from ..cores import ICore
from ..util import coerce_to_uri
_IMPL_REG_KEYS = []
_IMPL_REGISTRY = {}
[docs]def register_implementation(uri_prefix):
"""Registers a subclass of :class:`.interface.ICore`.
This is to be used as a decorator generator, as in::
@register_implementation("xtp://")
class XtpResource(rdfrest.interface.ICore):
'''Implementation of REST resource over the XTP protocol.'''
#...
:param str uri_prefix: the URI prefix that this implementation can handle
:return: the class decorator
The decorated class must implement
:meth:`factory <rdfrest.interface.ICore.factory>` as a class method.b
"""
uri_prefix = str(uri_prefix)
def decorator(cls):
"""Decorator created by :func:`register_implementation`"""
assert issubclass(cls, ICore)
assert cls.factory.im_self is cls, \
"%s.factory should be a classmethod" % cls.__name__
assert uri_prefix not in _IMPL_REGISTRY
_IMPL_REGISTRY[uri_prefix] = cls.factory
insort(_IMPL_REG_KEYS, uri_prefix)
return cls
return decorator
[docs]def register_service(service):
"""Register a `.local.Service`:class:.
NB: this need normally not be called directly, as
:meth:`.local.Serice.__init__` already does it.
"""
assert isinstance(service, rdfrest.cores.local.Service)
assert service.root_uri not in _IMPL_REGISTRY
_IMPL_REGISTRY[service.root_uri] = service.get
insort(_IMPL_REG_KEYS, service.root_uri)
[docs]def unregister_service(service):
"""Unregister a `.local.Service`:class:.
NB: this beed normally not be called directlt, as
:meth:`.local.Serice.__del__` already does it.
"""
assert isinstance(service, rdfrest.cores.local.Service)
if service.root_uri in _IMPL_REGISTRY:
assert _IMPL_REGISTRY[service.root_uri] == service.get
del _IMPL_REGISTRY[service.root_uri]
i = bisect(_IMPL_REG_KEYS, service.root_uri) - 1
assert _IMPL_REG_KEYS[i] is service.root_uri
del _IMPL_REG_KEYS[i]
[docs]def factory(uri, rdf_types=None, _no_spawn=False):
"""I return an instance for the resource identified by `uri`.
This module searches all registered implementations for an appropriate one.
If none is found (or if the implementation does not recognize the URI),
None will be returned.
If ``rdf_types`` is provided, the returned instance will inherit
all the `registered <register_wrapper>`:meth: mix-in classes
corresponding to those types.
:param uri: the URI of the resource to instanciate
:type uri: basestring
:param rdf_types: if provided, a list of expected RDF types of the resource
:type rdf_types: list of :class:`rdflib.term.URIRef`
:param _no_spawn: if True, only *pre-existing* python objects will be
returned (may not be honnored by all implementations)
:type _no_spawn: bool
:rtype: :class:`.interface.ICore`
When using this function, it is a good practice to indicate the expected
return type, either informally (with a comment) or formally, with a
statement of the form::
assert isinstance(returned_object, expected_class)
Note that the expected class will usually be an abstract class (a
`registered <register_wrapper>`:func: mix-in class) rather than a specific
implementation.
"""
uri = coerce_to_uri(uri)
match = ""
for i in _IMPL_REG_KEYS:
if uri.startswith(i) and len(i) > len(match):
match = i
if match:
return _IMPL_REGISTRY[match](uri, rdf_types, _no_spawn)
else:
return None
# ensure all shipped implementations are registered
import rdfrest.cores.http_client # unused import #pylint: disable=W0611
# needed by some assertions
import rdfrest.cores.local