[conduit/tracker] Use tralchemy master



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]