Package harold.prog.interface
Contractual interfaces for Python.
Rationale
Let us illustrate the rationale of this package with a short
example:
class C:
def m (self, x):
return "I'm a C and I like", x
class D (C):
def m (self, x, y):
return "I'm a D and I like", x, "and", y
def proc (a_c):
print a_c.m ("bananas")
c1 = C()
c2 = D()
proc (c1) # works fine
proc (c2) # raises a TypeError
Procedure proc
expects instances of class
C
, and invokes their method m
.
c1
and c2
are both instances of
C
because D
is a subclass of C
.
However, the last line raises an exception because D
overrides m
in a way which is incompatible with the
definition of C.m
: it expects 2 arguments instead of
1.
Such problems are usually considered as design flaws in a program,
because they do not respect the semantics of inheritance. Python being
a weakly typed language, it does not try to detect those problems at
compile time. This is an advantage in many situations, making
prototyping easier by allowing unfinished programs to run.
However, the drawback is that detecting such problems at runtime can
be tricky, even with thoroughful tests. This package proposes an
infrastructure allowing to detect such problems as soon as possible
(i.e. when the class is loaded).
Contractual interface class
This package also provides the metaclass interface_class.InterfaceClass
, whose
class instances force their subclasses to respect the interface of
their members (except all those starting with '__'). More
precisely:
-
if the member is a property, the overriding properties
accept al least the same operations (get, set, del);
-
if the member is a callable, the overriding callable
should accept at least the same range of parameter number;
furthermore, if the original member has a
*args
(resp.
**kw
) parameter, the overriding member should also
have one.
Failing to fullfill those requirements will issue an OverridingWarning
. For strict control,
those warnings can be turned into exception with the standard Python
warning framework.
See interface_class.InterfaceClass
for usage
examples.
Forcing members to be public
In some cases, some members whose name starts with '__' are to be
considered public with regard to the interface. This can be achieved
by decorating the given member with the public
decorator.
For example, the __init__
method is usually not
considered part of the contractual interface (because it defines the
interface of the class itself rather than the interface of instances,
which is what we are adressing here).
However, there are situations where the subclasses are expected to
produce instances in the same fashion as the superclass. In this
case, the __init__
method will be decorated with public
.
Warning/Disclaimer
It should be noted that InterfaceClass
does not (can
not) guarantee that overriding members respect the semantics of
the original method. It does not even check that a property or callable
will not throw an exception when used in a way it is supposed to
work.
This package is of course not intended to replace good coding, but
to help in early detecting coding mistakes while (hopefuly ;-P )
preserving much of python ease of use.
To Do: I plan to add method decorators to specify the expected types for
callable parameters, and to ensure that the parameter types in overriding
callables match those in the original callable.
Function Summary |
Decorators |
|
public(f)
|