[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