trac.core – the Trac “kernel”

Component model

The Trac component model is very simple, it is based on Interface classes that are used to document a particular set of methods and properties that must be defined by a Component subclass when it declares it implements that interface.

class trac.core.Interface

Bases: object

Marker base class for extension point interfaces.

class trac.core.Component

Bases: object

Base class for components.

Every component can declare what extension points it provides, as well as what extension points of other components it extends.

static implements(*interfaces)

Can be used in the class definition of Component subclasses to declare the extension points that are extended.

The static method Component.implements is never used as such, but rather via the global implements function. This globally registers that particular component subclass as an implementation of the listed interfaces.

trac.core.implements(*interfaces)

Can be used in the class definition of Component subclasses to declare the extension points that are extended.

For example:

class IStuffProvider(Interface):
    """All interfaces start by convention with an "I" and even if it's
    not a convention, in practice most interfaces are "Provider" of
    something ;-)
    """

    def get_stuff(color=None):
        """We usually don't specify "self" here, but try to describe
        as precisely as possible how the method might be called and
        what is the expected return type."""

class ComponentA(Component):

    implements(IStuffProvider)

    # IStuffProvider methods

    def get_stuff(self, color=None):
        if not color or color == 'yellow':
            yield ('duck', "the regular waterproof plastic duck")

The benefit of implementing an interface is to possibility to define an ExtensionPoint property for an Interface, in a Component subclass. Such a property provides a convenient way to retrieve all registered and enabled component instances for that interface. The enabling of components is the responsibility of the ComponentManager, see is_component_enabled below.

class trac.core.ExtensionPoint(interface)

Bases: property

Marker class for extension points in components.

Create the extension point.

Parameters:interface – the Interface subclass that defines the protocol for the extension point
extensions(component)

Return a list of components that declare to implement the extension point interface.

Continuing the example:

class StuffModule(Component):

    stuff_providers = ExtensionPoint(IStuffProvider)

    def get_all_stuff(self, color=None):
        stuff = {}
        for provider in self.stuff_provider:
            for name, descr in provider.get_stuff(color) or []:
                stuff[name] = descr
        return stuff

Note that besides going through an extension point, Component subclass instances can alternatively be retrieved directly by using the instantiation syntax. This is not an usual instantiation though, as this will always return the same instance in the given ComponentManager “scope” passed to the constructor:

>>> a1 = ComponentA(mgr)
>>> a2 = ComponentA(mgr)
>>> a1 is a2
True

The same thing happens when retrieving components via an extension point, the retrieved instances belong to the same “scope” as the instance used to access the extension point:

>>> b = StuffModule(mgr)
>>> any(a is a1 for a in b.stuff_providers)
True
class trac.core.ComponentManager

Bases: object

The component manager keeps a pool of active components.

Initialize the component manager.

component_activated(component)

Can be overridden by sub-classes so that special initialization for components can be provided.

disable_component(component)

Force a component to be disabled.

Parameters:component – can be a class or an instance.
enable_component(component)

Force a component to be enabled.

Parameters:component – can be a class or an instance.
Since:1.0.13
is_component_enabled(cls)

Can be overridden by sub-classes to veto the activation of a component.

If this method returns False, the component was disabled explicitly. If it returns None, the component was neither enabled nor disabled explicitly. In both cases, the component with the given class will not be available.

is_enabled(cls)

Return whether the given component class is enabled.

In practice, there’s only one kind of ComponentManager in the Trac application itself, the trac.env.Environment.

More on components

We have seen above that one way to retrieve a Component instance is to call the constructor on a ComponentManager instance mgr:

a1 = ComponentA(mgr)

This will eventually trigger the creation of a new ComponentA instance if there wasn’t already one created for mgr [*]. At this unique occasion, the constructor of the component subclass will be called without arguments, so if you define a constructor it must have the following signature:

def __init__(self):
    self.all_colors = set()

Note that one should try to do as little as possible in a Component constructor. The most complex operation could be for example the allocation of a lock to control the concurrent access to some data members and guarantee thread-safe initialization of more costly resources on first use. Never do such costly initializations in the constructor itself.

Exceptions

exception trac.core.TracBaseError

Bases: exceptions.Exception

Base class for all exceptions defined in Trac.

exception trac.core.TracError(message, title=None, show_traceback=False)

Bases: trac.core.TracBaseError

Standard exception for errors in Trac.

If message is an Element object, everything up to the first <p> will be displayed in the red box, and everything after will be displayed below the red box. If title is given, it will be displayed as the large header above the error message.

Miscellaneous

class trac.core.ComponentMeta

Bases: type

Meta class for components.

Takes care of component and extension point registration.

Create the component class.

classmethod deregister(component)

Remove a component from the registry.

trac.core.N_(string)

No-op translation marker, inlined here to avoid importing from trac.util.

[*]Ok, it might happen that more than one component instance get created due to a race condition. This is usually harmless, see #9418.