Commit 9d4c5f8c authored by Jim Fulton's avatar Jim Fulton

Backported Zope 3 Interface package to Python 2.1 and Zope 2. Some

modifications were needed in Zope (only two files). A lot of unused
(or rarely) features were removed from the Interface package. Slightly
deeper imports are needed.
parent 2186969f
...@@ -23,7 +23,7 @@ import os.path, re ...@@ -23,7 +23,7 @@ import os.path, re
import stat import stat
from DateTime import DateTime from DateTime import DateTime
from types import ListType, TupleType from types import ListType, TupleType
from Interface import instancesOfObjectImplements from Interface.Implements import instancesOfObjectImplements
import ZClasses # to enable 'PC.registerBaseClass()' import ZClasses # to enable 'PC.registerBaseClass()'
......
from InterfaceBase import InterfaceBase
class Attribute(InterfaceBase):
"""Attribute descriptions
"""
def __init__(self, __name__=None, __doc__=None):
"""Create an 'attribute' description
"""
self.__name__=__name__
self.__doc__=__doc__ or __name__
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Revision information:
$Id: Attribute.py,v 1.2 2002/06/07 17:18:29 jim Exp $
"""
from _Element import Element
class Attribute(Element):
"""Attribute descriptions
"""
# We can't say this yet because we don't have enough
# infrastructure in place.
#
#__implements__ = IAttribute
from iclass import Base
class Mutable(Base):
"""Mutable objects
"""
class Comparable(Base):
"""Objects that can be tested for equality
"""
class Orderable(Comparable):
"""Objects that can be compared with < > == <= >= !="""
class Hashable(Base):
"""Objects that support hash"""
class HashKey(Comparable, Hashable):
"""Objects that are immutable with respect to state that
influences comparisons or hash values"""
Interface package change history
Release 0.5 (Zope 2.3b1)
Many changes to API. Added unit tests, pretty printing, verification
code, tagged data, and other stuff.
Release 0.1.1
This release includes some changes to the documentation to reflect
comments made on 0.1.
Release 0.1
This was the initial release.
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Revision information:
$Id: Mapping.py,v 1.2 2002/06/07 17:18:29 jim Exp $
"""
from Interface import Interface
class IReadMapping(Interface):
"""Basic mapping interface
"""
def __getitem__(key):
"""Get a value for a key
A KeyError is raised if there is no value for the key.
"""
def get(key, default=None):
"""Get a value for a key
The default is returned if there is no value for the key.
"""
def has_key(key):
"""Tell if a key exists in the mapping
"""
class IEnumerableMapping(IReadMapping):
"""Mapping objects whose items can be enumerated
"""
def keys():
"""Return the keys of the mapping object
"""
def values():
"""Return the values of the mapping object
"""
def items():
"""Return the items of the mapping object
"""
def __len__():
"""Return the number of items
"""
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Revision information:
$Id: BaseTestMapping.py,v 1.2 2002/06/07 17:18:29 jim Exp $
"""
from operator import __getitem__
def testIReadMapping(self, inst, state, absent):
for key in state:
self.assertEqual(inst[key], state[key])
self.assertEqual(inst.get(key, None), state[key])
self.failUnless(inst.has_key(key))
for key in absent:
self.assertEqual(inst.get(key, None), None)
self.assertEqual(inst.get(key), None)
self.assertEqual(inst.get(key, self), self)
self.assertRaises(KeyError, __getitem__, inst, key)
def test_keys(self, inst, state):
"""Return the keys of the mapping object
"""
inst_keys = list(inst.keys()); inst_keys.sort()
state_keys = list(state.keys()) ; state_keys.sort()
self.assertEqual(inst_keys, state_keys)
def test_values(self, inst, state):
"""Return the values of the mapping object
"""
inst_values = list(inst.values()); inst_values.sort()
state_values = list(state.values()) ; state_values.sort()
self.assertEqual(inst_values, state_values)
def test_items(self, inst, state):
"""Return the items of the mapping object
"""
inst_items = list(inst.items()); inst_items.sort()
state_items = list(state.items()) ; state_items.sort()
self.assertEqual(inst_items, state_items)
def test___len__(self, inst, state):
"""Return the number of items
"""
self.assertEqual(len(inst), len(state))
def testIEnumerableMapping(self, inst, state):
test_keys(self, inst, state)
test_items(self, inst, state)
test_values(self, inst, state)
test___len__(self, inst, state)
class BaseTestIReadMapping:
def testIReadMapping(self):
inst = self._IReadMapping__sample()
state = self._IReadMapping__stateDict()
absent = self._IReadMapping__absentKeys()
testIReadMapping(self, inst, state, absent)
class BaseTestIEnumerableMapping(BaseTestIReadMapping):
"""Mapping objects whose items can be enumerated
"""
def test_keys(self):
"""Return the keys of the mapping object
"""
inst = self._IEnumerableMapping__sample()
state = self._IEnumerableMapping__stateDict()
test_keys(self, inst, state)
def test_values(self):
"""Return the values of the mapping object
"""
inst = self._IEnumerableMapping__sample()
state = self._IEnumerableMapping__stateDict()
test_values(self, inst, state)
def test_items(self):
"""Return the items of the mapping object
"""
inst = self._IEnumerableMapping__sample()
state = self._IEnumerableMapping__stateDict()
test_items(self, inst, state)
def test___len__(self):
"""Return the number of items
"""
inst = self._IEnumerableMapping__sample()
state = self._IEnumerableMapping__stateDict()
test___len__(self, inst, state)
def _IReadMapping__stateDict(self):
return self._IEnumerableMapping__stateDict()
def _IReadMapping__sample(self):
return self._IEnumerableMapping__sample()
def _IReadMapping__absentKeys(self):
return self._IEnumerableMapping__absentKeys()
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
""" Pretty-Print an Interface object as structured text (Yum)
This module provides a function, asStructuredText, for rendering an
interface as structured text.
Revision information:
$Id: Document.py,v 1.2 2002/06/07 17:18:29 jim Exp $
"""
from string import maketrans
def asStructuredText(I, munge=0):
""" Output structured text format. Note, this will wack any existing
'structured' format of the text. """
r = ["%s\n\n" % I.getName()]
outp = r.append
level = 1
if I.getDoc():
outp(_justify_and_indent(_trim_doc_string(I.getDoc()), level)+ "\n\n")
if I.getBases():
outp((" " * level) + "This interface extends:\n\n")
level = level + 1
for b in I.getBases():
item = "o %s" % b.getName()
outp(_justify_and_indent(_trim_doc_string(item), level, munge)
+ "\n\n")
level = level - 1
namesAndDescriptions = list(I.namesAndDescriptions())
namesAndDescriptions.sort()
outp(_justify_and_indent("Attributes:", level, munge)+'\n\n')
level = level + 1
for name, desc in namesAndDescriptions:
if not hasattr(desc, 'getSignatureString'): # ugh...
item = "%s -- %s" % (desc.getName(),
desc.getDoc() or 'no documentation')
outp(_justify_and_indent(_trim_doc_string(item), level, munge)
+ "\n\n")
level = level - 1
outp(_justify_and_indent("Methods:", level, munge)+'\n\n')
level = level + 1
for name, desc in namesAndDescriptions:
if hasattr(desc, 'getSignatureString'): # ugh...
item = "%s%s -- %s" % (desc.getName(),
desc.getSignatureString(),
desc.getDoc() or 'no documentation')
outp(_justify_and_indent(_trim_doc_string(item), level, munge)
+ "\n\n")
return "".join(r)
def _trim_doc_string(text):
"""
Trims a doc string to make it format
correctly with structured text.
"""
text = text.strip().replace('\r\n', '\n')
lines = text.split('\n')
nlines = [lines[0]]
if len(lines) > 1:
min_indent=None
for line in lines[1:]:
indent=len(line) - len(line.lstrip())
if indent < min_indent or min_indent is None:
min_indent=indent
for line in lines[1:]:
nlines.append(line[min_indent:])
return '\n'.join(nlines)
_trans = maketrans("\r\n", " ")
def _justify_and_indent(text, level, munge=0, width=72):
""" indent and justify text, rejustify (munge) if specified """
lines = []
if munge:
line = " " * level
text = text.translate(text, _trans).strip().split()
for word in text:
line = ' '.join([line, word])
if len(line) > width:
lines.append(line)
line = " " * level
else:
lines.append(line)
return "\n".join(lines)
else:
text = text.replace("\r\n", "\n").split("\n")
for line in text:
lines.append( (" " * level) + line)
return '\n'.join(lines)
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
class DoesNotImplement(Exception): class DoesNotImplement(Exception):
""" This object does not implement """ """ This object does not implement """
...@@ -27,15 +40,25 @@ class BrokenMethodImplementation(Exception): ...@@ -27,15 +40,25 @@ class BrokenMethodImplementation(Exception):
"""An method is not completely implemented. """An method is not completely implemented.
""" """
def __init__(self, method): def __init__(self, method, mess):
self.method=method self.method=method
self.mess=mess
def __str__(self): def __str__(self):
return """An object has failed to implement the method %(method)s return """The implementation of %(method)s violates it's contract
because %(mess)s.
The signature is incorrect.
""" % self.__dict__ """ % self.__dict__
class InvalidInterface(Exception): pass class InvalidInterface(Exception):
"""The interface has invalid contents
"""
class BadImplements(TypeError):
"""An implementation assertion is invalid
because it doesn't contain an interface or a sequence of valid
implementation assertions.
"""
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Revision information:
$Id: IAttribute.py,v 1.2 2002/06/07 17:18:29 jim Exp $
"""
from IElement import IElement
class IAttribute(IElement):
"""Attribute descriptors"""
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Revision information:
$Id: IElement.py,v 1.2 2002/06/07 17:18:29 jim Exp $
"""
from _Interface import Interface
from Attribute import Attribute
class IElement(Interface):
"""Objects that have basic documentation and tagged values.
"""
__name__ = Attribute('__name__', 'The object name')
__doc__ = Attribute('__doc__', 'The object doc string')
def getName():
"""Returns the name of the object."""
def getDoc():
"""Returns the documentation for the object."""
def getTaggedValue(tag):
"""Returns the value associated with 'tag'."""
def getTaggedValueTags():
"""Returns a list of all tags."""
def setTaggedValue(tag, value):
"""Associates 'value' with 'key'."""
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Revision information:
$Id: IInterface.py,v 1.2 2002/06/07 17:18:29 jim Exp $
"""
from IElement import IElement
class IInterface(IElement):
"""Interface objects
Interface objects describe the behavior of an object by containing
useful information about the object. This information includes:
o Prose documentation about the object. In Python terms, this
is called the "doc string" of the interface. In this element,
you describe how the object works in prose language and any
other useful information about the object.
o Descriptions of attributes. Attribute descriptions include
the name of the attribute and prose documentation describing
the attributes usage.
o Descriptions of methods. Method descriptions can include:
o Prose "doc string" documentation about the method and its
usage.
o A description of the methods arguments; how many arguments
are expected, optional arguments and their default values,
the position or arguments in the signature, whether the
method accepts arbitrary arguments and whether the method
accepts arbitrary keyword arguments.
o Optional tagged data. Interface objects (and their attributes and
methods) can have optional, application specific tagged data
associated with them. Examples uses for this are examples,
security assertions, pre/post conditions, and other possible
information you may want to associate with an Interface or its
attributes.
Not all of this information is mandatory. For example, you may
only want the methods of your interface to have prose
documentation and not describe the arguments of the method in
exact detail. Interface objects are flexible and let you give or
take any of these components.
Interfaces are created with the Python class statement using
either Interface.Interface or another interface, as in::
from Interface import Interface
class IMyInterface(Interface):
'''Interface documentation
'''
def meth(arg1, arg2):
'''Documentation for meth
'''
# Note that there is no self argument
class IMySubInterface(IMyInterface):
'''Interface documentation
'''
def meth2():
'''Documentation for meth2
'''
You use interfaces in two ways:
o You assert that your object implement the interfaces.
There are several ways that you can assert that an object
implements an interface::
1. Include an '__implements__' attribute in the object's class
definition. The value of the '__implements__' attribute must
be an implementation specification. An implementation
specification is either an interface or a tuple of
implementation specifications.
2. Incluse an '__implements__' attribute in the object.
Because Python classes don't have their own attributes, to
assert that a class implements interfaces, you must provide a
'__class_implements__' attribute in the class definition.
**Important**: A class usually doesn't implement the
interfaces that it's instances implement. The class and
it's instances are separate objects with their own
interfaces.
3. Call 'Interface.Implements.implements' to assert that instances
of a class implement an interface.
For example::
from Interface.Implements import implements
implements(some_class, some_interface)
This is approach is useful when it is not an option to modify
the class source. Note that this doesn't affect what the
class itself implements, but only what it's instances
implement.
4. For types that can't be modified, you can assert that
instances of the type implement an interface using
'Interface.Implements.assertTypeImplements'.
For example::
from Interface.Implements import assertTypeImplements
assertTypeImplements(some_type, some_interface)
o You query interface meta-data. See the IInterface methods and
attributes for details.
"""
def getBases():
"""Return a sequence of the base interfaces
"""
def extends(other, strict=1):
"""Test whether the interface extends another interface
A true value is returned in the interface extends the other
interface, and false otherwise.
Normally, an interface doesn't extend itself. If a false value
is passed as the second argument, or via the 'strict' keyword
argument, then a true value will be returned if the interface
and the other interface are the same.
"""
def isImplementedBy(object):
"""Test whether the interface is implemented by the object.
Return true of the object asserts that it implements the
interface, including asseting that it implements an extended
interface.
"""
def isImplementedByInstancesOf(class_):
"""Test whether the interface is implemented by instances of the class
Return true of the class asserts that it's instances implement the
interface, including asseting that they implement an extended
interface.
"""
def names(all=0):
"""Get the interface attribute names.
Return a sequence of the names of the attributes, including
methods, included in the interface definition.
Normally, only directly defined attributes are included. If
a true positional or keyword argument is given, then
attributes defined by nase classes will be included.
"""
def namesAndDescriptions(all=0):
"""Get the interface attribute names and descriptions.
Return a sequence of the names and descriptions of the
attributes, including methods, as name-value pairs, included
in the interface definition.
Normally, only directly defined attributes are included. If
a true positional or keyword argument is given, then
attributes defined by nase classes will be included.
"""
def getDescriptionFor(name):
"""Get the description for a name
If the named attribute does not exist, a KeyError is raised.
"""
def queryDescriptionFor(name, default=None):
"""Get the description for a name
Return the default if no description exists.
"""
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Revision information:
$Id: IMethod.py,v 1.2 2002/06/07 17:18:29 jim Exp $
"""
from IAttribute import IAttribute
class IMethod(IAttribute):
"""Method attributes
"""
# XXX What the heck should methods provide? Grrrr
def getSignatureString():
"""Return a signature string suitable for inclusion in documentation.
"""
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Implemantation assertion facilities.
Revision information:
$Id: Implements.py,v 1.2 2002/06/07 17:18:29 jim Exp $
"""
import Exceptions
from types import ClassType
from Verify import verifyClass
from _InterfaceClass import Interface as InterfaceClass
from types import TupleType, ClassType, StringType
# Special value indicating the object supports
# what its class supports.
CLASS_INTERFACES = 1
from _object import ClassTypes, isInstance
_typeImplements={}
def getImplements(object):
t = type(object)
if t in ClassTypes:
if hasattr(object, '__class_implements__'):
return object.__class_implements__
elif hasattr(object, '__implements__'):
return object.__implements__
return _typeImplements.get(t, None)
def getImplementsOfInstances(klass, tiget=_typeImplements.get):
if type(klass) in ClassTypes:
if hasattr(klass, '__implements__'):
return klass.__implements__
else:
return None
else:
return tiget(klass, None)
def visitImplements(implements, object, visitor, getInterface=None):
"""
Visits the interfaces described by an __implements__ attribute,
invoking the visitor for each interface object.
If the visitor returns anything true, the loop stops.
This does not, and should not, visit superinterfaces.
"""
# this allows us to work with proxy wrappers in Python 2.2,
# yet remain compatible with earlier versions of python.
implements_class = getattr(implements, '__class__', None)
if implements_class == InterfaceClass or \
isInstance(implements, InterfaceClass):
return visitor(implements)
elif implements == CLASS_INTERFACES:
klass = getattr(object, '__class__', None)
if klass is not None:
i = getImplementsOfInstances(klass)
if i:
return visitImplements(i, object, visitor, getInterface)
elif implements_class == StringType or type(implements) is StringType:
if getInterface is not None:
# Look up a named interface.
i = getInterface(object, implements)
if i is not None:
return visitImplements(i, object, visitor, getInterface)
elif implements_class == TupleType or type(implements) is TupleType:
for i in implements:
r = visitImplements(i, object, visitor, getInterface)
if r:
# If the visitor returns anything true, stop.
return r
else:
if implements_class is not None and \
type(implements) != implements_class:
raise Exceptions.BadImplements(
"""__implements__ should be an interface or tuple,
not a %s pretending to be a %s"""
% (type(implements).__name__, implements_class.__name__)
)
raise Exceptions.BadImplements(
"""__implements__ should be an interface or tuple,
not a %s""" % type(implements).__name__)
return None
def assertTypeImplements(type, interfaces):
"""Assign a set of interfaces to a Python type such as int, str, tuple,
list and dict.
"""
_typeImplements[type]=interfaces
def objectImplements(object, getInterface=None):
r = []
implements = getImplements(object)
if not implements:
return r
visitImplements(implements, object, r.append, getInterface)
return r
def instancesOfObjectImplements(klass, getInterface=None):
r = []
implements = getImplementsOfInstances(klass)
if not implements:
return r
visitImplements(implements, klass, r.append, getInterface)
return r
def _flatten(i, append):
append(i)
bases = i.getBases()
if bases:
for b in bases:
_flatten(b, append)
def flattenInterfaces(interfaces, remove_duplicates=1):
res = []
for i in interfaces:
_flatten(i, res.append)
if remove_duplicates:
# Remove duplicates in reverse.
# Similar to Python 2.2's method resolution order.
seen = {}
index = len(res) - 1
while index >= 0:
i = res[index]
if seen.has_key(i):
del res[index]
else:
seen[i] = 1
index = index - 1
return res
def implements(klass, interface, check=1):
if check:
verifyClass(interface, klass, tentative=1)
old=getattr(klass, '__implements__', None)
if old is None:
klass.__implements__ = interface
else:
klass.__implements__ = old, interface
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Method interfaces """Method interfaces
Revision information:
$Id: Method.py,v 1.9 2002/06/07 17:18:29 jim Exp $
""" """
import Exceptions import Exceptions
from Attr import Attribute from Attribute import Attribute
sig_traits = ['positional', 'required', 'optional', 'varargs', 'kwargs'] sig_traits = ['positional', 'required', 'optional', 'varargs', 'kwargs']
CO_VARARGS = 4 CO_VARARGS = 4
CO_VARKEYWORDS = 8 CO_VARKEYWORDS = 8
class MethodClass:
def fromFunction(self, func, interface='', strip_first=0):
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 strip_first and nr==0: # 'strip_first' implies method, has 'self'
defaults=defaults[1:]
nr=1
for i in range(len(defaults)):
d[names[i+nr]]=defaults[i]
start_index = strip_first and 1 or 0
m.positional=names[start_index:na]
m.required=names[start_index:nr]
m.optional=d
argno = na
if c.co_flags & CO_VARARGS:
m.varargs = names[argno]
argno = argno + 1
else:
m.varargs = None
if c.co_flags & CO_VARKEYWORDS:
m.kwargs = names[argno]
else:
m.kwargs = None
m.interface=interface
return m
def fromMethod(self, meth, interface=''):
func = meth.im_func
return self.fromFunction(func, interface, strip_first=1)
class Method(Attribute): class Method(Attribute):
"""Method interfaces """Method interfaces
...@@ -56,15 +31,12 @@ class Method(Attribute): ...@@ -56,15 +31,12 @@ class Method(Attribute):
This provides an opportunity for rich meta-data. This provides an opportunity for rich meta-data.
""" """
fromFunction=MethodClass().fromFunction # We can't say this yet because we don't have enough
fromMethod=MethodClass().fromMethod # infrastructure in place.
interface='' #
#__implements__ = IMethod
def __init__(self, __name__=None, __doc__=None): interface=''
"""Create a 'method' description
"""
self.__name__=__name__
self.__doc__=__doc__ or __name__
def __call__(self, *args, **kw): def __call__(self, *args, **kw):
raise Exceptions.BrokenImplementation(self.interface, self.__name__) raise Exceptions.BrokenImplementation(self.interface, self.__name__)
...@@ -96,6 +68,43 @@ class Method(Attribute): ...@@ -96,6 +68,43 @@ class Method(Attribute):
return sig return sig
def fromFunction(func, interface='', imlevel=0):
m=Method(func.__name__, func.__doc__)
defaults=func.func_defaults or ()
c=func.func_code
na=c.co_argcount-imlevel
names=c.co_varnames[imlevel:]
d={}
nr=na-len(defaults)
if nr < 0:
defaults=defaults[-nr:]
nr=0
for i in range(len(defaults)):
d[names[i+nr]]=defaults[i]
m.positional=names[:na]
m.required=names[:nr]
m.optional=d
argno = na
if c.co_flags & CO_VARARGS:
m.varargs = names[argno]
argno = argno + 1
else:
m.varargs = None
if c.co_flags & CO_VARKEYWORDS:
m.kwargs = names[argno]
else:
m.kwargs = None
m.interface=interface
return m
def fromMethod(meth, interface=''):
func = meth.im_func
return fromFunction(func, interface, imlevel=1)
......
import Basic, Util
class BitNumber(Basic.Base):
"""Numbers that can be interpreted as strings of bits
they support & | ~ ^ << >>
"""
class Number(Basic.Base):
"""Numeric interface
it is assumed that numeric types are embedded in each other in some
way (this is the Scheme numerical tower I think); they support
operators + - *, usually / and %, perhaps **
"""
class Offsetable(Basic.Base):
"""Things that you can subtract and add numbers too
e.g. Date-Time values.
"""
class Real(Number):
"""any subset of the real numbers (this includes ints and rationals)
"""
class Complex(Number):
"""has re and im fields (which are themselves real)
"""
class Exact(Number):
"""e.g. integers and rationals; also fixed point
"""
class AbritraryPrecision(Exact):
"""e.g. long, rationals
"""
class FixedPrecisionInt(Exact):
"""Fixed precision integers
"""
class FP64(FixedPrecisionInt):
"""int() applied to these returns an integer that fits in 32 bits
"""
class FP32(FP64):
"""int() applied to these returns an integer that fits in 32 bits
"""
class FP16(FP32):
"""int() applied to these returns an integer that fits in 16 bits
"""
class FP8(FP16):
"""int() applied to these returns an integer that fits in 16 bits
"""
class FP1(FP8):
"""int() applied to these returns an integer that fits in 16 bits
"""
class Signed(FixedPrecisionInt):
"""These can be < 0
"""
class Unsigned(FixedPrecisionInt):
"""These can not be < 0
"""
class CheckedOverflow(FixedPrecisionInt):
"""raises OverflowError when results don't fit
"""
class UncheckedOverflow(FixedPrecisionInt):
"""take results module 2**N (for example)
"""
class FD(Exact):
"""fixed_decimal<n+m>
a signed fixed decimal number with n digits
before and m digits after the decimal point
"""
class Inexact(Number):
"""floating point arithmetic
"""
Util.assertTypeImplements(type(1), (FP32, BitNumber, Signed))
Util.assertTypeImplements(type(1L), (AbritraryPrecision, BitNumber, Signed))
Util.assertTypeImplements(type(1.0), (Inexact, BitNumber, Signed))
Scarecrow interface implementation
Python Interfaces - The Scarecrow Implementation
*Note: This is Jim's original proposal. Up to date user docs can be found
at http://www.zope.org/Wikis/Interfaces*
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 "deferred" 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 misleading, 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 inheritance rules apply.
Note that the definition of interface assertions makes composition
of interfaces straightforward. 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 example:
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.
Deferred classes
You cannot use interfaces as base classes. You can, however,
create "deferred" 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.deferred()):
"This is supposed to implement a stack"
__implements__=StackInterface
Attempts to call methods inherited from a deferred class will
raise Interface.BrokenImplementation exceptions.
Trial balloon: abstract implementations
Tim Peters has expressed the desire to provide abstract
implementations in interface definitions, where, presumably, an
abstract implementation uses only features defined by the
interface.
Perhaps if a method definition has a body (other than a doc
string), then the corresponding method in the deferred class
will not be deferred. This would not be hard to do in CPython
if I cheat and sniff at method bytecodes.
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)
ListBase=ListInterface.deferred()
class ListImplementer(Listbase):
def append(self, v): ...
In this example, we can create a base class, ListBase, that provides an
abstract implementation of 'push' and an implementation of append
that raises an error if not overridden.
Standard interfaces
The module Interface.Standard defines interfaces for standard
python objects.
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.
Note that the information contained in a non-method attribute
object might contain the attribute value's interface as well as
other information, such as an attribute's usage.
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 A lot more work is needed on the standard interface hierarchy.
...
import iclass, Util
Interface=Util.impliedInterface(
iclass.Interface, "Interface",
"""Interface of Interface objects
""")
iclass.Interface.__implements__=Interface
from iclass import Named, Class
from Basic import *
Util.assertTypeImplements(iclass.ClassType, Class)
from Number import *
from Mapping import *
del iclass # cruft
del Util # cruft
from iclass import Interface, Class, ClassTypes, Base, \
assertTypeImplements, _typeImplements #uuh..
from types import FunctionType
def impliedInterface(klass, __name__=None, __doc__=None):
"""Create an interface object from a class
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__
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 (k[:2]=='__' and k[-2:]=='__'):
continue
items[k]=v
for b in klass.__bases__: _ii(b, items)
return items
def objectImplements(object, tiget=_typeImplements.get):
"""Return the interfaces implemented by the object
"""
r=[]
t=type(object)
if t in ClassTypes:
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(implements)
else: _wi(implements, r.append)
return r
def instancesOfObjectImplements(klass, tiget=_typeImplements.get):
"""Return the interfaces that instanced implement (by default)
"""
r=[]
if type(klass) in ClassTypes:
if hasattr(klass, '__implements__'):
implements=klass.__implements__
else: return r
elif hasattr(klass, 'instancesImplements'):
# Hook for ExtensionClass. :)
implements=klass.instancesImplements()
else:
implements=tiget(klass,None)
if implements is not None:
if isinstance(implements,Interface): r.append(implements)
else: _wi(implements, r.append)
return r
def _wi(interfaces, append):
for i in interfaces:
if isinstance(i,Interface): append(i)
else: _wi(i, append)
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
from Exceptions import BrokenImplementation, DoesNotImplement
from Exceptions import BrokenMethodImplementation
from types import FunctionType
from Method import fromMethod, fromFunction
from _object import MethodTypes
def _verify(iface, candidate, tentative=0, vtype=None):
"""
Verify that 'candidate' might correctly implements 'iface'.
This involves:
o Making sure the candidate defines all the necessary methods
o Making sure the methods have the correct signature
o Making sure the candidate asserts that it implements the interface
Note that this isn't the same as verifying that the class does
implement the interface.
If optional tentative is true, suppress the "is implemented by" test.
"""
if vtype is 'c':
tester = iface.isImplementedByInstancesOf
else:
tester = iface.isImplementedBy
if not tentative and not tester( candidate ):
raise DoesNotImplement(iface)
for n, d in iface.namesAndDescriptions():
if not hasattr(candidate, n):
raise BrokenImplementation(iface, n)
attr = getattr(candidate, n)
if type(attr) is FunctionType:
# should never get here
meth = fromFunction(attr, n)
elif type(attr) in MethodTypes:
meth = fromMethod(attr, n)
else:
continue # must be an attribute...
d=d.getSignatureInfo()
meth = meth.getSignatureInfo()
mess = _incompat(d, meth)
if mess:
raise BrokenMethodImplementation(n, mess)
return 1
def verifyClass(iface, candidate, tentative=0):
return _verify(iface, candidate, tentative, vtype='c')
def verifyObject(iface, candidate, tentative=0):
return _verify(iface, candidate, tentative, vtype='o')
def _incompat(required, implemented):
#if (required['positional'] !=
# implemented['positional'][:len(required['positional'])]
# and implemented['kwargs'] is None):
# return 'imlementation has different argument names'
if len(implemented['required']) > len(required['required']):
return 'implementation requires too many arguments'
if ((len(implemented['positional']) < len(required['positional']))
and not implemented['varargs']):
return "implementation doesn't allow enough arguments"
if required['kwargs'] and not implemented['kwargs']:
return "implementation doesn't support keyword arguments"
if required['varargs'] and not implemented['varargs']:
return "implementation doesn't support variable arguments"
##############################################################################
class InterfaceBase: #
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Revision information:
$Id: _Element.py,v 1.2 2002/06/07 17:18:29 jim Exp $
"""
from _object import object
class Element(object):
# We can't say this yet because we don't have enough
# infrastructure in place.
#
#__implements__ = IElement
__tagged_values = {} __tagged_values = {}
def __init__(self, __name__, __doc__=''):
"""Create an 'attribute' description
"""
if not __doc__ and __name__.find(' ') >= 0:
__doc__ = __name__
__name__ = None
self.__name__=__name__
self.__doc__=__doc__
def getName(self): def getName(self):
""" Returns the name of the object. """ """ Returns the name of the object. """
...@@ -23,7 +58,3 @@ class InterfaceBase: ...@@ -23,7 +58,3 @@ class InterfaceBase:
""" Associates 'value' with 'key'. """ """ Associates 'value' with 'key'. """
self.__tagged_values[tag] = value self.__tagged_values[tag] = value
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Interface object implementation
Revision information:
$Id: _Interface.py,v 1.2 2002/06/07 17:18:29 jim Exp $
"""
from _InterfaceClass import Interface as InterfaceClass
Interface = InterfaceClass("Interface")
# Now we can create the interesting interfaces and wire them up:
def wire():
from Implements import implements
from Attribute import Attribute
from IAttribute import IAttribute
implements(Attribute, IAttribute)
from Method import Method
from IMethod import IMethod
implements(Method, IMethod)
from IInterface import IInterface
implements(InterfaceClass, IInterface)
wire()
del wire
del InterfaceClass
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Interface object implementation
Revision information:
$Id: _InterfaceClass.py,v 1.2 2002/06/07 17:18:29 jim Exp $
"""
from inspect import currentframe
import sys
from Method import Method, fromFunction
from Attribute import Attribute
from types import FunctionType
import Exceptions
from _Element import Element
from _object import isInstance
class Interface(Element):
"""Prototype (scarecrow) Interfaces Implementation
"""
# We can't say this yet because we don't have enough
# infrastructure in place.
#
#__implements__ = IInterface
def __init__(self, name, bases=(), attrs=None, __doc__=None,
__module__=None):
if __module__ is None:
if attrs is not None and attrs.has_key('__module__'):
__module__ = attrs['__module__']
del attrs['__module__']
else:
try:
# Figure out what module defined the interface.
# This is how cPython figures out the module of
# a class, but of course it does it in C. :-/
__module__ = currentframe().f_back.f_globals['__name__']
except (AttributeError, KeyError):
pass
self.__module__ = __module__
for b in bases:
if not isInstance(b, Interface):
raise TypeError, 'Expected base interfaces'
self.__bases__=bases
if attrs is None: attrs={}
if attrs.has_key('__doc__'):
if __doc__ is None: __doc__=attrs['__doc__']
del attrs['__doc__']
if __doc__ is not None:
self.__doc__=__doc__
else:
self.__doc__ = ""
Element.__init__(self, name, __doc__)
for k, v in attrs.items():
if isInstance(v, Attribute):
v.interface=name
if not v.__name__:
v.__name__ = k
elif isinstance(v, FunctionType):
attrs[k]=fromFunction(v, name)
else:
raise Exceptions.InvalidInterface(
"Concrete attribute, %s" % k)
self.__attrs = attrs
def getBases(self):
return self.__bases__
def extends(self, other, strict=1):
"""Does an interface extend another?
"""
if not strict and self is other:
return 1
for b in self.__bases__:
if b == other: return 1
if b.extends(other): return 1
return 0
def isEqualOrExtendedBy(self, other):
"""Same interface or extends?
"""
if self == other:
return 1
return other.extends(self)
def isImplementedBy(self, object):
"""Does the given object implement the interface?
"""
i = getImplements(object)
if i is not None:
return visitImplements(
i, object, self.isEqualOrExtendedBy, self._getInterface)
return 0
def isImplementedByInstancesOf(self, klass):
"""Do instances of the given class implement the interface?
"""
i = getImplementsOfInstances(klass)
if i is not None:
return visitImplements(
i, klass, self.isEqualOrExtendedBy, self._getInterface)
return 0
def names(self, all=0):
"""Return the attribute names defined by the interface
"""
if not all:
return self.__attrs.keys()
r = {}
for name in self.__attrs.keys():
r[name] = 1
for base in self.__bases__:
for name in base.names():
r[name] = 1
return r.keys()
def namesAndDescriptions(self, all=0):
"""Return the attribute names and descriptions defined by the interface
"""
if not all:
return self.__attrs.items()
r = {}
for name, d in self.__attrs.items():
r[name] = d
for base in self.__bases__:
for name, d in base.namesAndDescriptions():
if not r.has_key(name):
r[name] = d
return r.items()
def getDescriptionFor(self, name):
"""Return the attribute description for the given name
"""
r = self.queryDescriptionFor(name)
if r is not None:
return r
raise KeyError, name
def queryDescriptionFor(self, name, default=None):
"""Return the attribute description for the given name
"""
r = self.__attrs.get(name, self)
if r is not self:
return r
for base in self.__bases__:
r = base.queryDescriptionFor(name, self)
if r is not self:
return r
return default
def deferred(self):
"""Return a defered class corresponding to the interface
"""
if hasattr(self, "_deferred"): return self._deferred
klass={}
exec "class %s: pass" % self.__name__ in klass
klass=klass[self.__name__]
self.__d(klass.__dict__)
self._deferred=klass
return klass
def _getInterface(self, ob, name):
'''
Retrieve a named interface.
'''
return None
def __d(self, dict):
for k, v in self.__attrs.items():
if isInstance(v, Method) and not dict.has_key(k):
dict[k]=v
for b in self.__bases__: b.__d(dict)
def __repr__(self):
name = self.__name__
m = self.__module__
if m:
name = '%s.%s' % (m, name)
return "<%s %s at %x>" % (self.__class__.__name__, name, id(self))
def __reduce__(self):
return self.__name__
# We import this here to deal with module dependencies.
from Implements import getImplementsOfInstances, visitImplements, getImplements
from Implements import instancesOfObjectImplements
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Interfaces
from Standard import Base This package implements the Python "scarecrow" proposal.
import iclass The package exports a single name, 'Interface' directly. Interface
new=iclass.Interface is used to create an interface with a class statement, as in:
InterfaceInterface=iclass.InterfaceInterface
# del iclass
from Util import impliedInterface from Interface import Interface
from Util import assertTypeImplements, objectImplements, instancesOfObjectImplements
from Attr import Attribute class IMyInterface(Interface):
from Method import Method '''Interface documentation
'''
from Exceptions import BrokenImplementation def meth(arg1, arg2):
'''Documentation for meth
'''
# Note that there is no self argument
To find out what you can do with interfaces, see the interface
interface, IInterface in the IInterface module.
The package has several public modules:
o Attribute has the implementation for interface attributes
for people who want to build interfaces by hand.
(Maybe someone should cry YAGNI for this. ;)
o Document has a utility for documenting an interface as structured text.
o Exceptions has the interface-defined exceptions
o IAttribute defines the attribute descriptor interface.
o IElement defined the base interface for IAttribute, IInterface,
and IMethod.
o IInterface defines the interface interface
o IMethod defined the method interface.
o Implements has various utilities for examining interface assertions.
o Method has the implementation for interface methods. See above.
o Verify has utilities for verifying (sort of) interfaces.
See the module doc strings for more information.
There is also a script, pyself.py in the package that can be used to
create interface skeletins. Run it without arguments to get documentation.
Revision information:
$Id: __init__.py,v 1.6 2002/06/07 17:18:29 jim Exp $
"""
from _Interface import Interface
Base = Interface # XXX We need to stamp out Base usage
from pprint import interface_as_stx
from verify import verify_class_implementation
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Provide a halfway believable rendition of Python 2.2's object
$Id: _object.py,v 1.2 2002/06/07 17:18:29 jim Exp $
"""
class _x:
def m(self):
pass
ClassTypes = (type(_x), )
MethodTypes = (type(_x.m), )
isInstance = isinstance
try:
object
except NameError:
# Python 2.1
try:
from ExtensionClass import Base as object
except ImportError:
# Python 2.1 wo ExtensionClass
class object: pass
else:
# Python 2.1 w ExtensionClass
def isInstance(ob, klass):
if type(type(ob)) is type(klass):
return isinstance(ob, klass)
return 0
class _x(object):
def m(self): pass
ClassTypes += (type(_x), )
MethodTypes += (type(_x.m), )
else:
# Python 2.2
ClassTypes += (type, )
object = object
isinstance = isinstance
...@@ -14,15 +14,7 @@ from Attr import Attribute ...@@ -14,15 +14,7 @@ from Attr import Attribute
from types import FunctionType, ClassType from types import FunctionType, ClassType
import Exceptions import Exceptions
from InterfaceBase import InterfaceBase from InterfaceBase import InterfaceBase
from _object import ClassTypes, isInstance
try:
from ExtensionClass import Base
except ImportError:
ClassTypes = (ClassType,)
else:
class dummy (Base): pass
ClassTypes = (type(dummy), ClassType)
_typeImplements={} _typeImplements={}
...@@ -34,7 +26,7 @@ class Interface(InterfaceBase): ...@@ -34,7 +26,7 @@ class Interface(InterfaceBase):
"""Create a new interface """Create a new interface
""" """
for b in bases: for b in bases:
if not isinstance(b, Interface): if not isInstance(b, Interface):
raise TypeError, 'Expected base interfaces' raise TypeError, 'Expected base interfaces'
self.__bases__=bases self.__bases__=bases
self.__name__=name self.__name__=name
...@@ -54,12 +46,12 @@ class Interface(InterfaceBase): ...@@ -54,12 +46,12 @@ class Interface(InterfaceBase):
self.__doc__ = "" self.__doc__ = ""
for k, v in attrs.items(): for k, v in attrs.items():
if isinstance(v, Method): if isInstance(v, Method):
v.interface=name v.interface=name
v.__name__=k v.__name__=k
elif isinstance(v, FunctionType): elif isInstance(v, FunctionType):
attrs[k]=Method.fromFunction(v, name) attrs[k]=Method.fromFunction(v, name)
elif not isinstance(v, Attribute): elif not isInstance(v, Attribute):
raise Exceptions.InvalidInterface( raise Exceptions.InvalidInterface(
"Concrete attribute, %s" % k) "Concrete attribute, %s" % k)
...@@ -91,7 +83,7 @@ class Interface(InterfaceBase): ...@@ -91,7 +83,7 @@ class Interface(InterfaceBase):
implements=tiget(t, None) implements=tiget(t, None)
if implements is None: return 0 if implements is None: return 0
if isinstance(implements,Interface): if isInstance(implements,Interface):
return implements is self or implements.extends(self) return implements is self or implements.extends(self)
else: else:
return self.__any(implements) return self.__any(implements)
...@@ -112,7 +104,7 @@ class Interface(InterfaceBase): ...@@ -112,7 +104,7 @@ class Interface(InterfaceBase):
if implements is None: return 0 if implements is None: return 0
if isinstance(implements,Interface): if isInstance(implements,Interface):
return implements is self or implements.extends(self) return implements is self or implements.extends(self)
else: else:
return self.__any(implements) return self.__any(implements)
...@@ -150,7 +142,7 @@ class Interface(InterfaceBase): ...@@ -150,7 +142,7 @@ class Interface(InterfaceBase):
def __d(self, dict): def __d(self, dict):
for k, v in self.__attrs.items(): for k, v in self.__attrs.items():
if isinstance(v, Method) and not dict.has_key(k): if isInstance(v, Method) and not dict.has_key(k):
dict[k]=v dict[k]=v
for b in self.__bases__: b.__d(dict) for b in self.__bases__: b.__d(dict)
...@@ -158,7 +150,7 @@ class Interface(InterfaceBase): ...@@ -158,7 +150,7 @@ class Interface(InterfaceBase):
def __any(self, interfaces): def __any(self, interfaces):
for i in interfaces: for i in interfaces:
if isinstance(i,Interface): if isInstance(i,Interface):
if i is self or i.extends(self): return 1 if i is self or i.extends(self): return 1
else: else:
if self.__any(i): return 1 if self.__any(i): return 1
...@@ -173,7 +165,7 @@ Base=Interface("Interface") ...@@ -173,7 +165,7 @@ Base=Interface("Interface")
class Named(Base): class Named(Base):
"Objects that have a name." "Objects that have a name."
__name__=Attribute("The name of the object") __name__ = Attribute("The name of the object")
class Class(Named): class Class(Named):
"""Implement shared instance behavior and create instances """Implement shared instance behavior and create instances
......
Attr.py
Basic.py
Exceptions.py
Mapping.py
Method.py
Number.py
Standard.py
Util.py
__init__.py
iclass.py
#!/usr/bin/env python
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Generate method skeletins for intefaces.
Usage: python pyskel.py dotted_name
Example:
cd lib/python
python Interfaces/pyskel.py Zope.App.Security.IRoleService.IRoleService
The dotted name is the module name and interface object name connected
with a dot.
Revision information: $Id: pyskel.py,v 1.2 2002/06/07 17:18:29 jim Exp $
"""
import sys, os, re
sys.path.insert(0, os.getcwd())
from _object import isInstance
from types import ModuleType
from Interface.Method import Method
from Interface.Attribute import Attribute
class_re = re.compile(r'\s*class\s+([a-zA-Z_][a-zA-Z0-9_]*)')
def_re = re.compile(r'\s*def\s+([a-zA-Z_][a-zA-Z0-9_]*)')
attr_re = re.compile(r'\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*Attribute')
def rskel(iface, print_iface=1):
name = "%s.%s" % (iface.__module__, iface.__name__)
file = resolve(iface.__module__).__file__
if file.endswith('pyc'):
file = file[:-1]
order = guessOrder(open(file))
namesAndDescriptions = getAttributesInOrder(iface, order)
if namesAndDescriptions and print_iface:
print
print " ######################################"
print " # from:", name
for aname, ades in namesAndDescriptions:
if isInstance(ades, Method):
sig = ades.getSignatureString()[1:-1]
if sig: sig = "self, %s" % sig
else: sig = "self"
print
print " def %s(%s):" % (aname, sig)
print " 'See %s'" % name
elif isInstance(ades, Attribute):
print
print " # See %s" % name
print " %s = None" %aname
for base in iface.__bases__:
if base.__name__ not in ('Interface',):
rskel(base)
def skel(name):
iface = resolve(name)
class_name = iface.__name__
if class_name.startswith('I'):
class_name = class_name[1:]
print "from %s import %s" % (iface.__module__, iface.__name__)
print
print "class %s:" %class_name
print
print " __implements__ = ", iface.__name__
print
print " ############################################################"
print " # Implementation methods for interface"
print " #", name
rskel(iface, 0)
print
print " #"
print " ############################################################"
def resolve(name, _silly=('__doc__',), _globals={}):
# Support for file path syntax; this way I can use TAB to search for
# the module.
if '/' in name or name.endswith('.py'):
# We got a relative path. Let's try to get the full one and then
# make a package path out of it.
if not name.startswith('/'):
cwd = os.getcwd()
for path in sys.path[1:]: # Yeah, we need to exclude the cwd itself
if path != '' and cwd.startswith(path):
name = os.path.join(cwd[len(path)+1:], name)
name = os.path.normpath(name)
break
# get rid of the file ending :)
if name.endswith('.py'):
name = name[:-3]
name = name.replace('/', '.')
# Now to the regular lookup
if name[:1]=='.':
name='ZopeProducts'+name
if name[-1:] == '.':
name = name[:-1]
repeat = 1
else:
repeat = 0
names=name.split('.')
last=names[-1]
mod='.'.join(names[:-1])
while 1:
m=__import__(mod, _globals, _globals, _silly)
try:
a=getattr(m, last)
except AttributeError:
pass
else:
if not repeat or (type(a) is not ModuleType):
return a
mod += '.' + last
def guessOrder(source_file):
order = {} # { class name -> list of methods }
lines = source_file.readlines()
class_name = None
for line in lines:
m = class_re.match(line)
if m and m.groups():
class_name = m.groups()[0]
else:
for m in (def_re.match(line),
attr_re.match(line)):
if m and m.groups():
def_name = m.groups()[0]
name_order = order.get(class_name)
if name_order is None:
name_order = []
order[class_name] = name_order
name_order.append(def_name)
return order
def getAttributesInOrder(interface, order):
# order is the dictionary returned from guessOrder().
# interface is a metaclass-based interface object.
name_order = order.get(interface.__name__)
if name_order is None:
# Something's wrong. Oh well.
return interface.__dict__.items()
else:
items = []
for key, value in interface.namesAndDescriptions():
if key in name_order:
items.append((name_order.index(key), key, value))
else:
items.append((99999, key, value)) # Go to end.
items.sort()
return map(lambda item: item[1:], items)
if __name__ == '__main__':
for a in sys.argv[1:]:
skel(a)
R=$1
M=Interface
for f in test.py `cat pyfiles`; do
python1.5.2 /projects/sbin/tabnanny.py $f
done
mkdir "$M-$R" "$M-$R/Interface"
for f in `cat pyfiles`; do
cp $f "$M-$R/Interface/"
done
for f in README.txt CHANGES.txt test.py; do
cp $f "$M-$R/"
done
tar cvf "$M-$R.tar" "$M-$R"
rm -f "$M-$R.tar.gz"
gzip "$M-$R.tar"
import Interface
class C:
def m1(self, a, b):
"return 1"
return 1
def m2(self, a, b):
"return 2"
return 2
IC=Interface.impliedInterface(C)
print "should be 0:", IC.isImplementedByInstancesOf(C)
C.__implements__=IC
print "should be 1:", IC.isImplementedByInstancesOf(C)
class I1(Interface.Base):
def ma(self):
"blah"
class I2(I1): pass
class I3(Interface.Base): pass
class I4(Interface.Base): pass
class A(I1.deferred()):
__implements__=I1
class B:
__implements__=I2, I3
class D(A, B): pass
class E(A, B):
__implements__ = A.__implements__, C.__implements__
print
for c in A, B, C, D, E:
print "%s implements: %s" % (
c.__name__, Interface.instancesOfObjectImplements(c))
print
for c in A, B, C, D, E:
print "an instance of %s implements: %s" % (
c.__name__,
Interface.objectImplements(c()))
for i in I1, I2, I3, I4, IC:
print
for c in A, B, C, D, E:
print "%s is implemented by instances of %s? %s" % (
i.__name__, c.__name__,
i.isImplementedByInstancesOf(c))
print
for c in A, B, C, D, E:
print "%s is implemented by an instance of %s? %s" % (
i.__name__, c.__name__,
i.isImplementedBy(c()))
a=A()
try:
a.ma()
print "something's wrong, this should have failed!"
except:
pass
FooInterface = Interface.new('FooInterface')
print Interface.interface_as_stx(FooInterface)
BarInterface = Interface.new('BarInterface', [FooInterface])
print Interface.interface_as_stx(BarInterface)
BobInterface = Interface.new('BobInterface')
BazInterface = Interface.new('BazInterface', [BobInterface, BarInterface])
print Interface.interface_as_stx(BazInterface)
ints = [BazInterface, BobInterface, BarInterface, FooInterface]
for int in ints:
for int2 in ints:
if int.extends(int2):
print "%s DOES extend %s" % (int.__name__, int2.__name__)
else:
print "%s DOES NOT extend %s" % (int.__name__, int2.__name__)
print "\n"
# methods and pretty printing
class AnAbstractBaseClass:
""" This is an Abstract Base Class """
foobar = "fuzzed over beyond all recognition"
def aMethod(self, foo, bar, bingo):
""" This is aMethod """
pass
def anotherMethod(self, foo=6, bar="where you get sloshed", bingo=(1,3,)):
""" This is anotherMethod """
pass
def wammy(self, zip, *argues):
""" yadda yadda """
pass
def useless(self, **keywords):
""" useless code is fun! """
pass
AnABCInterface = Interface.impliedInterface(AnAbstractBaseClass)
print Interface.interface_as_stx(AnABCInterface)
class AConcreteClass:
""" A concrete class """
__implements__ = AnABCInterface,
def aMethod(self, foo, bar, bingo):
""" This is aMethod """
return "barf!"
def anotherMethod(self, foo=6, bar="where you get sloshed", bingo=(1,3,)):
""" This is anotherMethod """
return "barf!"
def wammy(self, zip, *argues):
""" yadda yadda """
return "barf!"
def useless(self, **keywords):
""" useless code is fun! """
return "barf!"
concrete_instance = AConcreteClass()
class Blah:
pass
blah_instance = Blah()
if AnABCInterface.isImplementedBy(concrete_instance):
print "%s is an instance that implements %s" % (concrete_instance, AnABCInterface.__name__)
if AnABCInterface.isImplementedByInstancesOf(AConcreteClass):
print "%s is a class that implements %s" % (AConcreteClass, AnABCInterface.__name__)
if not AnABCInterface.isImplementedBy(blah_instance):
print "%s is NOT an instance that implements %s" % (blah_instance, AnABCInterface.__name__)
if not AnABCInterface.isImplementedByInstancesOf(Blah):
print "%s is NOT a class that implements %s" % (Blah, AnABCInterface.__name__)
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
from Interface import Interface
class IFoo( Interface ):
"""
Dummy interface for unit tests.
"""
def bar( baz ):
"""
Just a note.
"""
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
""" Packagize. """
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
from Interface.tests.IFoo import IFoo
__implements__ = IFoo
def bar( baz ):
pass
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
if not sys.modules.has_key('Testing'):
p0 = sys.path[0]
if p0 and __name__ == '__main__':
os.chdir(p0)
p0 = ''
p = d = os.path.abspath(os.curdir)
while d:
if os.path.isdir(os.path.join(p, 'Testing')):
sys.path[:1] = [p0, os.pardir, p]
break
p, d = os.path.split(p)
else:
print 'Unable to locate Testing package.'
sys.exit(1)
import Testing, unittest
execfile(os.path.join(os.path.split(Testing.__file__)[0], 'common.py'))
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Revision information:
$Id: testDocument.py,v 1.2 2002/06/07 17:18:29 jim Exp $
"""
from unittest import TestCase, TestSuite, main, makeSuite
from Interface import Interface
from Interface.Attribute import Attribute
class Test(TestCase):
def testBlech(self):
from Interface.Document import asStructuredText
self.assertEqual(asStructuredText(I2), '''\
I2
I2 doc
This interface extends:
o _I1
Attributes:
a1 -- no documentation
a2 -- a2 doc
Methods:
f21() -- f21 doc
f22() -- no documentation
f23() -- f23 doc
''')
def test_suite():
return TestSuite((
makeSuite(Test),
))
class _I1(Interface):
def f11(): pass
def f12(): pass
class I2(_I1):
"I2 doc"
a1 = Attribute('a1')
a2 = Attribute('a2', 'a2 doc')
def f21(): "f21 doc"
def f22(): pass
def f23(): "f23 doc"
if __name__=='__main__':
main(defaultTest='test_suite')
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
from __future__ import nested_scopes
from Interface import Interface
from Interface.Implements import implements
from Interface.Exceptions import DoesNotImplement, BrokenImplementation
from Interface.Exceptions import BrokenMethodImplementation
import unittest, sys
class Test(unittest.TestCase):
def testSimple(self):
class I(Interface):
def f(): pass
class C: pass
self.assertRaises(BrokenImplementation, implements, C, I)
C.f=lambda self: None
implements(C, I)
self.assertEqual(C.__implements__, I)
def testAdd(self):
class I(Interface):
def f(): pass
class I2(Interface):
def g(): pass
class C:
__implements__=I2
self.assertRaises(BrokenImplementation, implements, C, I)
self.assertRaises(BrokenImplementation, implements, C, I2)
C.f=lambda self: None
implements(C, I)
self.assertEqual(C.__implements__, (I2, I))
self.assertRaises(BrokenImplementation, implements, C, I2)
C.g=C.f
implements(C, I)
implements(C, I2)
def test_suite():
loader=unittest.TestLoader()
return loader.loadTestsFromTestCase(Test)
if __name__=='__main__':
unittest.TextTestRunner().run(test_suite())
##############################################################################
# usage from lib/python:
# #
# python unittest.py Interface.unittests.suite # Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
import unittest import unittest
import Interface import Interface
from unitfixtures import * # hehehe from unitfixtures import * # hehehe
from Interface.Exceptions import BrokenImplementation
from Interface.Implements import instancesOfObjectImplements
from Interface.Implements import objectImplements
from Interface import Interface
from Interface.Attribute import Attribute
class InterfaceTests(unittest.TestCase): class InterfaceTests(unittest.TestCase):
...@@ -30,7 +44,23 @@ class InterfaceTests(unittest.TestCase): ...@@ -30,7 +44,23 @@ class InterfaceTests(unittest.TestCase):
assert not I2.isImplementedByInstancesOf(D) assert not I2.isImplementedByInstancesOf(D)
assert not I2.isImplementedByInstancesOf(E) assert not I2.isImplementedByInstancesOf(E)
def testClassImplements(self): def testUtil(self):
f = instancesOfObjectImplements
assert IC in f(C)
assert I1 in f(A)
assert not I1 in f(C)
assert I2 in f(B)
assert not I2 in f(C)
f = objectImplements
assert IC in f(C())
assert I1 in f(A())
assert not I1 in f(C())
assert I2 in f(B())
assert not I2 in f(C())
def testObjectImplements(self):
assert IC.isImplementedBy(C()) assert IC.isImplementedBy(C())
assert I1.isImplementedBy(A()) assert I1.isImplementedBy(A())
...@@ -47,7 +77,7 @@ class InterfaceTests(unittest.TestCase): ...@@ -47,7 +77,7 @@ class InterfaceTests(unittest.TestCase):
def testDeferredClass(self): def testDeferredClass(self):
a = A() a = A()
self.assertRaises(Interface.BrokenImplementation, a.ma) self.assertRaises(BrokenImplementation, a.ma)
def testInterfaceExtendsInterface(self): def testInterfaceExtendsInterface(self):
...@@ -60,13 +90,55 @@ class InterfaceTests(unittest.TestCase): ...@@ -60,13 +90,55 @@ class InterfaceTests(unittest.TestCase):
assert not BarInterface.extends(BazInterface) assert not BarInterface.extends(BazInterface)
def testVerifyImplementation(self): def testVerifyImplementation(self):
assert Interface.verify_class_implementation(FooInterface, Foo) from Interface.Verify import verifyClass
assert Interface.verify_class_implementation(Interface.InterfaceInterface, I1) assert verifyClass(FooInterface, Foo)
assert Interface.isImplementedBy(I1)
def test_names(self):
names = list(_I2.names()); names.sort()
self.assertEqual(names, ['f21', 'f22', 'f23'])
names = list(_I2.names(1)); names.sort()
self.assertEqual(names, ['a1', 'f11', 'f12', 'f21', 'f22', 'f23'])
def test_namesAndDescriptions(self):
names = [nd[0] for nd in _I2.namesAndDescriptions()]; names.sort()
self.assertEqual(names, ['f21', 'f22', 'f23'])
names = [nd[0] for nd in _I2.namesAndDescriptions(1)]; names.sort()
self.assertEqual(names, ['a1', 'f11', 'f12', 'f21', 'f22', 'f23'])
for name, d in _I2.namesAndDescriptions(1):
self.assertEqual(name, d.__name__)
def test_getDescriptionFor(self):
self.assertEqual(_I2.getDescriptionFor('f11').__name__, 'f11')
self.assertEqual(_I2.getDescriptionFor('f22').__name__, 'f22')
self.assertEqual(_I2.queryDescriptionFor('f33', self), self)
self.assertRaises(KeyError, _I2.getDescriptionFor, 'f33')
def testAttr(self):
description = _I2.getDescriptionFor('a1')
self.assertEqual(description.__name__, 'a1')
self.assertEqual(description.__doc__, 'This is an attribute')
def suite(): class _I1(Interface):
return unittest.makeSuite(InterfaceTests)
a1 = Attribute("This is an attribute")
def f11(): pass
def f12(): pass
class _I2(_I1):
def f21(): pass
def f22(): pass
def f23(): pass
def test_suite():
return unittest.makeSuite(InterfaceTests)
def main():
unittest.TextTestRunner().run(test_suite())
if __name__=="__main__":
main()
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Revision information:
$Id: testVerify.py,v 1.2 2002/06/07 17:18:29 jim Exp $
"""
from __future__ import nested_scopes
from Interface import Interface
from Interface.Verify import verifyClass, verifyObject
from Interface.Exceptions import DoesNotImplement, BrokenImplementation
from Interface.Exceptions import BrokenMethodImplementation
import unittest, sys
class Test(unittest.TestCase):
def testNotImplemented(self):
class C: pass
class I(Interface): pass
self.assertRaises(DoesNotImplement, verifyClass, I, C)
C.__implements__=I
verifyClass(I, C)
def testMissingAttr(self):
class I(Interface):
def f(): pass
class C:
__implements__=I
self.assertRaises(BrokenImplementation, verifyClass, I, C)
C.f=lambda self: None
verifyClass(I, C)
def testWrongArgs(self):
class I(Interface):
def f(a): pass
class C:
def f(self, b): pass
__implements__=I
# We no longer require names to match.
#self.assertRaises(BrokenMethodImplementation, verifyClass, I, C)
C.f=lambda self, a: None
verifyClass(I, C)
C.f=lambda self, **kw: None
self.assertRaises(BrokenMethodImplementation, verifyClass, I, C)
C.f=lambda self, a, *args: None
verifyClass(I, C)
C.f=lambda self, a, *args, **kw: None
verifyClass(I, C)
C.f=lambda self, *args: None
verifyClass(I, C)
def testExtraArgs(self):
class I(Interface):
def f(a): pass
class C:
def f(self, a, b): pass
__implements__=I
self.assertRaises(BrokenMethodImplementation, verifyClass, I, C)
C.f=lambda self, a: None
verifyClass(I, C)
C.f=lambda self, a, b=None: None
verifyClass(I, C)
def testNoVar(self):
class I(Interface):
def f(a, *args): pass
class C:
def f(self, a): pass
__implements__=I
self.assertRaises(BrokenMethodImplementation, verifyClass, I, C)
C.f=lambda self, a, *foo: None
verifyClass(I, C)
def testNoKW(self):
class I(Interface):
def f(a, **args): pass
class C:
def f(self, a): pass
__implements__=I
self.assertRaises(BrokenMethodImplementation, verifyClass, I, C)
C.f=lambda self, a, **foo: None
verifyClass(I, C)
def testModule(self):
from Interface.tests.IFoo import IFoo
from Interface.tests import dummy
verifyObject(IFoo, dummy)
def test_suite():
loader=unittest.TestLoader()
return loader.loadTestsFromTestCase(Test)
if __name__=='__main__':
unittest.TextTestRunner().run(test_suite())
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
import unittest, sys
from Interface.Implements import visitImplements
from Interface import Interface
from Interface.Exceptions import BadImplements
class I1(Interface): pass
class I2(Interface): pass
class I3(Interface): pass
class Test(unittest.TestCase):
def testSimpleImplements(self):
data=[]
visitImplements(I1, None, data.append)
self.assertEqual(data, [I1])
def testSimpleBadImplements(self):
data=[]
self.assertRaises(BadImplements,
visitImplements, unittest, None, data.append)
def testComplexImplements(self):
data=[]
visitImplements((I1, (I2, I3)), None, data.append)
data = map(lambda i: i.__name__, data)
self.assertEqual(data, ['I1', 'I2', 'I3'])
def testComplexBadImplements(self):
data=[]
self.assertRaises(BadImplements,
visitImplements, (I1, (I2, unittest)),
None, data.append)
def test_suite():
loader=unittest.TestLoader()
return loader.loadTestsFromTestCase(Test)
if __name__=='__main__':
unittest.TextTestRunner().run(test_suite())
##############################################################################
import Interface #
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
from Interface import Interface
from Interface.Attribute import Attribute
class mytest(Interface):
pass
class C: class C:
def m1(self, a, b): def m1(self, a, b):
...@@ -12,19 +28,30 @@ class C: ...@@ -12,19 +28,30 @@ class C:
# testInstancesOfClassImplements # testInstancesOfClassImplements
IC=Interface.impliedInterface(C)
# YAGNI IC=Interface.impliedInterface(C)
class IC(Interface):
def m1(a, b):
"return 1"
def m2(a, b):
"return 2"
C.__implements__=IC C.__implements__=IC
class I1(Interface.Base): class I1(Interface):
def ma(self): def ma():
"blah" "blah"
class I2(I1): pass class I2(I1): pass
class I3(Interface.Base): pass class I3(Interface): pass
class I4(Interface.Base): pass class I4(Interface): pass
class A(I1.deferred()): class A(I1.deferred()):
__implements__=I1 __implements__=I1
...@@ -38,21 +65,21 @@ class E(A, B): ...@@ -38,21 +65,21 @@ class E(A, B):
__implements__ = A.__implements__, C.__implements__ __implements__ = A.__implements__, C.__implements__
class FooInterface(Interface.Base): class FooInterface(Interface):
""" This is an Abstract Base Class """ """ This is an Abstract Base Class """
foobar = Interface.Attribute("fuzzed over beyond all recognition") foobar = Attribute("fuzzed over beyond all recognition")
def aMethod(self, foo, bar, bingo): def aMethod(foo, bar, bingo):
""" This is aMethod """ """ This is aMethod """
def anotherMethod(self, foo=6, bar="where you get sloshed", bingo=(1,3,)): def anotherMethod(foo=6, bar="where you get sloshed", bingo=(1,3,)):
""" This is anotherMethod """ """ This is anotherMethod """
def wammy(self, zip, *argues): def wammy(zip, *argues):
""" yadda yadda """ """ yadda yadda """
def useless(self, **keywords): def useless(**keywords):
""" useless code is fun! """ """ useless code is fun! """
class Foo: class Foo:
...@@ -83,7 +110,8 @@ foo_instance = Foo() ...@@ -83,7 +110,8 @@ foo_instance = Foo()
class Blah: class Blah:
pass pass
FunInterface = Interface.new('FunInterface') new = Interface.__class__
BarInterface = Interface.new('BarInterface', [FunInterface]) FunInterface = new('FunInterface')
BobInterface = Interface.new('BobInterface') BarInterface = new('BarInterface', [FunInterface])
BazInterface = Interface.new('BazInterface', [BobInterface, BarInterface]) BobInterface = new('BobInterface')
BazInterface = new('BazInterface', [BobInterface, BarInterface])
from Exceptions import BrokenImplementation, DoesNotImplement, BrokenMethodImplementation from Exceptions import BrokenImplementation, DoesNotImplement, BrokenMethodImplementation
from Method import Method from Method import Method
import types from types import FunctionType
from _object import MethodTypes
def verify_class_implementation(iface, klass): def verify_class_implementation(iface, klass):
""" """
...@@ -23,9 +24,9 @@ def verify_class_implementation(iface, klass): ...@@ -23,9 +24,9 @@ def verify_class_implementation(iface, klass):
raise BrokenImplementation(iface, n) raise BrokenImplementation(iface, n)
attr = getattr(klass, n) attr = getattr(klass, n)
if type(attr) is types.FunctionType: if type(attr) is FunctionType:
meth = Method().fromFunction(attr, n) meth = Method().fromFunction(attr, n)
elif type(attr) is types.MethodType: elif type(attr) in MethodTypes:
meth = Method().fromMethod(attr, n) meth = Method().fromMethod(attr, n)
else: else:
continue # must be an attribute... continue # must be an attribute...
......
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