Rope part 4ΒΆ

Tags: rdf, plone

Long time since I updated this (well, two weeks or so), but I had a short holiday of a week. And I ran into severe zope vs. rdflib biting... Back from holiday

Just got back from a one-week holiday (Texel, the Netherlands). I've kept the nagging feeling that I did something wrong with removing the actual rdflib store to a local variable instead of subclassing all the rdflib functionality.

Some research made it clear that subclassing existing classes in zope isn't that easy. You need a few workarounds. So in the end I kept that store local variable. Rdflib uses the new-style-class's super() method a lot and that is stopped dead in its tracks by ExtensionClass. And ExtensionClass is deep in zope and used by acquisition... I better just admit defeat and keep rdflib a bit apart from the zope aparatus. Zope 3 promises to be much better in this regard, however.

Adding an icon proved to be simple. I created www/rope.gif and added icon = "www/rope.gif" to __init__.py, just after the line with the constructors.

Also I added a page template. I created them in skins/default. The first one is 'rope_view.pt':

        I am an RDF store

        I am an RDF store
        <p>
          My id is

            Subject
            Predicate
            Object

For this to work, some things have to be added to 'rope.py':

     # Import for page templates
     from Products.PageTemplates.PageTemplateFile import PageTemplateFile
     ....
         # Place this line just inside the class definition
         viewTemplateFile = PageTemplateFile('skins/default/rope_view.pt', globals())
         ....
         # Replace index_html with the following
         def index_html(self, REQUEST=None):
             """
             Default index page.
             """
             return self.viewTemplateFile.__of__(self)(self,REQUEST)
         ....
         security.declarePublic('triples')
         def triples(self, tripleQuestion):
             for s,p,o in self.store.triples(tripleQuestion):
                 yield s,p,o

Now index_html is rendered according to the page template, getting rid of hardcoded html in the code. Separation of logic and layout is a very good thing.

Biting the bullet: removing new-style classes

Well, in the end there was nothing to be done about it: either zope's extentionclass mechanism goes out or the new-style classes go out. As zope is much bigger than rdflib I decided to remove the new-style classes from rdflib :-)

For this I copied all imported rdflib files to the root directory of my product, removing all rdflib. thingies to make sure the local version of the file is loaded instead of the installed rdflib library. Putting everything in the product's root directory feels very sloppy to me, but subdirectories don't work in this case. An import subdirectory.filename sends python to look for a library subdirectory instead of having him look in the subdirectory. Ah well.

A small positive point in all this mess: in this way you don't need to install rdflib seperately, it's all one self-contained package.

To get meaningful error messages I added the following to the end of rope.py. This way I could do a python rope.py and get a few errors:

  if __name__ == '__main__':
      rope = RopeProduct('abc')
      ns = Namespace('http://vanrees.org/test#')
      rope.namespaces['rr'] = ns
      print "One namespace:"
      print rope.namespaces
      print rope.store

Copying the files and first clean-up

First I did an iterative process:

  1. Look at the rdflib imports in the .py files present (only rope.py at the beginning).
  2. Change those imports to local files by removing the rdflib. part (and other subdirectories if present).
  3. Run python rope.py and see what it's complaining about.
  4. Copy the missing files and repeat the process.

There are some points at which you encounter name clashes. For instance from rdflib.syntax.parser import Parser doesn't give a problem, but from parser import Parser imports the parser from some python or zope standard library. In a case like that I renamed the file from parser.py to rdflibparser.py. The same with rdflibexceptions.py. Sometimes you need to remove all *.pyc files also. Renaming parser.py doesn't have any visible effect on python's complaining when parser.pyc is still available (yes, I had that problem, it bugged me for 10 minutes).

Removing object class

New-style classes subclass from the object superclass. A grep class *py | grep object will reveal the culprits (or at least most of them). Next I removed that object-subclassing from the files.

At this moment I got rid of "metaclass error" messages when running python rope.py!

Removing super() calls

super() is used a lot by rdflib in order to coordinate the various __init__() methods, as rdflib uses subclassing a lot. It is also used in other places, but __init__() is the most common here.

This was actually quite a precision job as I had to call the superclasses' __init__() manually now. This means looking very carefully at the inheritance hierarchy and at the needed arguments.

As an example, 'loadsave.py':

   class LoadSave(NTParser, Parser, Serializer):

      def __init__(self):
          super(LoadSave, self).__init__()
          self.location = None
          self.__lock = Lock()

Here super() first calls NTparser's, Parser's and Serializer's init method (if available) and then does things of it's own. This is changed to:

      def __init__(self):
          Serializer.__init__(self)
          self.location = None
          self.__lock = Lock()

Explanation: NTParser doesn't have a meaningfull init method, so that one doesn't have to be called (I removed NTParser's __init__() later). Parser hasn't got an init method, so only serializer is left. That one is called.

It's an iterative process of running python rope.py, seeing where python complains and then correcting that super() method. This way you won't miss anything. My guess is that in this way you follow a remotely useful path through the source files.

At one moment I ran out of error messages (yahoo!!!) so I added another line to the test function:

       rope.store.add((ns['a'],ns['b'],ns['c']))

Hm. The error message I got didn't concern super() errors. Ok. While there are still a few super() calls left in the software, none of them directly interfere with the zope mechanism. Perhaps something doesn't get stored in the zope database, but I'll worry about that later.

The current set of errors centers around properties. Another new thingy with new-style classes. You say "this is a property, but use these two methods to get/set the data". Which probably means that this mechanism doesn't work when you just removed the subclassing of object from the class. I printed the new-style classes documentation. Reading fodder for the train home :-)

 
vanrees.org logo

Reinout van Rees

My name is Reinout van Rees and I program in Python, I live in the Netherlands, I cycle recumbent bikes and I have a model railway.

Weblog feeds

Most of my website content is in my weblog. You can keep up to date by subscribing to the automatic feeds (for instance with Google reader):