Archive for May 2008

UI Ideas for Managing Roles and Capabilities

2008-05-30-Fri

Liz and I have been busy discussing, and here’s what we’ve come up with initially:

DrProject web administration diagram

2008-05-28-Wed

Here is the state diagram of the current DrProject web administration interface:
DrProject web administration diagram

Reading books

2008-05-28-Wed

In the past week, Greg has silently put 2 books on my desk:

I’ve read through most of each book now.

Singleton metaclass example

2008-05-21-Wed

Here’s a metaprogramming example that I conceived, involving the use of a metaclass to implement the singleton property.

class Counter(object):

    global_count = 0
    count_by_class = {}

    @staticmethod
    def count(obj):
        cls = obj.__class__
        class_count = Counter.count_by_class.get(cls, 0)
        result = (Counter.global_count, class_count)
        Counter.global_count += 1
        Counter.count_by_class[cls] = class_count + 1
        return result

class CountedObject(object):

    def __init__(self):
        print "Creating instance of class %s" % self.__class__.__name__
        (self.big_count, self.small_count) = Counter.count(self)

    def __str__(self):
        return self.__repr__()

    def __repr__(self):
        return "<%s instance. Global ID: %d. %s ID: %d.>" % (self.__class__.__name__, self.big_count, self.__class__.__name__, self.small_count)

class SingletonMeta(type):

    def __init__(cls, name, bases, d):
        print "Creating class %s" % name
        type.__init__(cls, name, bases, d)

    def __call__(cls):
        print "Attempting to create instance of class %s" % cls.__name__
        if not hasattr(cls, "SINGLETON"):
            instance = type.__call__(cls)
            cls.SINGLETON = instance
        else:
            print "Returning singleton of class %s" % cls.__name__
            instance = cls.SINGLETON
        return instance

class SingletonBase(CountedObject):
    __metaclass__ = SingletonMeta

class FooSingleton(SingletonBase):
    pass

class BarSingleton(SingletonBase):
    pass

class NotSingleton(CountedObject):
    pass

Now, time for some queries:

Creating class SingletonBase
Creating class FooSingleton
Creating class BarSingleton

>>> FooSingleton()
Attempting to create instance of class FooSingleton
Creating instance of class FooSingleton
<FooSingleton instance. Global ID: 0. FooSingleton ID: 0.>

>>> FooSingleton()
Attempting to create instance of class FooSingleton
Returning singleton of class FooSingleton
<FooSingleton instance. Global ID: 0. FooSingleton ID: 0.>

>>> NotSingleton()
Creating instance of class NotSingleton
<NotSingleton instance. Global ID: 1. NotSingleton ID: 0.>

>>> NotSingleton()
Creating instance of class NotSingleton
<NotSingleton instance. Global ID: 2. NotSingleton ID: 1.>

>>> NotSingleton()
Creating instance of class NotSingleton
<NotSingleton instance. Global ID: 3. NotSingleton ID: 2.>

>>> SingletonBase()
Attempting to create instance of class SingletonBase
Creating instance of class SingletonBase
<SingletonBase instance. Global ID: 4. SingletonBase ID: 0.>

>>> SingletonBase()
Attempting to create instance of class SingletonBase
Returning singleton of class SingletonBase
<SingletonBase instance. Global ID: 4. SingletonBase ID: 0.>

>>> BarSingleton()
Attempting to create instance of class BarSingleton
Returning singleton of class BarSingleton
<SingletonBase instance. Global ID: 4. SingletonBase ID: 0.>

>>> BarSingleton()
Attempting to create instance of class BarSingleton
Returning singleton of class BarSingleton
<SingletonBase instance. Global ID: 4. SingletonBase ID: 0.>

As you can see, classes that extend Singleton (or more precisely, classes that are metaclassed by SingletonMeta) can only be instantiated once. This can be seen by the control flow implied by the printed messages, and can also be seen by the IDs of the objects returned.
One complication, though – instantiating BarSingleton seems to return the wrong result because its superclass already has its SINGLETON field set.

Core components in DrProject

2008-05-21-Wed

I will attempt to explain DrProject‘s core components, which are depended on by the rest of the components. Copious amounts of metaprogramming are used, so beware of your sanity. Some relevant reference resources will be mentioned at the end.

Class drproject.core.ComponentMeta: The metaclass of Component. This ensures that all subclasses of Component follow the singleton pattern. Also, ComponentMeta keeps track of all the interfaces implemented by a class based on this metaclass.

Class drproject.core.Component: An abstract class for singleton components. It has subclasses such as AdminSystem and WikiSystem. Instances of these classes usually consist of event handlers. In subclasses of Component, methods decorated with @listen are automatically connected to the signal dispatcher. Component has some direct subclasses, and some indirect subclasses like Controller and EnvironmentSetupParticipant.

Class drproject.core.Interface: A marker base class. Doesn’t do anything, except making the programmer aware of interface classes. An example of a class that extends Interface is IWikiSyntaxProvider.

Method drproject.core.implements: Marks the class as implementing the specified interfaces (a list of classes that extend Interface). This marking is processed in ComponentMeta.__call__ (when a Component class makes the singleton objects). implements does not add methods – e.g. class Foo: implements(IBar) does not add IBar‘s methods to Foo!

Decorator drproject.core.listen: Marks the method as a signal listener. This marking is also processed in ComponentMeta.__call__. The associated signal is obtained from the method’s name, e.g. @listen def project_added(...) will be called when the project_added signal (as declared in signals.py) is sent.

Class drproject.core.ExtensionPoint: This is a special kind of property. This class is not extended. An ExtensionPoint instance is associated with an interface. When an attribute is an instance of ExtensionPoint, getting its value returns the list of singleton components that implement the associated interface. For example, with class WikiSystem(Component): syntax_providers = ExtensionPoint(IWikiSyntaxProvider), reading WikiSystem().syntax_providers gives a list of singletons whose classes implement IWikiSyntaxProvider.

Class drproject.core.Environment (extends Component): In some ways, this is the God object. It is the starting point of the DrProject application. It handles setting up the database connection, and loading and initializing the components (e.g. ticket system, wiki system, mail system, etc.).

Class drproject.core.EnvironmentSetupParticipant (extends Component): Classes that extend ESP have their environment_created methods called when Environment is configured.

Class drproject.web.controller.Controller (extends Component): Instances of Controller subclasses (verb)process HTTP requests.

And lots of database tables declared using Elixir. These classes can be spotted by the fact that they extend elixir.Entity.

Resources:

Bug fixes (2008-05-15-Thu)

2008-05-19-Mon

The following are the bug fixes I completed on Thursday May 15th:

  • [4804] fixes the behaviour of the size formatter when asked format a size of exactly 512 bytes. It’s a pretty subtle bug, which I only managed to catch by luck by testing boundary cases. (512 is a magic constant in the function.)
  • [4807] fixes #1421. Works, but not sure if it’s absolutely correct.
  • [4813] fixes #1419. Seems right.
  • [4816] fixes #1374. Seems right, although it depends on how you choose to resolve ambiguous cases.

Tools I’m using

2008-05-19-Mon

Every trade has a set of tools for getting the job done efficiently, and programming is no exception. These are the tools that I am using to explore and develop DrProject:

  • Ubuntu: DrProject works best on Linux
  • Eclipse 3.2: My text editor
  • PyDev for Eclipse: IMO, the most useful feature is the ability to instantly jump to the declaration of a class/function/variable/etc.
  • IPython: An improved version of the Python command-line interpreter

(And then there are the obvious tools that aren’t worth mentioning, such as command-line SVN, Firefox, terminal, and so on and so forth.)