[Checkins] SVN: megrok.rdf/trunk/ Initial code import

Jasper Op de Coul jasper at infrae.com
Thu Jul 31 11:46:31 EDT 2008


Log message for revision 89098:
  Initial code import
  

Changed:
  A   megrok.rdf/trunk/
  A   megrok.rdf/trunk/README.txt
  A   megrok.rdf/trunk/bootstrap.py
  A   megrok.rdf/trunk/buildout.cfg
  A   megrok.rdf/trunk/setup.py
  A   megrok.rdf/trunk/src/
  A   megrok.rdf/trunk/src/megrok/
  A   megrok.rdf/trunk/src/megrok/__init__.py
  A   megrok.rdf/trunk/src/megrok/rdf/
  A   megrok.rdf/trunk/src/megrok/rdf/README.txt
  A   megrok.rdf/trunk/src/megrok/rdf/__init__.py
  A   megrok.rdf/trunk/src/megrok/rdf/components.py
  A   megrok.rdf/trunk/src/megrok/rdf/directive.py
  A   megrok.rdf/trunk/src/megrok/rdf/interfaces.py
  A   megrok.rdf/trunk/src/megrok/rdf/meta.py
  A   megrok.rdf/trunk/src/megrok/rdf/meta.zcml
  A   megrok.rdf/trunk/src/megrok/rdf/tests.py

-=-
Added: megrok.rdf/trunk/README.txt
===================================================================
--- megrok.rdf/trunk/README.txt	                        (rev 0)
+++ megrok.rdf/trunk/README.txt	2008-07-31 15:46:30 UTC (rev 89098)
@@ -0,0 +1,4 @@
+Introduction
+============
+
+

Added: megrok.rdf/trunk/bootstrap.py
===================================================================
--- megrok.rdf/trunk/bootstrap.py	                        (rev 0)
+++ megrok.rdf/trunk/bootstrap.py	2008-07-31 15:46:30 UTC (rev 89098)
@@ -0,0 +1,55 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 75593 2007-05-06 21:11:27Z jim $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+try:
+    import pkg_resources
+except ImportError:
+    ez = {}
+    exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                         ).read() in ez
+    ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+    import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)

Added: megrok.rdf/trunk/buildout.cfg
===================================================================
--- megrok.rdf/trunk/buildout.cfg	                        (rev 0)
+++ megrok.rdf/trunk/buildout.cfg	2008-07-31 15:46:30 UTC (rev 89098)
@@ -0,0 +1,56 @@
+[buildout]
+develop = .
+parts = app data zopectl test
+newest = false
+extends = http://grok.zope.org/releaseinfo/grok-0.13.cfg
+versions = versions
+
+[app]
+recipe = zc.zope3recipes>=0.5.3:application
+eggs = megrok.rdf
+site.zcml = <include package="rdfexample" />
+            <include package="zope.app.twisted" />
+
+            <configure i18n_domain="rdfexample">
+              <unauthenticatedPrincipal id="zope.anybody"
+                                        title="Unauthenticated User" />
+              <unauthenticatedGroup id="zope.Anybody"
+                                    title="Unauthenticated Users" />
+              <authenticatedGroup id="zope.Authenticated"
+                                title="Authenticated Users" />
+              <everybodyGroup id="zope.Everybody"
+                              title="All Users" />
+              <principal id="zope.manager"
+                         title="Manager"
+                         login="admin"
+                         password_manager="Plain Text"
+                         password="admin"
+                         />
+
+              <!-- Replace the following directive if you don't want
+                   public access -->
+              <grant permission="zope.View"
+                     principal="zope.Anybody" />
+              <grant permission="zope.app.dublincore.view"
+                     principal="zope.Anybody" />
+
+              <role id="zope.Manager" title="Site Manager" />
+              <role id="zope.Member" title="Site Member" />
+              <grantAll role="zope.Manager" />
+              <grant role="zope.Manager"
+                     principal="zope.manager" />
+           </configure>
+
+[data]
+recipe = zc.recipe.filestorage
+
+# this section named so that the start/stop script is called bin/zopectl
+[zopectl]
+recipe = zc.zope3recipes:instance
+application = app
+zope.conf = ${data:zconfig}
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = megrok.rdf
+defaults = ['--tests-pattern', '^f?tests$', '-v']

Added: megrok.rdf/trunk/setup.py
===================================================================
--- megrok.rdf/trunk/setup.py	                        (rev 0)
+++ megrok.rdf/trunk/setup.py	2008-07-31 15:46:30 UTC (rev 89098)
@@ -0,0 +1,33 @@
+from setuptools import setup, find_packages
+import os
+
+version = '0.1'
+
+setup(name='megrok.rdf',
+      version=version,
+      description="RDF based DB support for Grok.",
+      long_description=open("README.txt").read(),
+      # Get more strings from http://www.python.org/pypi?%3Aaction=list_classifiers
+      classifiers=[
+        "Programming Language :: Python",
+        "Topic :: Software Development :: Libraries :: Python Modules",
+        ],
+      keywords='',
+      author='Grok Team',
+      author_email='grok-dev at zope.org',
+      url='',
+      license='ZPL',
+      packages=find_packages('src'),
+      package_dir = {'': 'src'},
+      namespace_packages=['megrok'],
+      include_package_data=True,
+      zip_safe=True,
+      install_requires=[
+          'setuptools',
+          'grok >= 0.13',
+          'RDFLib == 2.4.0'
+         ],
+      entry_points="""
+      # -*- Entry points: -*-
+      """,
+      )

Added: megrok.rdf/trunk/src/megrok/__init__.py
===================================================================
--- megrok.rdf/trunk/src/megrok/__init__.py	                        (rev 0)
+++ megrok.rdf/trunk/src/megrok/__init__.py	2008-07-31 15:46:30 UTC (rev 89098)
@@ -0,0 +1,6 @@
+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)

Added: megrok.rdf/trunk/src/megrok/rdf/README.txt
===================================================================
--- megrok.rdf/trunk/src/megrok/rdf/README.txt	                        (rev 0)
+++ megrok.rdf/trunk/src/megrok/rdf/README.txt	2008-07-31 15:46:30 UTC (rev 89098)
@@ -0,0 +1,202 @@
+==========
+megrok.rdf
+==========
+
+The ``megrok.rdb`` package adds powerful relational database support
+to Grok, based on the powerful SQLAlchemy_ library. It makes available
+a new ``megrok.rdb.Model`` and ``megrok.rdb.Container`` which behave
+much like ones in core Grok, but are instead backed by a relational
+database.
+
+.. _SQLAlchemy: http://www.sqlalchemy.org
+
+In this document we will show you how to use ``megrok.rdb``.
+
+``megrok.rdb`` uses SQLAlchemy's ORM system, in particular its
+declarative extension almost directly. ``megrok.rdb`` just supplies a
+few special base classes and directives to make things easier, and a few
+other conveniences that help with integration with Grok.
+
+We first import import the SQLAlchemy bits we'll need later::
+
+  >>> from sqlalchemy import Column, ForeignKey
+  >>> from sqlalchemy.types import Integer, String
+  >>> from sqlalchemy.orm import relation
+
+SQLAlchemy groups database schema information into a unit called
+``MetaData``. The schema can be reflected from the database schema, or
+can be created from a schema defined in Python. With ``megrok.rdb`` we
+typically do the latter, from within the content classes that they are
+mapped to using the ORM. We need to have some metadata to associate
+our content classes with.
+
+Let's set up the metadata object::
+
+  >>> from megrok import rdb
+  >>> metadata = rdb.MetaData()
+
+Now we'll set up a few content classes. We'll have a very simple
+structure where a (university) department has zero or more courses
+associated with it. First we'll define a container that can contain
+courses::
+
+  >>> class Courses(rdb.Container):
+  ...    rdb.key('name')
+
+As you can see, we need to set up the attribute on which the key is
+based. We will use the ``name`` attribute, which we will define for
+the course later. Note that using the primary key attribute (such as
+``id`` in this example) is not a good idea if you expect the database
+to generate unique ids itself - the correct id will not be known yet
+before the object is commit to the database.
+
+Now we can set up the ``Department`` class. This has the ``courses``
+relation that links to its courses::
+
+  >>> class Department(rdb.Model):
+  ...   rdb.metadata(metadata)
+  ...
+  ...   id = Column('id', Integer, primary_key=True)
+  ...   name = Column('name', String(50))
+  ... 
+  ...   courses = relation('Course', 
+  ...                       backref='department',
+  ...                       collection_class=Courses)
+
+This is very similar to the way you'd use
+``sqlalchemy.ext.declarative``, but there are a few differences::
+
+* we inherit from ``rdb.Model`` to make this behave like a Grok model.
+
+* We don't need to use ``__tablename__`` to set up the table name. By
+  default the table name will be the class name, lowercased, but you
+  can override this by using the ``rdb.tablename`` directive.
+
+* we need to make explicit the metadata object that is used. We do
+  this in the tests, though in Grok applications it's enough to use
+  the ``rdb.metadata`` directive on a module-level to have all rdb
+  classes automatically associated with that metadata object.
+
+* we mark that the ``courses`` relation uses the ``Courses`` container
+  class we have defined before. This is a normal SQLAlchemy feature,
+  it's just we have to use it if we want to use Grok-style containers.
+
+We finish up our database definition by defining the ``Course``
+class::
+
+  >>> class Course(rdb.Model):
+  ...   rdb.metadata(metadata)
+  ...
+  ...   id = Column('id', Integer, primary_key=True)
+  ...   department_id = Column('department_id', Integer, 
+  ...                           ForeignKey('department.id'))
+  ...   name = Column('name', String(50))
+
+We see here that ``Course`` links back to the department it is in,
+using a foreign key.
+
+We need to actually grok these objects to have them fully set
+up  Normally grok takes care of this automatically, but in this case
+we'll need to do it manually.
+
+First we grok this package's grokkers::
+
+  >>> from grok.testing import grok
+  >>> grok('megrok.rdb.meta')
+
+
+Before we grok stuff in the doctest, we need to make sure that
+``__file__`` exists on our "module"::
+
+  >>> __file__ = 'foo'
+
+
+Now we can grok the components::
+
+  >>> from grok.testing import grok_component
+  >>> grok_component('Courses', Courses)
+  True
+  >>> grok_component('Department', Department)
+  True
+  >>> grok_component('Course', Course)
+  True
+
+Once we have our metadata and object relational map defined, we need
+to have a database to actually put these in. While it is possible to
+set up a different database per Grok application, here we will use a
+single global database::
+
+  >>> TEST_DSN = 'sqlite:///:memory:'
+  >>> from z3c.saconfig import EngineFactory
+  >>> from z3c.saconfig.interfaces import IEngineFactory
+  >>> engine_factory = EngineFactory(TEST_DSN)
+
+We need to supply the engine factory as a utility. Grok can do this
+automatically for you using the module-level ``grok.global_utility``
+directive, like this::
+
+  grok.global_utility(engine_factory, provides=IEngineFactory, direct=True)
+
+In the tests we'll use the component architecture directly::
+
+  >>> from zope import component
+  >>> component.provideUtility(engine_factory, provides=IEngineFactory)
+
+Now that we've set up an engine, we can set up the SQLAlchemy session
+utility::
+
+  >>> from z3c.saconfig import GloballyScopedSession
+  >>> from z3c.saconfig.interfaces import IScopedSession
+  >>> scoped_session = GloballyScopedSession()
+
+With Grok, we'd register it like this::
+
+  grok.global_utility(scoped_session, provides=IScopedSession, direct=True)
+
+But again we'll just register it directly for the tests::
+
+  >>> component.provideUtility(scoped_session, provides=IScopedSession)
+
+We now need to create the tables we defined in our database::
+
+  >>> engine = engine_factory()  
+  >>> metadata.create_all(engine)
+
+Now all that is out the way, we can use the ``rdb.Session`` object to make
+a connection to the database.
+  
+  >>> session = rdb.Session()
+
+Let's now create a database structure. We have a department of philosophy::
+
+  >>> philosophy = Department(name="Philosophy")
+
+We need to manually add it to the database, as we haven't defined a
+particular ``departments`` container in our database::
+
+  >>> session.add(philosophy)
+
+The philosophy department has a number of courses::
+
+  >>> logic = Course(name="Logic")
+  >>> ethics = Course(name="Ethics")
+  >>> metaphysics = Course(name="Metaphysics")
+  >>> session.add_all([logic, ethics, metaphysics])
+
+We'll add them to the philosophy department's courses container. Since
+we want to leave it up to the database what the key will be, we will
+use the special ``set`` method that ``rdb.Container`` objects have to
+add the objects::
+
+  >>> philosophy.courses.set(logic)
+  >>> philosophy.courses.set(ethics)
+  >>> philosophy.courses.set(metaphysics)
+
+We can now verify that the courses are there::
+
+  >>> for key, value in sorted(philosophy.courses.items()):
+  ...     print key, value.name, value.department.name
+  Ethics Ethics Philosophy
+  Logic Logic Philosophy
+  Metaphysics Metaphysics Philosophy
+

Added: megrok.rdf/trunk/src/megrok/rdf/__init__.py
===================================================================
--- megrok.rdf/trunk/src/megrok/rdf/__init__.py	                        (rev 0)
+++ megrok.rdf/trunk/src/megrok/rdf/__init__.py	2008-07-31 15:46:30 UTC (rev 89098)
@@ -0,0 +1,6 @@
+from megrok.rdf.components import Model, MultiProperty, Property, rdftype_registry, ns, getModel, Container, ContainerProperty
+from megrok.rdf.directive import type, key
+
+from rdflib.Graph import Graph
+
+

Added: megrok.rdf/trunk/src/megrok/rdf/components.py
===================================================================
--- megrok.rdf/trunk/src/megrok/rdf/components.py	                        (rev 0)
+++ megrok.rdf/trunk/src/megrok/rdf/components.py	2008-07-31 15:46:30 UTC (rev 89098)
@@ -0,0 +1,104 @@
+import grok
+from grok.interfaces import IContainer
+from grokcore.component import Context
+from grok.interfaces import IContainer
+
+import megrok.rdf
+
+import rdflib
+
+# This dictionary is used by the rdf.type directive
+# to map rdf types to rdfmodel classes
+rdftype_registry = {}
+
+class ns(object):
+    rdf = rdflib.Namespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#")
+    zope = rdflib.Namespace("http://namespaces.zope.org/rdf#")
+
+def getModel(node):
+    graph = grok.getSite().graph
+    type = list(graph.objects(node, ns.rdf['type']))[0]
+    return rdftype_registry[unicode(type)](node)
+
+class Model(Context):
+    def __init__(self, subject):
+        self.subject = subject
+
+class MultiProperty(object):
+
+    def __init__(self, predicate):
+        self.predicate = predicate
+
+    def __get__(self, instance, type):
+        result = []
+        graph = grok.getSite().graph
+        for object in graph.objects(instance.subject, self.predicate):
+            if isinstance(object, rdflib.Literal):
+                result.append(unicode(object))
+            else:
+                result.append(getModel(object))
+        return result
+
+class Container(object):
+
+    grok.implements(IContainer)
+
+    def __init__(self, subject, predicate):
+        self.subject = subject
+        self.predicate = predicate
+        self.key = megrok.rdf.key.bind().get(self)
+
+    def keys(self):
+        graph = grok.getSite().graph
+        for object in graph.objects(self.subject,
+                                    self.predicate):
+            for name in graph.objects(object, self.key):
+                yield unicode(name)
+                break
+
+    def values(self):
+        graph = grok.getSite().graph
+        for object in graph.objects(self.subject, self.predicate):
+            yield getModel(object)
+
+    def items(self):
+        graph = grok.getSite().graph
+        for object in graph.objects(self.subject,
+                                    self.predicate):
+            for name in graph.objects(object, self.key):
+                yield (unicode(name), getModel(object))
+                break
+
+    def get(self, key, default=None):
+        try:
+            return self.__getitem__(key)
+        except KeyError:
+            return default
+
+    def __getitem__(self, key):
+        graph = grok.getSite().graph
+        for object in graph.objects(self.subject,
+                                    self.predicate):
+            for name in graph.objects(object, self.key):
+                if unicode(name) == key:
+                    return getModel(object)
+        raise KeyError
+
+class ContainerProperty(MultiProperty):
+
+    def __init__(self, predicate, container_class):
+        self.predicate = predicate
+        self.container_class = container_class
+
+    def __get__(self, instance, type):
+        return self.container_class(instance.subject,
+                                   self.predicate)
+
+class Property(MultiProperty):
+    def __get__(self, instance, type):
+        result = super(Property, self).__get__(instance, type)
+        if not len(result):
+            return None
+        return result[0]
+
+

Added: megrok.rdf/trunk/src/megrok/rdf/directive.py
===================================================================
--- megrok.rdf/trunk/src/megrok/rdf/directive.py	                        (rev 0)
+++ megrok.rdf/trunk/src/megrok/rdf/directive.py	2008-07-31 15:46:30 UTC (rev 89098)
@@ -0,0 +1,13 @@
+from martian import Directive, CLASS, CLASS_OR_MODULE, ONCE
+
+# XXX add proper validation logic
+
+class type(Directive):
+    scope = CLASS
+    store = ONCE
+    default = u''
+
+class key(Directive):
+    scope = CLASS
+    store = ONCE
+    default = u''

Added: megrok.rdf/trunk/src/megrok/rdf/interfaces.py
===================================================================
--- megrok.rdf/trunk/src/megrok/rdf/interfaces.py	                        (rev 0)
+++ megrok.rdf/trunk/src/megrok/rdf/interfaces.py	2008-07-31 15:46:30 UTC (rev 89098)
@@ -0,0 +1,20 @@
+from zope.interface import Interface
+
+from zope.app.container.interfaces import IContainer as IContainerBase
+
+class IContainer(IContainerBase):
+    def set(value):
+        """Add a new value to the container without having to specify the key.
+
+        Lets the container figure out an appropriate key.
+
+
+        Defined by SQLAlchemy dictionary-based collections.
+        """
+
+    def remove(value):
+        """Remove a value from the container, by value.
+
+        
+        Defined by SQLAlchemy dictionary-based collections.
+        """

Added: megrok.rdf/trunk/src/megrok/rdf/meta.py
===================================================================
--- megrok.rdf/trunk/src/megrok/rdf/meta.py	                        (rev 0)
+++ megrok.rdf/trunk/src/megrok/rdf/meta.py	2008-07-31 15:46:30 UTC (rev 89098)
@@ -0,0 +1,12 @@
+import martian
+from martian.error import GrokError
+
+from megrok import rdf
+
+class ModelGrokker(martian.ClassGrokker):
+    martian.component(rdf.Model)
+    martian.directive(rdf.type)
+    
+    def execute(self, class_, config, type):
+        rdf.rdftype_registry[unicode(type)] = class_
+        return True

Added: megrok.rdf/trunk/src/megrok/rdf/meta.zcml
===================================================================
--- megrok.rdf/trunk/src/megrok/rdf/meta.zcml	                        (rev 0)
+++ megrok.rdf/trunk/src/megrok/rdf/meta.zcml	2008-07-31 15:46:30 UTC (rev 89098)
@@ -0,0 +1,9 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:meta="http://namespaces.zope.org/meta"
+    xmlns:grok="http://namespaces.zope.org/grok">
+
+  <!-- Load the grokkers -->
+  <grok:grok package=".meta" />
+
+</configure>

Added: megrok.rdf/trunk/src/megrok/rdf/tests.py
===================================================================
--- megrok.rdf/trunk/src/megrok/rdf/tests.py	                        (rev 0)
+++ megrok.rdf/trunk/src/megrok/rdf/tests.py	2008-07-31 15:46:30 UTC (rev 89098)
@@ -0,0 +1,29 @@
+import unittest
+import doctest
+from zope.testing import cleanup
+from zope.testing import module
+
+def setUp(test):
+    # using zope.testing.module.setUp to work around
+    # __module__ being '__builtin__' by default
+    module.setUp(test, '__main__')
+    
+def tearDown(test):
+    module.tearDown(test)
+    cleanup.cleanUp()
+
+    # XXX clean up SQLAlchemy?
+
+def test_suite():
+    optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
+    globs = {}
+    
+    suite = unittest.TestSuite()
+    
+    suite.addTest(doctest.DocFileSuite(
+        'README.txt',
+        optionflags=optionflags,
+        setUp=setUp,
+        tearDown=tearDown,
+        globs=globs))
+    return suite



More information about the Checkins mailing list