[conduit/tracker] Use tralchemy master
- From: John Carr <johncarr src gnome org>
- To: svn-commits-list gnome org
- Subject: [conduit/tracker] Use tralchemy master
- Date: Fri, 3 Jul 2009 14:16:59 +0000 (UTC)
commit ea2387fb9cff2165f76f011d4633e0b60b36a0d7
Author: John Carr <john carr unrouted co uk>
Date: Fri Jul 3 13:09:52 2009 +0100
Use tralchemy master
.../modules/TrackerModule/tralchemy/__init__.py | 20 +++
conduit/modules/TrackerModule/tralchemy/core.py | 164 ++++++++++++++++----
.../modules/TrackerModule/tralchemy/namespace.py | 87 ++++++++---
3 files changed, 216 insertions(+), 55 deletions(-)
---
diff --git a/conduit/modules/TrackerModule/tralchemy/__init__.py b/conduit/modules/TrackerModule/tralchemy/__init__.py
index acbde4b..c52a725 100644
--- a/conduit/modules/TrackerModule/tralchemy/__init__.py
+++ b/conduit/modules/TrackerModule/tralchemy/__init__.py
@@ -1,3 +1,23 @@
+# __init__.py
+#
+# Copyright (C) 2009, Codethink Ltd.
+#
+# This library 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 2.1 of the License, or (at your option) any later version.
+#
+# This library 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 this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author:
+# John Carr <john carr unrouted co uk>
from .namespace import install_importhook
install_importhook()
diff --git a/conduit/modules/TrackerModule/tralchemy/core.py b/conduit/modules/TrackerModule/tralchemy/core.py
index 6f909fa..f61e203 100644
--- a/conduit/modules/TrackerModule/tralchemy/core.py
+++ b/conduit/modules/TrackerModule/tralchemy/core.py
@@ -1,3 +1,23 @@
+# core.py
+#
+# Copyright (C) 2009, Codethink Ltd.
+#
+# This library 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 2.1 of the License, or (at your option) any later version.
+#
+# This library 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 this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author:
+# John Carr <john carr unrouted co uk>
import dbus
import uuid
@@ -17,16 +37,15 @@ def tracker_update(sparql):
return tracker.SparqlUpdate(sparql)
# Map tracker prefixes to ontology namespaces and back
-prefix_to_ns = {}
-ns_to_prefix = {}
+namespaces = {}
for prefix, namespace in tracker_query("SELECT ?prefix, ?ns WHERE { ?ns tracker:prefix ?prefix }"):
- prefix_to_ns[prefix] = namespace
- ns_to_prefix[namespace] = prefix
+ namespaces[namespace] = prefix
+del prefix, namespace
def get_classname(classname):
""" Takes a classname and flattens it into tracker form """
if classname.startswith("http://"):
- for ns, prefix in ns_to_prefix.iteritems():
+ for ns, prefix in namespaces.iteritems():
if classname.startswith(ns):
return prefix + ":" + classname[len(ns):]
return classname
@@ -50,10 +69,12 @@ class Resource(object):
""" Everything is a resource """
_type_ = "rdfs:Resource"
+ __module__ = "tralchemy.rdfs"
def __init__(self, uri):
self.uri = get_classname(uri)
self.triples = {}
+ self.cache = {}
def properties(self):
uri = self.uri
@@ -82,7 +103,7 @@ class Resource(object):
if k == "uid" or k =="commit":
continue
setattr(o, k, v)
- if not 'commit' in kwargs or kwargs['commit'] == True:
+ if not 'commit' in kwargs or kwargs['commit']:
o.commit()
return o
@@ -103,19 +124,93 @@ class Resource(object):
self.triples = {}
+class PropertyList(object):
+
+ def __init__(self, uri, range, instance):
+ self.uri = uri
+ self._range_ = range
+ self.instance = instance
+ self.vals = []
+
+ instance_uri = instance.uri
+ if instance_uri.startswith("http://"):
+ instance_uri = "<%s>" % instance_uri
+
+ # Get all items of this type
+ for result in tracker_query("SELECT ?o WHERE { %s %s ?o }" % (instance_uri, uri)):
+ result = result[0]
+ self.vals.append(types.get_class(range)(result))
+
+ def append(self, value):
+ if isinstance(value, Resource):
+ if value.uri.startswith("http://"):
+ eval = "<%s>" % value.uri
+ else:
+ eval = value.uri
+ else:
+ eval = '"%s"' % value
+
+ self.instance.triples.setdefault(self.uri, []).append(eval)
+ self.vals.append(value)
+
+ def __delitem__(self, idx):
+ raise NotImplementedError
+
+ def __setitem__(self, idx, value):
+ raise NotImplementedError
+
+ def __getitem__(self, idx):
+ return self.vals[idx]
+
+ def __len__(self):
+ return len(self.vals)
+
+ def __iter__(self):
+ return iter(self.vals)
+
+ def __repr__(self):
+ return repr(self.vals)
+
+ def __str__(self):
+ return str(self.vals)
+
+
class Property(Resource, property):
_type_ = "rdf:Property"
+ __module__ = "tralchemy.rdf"
- def __init__(self, uri, doc=""):
+ def __init__(self, uri):
super(Property, self).__init__(uri)
- if self.uri != 'rdfs:comment' and self.comment:
- self.__doc__ = "%s\n\n type: %s" % (self.comment, get_classname(self.range))
+
+ sparql = "SELECT ?max ?range ?comment WHERE { " \
+ "%s a rdf:Property . " \
+ "OPTIONAL { %s nrl:maxCardinality ?max } . " \
+ "OPTIONAL { %s rdfs:range ?range } . " \
+ "OPTIONAL { %s rdfs:comment ?comment } " \
+ "}"
+
+ results = tracker_query(sparql % (uri, uri, uri, uri))
+ for result in results:
+ self.maxcardinality = int(result[0]) if result[0] else None
+ self._range_ = result[1]
+ self.__doc__ = "%s\n\n type: %s" % (result[2], get_classname(self._range_))
+
+ def is_list(self):
+ return not self.maxcardinality or self.maxcardinality > 1
def __get__(self, instance, instance_type):
if instance is None:
return self
+ # Just so pychecker doesnt moan
+ assert instance_type
+
+ if self.is_list():
+ if not self.uri in instance.cache:
+ instance.cache[self.uri] = PropertyList(self.uri, self._range_, instance)
+ return instance.cache[self.uri]
+
uri = instance.uri
if uri.startswith("http://"):
uri = "<%s>" % uri
@@ -127,11 +222,15 @@ class Property(Resource, property):
if self.uri == "rdfs:range":
return str(result)
else:
- return types.get_class(self.range)(result)
+ return types.get_class(self._range_)(result)
def __set__(self, instance, value):
if instance is None:
return
+
+ if self.is_list():
+ raise ValueError("Cannot assign directly to a list property")
+
if isinstance(value, Resource):
if value.uri.startswith("http://"):
instance.triples[self.uri] = "<%s>" % value.uri
@@ -161,8 +260,9 @@ Property.transient = Property("tracker:transient")
class Class(Resource):
_type_ = "rdfs:Class"
+ __module__ = "tralchemy.rdfs"
- subclassof = Property("rdfs:subClassOf")
+ subClassOf = Property("rdfs:subClassOf")
notify = Property("tracker:notify")
@@ -195,12 +295,15 @@ class WrapperFactory(object):
if classname in self.wrapped:
return self.wrapped[classname]
+ ns, name = classname.split(":")
+
# Look up this class in tracker
cls = Class(classname)
attrs = {
"_type_": cls.uri,
- "__doc__": cls.comment or ""
+ "__doc__": cls.comment or "",
+ "__module__": "tralchemy.%s" % ns,
}
baseclass = []
@@ -210,17 +313,33 @@ class WrapperFactory(object):
if not baseclass:
baseclass.append(Resource)
+ # An ontology might well have a pointless inheritenance chain
+ # For example nmo:Message(TextDocument, InformationElement) - TextDocument is already an InfoEle!
+ # Lets ignore the pointless inheritance and reduce chances of MRO related fail
+ filtered = []
+ for j in baseclass:
+ for k in baseclass:
+ if j != k and issubclass(k, j):
+ break
+ else:
+ filtered.append(j)
+
# Does this class have notifications?
if cls.notify:
attrs['notifications'] = Notifications(cls.uri)
# Enumerate all properties of this class
for prop in Property.get(domain=cls.uri):
- if prop.label:
+ if len(prop.uri.split(":")) == 2:
+ attrs[prop.uri.split(":")[1]] = prop
+ elif len(prop.uri.split("#")) == 2:
+ attrs[prop.uri.split("#")[1]] = prop
+ elif prop.label:
+ # shouldn't ever get here?
attrs[prop.label.lower().replace(" ", "_")] = prop
# Make a new class
- klass = type(str(classname), tuple(baseclass), attrs)
+ klass = type(str(name), tuple(filtered), attrs)
# Cache it for later
self.wrapped[klass._type_] = klass
@@ -229,20 +348,3 @@ class WrapperFactory(object):
types = WrapperFactory()
-if __name__ == "__main__":
- #help(w.get_class("rdfs:Resource"))
- #help(w.get_class("rdfs:Class"))
- self = __import__(__name__)
- for cls in Class.get():
- cls = types.get_class(cls.uri)
- setattr(self, cls.__name__, cls)
- help(self)
-
- foo = {}
- for p in Property.get():
- foo.setdefault(p.range, 0)
- foo[p.range] += 1
-
- for k, v in foo.items():
- print k, v
-
diff --git a/conduit/modules/TrackerModule/tralchemy/namespace.py b/conduit/modules/TrackerModule/tralchemy/namespace.py
index 7082d2f..b0c181d 100644
--- a/conduit/modules/TrackerModule/tralchemy/namespace.py
+++ b/conduit/modules/TrackerModule/tralchemy/namespace.py
@@ -1,32 +1,53 @@
+# namespace.py
+#
+# Copyright (C) 2009, Codethink Ltd.
+#
+# This library 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 2.1 of the License, or (at your option) any later version.
+#
+# This library 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 this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author:
+# John Carr <john carr unrouted co uk>
import os, sys
+from types import ModuleType
+from .core import types, namespaces
-class Namespace(object):
+class Namespace(ModuleType):
""" Class representing a tracker namespace """
- def __init__(self, namespace, path):
- self._namespace = namespace
- self._path = path
+ def __init__(self, loader):
+ self._loader = loader
@property
def __file__(self):
- return self._namespace
+ return self._loader.name
@property
def __name__(self):
- return self._namespace
+ return self._loader.name
@property
def __path__(self):
- return [os.path.dirname(self.__file__)]
+ return []
def __repr__(self):
- return "<namespace %r from %r>" % (self._namespace, self._path)
+ return "<namespace %r from %r>" % (self.__name__, self._path)
def __getattr__(self, name):
- from .core import WrapperFactory
- cls = WrapperFactory().get_class("%s:%s" % (self._namespace, name))
+ from .core import types
+ cls = types.get_class("%s:%s" % (self.__name__, name))
if not cls:
raise AttributeError("%r object has no attribute %r" % (
self.__class__.__name__, name))
@@ -34,14 +55,19 @@ class Namespace(object):
self.__dict__[name] = cls
return cls
- @property
- def __members__(self):
- r = []
- #FIXME: Return names of all objects defined in this namespace
- return r
+ def __dir__(self):
+ from .core import types
+ Class = types.get_class("rdfs:Class")
+ members = []
+ for cls in Class.get():
+ if cls.uri.startswith(self.__name__ + ":"):
+ members.append(cls.uri[len(self.__name__)+1:])
+ return members
+
+ __all__ = property(__dir__)
-class Importer(object):
+class NamespaceFinder(object):
""" Class to register with python so we can dynamically generate modules """
def __init__(self, name, path):
@@ -50,20 +76,33 @@ class Importer(object):
@staticmethod
def find_module(name, path=None):
- #FIXME: This function is a bit of a hack
- if not "tralchemy." in name:
+ names = name.split(".")
+
+ # We only support imports of tralchemy.<namespace_name>
+ if len(names) != 2 or names[0] != "tralchemy":
return None
- name = name[name.find("tralchemy.")+10:]
- if name in ('namespace', 'core', 'dbus', 'uuid', 'sys', 'dateutil'):
+
+ # To avoid pain misery and suffering, don't do anything clever for
+ # our interals..
+ if names[1] in ("core", "namespace"):
return None
- if '.' in name:
+
+ if not names[1] in namespaces.values():
return None
- return Importer(name, path)
+
+ return NamespaceLoader(names[1], path)
+
+
+class NamespaceLoader(object):
+
+ def __init__(self, name, path):
+ self.name = name
+ self.path = path
def load_module(self, name):
- return Namespace(self.name, self.path)
+ return Namespace(self)
def install_importhook():
- sys.meta_path.append(Importer)
+ sys.meta_path.append(NamespaceFinder)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]