Wednesday, 13 January 2010

Sorrows mudlib: An event notification/subscription model, part 3

Previous post: Sorrows mudlib: An event notification/subscription model, part 2

I've settled on three methods of registering for events using my module. My events module will only directly support the first two, and while the third is my preferred approach, it requires the legwork to be done externally.

  1. Manual registration of object instances.
  2. Manual registration of direct callbacks.
  3. Automatic registration of object instances.
All of the following examples, assume the presence of a pre-existing instance of EventHandler, named events. Remember that what events an instance is registered for, is intended to be determined by the name of functions within the class of the instance. Specifically, functions named with the prefix of event_.

Manual registration of object instances

The most likely use of this approach, is an instance registering itself by a call within its __init__ function.
class SomeObject:
def __init__(self):
events.Register(self)
This approach is problematic however if you are using a code reloading system, like I am. Adding a call to Register within __init__ in a change to a relevant class, will leave existing instances unregistered for any events you have added. Removing a call to Register within __init__ in a change to a relevant class, will leave existing instances registered for any events they were previously registered for.

Manual registration of direct callbacks

Often there is a need to dynamically register a callback for events, which is what this approach allows.
class SomeObject:
def Callback(self):
pass

instance = SomeObject()
events.SomeEvent.Register(instance.Callback)
It also suffers from the same problems related to code reloading systems, as the previous approach.

Automatic registration of object instances

As mentioned at the top of this post, this approach has to be driven by external legwork. In my case, this will be my code reloading system. All a developer will have to do, is name a function intended to be notified of an event within a class, with the event_ prefix.
class SomeObject:
def event_SomeEvent(self):
pass
The external legwork would look something like the following.
def OnClassChanged(class_, instances):
oldEvents = getattr(class_, "__EVENTS__", set())

matches = eh.ProcessClass(class_)
if len(matches):
## Handle instances yet to be created.
old_init = class_.__init__
def new_init(self, *args, **kwargs):
eh.Register(self)
old_init(*args, **kwargs)
class_.__init__ = new_init

## Handle existing instances.
newEvents = set([ t[0] for t in matches ])

# Remove existing registrations.
removedEvents = oldEvents - newEvents
addedEvents = newEvents - oldEvents
matchesLookup = dict(matches)
for instance in instances:
for eventName in removedEvents:
eh._Unregister(eventName, instance)
functionName = matchesLookup[eventName]
for eventName in addedEvents:
eh._Register(eventName, instance, functionName)

class_.__EVENTS__ = newEvents
The code reloading system would be required to provide a callback notifying that a class had been changed, and also a list of the instances of the class that are in existence. This is information that the code reloading system would most likely have.

Conclusion

The event model is now ready for use in my MUD framework. I still need to decide how to make it available for use, whether through a built-in EventHandler instance or otherwise. And I still need to link it up to the code reloading system.

Source code: events.py

No comments:

Post a Comment