Source code for PyOpenWorm.identifier_mixin

from __future__ import print_function
from rdflib.term import URIRef
from six.moves.urllib.parse import quote
from six import string_types
import hashlib
import logging
from yarom.graphObject import IdentifierMissingException


__all__ = ['IdMixin']
# Dictionary of previously created mixins
_IdMixins = dict()

L = logging.getLogger(__name__)


[docs]def IdMixin(typ=object, hashfunc=None): """ Mixin that provides common identifier logic Parameters ---------- typ : type The type of object to use as the hash function's super class. Defaults to 'object' hashfunc : function The function to use for encoding data provided to make_identifier. Should return an object can ``.encode()`` to a :py:class:`bytes` (a.k.a. :py:class:`str` in Python 2). Defaults to :py:func:`hashlib.sha224` """ res = _IdMixins.get((id(typ), hashfunc), None) if res is None: class _IdMixin(typ): hashfun = hashfunc if hashfunc is not None else hashlib.sha224 def __init__(self, ident=None, key=None, *args, **kwargs): super(_IdMixin, self).__init__(*args, **kwargs) if key is not None and ident is not None: raise Exception("Only one of 'key' or 'ident' can be given to Context") if ident is not None: self._id = URIRef(ident) else: # Randomly generate an identifier if the derived class can't # come up with one from the start. Ensures we always have something # that functions as an identifier self._id = None self._key = None if key is not None: self.set_key(key) @classmethod def make_identifier(cls, data): ''' Makes an identifier based on this class' namespace by calling __str__ on the data and passing to the class' hashfunc. If the __str__ for data's type doesn't function as an identifier, you should use either :meth:`make_identifier_direct` or override :meth:`identifier_augment` and :meth:`defined_augment` ''' strdata = str(data) if strdata: hsh = "a" + cls.hashfun(strdata.encode()).hexdigest() return URIRef(cls.rdf_namespace[hsh]) else: raise ValueError('Cannot use falsy value' ' {} to make an identifier'.format(strdata)) @classmethod def make_identifier_direct(cls, string): if not isinstance(string, string_types): raise ValueError('make_identifier_direct only accepts strings') return URIRef(cls.rdf_namespace[quote(string)]) @property def key(self): return self._key @key.setter def key(self, key): self.set_key(key) def set_key(self, key): ''' Sets the identifier for this object based on the given key Equivalent to self.key = key ''' if isinstance(key, string_types): self._id = self.make_identifier_direct(key) else: self._id = self.make_identifier(key) self._key = str(key) @property def identifier(self): if self._id is not None: return self._id elif self.defined_augment(): return self.identifier_augment() else: raise IdentifierMissingException(self) def identifier_augment(self): """ Override this method to define an identifier in lieu of one explicity set. One must also override :meth:`defined_augment` to return True whenever this method could return a valid identifier. :exc:`~yarom.graphObject.IdentifierMissingException` should be raised if an identifier cannot be generated by this method. Raises ------ IdentifierMissingException """ raise IdentifierMissingException(self) @property def defined(self): if self._id is not None: return True else: return self.defined_augment() def defined_augment(self): """ This fuction must return False if :meth:`identifier_augment` would raise an :exc:`~yarom.graphObject.IdentifierMissingException`. Override it when defining a non-standard identifier for subclasses of DataObjects. """ return False _IdMixins[(id(typ), hashfunc)] = _IdMixin res = _IdMixin return res