Getting started with Harold

Pierre-Antoine Champin

This very short tutorial demonstrates how to use the abstract methods and contractual interfaces of Harold. Let us start with an example python module:

# circles.py

from harold.prog import HaroldClass
import harold.prog.abstract as abstract

import math

class CircleInterface (object):
    "A pure interface"
    __metaclass__ = HaroldClass

    @abstract.attr_r
    def radius (self):
        "The radius of this circle as a positive float."

    @abstract.attr_rw
    def center (self):
        "The tuple (x,y) representing this circle's center."
        
    @abstract.method
    def contains (self, x, y):
        "Does this circle contains point (x,y)"

class CirclePartial (CircleInterface):
    "A partial (abstract) implementation of CircleInterface."
    def contains (self, x, y, boundary=True):
        xc,yc = self.center
        dx,dy = (xc-x,yc-y)
        d = math.sqrt (dx*dx+dy*dy)
        if boundary:
            return d <= self.radius
        else:
            return d <  self.radius

class CircleImplementation (CirclePartial):
    "This class is concrete"
    def __init__ (self, x, y, r):
        self._set_center ((x,y))
        self._set_radius (r)
    def _set_center (self, (x,y)):
        self._x, self._y = x, y
    def _set_radius (self, r):
        if r < 0: raise Exception, "negative radius"
        self._r = r
    center = property (
        lambda self: (self._x, self._y),
        _set_center,
    )
    radius = property (
        lambda self: self._r,
        _set_radius,
    )

  

This example contains three python classes, using the metaclass HaroldClass (note that only CircleInterface explicitly uses the metaclass; the others inherit it by subclassing the former):

Executing the code above will generate the three classes. The first two will be recognized as abstract classes for they contain abstract members. All HaroldClass's have an abstract attribute indicating whether they are abstract or not, as well as an abstract_member attribute containing the names of their abstract members. Trying to instantiate abstract classes will issue a warning, and should generally be avoided: the abstract members will raise exceptions whenever accessed. The third class above, CircleImplementation can be instantiated normally, since it has no abstract member.

Using Harold without HaroldClass

It is possible to use some functionalities of Harold without making your classes instances of the metaclass HaroldClass. For example, abstract members can be added to any python class. The difference is that the class will not “know” that it is abstract, and will instantiate silently (without any warning). However, accessing the abstract members will still raise exceptions.

Using Harold enabled libraries without Harold functionalities

Though Harold aims to be as unintrusive as possible, by issuing warnings instead of exceptions in a number of situations, all the additional checks performed by HaroldClass can be considered a waste of CPU time by people not interested in those functionalities. It is possible to disable them globally by changing HaroldClass into the standard metaclass type, thus turning Harold-aware libraries into lightweight library. The example below shows how:

import harold
harold.lightweight = True  # disable HaroldClass checks

from circles import *

c1 = CircleImplementation(0,0,1)
c2 = CircleInterface() # this is not wise,
                       # but it will work without any warning
print c2.radius # this will however raise an exception

Not that harold.lightweight must be set before any Harold-aware module is imported. This can also be done by defining the environment variable HAROLD_LIGHTWEIGHT to any value. In the latter case, no additional python code is required.