Commit 0318a786 authored by Jim Fulton's avatar Jim Fulton

more work on first cut

parent 58b45b84
class Attribute:
"""Attribute descriptions
"""
def __init__(self, __name__=None, __doc__=None):
"""Create an 'attribute' description
"""
self.__name__=__name__
self.__doc__=__doc__ or __name__
......@@ -15,7 +15,7 @@ class Orderable(Comparable):
class Hashable(Base):
"""Objects that support hash"""
class Key(Comparable, Hashable):
class HashKey(Comparable, Hashable):
"""Objects that are immutable with respect to state that
influences comparisons or hash values"""
......
class BrokenImplementation(Exception):
"""An attribute is not completely implemented.
"""
def __init__(self, interface, name):
self.interface=interface
self.name=name
def __str__(self):
return """An object has failed to implement interface %(interface)s
The %(name)s attribute was not provided.
""" % self.__dict__
class InvalidInterface(Exception): pass
......@@ -16,7 +16,7 @@ class Sequence(Mapping):
class Sequential(Sequence):
"Keys must be used in order"
Util.assertTypeImplements(type(()), (Sequence, Sized, Basic.Key))
Util.assertTypeImplements(type(()), (Sequence, Sized, Basic.HashKey))
Util.assertTypeImplements(type([]), (Sequence, Sized, MutableMapping))
Util.assertTypeImplements(type({}), (Mapping, Sized, MutableMapping))
"""Method interfaces
"""
import Exceptions
from Attr import Attribute
class MethodClass:
def fromFunction(self, func, interface=''):
m=Method(func.__name__, func.__doc__)
defaults=func.func_defaults or ()
c=func.func_code
na=c.co_argcount
names=c.co_varnames
d={}
nr=na-len(defaults)
if nr==0:
defaults=defaults[1:]
nr=1
for i in range(len(defaults)):
d[names[i+nr]]=defaults[i]
m.positional=names[1:na]
m.required=names[1:nr]
m.optional=d
m.varargs = not not (c.co_flags & 4)
m.kwargs = not not (c.co_flags & 8)
m.interface=interface
return m
class Method(Attribute):
"""Method interfaces
The idea here is that you have objects that describe methods.
This provides an opportunity for rich meta-data.
"""
fromFunction=MethodClass().fromFunction
interface=''
def __init__(self, __name__=None, __doc__=None):
"""Create a 'method' description
"""
self.__name__=__name__
self.__doc__=__doc__ or __name__
def __call__(self, *args, **kw):
raise Exception.BrokenImplementation(self.interface, self.name)
Python Interfaces - The Scarecrow Implementation
This document describes my implementation of the Python interfaces
scarecrow proposal.
Status
This is a first-cut implementation of the proposal. My primary goal
is to shed light on some ideas and to provide a framework for
concrete discussion.
This implementation has had minimal testing. I expect many aspects
of the implementation to evolve over time.
Basic assumptions:
Interfaces are *not* classes:
o Interfaces have their own "hierarchy" (DAG really)
o Interfaces are objects that provide a protocol for
querying attributes (including methods) defined by an
an interface:
names() -- return a sequence of defined names
getDescriptionFor(name, [default]) --
Get a description of a name.
o You cannot mix interfaces and classes in base-class lists.
There are utilities and methods for computing implied interfaces
from classes and for computing "defered" classes from interfaces.
Why aren't interface classes? Interfaces perform a different
function that classes. Classes are for sharing implementation.
Interfaces are for denoting, defining, and documenting abstract
behavior.
Details
Software layout
There is an 'Interface' package that exports a variety of useful
facilities. These are described below.
Creating Interfaces
Interfaces can be created in several ways. The class statement
can be used with one or more interfaces provided as base classes.
This approach is convenient, syntactically, although it is a
little missleading, since interfaces are *not* classes. A minimal
interface that can be used as a base is Interface.Base.
You can also call Interface.new:
new(name, [bases, attrs, __doc__]) --
Create a new interface. The arguments are:
name -- the interface name
bases -- a sequence of "base" interfaces. Base interfaces
are "extended" by the new interface.
attrs -- an object that conforms to
'Interfaces.Standard.Dictionary' that provides attributes
defined by an interface. The attributes should be
'Interface.Attribute objects'.
Finally you can compute an implied interface from a class by calling
'Interface.impliedInterface':
impliedInterface(klass, [__name__, __doc__])
klass -- a class from which to create an interface.
__name__ -- The name of the interface. The default name is the
class name with the suffix "Interface".
__doc__ -- a doc string for the interface. The default doc
string is the class doc string.
The generated interface has attributes for each public method
defined in or inherited by the interface. A method is considered
public if it has a non-empty doc string and if it's name does
not begin with '_' or does begin and end with '__' and is
greater than 4 characters in length.
Note that computing an interface from a class does not automatically
assert that the class implements an interface.
Here's an example:
class X:
def foo(self, a, b):
...
XInterface=Interface.impliedInterface(X)
X.__implements__=XInterface
Interface assertions
Objects can assert that they implement one or more interfaces.
They do this by by defining an '__interfaces__' attribute that is
bound to an interface assertion.
An *interface assertion* is either:
- an Interface or
- a sequence of interface assertions.
Here are some examples of interface assertions:
I1
I1, I2
I1, (I2, I3)
where I1, I2, and I3 are interfaces.
Classes may provide (default) assertions for their instances
(and subclass instances). The usual inheritence rules apply.
Note that the definition of interface assertions makes composition
of interfaces straightforword. For example:
class A:
__implements__ = I1, I2
...
class B
__implements__ = I3, I4
class C(A. B):
...
class D:
__implements__ = I5
class E:
__implements__ = I5, A.__implements__
Special-case handling of classes
Special handling is required for Python classes to make assertions
about the interfaces a class implements, as opposed to the
interfaces that the instances of the class implement. You cannot
simply define an '__implements__' attribute for the class because
class "attributes" apply to instances.
By default, classes are assumed to implement the Interface.Standard.Class
interface. A class may override the default by providing a
'__class_implements__' attribute which will be treated as if it were
the '__implements__' attribute of the class.
Testing assertions
You can test whether an object implements an interface by calling
the 'implementedBy' method of the interface and passing the
object::
I1.implementedBy(x)
Similarly, you can test whether, by default, instances of a class
implement an interface by calling the 'implementedByInstancesOf'
method on the interface and passing the class::
I1.implementedByInstancesOf(A)
Testing interfaces
You can test whether one interface extends another by calling the
extends method on an interface:
I1.extends(I2)
Note that an interface does not extend itself.
Interface attributes
The purpose of an interface is to describe behavior, not to
provide implementation. In a similar fashion the attributes of
an interface describe and document the attributes provided by an
object that implements the interface.
There are currently two kinds of supported attributes:
Interface.Attribute -- The objects describe interface
attributes. They define at least names and doc strings and
may define other information as well.
Interface.Method -- These are interface attributes that
describe methods. They *may* define information about method
signatures. (Note Methods are kinds of Attributes.)
When a class statement is used to define an interface, method
definitions may be provided. These get converted to Method
objects during interface creation. For examle:
class I1(Interface.Base):
__name__=Attribute("The object's name")
def foo(self, a, b):
"blah blah"
defines an interface, 'I1' that has two attributes, '__name__' and
'foo'. The attribute 'foo' is a Method instance. It is *not* a
Python method.
It is my expectation that Attribute objects will eventually be
able to provide all sorts of interesting meta-data.
Defered classes
You cannot use interfaces as base classes. You can, however,
create "defered" classes from an interface:
class StackInterface(Interface.Base):
def push(self, v):
"Add a value to the top of a stack"
def pop(self):
"Remove and return an object from the top of the stack"
class Stack(StackInterface.defered()):
"This is supposed to implement a stack"
__implements__=StackInterface
Attempts to call methods inherited from a defered class will
raise Interface.BrokenImplementation exceptions.
Trial baloon: abstract implementations
Tim Peter's has expressed the desire to provide abstract
implementations in an interface definitions, where, presumably, an
abstract implementation uses only features defined by the
interface. For example:
class ListInterface(Interface.Standard.MutableSequence):
def append(self, v):
"add a value to the end of the object"
def push(self, v):
"add a value to the end of the object"
self.append(v)
Perhaps if a method definition has a body (other than a doc
string) then the corresponding method in the defered class
will not be defered. This would not be hard to do in CPython
if I cheat and sniff at method bytecodes.
Standard interfaces
The module Interface.Standard defines interfaces for standard
python obnjects.
This module, and the modules it uses need a lot more work!
Handling existing built-in types
A hack is provided to allow implementation assertions to be made
for builtin types. Interfaces.assertTypeImplements can be called
to assert that instances of a built-in type implement one or more
interfaces::
Util.assertTypeImplements(
type(1L),
(AbritraryPrecision, BitNumber, Signed))
Issues
o What should the objects that define attributes look like?
They shouldn't *be* the attributes, but should describe the
the attributes.
Note that we've made a first cut with 'Attribute' and
'Method' objects.
o There are places in the current implementation that use
'isinstance' that should be changed to use interface
checks.
o When the interface interfaces are finalized, C implementations
will be highly desirable for performance reasons.
o Alot more work is needed on the standard interface hierarchy.
...
......@@ -7,16 +7,14 @@ Interface=Util.impliedInterface(
""")
iclass.Interface.__implements__=Interface
from Basic import *
from iclass import Named, Class
class Class(Base):
"""Implement shared instance behavior and create instances
Classes can be called to create an instance. This interface does
not specify what if any arguments are required.
"""
from Basic import *
Util.assertTypeImplements(iclass.ClassType, Class)
from Number import *
from Mapping import *
del iclass # cruft
del Util # cruft
from iclass import Interface, ClassType, Base, assertTypeImplements
from iclass import Interface, Class, ClassType, Base, assertTypeImplements
from types import FunctionType
def impliedInterface(klass, __name__=None, __doc__=None):
"""Create an interface object from a class
......@@ -6,11 +7,13 @@ def impliedInterface(klass, __name__=None, __doc__=None):
The interface will contain only objects with doc strings and with names
that begin and end with '__' or names that don't begin with '_'.
"""
if __name__ is None: name="%sInterface" % klass.__name__
if __name__ is None: __name__="%sInterface" % klass.__name__
return Interface(__name__, (), _ii(klass, {}), __doc__)
def _ii(klass, items):
for k, v in klass.__dict__.items():
if type(v) is not FunctionType or not v.__doc__:
continue
if k[:1]=='_' and not (len(k) > 4 and k[:2]=='__' and k[-2:]=='__'):
continue
items[k]=v
......@@ -22,19 +25,25 @@ def implementedBy(object):
"""
r=[]
implements=tiget(type(object), None)
if implements is None:
if hasattr(object, '__implements__'):
implements=object.__implements__
else: return r
t=type(object)
if t is ClassType:
if hasattr(object, '__class_implements__'):
implements=object.__class_implements__
else:
implements=Class
elif hasattr(object, '__implements__'):
implements=object.__implements__
else:
implements=tiget(t, None)
if implements is None: return r
if isinstance(implements,Interface): r.append(i)
if isinstance(implements,Interface): r.append(implements)
else: _wi(implements, r.append)
return r
def implementedByInstances(object):
def implementedByInstancesOf(klass):
"""Return the interfaces that instanced implement (by default)
"""
r=[]
......@@ -50,7 +59,7 @@ def implementedByInstances(object):
implements=tiget(klass,None)
if implements is not None:
if isinstance(implements,Interface): r.append(i)
if isinstance(implements,Interface): r.append(implements)
else: _wi(implements, r.append)
return r
......
from Standard import Base
import iclass
new=iclass.Interface
del iclass
from Util import impliedInterface
from Util import assertTypeImplements, implementedBy, implementedByInstances
from Util import assertTypeImplements, implementedBy, implementedByInstancesOf
from Attr import Attribute
from Method import Method
from Exceptions import BrokenImplementation
......@@ -3,12 +3,17 @@
Scarecrow version:
Classes or objects can implement an __implements__ attribute that
names a sequence of interface objects.
names an interface object or a collection of interface objects.
An interface is defined using the class statement and one or more base
interfaces.
"""
from Method import Method
from Attr import Attribute
from types import FunctionType, ClassType
import Exceptions
_typeImplements={}
class Interface:
......@@ -31,7 +36,40 @@ class Interface:
self.__attrs=attrs
if __doc__ is not None: self.__doc__=__doc__
for k, v in attrs.items():
if isinstance(v, Method):
v.interface=name
v.__name__=k
elif isinstance(v, FunctionType):
attrs[k]=Method.fromFunction(v, name)
elif not isinstance(v, Attribute):
raise Exceptions.InvalidInterface(
"Concrete attribute, %s" % k)
def defered(self):
"""Return a defered class corresponding to the interface
"""
if hasattr(self, "_defered"): return self._defered
klass={}
exec "class %s: pass" % self.__name__ in klass
klass=klass[self.__name__]
self.__d(klass.__dict__)
self._defered=klass
return klass
def __d(self, dict):
for k, v in self.__dict__.items():
if isinstance(v, Method) and not dict.has_key(k):
dict[k]=v
for b in self.__bases__: b.__d(dict)
def extends(self, other):
"""Does an interface extend another?
......@@ -45,11 +83,16 @@ class Interface:
tiget=_typeImplements.get):
"""Does the given object implement the interface?
"""
implements=tiget(type(object), None)
if implements is None:
if hasattr(object, '__implements__'):
implements=object.__implements__
else: return 0
t=type(object)
if t is ClassType:
if hasattr(object, '__class_implements__'):
implements=object.__class_implements__
else: implements=Class
elif hasattr(object, '__implements__'):
implements=object.__implements__
else:
implements=tiget(t, None)
if implements is None: return 0
if isinstance(implements,Interface):
return implements is self or implements.extends(self)
......@@ -64,9 +107,9 @@ class Interface:
if hasattr(klass, '__implements__'):
implements=klass.__implements__
else: return 0
elif hasattr(klass, 'instancesImplements'):
elif hasattr(klass, 'instancesImplement'):
# Hook for ExtensionClass. :)
implements=klass.instancesImplements()
implements=klass.instancesImplement()
else:
implements=tiget(klass,None)
......@@ -95,16 +138,36 @@ class Interface:
def __any(self, interfaces):
for i in interfaces:
if isinstance(i,Interface):
return i is self or i.extends(self)
if i is self or i.extends(self): return 1
else:
return self.__any(i)
if self.__any(i): return 1
return 0
ClassType=type(Interface)
def __repr__(self):
return "<Interface %s at %x>" % (self.__name__, id(self))
Base=Interface("Interface")
class Named(Base):
"Objects that have a name."
__name__=Attribute("The name of the object")
class Class(Named):
"""Implement shared instance behavior and create instances
Classes can be called to create an instance. This interface does
not specify what if any arguments are required.
"""
# Note that we don't use a function definition here, because
# we don't want to specify a signature!
__call__=Method("Instantiate instances of the class")
__bases__=Attribute("A sequence of base classes")
def assertTypeImplements(type, interfaces):
"""Return the interfaces implemented by objects of the given type
"""
_typeImplements[type]=interfaces
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment