Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Z
ZODB
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kirill Smelkov
ZODB
Commits
a3c091cb
Commit
a3c091cb
authored
Mar 02, 2005
by
Tim Peters
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Stitch in current Zope3 versions of zope.{interface, testing}.
parent
8bfe1f10
Changes
54
Show whitespace changes
Inline
Side-by-side
Showing
54 changed files
with
13003 additions
and
0 deletions
+13003
-0
trunk/src/zope/interface/DEPENDENCIES.cfg
trunk/src/zope/interface/DEPENDENCIES.cfg
+1
-0
trunk/src/zope/interface/PUBLICATION.cfg
trunk/src/zope/interface/PUBLICATION.cfg
+8
-0
trunk/src/zope/interface/README.txt
trunk/src/zope/interface/README.txt
+697
-0
trunk/src/zope/interface/SETUP.cfg
trunk/src/zope/interface/SETUP.cfg
+5
-0
trunk/src/zope/interface/__init__.py
trunk/src/zope/interface/__init__.py
+80
-0
trunk/src/zope/interface/_flatten.py
trunk/src/zope/interface/_flatten.py
+37
-0
trunk/src/zope/interface/_zope_interface_coptimizations.c
trunk/src/zope/interface/_zope_interface_coptimizations.c
+553
-0
trunk/src/zope/interface/adapter.py
trunk/src/zope/interface/adapter.py
+732
-0
trunk/src/zope/interface/adapter.txt
trunk/src/zope/interface/adapter.txt
+505
-0
trunk/src/zope/interface/advice.py
trunk/src/zope/interface/advice.py
+192
-0
trunk/src/zope/interface/common/__init__.py
trunk/src/zope/interface/common/__init__.py
+2
-0
trunk/src/zope/interface/common/idatetime.py
trunk/src/zope/interface/common/idatetime.py
+577
-0
trunk/src/zope/interface/common/interfaces.py
trunk/src/zope/interface/common/interfaces.py
+98
-0
trunk/src/zope/interface/common/mapping.py
trunk/src/zope/interface/common/mapping.py
+127
-0
trunk/src/zope/interface/common/sequence.py
trunk/src/zope/interface/common/sequence.py
+129
-0
trunk/src/zope/interface/common/tests/__init__.py
trunk/src/zope/interface/common/tests/__init__.py
+2
-0
trunk/src/zope/interface/common/tests/basemapping.py
trunk/src/zope/interface/common/tests/basemapping.py
+115
-0
trunk/src/zope/interface/common/tests/test_idatetime.py
trunk/src/zope/interface/common/tests/test_idatetime.py
+49
-0
trunk/src/zope/interface/declarations.py
trunk/src/zope/interface/declarations.py
+1388
-0
trunk/src/zope/interface/document.py
trunk/src/zope/interface/document.py
+121
-0
trunk/src/zope/interface/exceptions.py
trunk/src/zope/interface/exceptions.py
+69
-0
trunk/src/zope/interface/human.txt
trunk/src/zope/interface/human.txt
+152
-0
trunk/src/zope/interface/interface.py
trunk/src/zope/interface/interface.py
+942
-0
trunk/src/zope/interface/interfaces.py
trunk/src/zope/interface/interfaces.py
+681
-0
trunk/src/zope/interface/ro.py
trunk/src/zope/interface/ro.py
+63
-0
trunk/src/zope/interface/tests/__init__.py
trunk/src/zope/interface/tests/__init__.py
+2
-0
trunk/src/zope/interface/tests/dummy.py
trunk/src/zope/interface/tests/dummy.py
+25
-0
trunk/src/zope/interface/tests/foodforthought.txt
trunk/src/zope/interface/tests/foodforthought.txt
+61
-0
trunk/src/zope/interface/tests/ifoo.py
trunk/src/zope/interface/tests/ifoo.py
+28
-0
trunk/src/zope/interface/tests/m1.py
trunk/src/zope/interface/tests/m1.py
+23
-0
trunk/src/zope/interface/tests/m2.py
trunk/src/zope/interface/tests/m2.py
+17
-0
trunk/src/zope/interface/tests/odd.py
trunk/src/zope/interface/tests/odd.py
+129
-0
trunk/src/zope/interface/tests/test_adapter.py
trunk/src/zope/interface/tests/test_adapter.py
+297
-0
trunk/src/zope/interface/tests/test_advice.py
trunk/src/zope/interface/tests/test_advice.py
+177
-0
trunk/src/zope/interface/tests/test_declarations.py
trunk/src/zope/interface/tests/test_declarations.py
+366
-0
trunk/src/zope/interface/tests/test_document.py
trunk/src/zope/interface/tests/test_document.py
+71
-0
trunk/src/zope/interface/tests/test_element.py
trunk/src/zope/interface/tests/test_element.py
+44
-0
trunk/src/zope/interface/tests/test_interface.py
trunk/src/zope/interface/tests/test_interface.py
+296
-0
trunk/src/zope/interface/tests/test_odd_declarations.py
trunk/src/zope/interface/tests/test_odd_declarations.py
+204
-0
trunk/src/zope/interface/tests/test_sorting.py
trunk/src/zope/interface/tests/test_sorting.py
+49
-0
trunk/src/zope/interface/tests/test_verify.py
trunk/src/zope/interface/tests/test_verify.py
+196
-0
trunk/src/zope/interface/tests/unitfixtures.py
trunk/src/zope/interface/tests/unitfixtures.py
+142
-0
trunk/src/zope/interface/verify.py
trunk/src/zope/interface/verify.py
+111
-0
trunk/src/zope/testing/DEPENDENCIES.cfg
trunk/src/zope/testing/DEPENDENCIES.cfg
+1
-0
trunk/src/zope/testing/__init__.py
trunk/src/zope/testing/__init__.py
+30
-0
trunk/src/zope/testing/cleanup.py
trunk/src/zope/testing/cleanup.py
+65
-0
trunk/src/zope/testing/doctest.py
trunk/src/zope/testing/doctest.py
+2704
-0
trunk/src/zope/testing/doctestunit.py
trunk/src/zope/testing/doctestunit.py
+33
-0
trunk/src/zope/testing/formparser.py
trunk/src/zope/testing/formparser.py
+212
-0
trunk/src/zope/testing/formparser.txt
trunk/src/zope/testing/formparser.txt
+130
-0
trunk/src/zope/testing/loggingsupport.py
trunk/src/zope/testing/loggingsupport.py
+122
-0
trunk/src/zope/testing/loghandler.py
trunk/src/zope/testing/loghandler.py
+77
-0
trunk/src/zope/testing/module.py
trunk/src/zope/testing/module.py
+36
-0
trunk/src/zope/testing/tests.py
trunk/src/zope/testing/tests.py
+30
-0
No files found.
trunk/src/zope/interface/DEPENDENCIES.cfg
0 → 100644
View file @
a3c091cb
zope.testing
trunk/src/zope/interface/PUBLICATION.cfg
0 → 100644
View file @
a3c091cb
Metadata-Version: 1.0
Name: zope.interface
Summary: Zope 3 Interface Infrastructure
Author: Zope Corporation and Contributors
Author-email: zope3-dev@zope.org
License: ZPL 2.1
Description:
The implementation of interface definitions for Zope 3.
trunk/src/zope/interface/README.txt
0 → 100644
View file @
a3c091cb
==========
Interfaces
==========
.. contents::
Interfaces are objects that specify (document) the external behavior
of objects that "provide" them. An interface specifies behavior
through:
- Informal documentation in a doc string
- Attribute definitions
- Invariants, which are conditions that must hold for objects that
provide the interface
Attribute definitions specify specific attributes. They define the
attribute name and provide documentation and constraints of attribute
values. Attribute definitions can take a number of forms, as we'll
see below.
Defining interfaces
===================
Interfaces are defined using Python class statements:
>>> import zope.interface
>>> class IFoo(zope.interface.Interface):
... """Foo blah blah"""
...
... x = zope.interface.Attribute("""X blah blah""")
...
... def bar(q, r=None):
... """bar blah blah"""
In the example above, we've created an interface, `IFoo`. We
subclassed `zope.interface.Interface`, which is an ancestor interface for
all interfaces, much as `object` is an ancestor of all new-style
classes [#create]_. The interface is not a class, it's an Interface,
an instance of `InterfaceClass`::
>>> type(IFoo)
<class 'zope.interface.interface.InterfaceClass'>
We can ask for the interface's documentation::
>>> IFoo.__doc__
'Foo blah blah'
and its name::
>>> IFoo.__name__
'IFoo'
and even its module::
>>> IFoo.__module__
'__main__'
The interface defined two attributes:
`x`
This is the simplest form of attribute definition. It has a name
and a doc string. It doesn't formally specify anything else.
`bar`
This is a method. A method is defined via a function definition. A
method is simply an attribute constrained to be a callable with a
particular signature, as provided by the function definition.
Note that `bar` doesn't take a `self` argument. Interfaces document
how an object is *used*. When calling instance methods, you don't
pass a `self` argument, so a `self` argument isn't included in the
interface signature. The `self` argument in instance methods is
really an implementation detail of Python instances. Other objects,
besides instances can provide interfaces and their methods might not
be instance methods. For example, modules can provide interfaces and
their methods are usually just functions. Even instances can have
methods that are not instance methods.
You can access the attributes defined by an interface using mapping
syntax::
>>> x = IFoo['x']
>>> type(x)
<class 'zope.interface.interface.Attribute'>
>>> x.__name__
'x'
>>> x.__doc__
'X blah blah'
>>> IFoo.get('x').__name__
'x'
>>> IFoo.get('y')
You can use `in` to determine if an interface defines a name::
>>> 'x' in IFoo
True
You can iterate over interfaces to get the names they define::
>>> names = list(IFoo)
>>> names.sort()
>>> names
['bar', 'x']
Remember that interfaces aren't classes. You can't access attribute
definitions as attributes of interfaces::
>>> IFoo.x
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'InterfaceClass' object has no attribute 'x'
Methods provide access to the method signature::
>>> bar = IFoo['bar']
>>> bar.getSignatureString()
'(q, r=None)'
TODO
Methods really should have a better API. This is something that
needs to be improved.
Declaring interfaces
====================
Having defined interfaces, we can *declare* that objects provide
them. Before we describe the details, lets define some some terms:
*provide*
We say that objects *provide* interfaces. If an object provides an
interface, then the interface specifies the behavior of the
object. In other words, interfaces specify the behavior of the
objects that provide them.
*implement*
We normally say that classes *implement* interfaces. If a class
implements an interface, then the instances of the class provide
the interface. Objects provide interfaces that their classes
implement [#factory]_. (Objects can provide interfaces directly,
in addition to what their classes implement.)
It is important to note that classes don't usually provide the
interfaces that the implement.
We can generalize this to factories. For any callable object we
can declare that it produces objects that provides some interfaces
by saying that the factory implements the interfaces.
Now that we've defined these terms, we can talk about the API for
declaring interfaces.
Declaring implemented interfaces
--------------------------------
The most common way to declare interfaces is using the implements
function in a class statement::
>>> class Foo:
... zope.interface.implements(IFoo)
...
... def __init__(self, x=None):
... self.x = x
...
... def bar(self, q, r=None):
... return q, r, self.x
...
... def __repr__(self):
... return "Foo(%s)" % self.x
In this example, we declared that `Foo` implements `IFoo`. This means
that instances of `Foo` provide `IFoo`. Having made this declaration,
there are several ways we can introspect the declarations. First, we
can ask an interface whether it is implemented by a class::
>>> IFoo.implementedBy(Foo)
True
And we can ask whether an interface is provided by an object::
>>> foo = Foo()
>>> IFoo.providedBy(foo)
True
Of course, `Foo` doesn't provide `IFoo`, it implements it::
>>> IFoo.providedBy(Foo)
False
We can also ask what interfaces are implemented by an object::
>>> list(zope.interface.implementedBy(Foo))
[<InterfaceClass __main__.IFoo>]
It's an error to ask for interfaces implemented by a non-callable
object::
>>> IFoo.implementedBy(foo)
Traceback (most recent call last):
...
TypeError: ('ImplementedBy called for non-factory', Foo(None))
>>> list(zope.interface.implementedBy(foo))
Traceback (most recent call last):
...
TypeError: ('ImplementedBy called for non-factory', Foo(None))
Similarly, we can ask what interfaces are provided by an object::
>>> list(zope.interface.providedBy(foo))
[<InterfaceClass __main__.IFoo>]
>>> list(zope.interface.providedBy(Foo))
[]
We can declare interfaces implemented by other factories (besides
classes). We do this using a Python-2.4-style decorator named
`implementer`. In versions of Python before 2.4, this looks like:
>>> def yfoo(y):
... foo = Foo()
... foo.y = y
... return foo
>>> yfoo = zope.interface.implementer(IFoo)(yfoo)
>>> list(zope.interface.implementedBy(yfoo))
[<InterfaceClass __main__.IFoo>]
Note that the implementer decorator may modify it's argument. Callers
should not assume that a new object is created.
Also note that, at least for now, implementer cannt be used with
classes:
>>> zope.interface.implementer(IFoo)(Foo)
... # doctest: +NORMALIZE_WHITESPACE
Traceback (most recent call last):
...
TypeError: Can't use implementer with classes.
Use one of the class-declaration functions instead.
Declaring provided interfaces
-----------------------------
We can declare interfaces directly provided by objects. Suppose that
we want to document what the `__init__` method of the `Foo` class
does. It's not *really* part of `IFoo`. You wouldn't normally call
the `__init__` method on Foo instances. Rather, the `__init__` method
is part of the `Foo`'s `__call__` method::
>>> class IFooFactory(zope.interface.Interface):
... """Create foos"""
...
... def __call__(x=None):
... """Create a foo
...
... The argument provides the initial value for x ...
... """
It's the class that provides this interface, so we declare the
interface on the class::
>>> zope.interface.directlyProvides(Foo, IFooFactory)
And then, we'll see that Foo provides some interfaces::
>>> list(zope.interface.providedBy(Foo))
[<InterfaceClass __main__.IFooFactory>]
>>> IFooFactory.providedBy(Foo)
True
Declaring class interfaces is common enough that there's a special
declaration function for it, `classProvides`, that allows the
declaration from within a class statement::
>>> class Foo2:
... zope.interface.implements(IFoo)
... zope.interface.classProvides(IFooFactory)
...
... def __init__(self, x=None):
... self.x = x
...
... def bar(self, q, r=None):
... return q, r, self.x
...
... def __repr__(self):
... return "Foo(%s)" % self.x
>>> list(zope.interface.providedBy(Foo2))
[<InterfaceClass __main__.IFooFactory>]
>>> IFooFactory.providedBy(Foo2)
True
There's a similar function, `moduleProvides`, that supports interface
declarations from within module definitions. For example, see the use
of `moduleProvides` call in `zope.interface.__init__`, which declares that
the package `zope.interface` provides `IInterfaceDeclaration`.
Sometimes, we want to declare interfaces on instances, even though
those instances get interfaces from their classes. Suppose we create
a new interface, `ISpecial`::
>>> class ISpecial(zope.interface.Interface):
... reason = zope.interface.Attribute("Reason why we're special")
... def brag():
... "Brag about being special"
We can make a an existing foo instance special by providing `reason`
and `brag` attributes::
>>> foo.reason = 'I just am'
>>> def brag():
... return "I'm special!"
>>> foo.brag = brag
>>> foo.reason
'I just am'
>>> foo.brag()
"I'm special!"
and by declaring the interface::
>>> zope.interface.directlyProvides(foo, ISpecial)
then the new interface is included in the provided interfaces::
>>> ISpecial.providedBy(foo)
True
>>> list(zope.interface.providedBy(foo))
[<InterfaceClass __main__.ISpecial>, <InterfaceClass __main__.IFoo>]
We can find out what interfaces are directly provided by an object::
>>> list(zope.interface.directlyProvidedBy(foo))
[<InterfaceClass __main__.ISpecial>]
>>> newfoo = Foo()
>>> list(zope.interface.directlyProvidedBy(newfoo))
[]
Inherited declarations
----------------------
Normally, declarations are inherited::
>>> class SpecialFoo(Foo):
... zope.interface.implements(ISpecial)
... reason = 'I just am'
... def brag(self):
... return "I'm special because %s" % self.reason
>>> list(zope.interface.implementedBy(SpecialFoo))
[<InterfaceClass __main__.ISpecial>, <InterfaceClass __main__.IFoo>]
>>> list(zope.interface.providedBy(SpecialFoo()))
[<InterfaceClass __main__.ISpecial>, <InterfaceClass __main__.IFoo>]
Sometimes, you don't want to inherit declarations. In that case, you
can use `implementsOnly`, instead of `implements`::
>>> class Special(Foo):
... zope.interface.implementsOnly(ISpecial)
... reason = 'I just am'
... def brag(self):
... return "I'm special because %s" % self.reason
>>> list(zope.interface.implementedBy(Special))
[<InterfaceClass __main__.ISpecial>]
>>> list(zope.interface.providedBy(Special()))
[<InterfaceClass __main__.ISpecial>]
External declarations
---------------------
Normally, we make implementation declarations as part of a class
definition. Sometimes, we may want to make declarations from outside
the class definition. For example, we might want to declare interfaces
for classes that we didn't write. The function `classImplements` can
be used for this purpose::
>>> class C:
... pass
>>> zope.interface.classImplements(C, IFoo)
>>> list(zope.interface.implementedBy(C))
[<InterfaceClass __main__.IFoo>]
We can use `classImplementsOnly` to exclude inherited interfaces::
>>> class C(Foo):
... pass
>>> zope.interface.classImplementsOnly(C, ISpecial)
>>> list(zope.interface.implementedBy(C))
[<InterfaceClass __main__.ISpecial>]
Declaration Objects
-------------------
When we declare interfaces, we create *declaration* objects. When we
query declarations, declaration objects are returned::
>>> type(zope.interface.implementedBy(Special))
<class 'zope.interface.declarations.Implements'>
Declaration objects and interface objects are similar in many ways. In
fact, they share a common base class. The important thing to realize
about them is that they can be used where interfaces are expected in
declarations. Here's a silly example::
>>> class Special2(Foo):
... zope.interface.implementsOnly(
... zope.interface.implementedBy(Foo),
... ISpecial,
... )
... reason = 'I just am'
... def brag(self):
... return "I'm special because %s" % self.reason
The declaration here is almost the same as
``zope.interface.implements(ISpecial)``, except that the order of
interfaces in the resulting declaration is different::
>>> list(zope.interface.implementedBy(Special2))
[<InterfaceClass __main__.IFoo>, <InterfaceClass __main__.ISpecial>]
Interface Inheritance
=====================
Interfaces can extend other interfaces. They do this simply by listing
the other interfaces as base interfaces::
>>> class IBlat(zope.interface.Interface):
... """Blat blah blah"""
...
... y = zope.interface.Attribute("y blah blah")
... def eek():
... """eek blah blah"""
>>> IBlat.__bases__
(<InterfaceClass zope.interface.Interface>,)
>>> class IBaz(IFoo, IBlat):
... """Baz blah"""
... def eek(a=1):
... """eek in baz blah"""
...
>>> IBaz.__bases__
(<InterfaceClass __main__.IFoo>, <InterfaceClass __main__.IBlat>)
>>> names = list(IBaz)
>>> names.sort()
>>> names
['bar', 'eek', 'x', 'y']
Note that `IBaz` overrides eek::
>>> IBlat['eek'].__doc__
'eek blah blah'
>>> IBaz['eek'].__doc__
'eek in baz blah'
We were careful to override eek in a compatible way. When an
extending an interface, the extending interface should be compatible
[#compat]_ with the extended interfaces.
We can ask whether one interface extends another::
>>> IBaz.extends(IFoo)
True
>>> IBlat.extends(IFoo)
False
Note that interfaces don't extend themselves::
>>> IBaz.extends(IBaz)
False
Sometimes we wish they did, but we can, instead use `isOrExtends`::
>>> IBaz.isOrExtends(IBaz)
True
>>> IBaz.isOrExtends(IFoo)
True
>>> IFoo.isOrExtends(IBaz)
False
When we iterate over an interface, we get all of the names it defines,
including names defined by base interfaces. Sometimes, we want *just*
the names defined by the interface directly. We bane use the `names`
method for that::
>>> list(IBaz.names())
['eek']
Inheritance if attribute specifications
---------------------------------------
An interface may override attribute definitions frob base interfaces.
If two base interfaces define the same attribute, the attribute is
inherited from the most specific interface. For example, with:
>>> class IBase(zope.interface.Interface):
...
... def foo():
... "base foo doc"
>>> class IBase1(IBase):
... pass
>>> class IBase2(IBase):
...
... def foo():
... "base2 foo doc"
>>> class ISub(IBase1, IBase2):
... pass
ISub's definition of foo is the one from IBase2, since IBase2 is more
specific that IBase:
>>> ISub['foo'].__doc__
'base2 foo doc'
Note that this differs from a depth-first search.
Sometimes, it's useful to ask whether an interface defines an
attribute directly. You can use the direct method to get a directly
defined definitions:
>>> IBase.direct('foo').__doc__
'base foo doc'
>>> ISub.direct('foo')
Specifications
--------------
Interfaces and declarations are both special cases of specifications.
What we described above for interface inheritence applies to both
declarations and specifications. Declarations actually extend the
interfaces that they declare:
>>> class Baz:
... zope.interface.implements(IBaz)
>>> baz_implements = zope.interface.implementedBy(Baz)
>>> baz_implements.__bases__
(<InterfaceClass __main__.IBaz>,)
>>> baz_implements.extends(IFoo)
True
>>> baz_implements.isOrExtends(IFoo)
True
>>> baz_implements.isOrExtends(baz_implements)
True
Specifications (interfaces and declarations) provide an `__sro__`
that lists the specification and all of it's ancestors:
>>> baz_implements.__sro__
(<implementedBy __main__.Baz>,
<InterfaceClass __main__.IBaz>,
<InterfaceClass __main__.IFoo>,
<InterfaceClass __main__.IBlat>,
<InterfaceClass zope.interface.Interface>)
Tagged Values
=============
Interfaces and attribute descriptions support an extension mechanism,
borrowed from UML, called "tagged values" that lets us store extra
data::
>>> IFoo.setTaggedValue('date-modified', '2004-04-01')
>>> IFoo.setTaggedValue('author', 'Jim Fulton')
>>> IFoo.getTaggedValue('date-modified')
'2004-04-01'
>>> IFoo.queryTaggedValue('date-modified')
'2004-04-01'
>>> IFoo.queryTaggedValue('datemodified')
>>> tags = list(IFoo.getTaggedValueTags())
>>> tags.sort()
>>> tags
['author', 'date-modified']
Function attributes are converted to tagged values when method
attribute definitions are created::
>>> class IBazFactory(zope.interface.Interface):
... def __call__():
... "create one"
... __call__.return_type = IBaz
>>> IBazFactory['__call__'].getTaggedValue('return_type')
<InterfaceClass __main__.IBaz>
Invariants
==========
Interfaces can express conditions that must hold for objects that
provide them. These conditions are expressed using one or more
invariants. Invariants are callable objects that will be called with
an object that provides an interface. An invariant raises an `Invalid`
exception if the condition doesn't hold. Here's an example::
>>> class RangeError(zope.interface.Invalid):
... """A range has invalid limits"""
... def __repr__(self):
... return "RangeError(%r)" % self.args
>>> def range_invariant(ob):
... if ob.max < ob.min:
... raise RangeError(ob)
Given this invariant, we can use it in an interface definition::
>>> class IRange(zope.interface.Interface):
... min = zope.interface.Attribute("Lower bound")
... max = zope.interface.Attribute("Upper bound")
...
... zope.interface.invariant(range_invariant)
Interfaces have a method for checking their invariants::
>>> class Range(object):
... zope.interface.implements(IRange)
...
... def __init__(self, min, max):
... self.min, self.max = min, max
...
... def __repr__(self):
... return "Range(%s, %s)" % (self.min, self.max)
>>> IRange.validateInvariants(Range(1,2))
>>> IRange.validateInvariants(Range(1,1))
>>> IRange.validateInvariants(Range(2,1))
Traceback (most recent call last):
...
RangeError: Range(2, 1)
If you have multiple invariants, you may not want to stop checking
after the first error. If you pass a list to `validateInvariants`,
then a single `Invalid` exception will be raised with the list of
exceptions as it's argument::
>>> errors = []
>>> IRange.validateInvariants(Range(2,1), errors)
Traceback (most recent call last):
...
Invalid: [RangeError(Range(2, 1))]
And the list will be filled with the individual exceptions::
>>> errors
[RangeError(Range(2, 1))]
.. [#create] The main reason we subclass `Interface` is to cause the
Python class statement to create an interface, rather
than a class.
It's possible to create interfaces by calling a special
interface class directly. Doing this, it's possible
(and, on rare occasions, useful) to create interfaces
that don't descend from `Interface`. Using this
technique is beyond the scope of this document.
.. [#factory] Classes are factories. They can be called to create
their instances. We expect that we will eventually
extend the concept of implementation to other kinds of
factories, so that we can declare the interfaces
provided by the objects created.
.. [#compat] The goal is substitutability. An object that provides an
extending interface should be substitutable for an object
that provides the extended interface. In our example, an
object that provides IBaz should be usable whereever an
object that provides IBlat is expected.
The interface implementation doesn't enforce this. XXX
but maybe it should do some checks.
trunk/src/zope/interface/SETUP.cfg
0 → 100644
View file @
a3c091cb
# Extension information for zpkg:
<extension _zope_interface_coptimizations>
source _zope_interface_coptimizations.c
</extension>
trunk/src/zope/interface/__init__.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# 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.1 (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
This package implements the Python "scarecrow" proposal.
The package exports two objects, `Interface` and `Attribute` directly. It also
exports several helper methods. Interface is used to create an interface with
a class statement, as in:
class IMyInterface(Interface):
'''Interface documentation
'''
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 `interfaces` module.
The package has several public modules:
o `declarations` provides utilities to declare interfaces on objects. It
also provides a wide range of helpful utilities that aid in managing
declared interfaces. Most of its public names are however imported here.
o `document` has a utility for documenting an interface as structured text.
o `exceptions` has the interface-defined exceptions
o `interfaces` contains a list of all public interfaces for this package.
o `verify` has utilities for verifying implementations of interfaces.
See the module doc strings for more information.
$Id$
"""
__docformat__
=
'restructuredtext'
from
zope.interface.interface
import
Interface
,
_wire
# Need to actually get the interface elements to implement the right interfaces
_wire
()
del
_wire
from
zope.interface.interface
import
Attribute
,
invariant
from
zope.interface.declarations
import
providedBy
,
implementedBy
from
zope.interface.declarations
import
classImplements
,
classImplementsOnly
from
zope.interface.declarations
import
directlyProvidedBy
,
directlyProvides
from
zope.interface.declarations
import
alsoProvides
,
implementer
from
zope.interface.declarations
import
implements
,
implementsOnly
from
zope.interface.declarations
import
classProvides
,
moduleProvides
from
zope.interface.declarations
import
Declaration
from
zope.interface.exceptions
import
Invalid
# The following are to make spec pickles cleaner
from
zope.interface.declarations
import
Provides
from
zope.interface.interfaces
import
IInterfaceDeclaration
moduleProvides
(
IInterfaceDeclaration
)
__all__
=
(
'Interface'
,
'Attribute'
)
+
tuple
(
IInterfaceDeclaration
)
trunk/src/zope/interface/_flatten.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Adapter-style interface registry
See Adapter class.
$Id$
"""
from
zope.interface
import
Declaration
def
_flatten
(
implements
,
include_None
=
0
):
try
:
r
=
implements
.
flattened
()
except
AttributeError
:
if
implements
is
None
:
r
=
()
else
:
r
=
Declaration
(
implements
).
flattened
()
if
not
include_None
:
return
r
r
=
list
(
r
)
r
.
append
(
None
)
return
r
trunk/src/zope/interface/_zope_interface_coptimizations.c
0 → 100644
View file @
a3c091cb
/*###########################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
############################################################################*/
#include "Python.h"
#include "structmember.h"
#define TYPE(O) ((PyTypeObject*)(O))
#define OBJECT(O) ((PyObject*)(O))
#define CLASSIC(O) ((PyClassObject*)(O))
static
PyObject
*
str__dict__
,
*
str__implemented__
,
*
strextends
;
static
PyObject
*
BuiltinImplementationSpecifications
,
*
str__provides__
;
static
PyObject
*
str__class__
,
*
str__providedBy__
,
*
strisOrExtends
;
static
PyObject
*
empty
,
*
fallback
,
*
str_implied
,
*
str_cls
,
*
str_implements
;
static
PyTypeObject
*
Implements
;
static
int
imported_declarations
=
0
;
static
int
import_declarations
(
void
)
{
PyObject
*
declarations
,
*
i
;
declarations
=
PyImport_ImportModule
(
"zope.interface.declarations"
);
if
(
declarations
==
NULL
)
return
-
1
;
BuiltinImplementationSpecifications
=
PyObject_GetAttrString
(
declarations
,
"BuiltinImplementationSpecifications"
);
if
(
BuiltinImplementationSpecifications
==
NULL
)
return
-
1
;
empty
=
PyObject_GetAttrString
(
declarations
,
"_empty"
);
if
(
empty
==
NULL
)
return
-
1
;
fallback
=
PyObject_GetAttrString
(
declarations
,
"implementedByFallback"
);
if
(
fallback
==
NULL
)
return
-
1
;
i
=
PyObject_GetAttrString
(
declarations
,
"Implements"
);
if
(
i
==
NULL
)
return
-
1
;
if
(
!
PyType_Check
(
i
))
{
PyErr_SetString
(
PyExc_TypeError
,
"zope.declarations.Implements is not a type"
);
return
-
1
;
}
Implements
=
(
PyTypeObject
*
)
i
;
Py_DECREF
(
declarations
);
imported_declarations
=
1
;
return
0
;
}
static
PyTypeObject
SpecType
;
/* Forward */
static
PyObject
*
implementedByFallback
(
PyObject
*
cls
)
{
if
(
imported_declarations
==
0
&&
import_declarations
()
<
0
)
return
NULL
;
return
PyObject_CallFunctionObjArgs
(
fallback
,
cls
,
NULL
);
}
static
PyObject
*
implementedBy
(
PyObject
*
ignored
,
PyObject
*
cls
)
{
/* Fast retrieval of implements spec, if possible, to optimize
common case. Use fallback code if we get stuck.
*/
PyObject
*
dict
=
NULL
,
*
spec
;
if
(
PyType_Check
(
cls
))
{
dict
=
TYPE
(
cls
)
->
tp_dict
;
Py_XINCREF
(
dict
);
}
if
(
dict
==
NULL
)
dict
=
PyObject_GetAttr
(
cls
,
str__dict__
);
if
(
dict
==
NULL
)
{
/* Probably a security proxied class, use more expensive fallback code */
PyErr_Clear
();
return
implementedByFallback
(
cls
);
}
spec
=
PyObject_GetItem
(
dict
,
str__implemented__
);
Py_DECREF
(
dict
);
if
(
spec
)
{
if
(
imported_declarations
==
0
&&
import_declarations
()
<
0
)
return
NULL
;
if
(
PyObject_TypeCheck
(
spec
,
Implements
))
return
spec
;
/* Old-style declaration, use more expensive fallback code */
Py_DECREF
(
spec
);
return
implementedByFallback
(
cls
);
}
PyErr_Clear
();
/* Maybe we have a builtin */
if
(
imported_declarations
==
0
&&
import_declarations
()
<
0
)
return
NULL
;
spec
=
PyDict_GetItem
(
BuiltinImplementationSpecifications
,
cls
);
if
(
spec
!=
NULL
)
{
Py_INCREF
(
spec
);
return
spec
;
}
/* We're stuck, use fallback */
return
implementedByFallback
(
cls
);
}
static
PyObject
*
getObjectSpecification
(
PyObject
*
ignored
,
PyObject
*
ob
)
{
PyObject
*
cls
,
*
result
;
result
=
PyObject_GetAttr
(
ob
,
str__provides__
);
if
(
result
!=
NULL
)
return
result
;
PyErr_Clear
();
/* We do a getattr here so as not to be defeated by proxies */
cls
=
PyObject_GetAttr
(
ob
,
str__class__
);
if
(
cls
==
NULL
)
{
PyErr_Clear
();
if
(
imported_declarations
==
0
&&
import_declarations
()
<
0
)
return
NULL
;
Py_INCREF
(
empty
);
return
empty
;
}
result
=
implementedBy
(
NULL
,
cls
);
Py_DECREF
(
cls
);
return
result
;
}
static
PyObject
*
providedBy
(
PyObject
*
ignored
,
PyObject
*
ob
)
{
PyObject
*
result
,
*
cls
,
*
cp
;
result
=
PyObject_GetAttr
(
ob
,
str__providedBy__
);
if
(
result
==
NULL
)
{
PyErr_Clear
();
return
getObjectSpecification
(
NULL
,
ob
);
}
/* We want to make sure we have a spec. We can't do a type check
because we may have a proxy, so we'll just try to get the
only attribute.
*/
if
(
PyObject_HasAttr
(
result
,
strextends
))
return
result
;
/*
The object's class doesn't understand descriptors.
Sigh. We need to get an object descriptor, but we have to be
careful. We want to use the instance's __provides__,l if
there is one, but only if it didn't come from the class.
*/
Py_DECREF
(
result
);
cls
=
PyObject_GetAttr
(
ob
,
str__class__
);
if
(
cls
==
NULL
)
return
NULL
;
result
=
PyObject_GetAttr
(
ob
,
str__provides__
);
if
(
result
==
NULL
)
{
/* No __provides__, so just fall back to implementedBy */
PyErr_Clear
();
result
=
implementedBy
(
NULL
,
cls
);
Py_DECREF
(
cls
);
return
result
;
}
cp
=
PyObject_GetAttr
(
cls
,
str__provides__
);
if
(
cp
==
NULL
)
{
/* The the class has no provides, assume we're done: */
PyErr_Clear
();
Py_DECREF
(
cls
);
return
result
;
}
if
(
cp
==
result
)
{
/*
Oops, we got the provides from the class. This means
the object doesn't have it's own. We should use implementedBy
*/
Py_DECREF
(
result
);
result
=
implementedBy
(
NULL
,
cls
);
}
Py_DECREF
(
cls
);
Py_DECREF
(
cp
);
return
result
;
}
static
PyObject
*
inst_attr
(
PyObject
*
self
,
PyObject
*
name
)
{
/* Get an attribute from an inst dict. Return a borrowed reference.
*/
PyObject
**
dictp
,
*
v
;
dictp
=
_PyObject_GetDictPtr
(
self
);
if
(
dictp
&&
*
dictp
&&
(
v
=
PyDict_GetItem
(
*
dictp
,
name
)))
return
v
;
PyErr_SetObject
(
PyExc_AttributeError
,
name
);
return
NULL
;
}
static
PyObject
*
Spec_extends
(
PyObject
*
self
,
PyObject
*
other
)
{
PyObject
*
implied
;
implied
=
inst_attr
(
self
,
str_implied
);
if
(
implied
==
NULL
)
return
NULL
;
#ifdef Py_True
if
(
PyDict_GetItem
(
implied
,
other
)
!=
NULL
)
{
Py_INCREF
(
Py_True
);
return
Py_True
;
}
Py_INCREF
(
Py_False
);
return
Py_False
;
#else
return
PyInt_FromLong
(
PyDict_GetItem
(
implied
,
other
)
!=
NULL
);
#endif
}
static
char
Spec_extends__doc__
[]
=
"Test whether a specification is or extends another"
;
static
char
Spec_providedBy__doc__
[]
=
"Test whether an interface is implemented by the specification"
;
static
PyObject
*
Spec_providedBy
(
PyObject
*
self
,
PyObject
*
ob
)
{
PyObject
*
decl
,
*
item
;
decl
=
providedBy
(
NULL
,
ob
);
if
(
decl
==
NULL
)
return
NULL
;
if
(
PyObject_TypeCheck
(
ob
,
&
SpecType
))
item
=
Spec_extends
(
decl
,
self
);
else
/* decl is probably a security proxy. We have to go the long way
around.
*/
item
=
PyObject_CallMethodObjArgs
(
decl
,
strisOrExtends
,
self
,
NULL
);
Py_DECREF
(
decl
);
return
item
;
}
static
char
Spec_implementedBy__doc__
[]
=
"Test whether the specification is implemented by instances of a class"
;
static
PyObject
*
Spec_implementedBy
(
PyObject
*
self
,
PyObject
*
cls
)
{
PyObject
*
decl
,
*
item
;
decl
=
implementedBy
(
NULL
,
cls
);
if
(
decl
==
NULL
)
return
NULL
;
if
(
PyObject_TypeCheck
(
decl
,
&
SpecType
))
item
=
Spec_extends
(
decl
,
self
);
else
item
=
PyObject_CallMethodObjArgs
(
decl
,
strisOrExtends
,
self
,
NULL
);
Py_DECREF
(
decl
);
return
item
;
}
static
struct
PyMethodDef
Spec_methods
[]
=
{
{
"providedBy"
,
(
PyCFunction
)
Spec_providedBy
,
METH_O
,
Spec_providedBy__doc__
},
{
"implementedBy"
,
(
PyCFunction
)
Spec_implementedBy
,
METH_O
,
Spec_implementedBy__doc__
},
{
"isOrExtends"
,
(
PyCFunction
)
Spec_extends
,
METH_O
,
Spec_extends__doc__
},
{
NULL
,
NULL
}
/* sentinel */
};
static
PyTypeObject
SpecType
=
{
PyObject_HEAD_INIT
(
NULL
)
/* ob_size */
0
,
/* tp_name */
"_interface_coptimizations."
"SpecificationBase"
,
/* tp_basicsize */
0
,
/* tp_itemsize */
0
,
/* tp_dealloc */
(
destructor
)
0
,
/* tp_print */
(
printfunc
)
0
,
/* tp_getattr */
(
getattrfunc
)
0
,
/* tp_setattr */
(
setattrfunc
)
0
,
/* tp_compare */
(
cmpfunc
)
0
,
/* tp_repr */
(
reprfunc
)
0
,
/* tp_as_number */
0
,
/* tp_as_sequence */
0
,
/* tp_as_mapping */
0
,
/* tp_hash */
(
hashfunc
)
0
,
/* tp_call */
(
ternaryfunc
)
0
,
/* tp_str */
(
reprfunc
)
0
,
/* tp_getattro */
(
getattrofunc
)
0
,
/* tp_setattro */
(
setattrofunc
)
0
,
/* tp_as_buffer */
0
,
/* tp_flags */
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
,
"Base type for Specification objects"
,
/* tp_traverse */
(
traverseproc
)
0
,
/* tp_clear */
(
inquiry
)
0
,
/* tp_richcompare */
(
richcmpfunc
)
0
,
/* tp_weaklistoffset */
(
long
)
0
,
/* tp_iter */
(
getiterfunc
)
0
,
/* tp_iternext */
(
iternextfunc
)
0
,
/* tp_methods */
Spec_methods
,
};
static
PyObject
*
OSD_descr_get
(
PyObject
*
self
,
PyObject
*
inst
,
PyObject
*
cls
)
{
PyObject
*
provides
;
if
(
inst
==
NULL
)
return
getObjectSpecification
(
NULL
,
cls
);
provides
=
PyObject_GetAttr
(
inst
,
str__provides__
);
if
(
provides
!=
NULL
)
return
provides
;
PyErr_Clear
();
return
implementedBy
(
NULL
,
cls
);
}
static
PyTypeObject
OSDType
=
{
PyObject_HEAD_INIT
(
NULL
)
/* ob_size */
0
,
/* tp_name */
"_interface_coptimizations."
"ObjectSpecificationDescriptor"
,
/* tp_basicsize */
0
,
/* tp_itemsize */
0
,
/* tp_dealloc */
(
destructor
)
0
,
/* tp_print */
(
printfunc
)
0
,
/* tp_getattr */
(
getattrfunc
)
0
,
/* tp_setattr */
(
setattrfunc
)
0
,
/* tp_compare */
(
cmpfunc
)
0
,
/* tp_repr */
(
reprfunc
)
0
,
/* tp_as_number */
0
,
/* tp_as_sequence */
0
,
/* tp_as_mapping */
0
,
/* tp_hash */
(
hashfunc
)
0
,
/* tp_call */
(
ternaryfunc
)
0
,
/* tp_str */
(
reprfunc
)
0
,
/* tp_getattro */
(
getattrofunc
)
0
,
/* tp_setattro */
(
setattrofunc
)
0
,
/* tp_as_buffer */
0
,
/* tp_flags */
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
,
"Object Specification Descriptor"
,
/* tp_traverse */
(
traverseproc
)
0
,
/* tp_clear */
(
inquiry
)
0
,
/* tp_richcompare */
(
richcmpfunc
)
0
,
/* tp_weaklistoffset */
(
long
)
0
,
/* tp_iter */
(
getiterfunc
)
0
,
/* tp_iternext */
(
iternextfunc
)
0
,
/* tp_methods */
0
,
/* tp_members */
0
,
/* tp_getset */
0
,
/* tp_base */
0
,
/* tp_dict */
0
,
/* internal use */
/* tp_descr_get */
(
descrgetfunc
)
OSD_descr_get
,
};
static
PyObject
*
CPB_descr_get
(
PyObject
*
self
,
PyObject
*
inst
,
PyObject
*
cls
)
{
PyObject
*
mycls
,
*
implements
;
mycls
=
inst_attr
(
self
,
str_cls
);
if
(
mycls
==
NULL
)
return
NULL
;
if
(
cls
==
mycls
)
{
if
(
inst
==
NULL
)
{
Py_INCREF
(
self
);
return
OBJECT
(
self
);
}
implements
=
inst_attr
(
self
,
str_implements
);
Py_XINCREF
(
implements
);
return
implements
;
}
PyErr_SetObject
(
PyExc_AttributeError
,
str__provides__
);
return
NULL
;
}
static
PyTypeObject
CPBType
=
{
PyObject_HEAD_INIT
(
NULL
)
/* ob_size */
0
,
/* tp_name */
"_interface_coptimizations."
"ClassProvidesBase"
,
/* tp_basicsize */
0
,
/* tp_itemsize */
0
,
/* tp_dealloc */
(
destructor
)
0
,
/* tp_print */
(
printfunc
)
0
,
/* tp_getattr */
(
getattrfunc
)
0
,
/* tp_setattr */
(
setattrfunc
)
0
,
/* tp_compare */
(
cmpfunc
)
0
,
/* tp_repr */
(
reprfunc
)
0
,
/* tp_as_number */
0
,
/* tp_as_sequence */
0
,
/* tp_as_mapping */
0
,
/* tp_hash */
(
hashfunc
)
0
,
/* tp_call */
(
ternaryfunc
)
0
,
/* tp_str */
(
reprfunc
)
0
,
/* tp_getattro */
(
getattrofunc
)
0
,
/* tp_setattro */
(
setattrofunc
)
0
,
/* tp_as_buffer */
0
,
/* tp_flags */
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
,
"C Base class for ClassProvides"
,
/* tp_traverse */
(
traverseproc
)
0
,
/* tp_clear */
(
inquiry
)
0
,
/* tp_richcompare */
(
richcmpfunc
)
0
,
/* tp_weaklistoffset */
(
long
)
0
,
/* tp_iter */
(
getiterfunc
)
0
,
/* tp_iternext */
(
iternextfunc
)
0
,
/* tp_methods */
0
,
/* tp_members */
0
,
/* tp_getset */
0
,
/* tp_base */
&
SpecType
,
/* tp_dict */
0
,
/* internal use */
/* tp_descr_get */
(
descrgetfunc
)
CPB_descr_get
,
};
static
struct
PyMethodDef
m_methods
[]
=
{
{
"implementedBy"
,
(
PyCFunction
)
implementedBy
,
METH_O
,
"Interfaces implemented by instances of a class"
},
{
"getObjectSpecification"
,
(
PyCFunction
)
getObjectSpecification
,
METH_O
,
"Get an object's interfaces (internal api)"
},
{
"providedBy"
,
(
PyCFunction
)
providedBy
,
METH_O
,
"Get an object's interfaces"
},
{
NULL
,
(
PyCFunction
)
NULL
,
0
,
NULL
}
/* sentinel */
};
#ifndef PyMODINIT_FUNC
/* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC
init_zope_interface_coptimizations
(
void
)
{
PyObject
*
m
;
#define DEFINE_STRING(S) \
if(! (str ## S = PyString_FromString(# S))) return
DEFINE_STRING
(
__dict__
);
DEFINE_STRING
(
__implemented__
);
DEFINE_STRING
(
__provides__
);
DEFINE_STRING
(
__class__
);
DEFINE_STRING
(
__providedBy__
);
DEFINE_STRING
(
isOrExtends
);
DEFINE_STRING
(
extends
);
DEFINE_STRING
(
_implied
);
DEFINE_STRING
(
_implements
);
DEFINE_STRING
(
_cls
);
#undef DEFINE_STRING
/* Initialize types: */
SpecType
.
tp_new
=
PyBaseObject_Type
.
tp_new
;
if
(
PyType_Ready
(
&
SpecType
)
<
0
)
return
;
OSDType
.
tp_new
=
PyBaseObject_Type
.
tp_new
;
if
(
PyType_Ready
(
&
OSDType
)
<
0
)
return
;
CPBType
.
tp_new
=
PyBaseObject_Type
.
tp_new
;
if
(
PyType_Ready
(
&
CPBType
)
<
0
)
return
;
/* Create the module and add the functions */
m
=
Py_InitModule3
(
"_zope_interface_coptimizations"
,
m_methods
,
"C optimizations for zope.interface
\n\n
"
"$Id$"
);
if
(
m
==
NULL
)
return
;
/* Add types: */
if
(
PyModule_AddObject
(
m
,
"SpecificationBase"
,
(
PyObject
*
)
&
SpecType
)
<
0
)
return
;
if
(
PyModule_AddObject
(
m
,
"ObjectSpecificationDescriptor"
,
(
PyObject
*
)
&
OSDType
)
<
0
)
return
;
if
(
PyModule_AddObject
(
m
,
"ClassProvidesBase"
,
(
PyObject
*
)
&
CPBType
)
<
0
)
return
;
}
trunk/src/zope/interface/adapter.py
0 → 100644
View file @
a3c091cb
############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
############################################################################
"""Adapter-style interface registry
This implementation is based on a notion of "surrogate" interfaces.
$Id$
"""
# Implementation notes
# We keep a collection of surrogates.
# A surrogate is a surrogate for a specification (interface or
# declaration). We use weak references in order to remove surrogates
# if the corresponding specification goes away.
# Each surrogate keeps track of:
# - The adapters registered directly for that surrogate, and
# - The "implied" adapters, which is the adapters that can be computed
# from instances of that surrogate.
# The later data structure takes into account adapters registered for
# specifications that the registered surrogate extends.
# The registrations are of the form:
# {(subscription, with, name, specification) -> factories}
# where:
# 'subscription' is a flag indicating if this registration is for
# subscription adapters.
# 'with' is a tuple of specs that is non-empty only in the case
# of multi-adapters.
# 'name' is a unicode adapter name. Unnamed adapters have an empty
# name.
# 'specification' is the interface being adapted to.
# 'factories' is normally a tuple of factories, but can be anything.
# (See the "raw" option to the query-adapter calls.) For subscription
# adapters, it is a tuple of tuples of factories.
# The implied adapters are held in a single dictionary. The items in the
# dictionary are of several forms:
# For single adapters:
#
# {specification -> {name -> object}
#
# where object is usually a sequence of factories
# For multiple adapters:
#
# {(specification, order) -> {name -> {with -> object}}}
# For single subscription adapters:
#
# {('s', specification) -> tuple([object])}
# For multiple-subscription adapters:
#
# {('s', specification, order) -> {with -> tuple([object])}}
from
__future__
import
generators
import
weakref
from
zope.interface.ro
import
ro
from
zope.interface.declarations
import
providedBy
from
zope.interface.interface
import
InterfaceClass
,
Interface
Default
=
InterfaceClass
(
"Default"
,
(),
{})
Null
=
InterfaceClass
(
"Null"
,
(),
{})
# 2.2 backwards compatability
try
:
enumerate
except
NameError
:
def
enumerate
(
l
):
i
=
0
for
o
in
l
:
yield
i
,
o
i
+=
1
try
:
basestring
except
NameError
:
basestring
=
(
str
,
unicode
)
class
ReadProperty
(
object
):
def
__init__
(
self
,
func
):
self
.
func
=
func
def
__get__
(
self
,
inst
,
class_
):
if
inst
is
None
:
return
self
return
self
.
func
(
inst
)
class
Surrogate
(
object
):
"""Specification surrogate
A specification surrogate is used to hold adapter registrations on
behalf of a specification.
"""
def
__init__
(
self
,
spec
,
registry
):
self
.
spec
=
spec
.
weakref
()
spec
.
subscribe
(
self
)
self
.
adapters
=
{}
self
.
dependents
=
weakref
.
WeakKeyDictionary
()
self
.
registry
=
registry
self
.
__bases__
=
[
registry
.
get
(
base
)
for
base
in
spec
.
__bases__
]
for
base
in
self
.
__bases__
:
base
.
subscribe
(
self
)
def
dirty
(
self
):
if
'get'
in
self
.
__dict__
:
# Not already dirty
del
self
.
selfImplied
del
self
.
multImplied
del
self
.
get
for
dependent
in
self
.
dependents
.
keys
():
dependent
.
dirty
()
def
clean
(
self
):
for
base
in
self
.
__bases__
:
base
.
unsubscribe
(
self
)
self
.
__bases__
=
[
self
.
registry
.
get
(
base
)
for
base
in
self
.
spec
().
__bases__
]
for
base
in
self
.
__bases__
:
base
.
subscribe
(
self
)
self
.
selfImplied
,
self
.
multImplied
=
adapterImplied
(
self
.
adapters
)
implied
=
{}
ancestors
=
ro
(
self
)
# Collect implied data in reverse order to have more specific data
# override less-specific data.
ancestors
.
reverse
()
for
ancestor
in
ancestors
:
for
key
,
v
in
ancestor
.
selfImplied
.
iteritems
():
# key is specification or ('s', specification)
subscription
=
isinstance
(
key
,
tuple
)
and
key
[
0
]
==
's'
if
subscription
:
# v is tuple of subs
implied
[
key
]
=
implied
.
get
(
key
,
())
+
v
else
:
oldbyname
=
implied
.
get
(
key
)
if
not
oldbyname
:
implied
[
key
]
=
oldbyname
=
{}
# v is name -> object
oldbyname
.
update
(
v
)
for
key
,
v
in
ancestor
.
multImplied
.
iteritems
():
# key is (specification, order)
# or ('s', specification, order)
subscription
=
key
[
0
]
==
's'
if
subscription
:
oldwithobs
=
implied
.
get
(
key
)
if
not
oldwithobs
:
oldwithobs
=
implied
[
key
]
=
{}
# v is {with -> tuple([object])}
for
with
,
objects
in
v
.
iteritems
():
oldwithobs
[
with
]
=
oldwithobs
.
get
(
with
,
())
+
objects
else
:
oldbyname
=
implied
.
get
(
key
)
if
not
oldbyname
:
implied
[
key
]
=
oldbyname
=
{}
# v is {name -> {with -> ?}}
for
name
,
withobs
in
v
.
iteritems
():
oldwithobs
=
oldbyname
.
get
(
name
)
if
not
oldwithobs
:
oldwithobs
=
oldbyname
[
name
]
=
{}
# withobs is {with -> object}
oldwithobs
.
update
(
withobs
)
# Now flatten with mappings to tuples
for
key
,
v
in
implied
.
iteritems
():
if
isinstance
(
key
,
tuple
):
if
key
[
0
]
==
's'
:
# subscriptions
if
isinstance
(
v
,
dict
):
implied
[
key
]
=
v
.
items
()
else
:
byname
=
v
for
name
,
value
in
byname
.
iteritems
():
if
isinstance
(
value
,
dict
):
# We have {with -> value}
# convert it to sorted [(with, value]
byname
[
name
]
=
orderwith
(
value
)
self
.
get
=
implied
.
get
def
get
(
self
,
key
):
"""Get an implied value
This is only called when the surrogate is dirty
"""
self
.
clean
()
return
self
.
__dict__
[
'get'
](
key
)
def
selfImplied
(
self
):
"""Return selfImplied when dirty
"""
self
.
clean
()
return
self
.
__dict__
[
'selfImplied'
]
selfImplied
=
ReadProperty
(
selfImplied
)
def
multiImplied
(
self
):
"""Return _multiImplied when dirty
"""
self
.
clean
()
return
self
.
__dict__
[
'multiImplied'
]
multiImplied
=
ReadProperty
(
multiImplied
)
def
subscribe
(
self
,
dependent
):
self
.
dependents
[
dependent
]
=
1
def
unsubscribe
(
self
,
dependent
):
del
self
.
dependents
[
dependent
]
def
_adaptTo
(
self
,
specification
,
object
,
name
=
''
,
with
=
()):
if
object
is
None
:
try
:
del
self
.
adapters
[
False
,
tuple
(
with
),
name
,
specification
]
except
KeyError
:
pass
else
:
self
.
adapters
[
False
,
tuple
(
with
),
name
,
specification
]
=
object
self
.
dirty
()
def
_subscriptionAdaptTo
(
self
,
specification
,
object
,
with
=
()):
if
object
is
None
:
raise
TypeError
,
(
"Unregistering subscription adapters"
" isn't implemented"
)
key
=
(
True
,
tuple
(
with
),
''
,
specification
)
self
.
adapters
[
key
]
=
self
.
adapters
.
get
(
key
,
())
+
(
object
,
)
self
.
dirty
()
def
changed
(
self
,
which
=
None
):
self
.
dirty
()
def
__repr__
(
self
):
return
'<%s(%s)>'
%
(
self
.
__class__
.
__name__
,
self
.
spec
())
def
orderwith
(
bywith
):
# Convert {with -> adapter} to withs, [(with, value)]
# such that there are no i, j, i < j, such that
# withs[j][0] extends withs[i][0].
withs
=
[]
for
with
,
value
in
bywith
.
iteritems
():
for
i
,
(
w
,
v
)
in
enumerate
(
withs
):
if
withextends
(
with
,
w
):
withs
.
insert
(
i
,
(
with
,
value
))
break
else
:
withs
.
append
((
with
,
value
))
return
withs
def
withextends
(
with1
,
with2
):
for
spec1
,
spec2
in
zip
(
with1
,
with2
):
if
spec1
.
extends
(
spec2
):
return
True
if
spec1
!=
spec2
:
break
return
False
class
AdapterLookup
(
object
):
# Adapter lookup support
# We have a class here because we want to provide very
# fast lookup support in C and making this part of the adapter
# registry itself would provide problems if someone wanted
# persistent adapter registries, because we want C slots for fast
# lookup that would clash with persistence-supplied slots.
# so this class acts a little bit like a lookup adapter for the adapter
# registry.
def
__init__
(
self
,
registry
,
surrogates
,
_remove
):
self
.
_registry
=
registry
self
.
_surrogateClass
=
registry
.
_surrogateClass
self
.
_default
=
registry
.
_default
self
.
_null
=
registry
.
_null
self
.
_surrogates
=
surrogates
self
.
_remove
=
_remove
def
lookup
(
self
,
required
,
provided
,
name
=
''
,
default
=
None
):
order
=
len
(
required
)
if
order
==
1
:
# Simple adapter:
s
=
self
.
get
(
required
[
0
])
byname
=
s
.
get
(
provided
)
if
byname
:
value
=
byname
.
get
(
name
)
else
:
value
=
None
if
value
is
None
:
byname
=
self
.
_default
.
get
(
provided
)
if
byname
:
value
=
byname
.
get
(
name
,
default
)
else
:
return
default
return
value
elif
order
==
0
:
# null adapter
byname
=
self
.
_null
.
get
(
provided
)
if
byname
:
return
byname
.
get
(
name
,
default
)
else
:
return
default
# Multi adapter
with
=
required
[
1
:]
key
=
provided
,
order
for
surrogate
in
self
.
get
(
required
[
0
]),
self
.
_default
:
byname
=
surrogate
.
get
(
key
)
if
not
byname
:
continue
bywith
=
byname
.
get
(
name
)
if
not
bywith
:
continue
# Selecting multi-adapters is not just a matter of matching the
# required interfaces of the adapter to the ones passed. Several
# adapters might match, but we only want the best one. We use a
# ranking algorithm to determine the best match.
# `best` carries the rank and value of the best found adapter.
best
=
None
for
rwith
,
value
in
bywith
:
# the `rank` describes how well the found adapter matches.
rank
=
[]
for
rspec
,
spec
in
zip
(
rwith
,
with
):
if
not
spec
.
isOrExtends
(
rspec
):
break
# This one is no good
# Determine the rank of this particular specification.
rank
.
append
(
list
(
spec
.
__sro__
).
index
(
rspec
))
else
:
# If the new rank is better than the best previously
# recorded one, make the new adapter the best one found.
rank
=
tuple
(
rank
)
if
best
is
None
or
rank
<
best
[
0
]:
best
=
rank
,
value
# If any match was found, return the best one.
if
best
:
return
best
[
1
]
return
default
def
lookup1
(
self
,
required
,
provided
,
name
=
''
,
default
=
None
):
return
self
.
lookup
((
required
,),
provided
,
name
,
default
)
def
adapter_hook
(
self
,
interface
,
object
,
name
=
''
,
default
=
None
):
"""Hook function used when calling interfaces.
When called from Interface.__adapt__, only the interface and
object parameters will be passed.
If the factory produces `None`, then the default is returned. This
allows us to prevent adaptation (if desired) and make the factory
decide whether an adapter will be available.
"""
factory
=
self
.
lookup1
(
providedBy
(
object
),
interface
,
name
)
if
factory
is
not
None
:
adapter
=
factory
(
object
)
if
adapter
is
not
None
:
return
adapter
return
default
def
queryAdapter
(
self
,
object
,
interface
,
name
=
''
,
default
=
None
):
# Note that we rarely call queryAdapter directly
# We usually end up calling adapter_hook
return
self
.
adapter_hook
(
interface
,
object
,
name
,
default
)
def
subscriptions
(
self
,
required
,
provided
):
if
provided
is
None
:
provided
=
Null
order
=
len
(
required
)
if
order
==
1
:
# Simple subscriptions:
s
=
self
.
get
(
required
[
0
])
result
=
s
.
get
((
's'
,
provided
))
if
result
:
result
=
list
(
result
)
else
:
result
=
[]
default
=
self
.
_default
.
get
((
's'
,
provided
))
if
default
:
result
.
extend
(
default
)
return
result
elif
order
==
0
:
result
=
self
.
_null
.
get
((
's'
,
provided
))
if
result
:
return
list
(
result
)
else
:
return
[]
# Multi
key
=
's'
,
provided
,
order
with
=
required
[
1
:]
result
=
[]
for
surrogate
in
self
.
get
(
required
[
0
]),
self
.
_default
:
bywith
=
surrogate
.
get
(
key
)
if
not
bywith
:
continue
for
rwith
,
values
in
bywith
:
for
rspec
,
spec
in
zip
(
rwith
,
with
):
if
not
spec
.
isOrExtends
(
rspec
):
break
# This one is no good
else
:
# we didn't break, so we have a match
result
.
extend
(
values
)
return
result
def
queryMultiAdapter
(
self
,
objects
,
interface
,
name
=
''
,
default
=
None
):
factory
=
self
.
lookup
(
map
(
providedBy
,
objects
),
interface
,
name
)
if
factory
is
not
None
:
return
factory
(
*
objects
)
return
default
def
subscribers
(
self
,
objects
,
interface
):
subscriptions
=
self
.
subscriptions
(
map
(
providedBy
,
objects
),
interface
)
return
[
subscription
(
*
objects
)
for
subscription
in
subscriptions
]
def
get
(
self
,
declaration
):
if
declaration
is
None
:
return
self
.
_default
ref
=
declaration
.
weakref
(
self
.
_remove
)
surrogate
=
self
.
_surrogates
.
get
(
ref
)
if
surrogate
is
None
:
surrogate
=
self
.
_surrogateClass
(
declaration
,
self
.
_registry
)
self
.
_surrogates
[
ref
]
=
surrogate
return
surrogate
class
AdapterRegistry
(
object
):
"""Adapter registry
"""
# Implementation note:
# We are like a weakref dict ourselves. We can't use a weakref
# dict because we have to use spec.weakref() rather than
# weakref.ref(spec) to get weak refs to specs.
_surrogateClass
=
Surrogate
def
__init__
(
self
):
default
=
self
.
_surrogateClass
(
Default
,
self
)
self
.
_default
=
default
null
=
self
.
_surrogateClass
(
Null
,
self
)
self
.
_null
=
null
# Create separate lookup object and copy it's methods
surrogates
=
{
Default
.
weakref
():
default
,
Null
.
weakref
():
null
}
def
_remove
(
k
):
try
:
del
surrogates
[
k
]
except
KeyError
:
pass
lookup
=
AdapterLookup
(
self
,
surrogates
,
_remove
)
for
name
in
(
'lookup'
,
'lookup1'
,
'queryAdapter'
,
'get'
,
'adapter_hook'
,
'subscriptions'
,
'queryMultiAdapter'
,
'subscribers'
,
):
setattr
(
self
,
name
,
getattr
(
lookup
,
name
))
def
register
(
self
,
required
,
provided
,
name
,
value
):
if
required
:
with
=
[]
for
iface
in
required
[
1
:]:
if
iface
is
None
:
iface
=
Interface
with
.
append
(
iface
)
with
=
tuple
(
with
)
required
=
self
.
get
(
required
[
0
])
else
:
with
=
()
required
=
self
.
_null
if
not
isinstance
(
name
,
basestring
):
raise
TypeError
(
"The name provided to provideAdapter "
"must be a string or unicode"
)
required
.
_adaptTo
(
provided
,
value
,
unicode
(
name
),
with
)
def
lookupAll
(
self
,
required
,
provided
):
order
=
len
(
required
)
if
order
==
1
:
# Simple adapter:
s
=
self
.
get
(
required
[
0
])
byname
=
s
.
get
(
provided
)
if
byname
:
for
item
in
byname
.
iteritems
():
yield
item
defbyname
=
self
.
_default
.
get
(
provided
)
if
defbyname
:
for
name
,
value
in
defbyname
.
iteritems
():
if
name
in
byname
:
continue
yield
name
,
value
return
elif
order
==
0
:
# null adapter
byname
=
self
.
_null
.
get
(
provided
)
if
byname
:
for
item
in
byname
.
iteritems
():
yield
item
return
# Multi adapter
with
=
required
[
1
:]
key
=
provided
,
order
first
=
()
for
surrogate
in
self
.
get
(
required
[
0
]),
self
.
_default
:
byname
=
surrogate
.
get
(
key
)
if
not
byname
:
continue
for
name
,
bywith
in
byname
.
iteritems
():
if
not
bywith
or
name
in
first
:
continue
# See comments on lookup() above
best
=
None
for
rwith
,
value
in
bywith
:
# the `rank` describes how well the found adapter matches.
rank
=
[]
for
rspec
,
spec
in
zip
(
rwith
,
with
):
if
not
spec
.
isOrExtends
(
rspec
):
break
# This one is no good
# Determine the rank of this particular specification.
rank
.
append
(
list
(
spec
.
__sro__
).
index
(
rspec
))
else
:
# If the new rank is better than the best previously
# recorded one, make the new adapter the best one found.
rank
=
tuple
(
rank
)
if
best
is
None
or
rank
<
best
[
0
]:
best
=
rank
,
value
# If any match was found, return the best one.
if
best
:
yield
name
,
best
[
1
]
first
=
byname
def
subscribe
(
self
,
required
,
provided
,
value
):
if
required
:
required
,
with
=
self
.
get
(
required
[
0
]),
tuple
(
required
[
1
:])
else
:
required
=
self
.
_null
with
=
()
if
provided
is
None
:
provided
=
Null
required
.
_subscriptionAdaptTo
(
provided
,
value
,
with
)
def
mextends
(
with
,
rwith
):
if
len
(
with
)
==
len
(
rwith
):
for
w
,
r
in
zip
(
with
,
rwith
):
if
not
w
.
isOrExtends
(
r
):
break
else
:
return
True
return
False
def
adapterImplied
(
adapters
):
implied
=
{}
multi
=
{}
# This dictionary is used to catch situations specific adapters
# override less specific adapters.
# Because subscriptions are cumulative, registered doesn't apply.
registered
=
{}
# Add adapters and interfaces directly implied by same:
for
key
,
value
in
adapters
.
iteritems
():
# TODO: Backward compatibility
# BBB ? Don't need to handle 3-tuples some day
try
:
(
subscription
,
with
,
name
,
target
)
=
key
except
ValueError
:
(
with
,
name
,
target
)
=
key
subscription
=
False
if
subscription
:
if
with
:
_add_multi_sub_adapter
(
with
,
target
,
multi
,
value
)
else
:
_add_named_sub_adapter
(
target
,
implied
,
value
)
else
:
if
with
:
_add_multi_adapter
(
with
,
name
,
target
,
target
,
multi
,
registered
,
value
)
else
:
_add_named_adapter
(
target
,
target
,
name
,
implied
,
registered
,
value
)
return
implied
,
multi
def
_add_named_adapter
(
target
,
provided
,
name
,
implied
,
registered
,
value
):
ikey
=
target
rkey
=
target
,
name
byname
=
implied
.
get
(
ikey
)
if
not
byname
:
byname
=
implied
[
ikey
]
=
{}
if
(
name
not
in
byname
or
(
rkey
in
registered
and
registered
[
rkey
].
extends
(
provided
))
):
registered
[
rkey
]
=
provided
byname
[
name
]
=
value
for
b
in
target
.
__bases__
:
_add_named_adapter
(
b
,
provided
,
name
,
implied
,
registered
,
value
)
def
_add_multi_adapter
(
with
,
name
,
target
,
provided
,
implied
,
registered
,
object
):
ikey
=
target
,
(
len
(
with
)
+
1
)
byname
=
implied
.
get
(
ikey
)
if
not
byname
:
byname
=
implied
[
ikey
]
=
{}
bywith
=
byname
.
get
(
name
)
if
not
bywith
:
bywith
=
byname
[
name
]
=
{}
rkey
=
ikey
,
name
,
with
# The full key has all 4
if
(
with
not
in
bywith
or
(
rkey
not
in
registered
or
registered
[
rkey
].
extends
(
provided
))
):
# This is either a new entry or it is an entry for a more
# general interface that is closer provided than what we had
# before
registered
[
rkey
]
=
provided
bywith
[
with
]
=
object
for
b
in
target
.
__bases__
:
_add_multi_adapter
(
with
,
name
,
b
,
provided
,
implied
,
registered
,
object
)
def
_add_named_sub_adapter
(
target
,
implied
,
objects
):
key
=
(
's'
,
target
)
implied
[
key
]
=
implied
.
get
(
key
,
())
+
objects
for
b
in
target
.
__bases__
:
_add_named_sub_adapter
(
b
,
implied
,
objects
)
def
_add_multi_sub_adapter
(
with
,
target
,
implied
,
objects
):
key
=
's'
,
target
,
(
len
(
with
)
+
1
)
bywith
=
implied
.
get
(
key
)
if
not
bywith
:
bywith
=
implied
[
key
]
=
{}
bywith
[
with
]
=
bywith
.
get
(
with
,
())
+
objects
for
b
in
target
.
__bases__
:
_add_multi_sub_adapter
(
with
,
b
,
implied
,
objects
)
trunk/src/zope/interface/adapter.txt
0 → 100644
View file @
a3c091cb
================
Adapter Registry
================
Adapter registries provide a way to register objects that depend on
one or more interface specifications and provide (perhaps indirectly)
some interface. In addition, the registrations have names. (You can
think of the names as qualifiers of the provided interfaces.)
The term "interface specification" refers both to interfaces and to
interface declarations, such as declarations of interfaces implemented
by a class.
Single Adapters
===============
Let's look at a simple example, using a single required specification::
>>> from zope.interface.adapter import AdapterRegistry
>>> import zope.interface
>>> class IR1(zope.interface.Interface):
... pass
>>> class IP1(zope.interface.Interface):
... pass
>>> class IP2(IP1):
... pass
>>> registry = AdapterRegistry()
We'll register an object that depends on IR1 and "provides" IP2::
>>> registry.register([IR1], IP2, '', 12)
Given the registration, we can look it up again::
>>> registry.lookup([IR1], IP2, '')
12
Note that we used an integer in the example. In real applications,
one would use some objects that actually depend on or provide
interfaces. The registry doesn't care about what gets registered, so
we'll use integers and strings to keep the examples simple. There is
one exception. Registering a value of None unregisters any
previously-registered value.
If an object depends on a specification, it can be looked up with a
specification that extends the specification that it depends on::
>>> class IR2(IR1):
... pass
>>> registry.lookup([IR2], IP2, '')
12
We can use a class implementation specification to look up the object::
>>> class C2:
... zope.interface.implements(IR2)
>>> registry.lookup([zope.interface.implementedBy(C2)], IP2, '')
12
and it can be looked up for interfaces that its provided interface
extends::
>>> registry.lookup([IR1], IP1, '')
12
>>> registry.lookup([IR2], IP1, '')
12
But if you require a specification that doesn't extend the specification the
object depends on, you won't get anything::
>>> registry.lookup([zope.interface.Interface], IP1, '')
By the way, you can pass a default value to lookup::
>>> registry.lookup([zope.interface.Interface], IP1, '', 42)
42
If you try to get an interface the object doesn't provide, you also
won't get anything::
>>> class IP3(IP2):
... pass
>>> registry.lookup([IR1], IP3, '')
You also won't get anything if you use the wrong name::
>>> registry.lookup([IR1], IP1, 'bob')
>>> registry.register([IR1], IP2, 'bob', "Bob's 12")
>>> registry.lookup([IR1], IP1, 'bob')
"Bob's 12"
You can leave the name off when doing a lookup::
>>> registry.lookup([IR1], IP1)
12
If we register an object that provides IP1::
>>> registry.register([IR1], IP1, '', 11)
then that object will be prefered over O(12)::
>>> registry.lookup([IR1], IP1, '')
11
Also, if we register an object for IR2, then that will be prefered
when using IR2::
>>> registry.register([IR2], IP1, '', 21)
>>> registry.lookup([IR2], IP1, '')
21
lookup1
-------
Lookup of single adapters is common enough that there is a specialized
version of lookup that takes a single required interface::
>>> registry.lookup1(IR2, IP1, '')
21
>>> registry.lookup1(IR2, IP1)
21
Actual Adaptation
-----------------
The adapter registry is intended to support adaptation, where one
object that implements an interface is adapted to another object that
supports a different interface. The adapter registry supports the
computation of adapters. In this case, we have to register adapter
factories::
>>> class IR(zope.interface.Interface):
... pass
>>> class X:
... zope.interface.implements(IR)
>>> class Y:
... zope.interface.implements(IP1)
... def __init__(self, context):
... self.context = context
>>> registry.register([IR], IP1, '', Y)
In this case, we registered a class as the factory. Now we can call
`queryAdapter` to get the adapted object::
>>> x = X()
>>> y = registry.queryAdapter(x, IP1)
>>> y.__class__.__name__
'Y'
>>> y.context is x
True
We can register and lookup by name too::
>>> class Y2(Y):
... pass
>>> registry.register([IR], IP1, 'bob', Y2)
>>> y = registry.queryAdapter(x, IP1, 'bob')
>>> y.__class__.__name__
'Y2'
>>> y.context is x
True
When the adapter factory produces `None`, then this is treated as if no
adapter has been found. This allows us to prevent adaptation (when desired)
and let the adapter factory determine whether adaptation is possible based on
the state of the object being adapted.
>>> def factory(context):
... if context.name == 'object':
... return 'adapter'
... return None
>>> class Object(object):
... zope.interface.implements(IR)
... name = 'object'
>>> registry.register([IR], IP1, 'conditional', factory)
>>> obj = Object()
>>> registry.queryAdapter(obj, IP1, 'conditional')
'adapter'
>>> obj.name = 'no object'
>>> registry.queryAdapter(obj, IP1, 'conditional') is None
True
>>> registry.queryAdapter(obj, IP1, 'conditional', 'default')
'default'
An alternate method that provides the same function as `queryAdapter()` is
`adapter_hook()`::
>>> y = registry.adapter_hook(IP1, x)
>>> y.__class__.__name__
'Y'
>>> y.context is x
True
>>> y = registry.adapter_hook(IP1, x, 'bob')
>>> y.__class__.__name__
'Y2'
>>> y.context is x
True
The `adapter_hook()` simply switches the order of the object and
interface arguments. It is used to hook into the interface call
mechanism.
Default Adapters
----------------
Sometimes, you want to provide an adapter that will adapt anything.
For that, provide None as the required interface::
>>> registry.register([None], IP1, '', 1)
then we can use that adapter for interfaces we don't have specific
adapters for::
>>> class IQ(zope.interface.Interface):
... pass
>>> registry.lookup([IQ], IP1, '')
1
Of course, specific adapters are still used when applicable::
>>> registry.lookup([IR2], IP1, '')
21
Class adapters
--------------
You can register adapters for class declarations, which is almost the
same as registering them for a class::
>>> registry.register([zope.interface.implementedBy(C2)], IP1, '', 'C21')
>>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '')
'C21'
Dict adapters
-------------
At some point it was impossible to register dictionary-based adapters due a
bug. Let's make sure this works now:
>>> adapter = {}
>>> registry.register((), IQ, '', adapter)
>>> registry.lookup((), IQ, '') is adapter
True
Unregistering
-------------
You can unregister by registering None, rather than an object::
>>> registry.register([zope.interface.implementedBy(C2)], IP1, '', None)
>>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '')
21
Of course, this means that None can't be registered. This is an
exception to the statement, made earlier, that the registry doesn't
care what gets registered.
Multi-adapters
==============
You can adapt multiple specifications::
>>> registry.register([IR1, IQ], IP2, '', '1q2')
>>> registry.lookup([IR1, IQ], IP2, '')
'1q2'
>>> registry.lookup([IR2, IQ], IP1, '')
'1q2'
>>> class IS(zope.interface.Interface):
... pass
>>> registry.lookup([IR2, IS], IP1, '')
>>> class IQ2(IQ):
... pass
>>> registry.lookup([IR2, IQ2], IP1, '')
'1q2'
>>> registry.register([IR1, IQ2], IP2, '', '1q22')
>>> registry.lookup([IR2, IQ2], IP1, '')
'1q22'
Multi-adaptation
----------------
You can adapt multiple objects::
>>> class Q:
... zope.interface.implements(IQ)
As with single adapters, we register a factory, which is often a class::
>>> class IM(zope.interface.Interface):
... pass
>>> class M:
... zope.interface.implements(IM)
... def __init__(self, x, q):
... self.x, self.q = x, q
>>> registry.register([IR, IQ], IM, '', M)
And then we can call `queryMultiAdapter` to compute an adapter::
>>> q = Q()
>>> m = registry.queryMultiAdapter((x, q), IM)
>>> m.__class__.__name__
'M'
>>> m.x is x and m.q is q
True
and, of course, we can use names::
>>> class M2(M):
... pass
>>> registry.register([IR, IQ], IM, 'bob', M2)
>>> m = registry.queryMultiAdapter((x, q), IM, 'bob')
>>> m.__class__.__name__
'M2'
>>> m.x is x and m.q is q
True
Default Adapters
----------------
As with single adapters, you can define default adapters by specifying
None for the *first* specification::
>>> registry.register([None, IQ], IP2, '', 'q2')
>>> registry.lookup([IS, IQ], IP2, '')
'q2'
Null Adapters
=============
You can also adapt no specification::
>>> registry.register([], IP2, '', 2)
>>> registry.lookup([], IP2, '')
2
>>> registry.lookup([], IP1, '')
2
Listing named adapters
----------------------
Adapters are named. Sometimes, it's useful to get all of the named
adapters for given interfaces::
>>> adapters = list(registry.lookupAll([IR1], IP1))
>>> adapters.sort()
>>> adapters
[(u'', 11), (u'bob', "Bob's 12")]
This works for multi-adapters too::
>>> registry.register([IR1, IQ2], IP2, 'bob', '1q2 for bob')
>>> adapters = list(registry.lookupAll([IR2, IQ2], IP1))
>>> adapters.sort()
>>> adapters
[(u'', '1q22'), (u'bob', '1q2 for bob')]
And even null adapters::
>>> registry.register([], IP2, 'bob', 3)
>>> adapters = list(registry.lookupAll([], IP1))
>>> adapters.sort()
>>> adapters
[(u'', 2), (u'bob', 3)]
Subscriptions
=============
Normally, we want to look up an object that most-closely matches a
specification. Sometimes, we want to get all of the objects that
match some specification. We use subscriptions for this. We
subscribe objects against specifications and then later find all of
the subscribed objects::
>>> registry.subscribe([IR1], IP2, 'sub12 1')
>>> registry.subscriptions([IR1], IP2)
['sub12 1']
Note that, unlike regular adapters, subscriptions are unnamed.
The order of returned subscriptions is not specified.
You can have multiple subscribers for the same specification::
>>> registry.subscribe([IR1], IP2, 'sub12 2')
>>> subs = registry.subscriptions([IR1], IP2)
>>> subs.sort()
>>> subs
['sub12 1', 'sub12 2']
You can register subscribers for all specifications using None::
>>> registry.subscribe([None], IP1, 'sub_1')
>>> subs = registry.subscriptions([IR2], IP1)
>>> subs.sort()
>>> subs
['sub12 1', 'sub12 2', 'sub_1']
Subscriptions may be combined over multiple compatible specifications::
>>> subs = registry.subscriptions([IR2], IP1)
>>> subs.sort()
>>> subs
['sub12 1', 'sub12 2', 'sub_1']
>>> registry.subscribe([IR1], IP1, 'sub11')
>>> subs = registry.subscriptions([IR2], IP1)
>>> subs.sort()
>>> subs
['sub11', 'sub12 1', 'sub12 2', 'sub_1']
>>> registry.subscribe([IR2], IP2, 'sub22')
>>> subs = registry.subscriptions([IR2], IP1)
>>> subs.sort()
>>> subs
['sub11', 'sub12 1', 'sub12 2', 'sub22', 'sub_1']
>>> subs = registry.subscriptions([IR2], IP2)
>>> subs.sort()
>>> subs
['sub12 1', 'sub12 2', 'sub22']
Subscriptions can be on multiple specifications::
>>> registry.subscribe([IR1, IQ], IP2, 'sub1q2')
>>> registry.subscriptions([IR1, IQ], IP2)
['sub1q2']
As with single subscriptions and non-subscription adapters, you can
specify None for the first required interface, to specify a default::
>>> registry.subscribe([None, IQ], IP2, 'sub_q2')
>>> registry.subscriptions([IS, IQ], IP2)
['sub_q2']
>>> subs = registry.subscriptions([IR1, IQ], IP2)
>>> subs.sort()
>>> subs
['sub1q2', 'sub_q2']
You can have subscriptions that are indepenent of any specifications::
>>> registry.subscriptions([], IP1)
[]
>>> registry.subscribe([], IP2, 'sub2')
>>> registry.subscriptions([], IP1)
['sub2']
>>> registry.subscribe([], IP1, 'sub1')
>>> subs = registry.subscriptions([], IP1)
>>> subs.sort()
>>> subs
['sub1', 'sub2']
>>> registry.subscriptions([], IP2)
['sub2']
Subscription adapters
---------------------
We normally register adapter factories, which then allow us to compute
adapters, but with subscriptions, we get multiple adapters. Here's an
example of multiple-object subscribers::
>>> registry.subscribe([IR, IQ], IM, M)
>>> registry.subscribe([IR, IQ], IM, M2)
>>> subscribers = registry.subscribers((x, q), IM)
>>> len(subscribers)
2
>>> class_names = [s.__class__.__name__ for s in subscribers]
>>> class_names.sort()
>>> class_names
['M', 'M2']
>>> [(s.x is x and s.q is q) for s in subscribers]
[True, True]
Handlers
--------
A handler is a subscriber factory that doesn't produce any normal
output. It returns None. A handler is unlike adapters in that it does
all of its work when the factory is called.
To register a handler, simply provide None as the provided interface::
>>> def handler(event):
... print 'handler', event
>>> registry.subscribe([IR1], None, handler)
>>> registry.subscriptions([IR1], None) == [handler]
True
trunk/src/zope/interface/advice.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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 advice.
This module was adapted from 'protocols.advice', part of the Python
Enterprise Application Kit (PEAK). Please notify the PEAK authors
(pje@telecommunity.com and tsarna@sarna.org) if bugs are found or
Zope-specific changes are required, so that the PEAK version of this module
can be kept in sync.
PEAK is a Python application framework that interoperates with (but does
not require) Zope 3 and Twisted. It provides tools for manipulating UML
models, object-relational persistence, aspect-oriented programming, and more.
Visit the PEAK home page at http://peak.telecommunity.com for more information.
$Id$
"""
from
types
import
ClassType
,
FunctionType
import
sys
def
getFrameInfo
(
frame
):
"""Return (kind,module,locals,globals) for a frame
'kind' is one of "exec", "module", "class", "function call", or "unknown".
"""
f_locals
=
frame
.
f_locals
f_globals
=
frame
.
f_globals
sameNamespace
=
f_locals
is
f_globals
hasModule
=
'__module__'
in
f_locals
hasName
=
'__name__'
in
f_globals
sameName
=
hasModule
and
hasName
sameName
=
sameName
and
f_globals
[
'__name__'
]
==
f_locals
[
'__module__'
]
module
=
hasName
and
sys
.
modules
.
get
(
f_globals
[
'__name__'
])
or
None
namespaceIsModule
=
module
and
module
.
__dict__
is
f_globals
if
not
namespaceIsModule
:
# some kind of funky exec
kind
=
"exec"
elif
sameNamespace
and
not
hasModule
:
kind
=
"module"
elif
sameName
and
not
sameNamespace
:
kind
=
"class"
elif
not
sameNamespace
:
kind
=
"function call"
else
:
# How can you have f_locals is f_globals, and have '__module__' set?
# This is probably module-level code, but with a '__module__' variable.
kind
=
"unknown"
return
kind
,
module
,
f_locals
,
f_globals
def
addClassAdvisor
(
callback
,
depth
=
2
):
"""Set up 'callback' to be passed the containing class upon creation
This function is designed to be called by an "advising" function executed
in a class suite. The "advising" function supplies a callback that it
wishes to have executed when the containing class is created. The
callback will be given one argument: the newly created containing class.
The return value of the callback will be used in place of the class, so
the callback should return the input if it does not wish to replace the
class.
The optional 'depth' argument to this function determines the number of
frames between this function and the targeted class suite. 'depth'
defaults to 2, since this skips this function's frame and one calling
function frame. If you use this function from a function called directly
in the class suite, the default will be correct, otherwise you will need
to determine the correct depth yourself.
This function works by installing a special class factory function in
place of the '__metaclass__' of the containing class. Therefore, only
callbacks *after* the last '__metaclass__' assignment in the containing
class will be executed. Be sure that classes using "advising" functions
declare any '__metaclass__' *first*, to ensure all callbacks are run."""
frame
=
sys
.
_getframe
(
depth
)
kind
,
module
,
caller_locals
,
caller_globals
=
getFrameInfo
(
frame
)
# This causes a problem when zope interfaces are used from doctest.
# In these cases, kind == "exec".
#
#if kind != "class":
# raise SyntaxError(
# "Advice must be in the body of a class statement"
# )
previousMetaclass
=
caller_locals
.
get
(
'__metaclass__'
)
defaultMetaclass
=
caller_globals
.
get
(
'__metaclass__'
,
ClassType
)
def
advise
(
name
,
bases
,
cdict
):
if
'__metaclass__'
in
cdict
:
del
cdict
[
'__metaclass__'
]
if
previousMetaclass
is
None
:
if
bases
:
# find best metaclass or use global __metaclass__ if no bases
meta
=
determineMetaclass
(
bases
)
else
:
meta
=
defaultMetaclass
elif
isClassAdvisor
(
previousMetaclass
):
# special case: we can't compute the "true" metaclass here,
# so we need to invoke the previous metaclass and let it
# figure it out for us (and apply its own advice in the process)
meta
=
previousMetaclass
else
:
meta
=
determineMetaclass
(
bases
,
previousMetaclass
)
newClass
=
meta
(
name
,
bases
,
cdict
)
# this lets the callback replace the class completely, if it wants to
return
callback
(
newClass
)
# introspection data only, not used by inner function
advise
.
previousMetaclass
=
previousMetaclass
advise
.
callback
=
callback
# install the advisor
caller_locals
[
'__metaclass__'
]
=
advise
def
isClassAdvisor
(
ob
):
"""True if 'ob' is a class advisor function"""
return
isinstance
(
ob
,
FunctionType
)
and
hasattr
(
ob
,
'previousMetaclass'
)
def
determineMetaclass
(
bases
,
explicit_mc
=
None
):
"""Determine metaclass from 1+ bases and optional explicit __metaclass__"""
meta
=
[
getattr
(
b
,
'__class__'
,
type
(
b
))
for
b
in
bases
]
if
explicit_mc
is
not
None
:
# The explicit metaclass needs to be verified for compatibility
# as well, and allowed to resolve the incompatible bases, if any
meta
.
append
(
explicit_mc
)
if
len
(
meta
)
==
1
:
# easy case
return
meta
[
0
]
candidates
=
minimalBases
(
meta
)
# minimal set of metaclasses
if
not
candidates
:
# they're all "classic" classes
return
ClassType
elif
len
(
candidates
)
>
1
:
# We could auto-combine, but for now we won't...
raise
TypeError
(
"Incompatible metatypes"
,
bases
)
# Just one, return it
return
candidates
[
0
]
def
minimalBases
(
classes
):
"""Reduce a list of base classes to its ordered minimum equivalent"""
classes
=
[
c
for
c
in
classes
if
c
is
not
ClassType
]
candidates
=
[]
for
m
in
classes
:
for
n
in
classes
:
if
issubclass
(
n
,
m
)
and
m
is
not
n
:
break
else
:
# m has no subclasses in 'classes'
if
m
in
candidates
:
candidates
.
remove
(
m
)
# ensure that we're later in the list
candidates
.
append
(
m
)
return
candidates
trunk/src/zope/interface/common/__init__.py
0 → 100644
View file @
a3c091cb
#
# This file is necessary to make this directory a package.
trunk/src/zope/interface/common/idatetime.py
0 → 100644
View file @
a3c091cb
##############################################################################
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
##############################################################################
"""Datetime interfaces.
This module is called idatetime because if it were called datetime the import
of the real datetime would fail.
$Id$
"""
from
zope.interface
import
Interface
,
Attribute
from
zope.interface
import
classImplements
,
directlyProvides
from
datetime
import
timedelta
,
date
,
datetime
,
time
,
tzinfo
class
ITimeDeltaClass
(
Interface
):
"""This is the timedelta class interface."""
min
=
Attribute
(
"The most negative timedelta object"
)
max
=
Attribute
(
"The most positive timedelta object"
)
resolution
=
Attribute
(
"The smallest difference between non-equal timedelta objects"
)
class
ITimeDelta
(
ITimeDeltaClass
):
"""Represent the difference between two datetime objects.
Supported operators:
- add, subtract timedelta
- unary plus, minus, abs
- compare to timedelta
- multiply, divide by int/long
In addition, datetime supports subtraction of two datetime objects
returning a timedelta, and addition or subtraction of a datetime
and a timedelta giving a datetime.
Representation: (days, seconds, microseconds).
"""
days
=
Attribute
(
"Days between -999999999 and 999999999 inclusive"
)
seconds
=
Attribute
(
"Seconds between 0 and 86399 inclusive"
)
microseconds
=
Attribute
(
"Microseconds between 0 and 999999 inclusive"
)
class
IDateClass
(
Interface
):
"""This is the date class interface."""
min
=
Attribute
(
"The earliest representable date"
)
max
=
Attribute
(
"The latest representable date"
)
resolution
=
Attribute
(
"The smallest difference between non-equal date objects"
)
def
today
():
"""Return the current local time.
This is equivalent to date.fromtimestamp(time.time())"""
def
fromtimestamp
(
timestamp
):
"""Return the local date from a POSIX timestamp (like time.time())
This may raise ValueError, if the timestamp is out of the range of
values supported by the platform C localtime() function. It's common
for this to be restricted to years from 1970 through 2038. Note that
on non-POSIX systems that include leap seconds in their notion of a
timestamp, leap seconds are ignored by fromtimestamp().
"""
def
fromordinal
(
ordinal
):
"""Return the date corresponding to the proleptic Gregorian ordinal.
January 1 of year 1 has ordinal 1. ValueError is raised unless
1 <= ordinal <= date.max.toordinal().
For any date d, date.fromordinal(d.toordinal()) == d.
"""
class
IDate
(
IDateClass
):
"""Represents a date (year, month and day) in an idealized calendar.
Operators:
__repr__, __str__
__cmp__, __hash__
__add__, __radd__, __sub__ (add/radd only with timedelta arg)
"""
year
=
Attribute
(
"Between MINYEAR and MAXYEAR inclusive."
)
month
=
Attribute
(
"Between 1 and 12 inclusive"
)
day
=
Attribute
(
"Between 1 and the number of days in the given month of the given year."
)
def
replace
(
year
,
month
,
day
):
"""Return a date with the same value.
Except for those members given new values by whichever keyword
arguments are specified. For example, if d == date(2002, 12, 31), then
d.replace(day=26) == date(2000, 12, 26).
"""
def
timetuple
():
"""Return a 9-element tuple of the form returned by time.localtime().
The hours, minutes and seconds are 0, and the DST flag is -1.
d.timetuple() is equivalent to
(d.year, d.month, d.day, 0, 0, 0, d.weekday(), d.toordinal() -
date(d.year, 1, 1).toordinal() + 1, -1)
"""
def
toordinal
():
"""Return the proleptic Gregorian ordinal of the date
January 1 of year 1 has ordinal 1. For any date object d,
date.fromordinal(d.toordinal()) == d.
"""
def
weekday
():
"""Return the day of the week as an integer.
Monday is 0 and Sunday is 6. For example,
date(2002, 12, 4).weekday() == 2, a Wednesday.
See also isoweekday().
"""
def
isoweekday
():
"""Return the day of the week as an integer.
Monday is 1 and Sunday is 7. For example,
date(2002, 12, 4).isoweekday() == 3, a Wednesday.
See also weekday(), isocalendar().
"""
def
isocalendar
():
"""Return a 3-tuple, (ISO year, ISO week number, ISO weekday).
The ISO calendar is a widely used variant of the Gregorian calendar.
See http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm for a good
explanation.
The ISO year consists of 52 or 53 full weeks, and where a week starts
on a Monday and ends on a Sunday. The first week of an ISO year is the
first (Gregorian) calendar week of a year containing a Thursday. This
is called week number 1, and the ISO year of that Thursday is the same
as its Gregorian year.
For example, 2004 begins on a Thursday, so the first week of ISO year
2004 begins on Monday, 29 Dec 2003 and ends on Sunday, 4 Jan 2004, so
that date(2003, 12, 29).isocalendar() == (2004, 1, 1) and
date(2004, 1, 4).isocalendar() == (2004, 1, 7).
"""
def
isoformat
():
"""Return a string representing the date in ISO 8601 format.
This is 'YYYY-MM-DD'.
For example, date(2002, 12, 4).isoformat() == '2002-12-04'.
"""
def
__str__
():
"""For a date d, str(d) is equivalent to d.isoformat()."""
def
ctime
():
"""Return a string representing the date.
For example date(2002, 12, 4).ctime() == 'Wed Dec 4 00:00:00 2002'.
d.ctime() is equivalent to time.ctime(time.mktime(d.timetuple()))
on platforms where the native C ctime() function
(which time.ctime() invokes, but which date.ctime() does not invoke)
conforms to the C standard.
"""
def
strftime
(
format
):
"""Return a string representing the date.
Controlled by an explicit format string. Format codes referring to
hours, minutes or seconds will see 0 values.
"""
class
IDateTimeClass
(
Interface
):
"""This is the datetime class interface."""
min
=
Attribute
(
"The earliest representable datetime"
)
max
=
Attribute
(
"The latest representable datetime"
)
resolution
=
Attribute
(
"The smallest possible difference between non-equal datetime objects"
)
def
today
():
"""Return the current local datetime, with tzinfo None.
This is equivalent to datetime.fromtimestamp(time.time()).
See also now(), fromtimestamp().
"""
def
now
(
tz
=
None
):
"""Return the current local date and time.
If optional argument tz is None or not specified, this is like today(),
but, if possible, supplies more precision than can be gotten from going
through a time.time() timestamp (for example, this may be possible on
platforms supplying the C gettimeofday() function).
Else tz must be an instance of a class tzinfo subclass, and the current
date and time are converted to tz's time zone. In this case the result
is equivalent to tz.fromutc(datetime.utcnow().replace(tzinfo=tz)).
See also today(), utcnow().
"""
def
utcnow
():
"""Return the current UTC date and time, with tzinfo None.
This is like now(), but returns the current UTC date and time, as a
naive datetime object.
See also now().
"""
def
fromtimestamp
(
timestamp
,
tz
=
None
):
"""Return the local date and time corresponding to the POSIX timestamp.
Same as is returned by time.time(). If optional argument tz is None or
not specified, the timestamp is converted to the platform's local date
and time, and the returned datetime object is naive.
Else tz must be an instance of a class tzinfo subclass, and the
timestamp is converted to tz's time zone. In this case the result is
equivalent to
tz.fromutc(datetime.utcfromtimestamp(timestamp).replace(tzinfo=tz)).
fromtimestamp() may raise ValueError, if the timestamp is out of the
range of values supported by the platform C localtime() or gmtime()
functions. It's common for this to be restricted to years in 1970
through 2038. Note that on non-POSIX systems that include leap seconds
in their notion of a timestamp, leap seconds are ignored by
fromtimestamp(), and then it's possible to have two timestamps
differing by a second that yield identical datetime objects.
See also utcfromtimestamp().
"""
def
utcfromtimestamp
(
timestamp
):
"""Return the UTC datetime from the POSIX timestamp with tzinfo None.
This may raise ValueError, if the timestamp is out of the range of
values supported by the platform C gmtime() function. It's common for
this to be restricted to years in 1970 through 2038.
See also fromtimestamp().
"""
def
fromordinal
(
ordinal
):
"""Return the datetime from the proleptic Gregorian ordinal.
January 1 of year 1 has ordinal 1. ValueError is raised unless
1 <= ordinal <= datetime.max.toordinal().
The hour, minute, second and microsecond of the result are all 0, and
tzinfo is None.
"""
def
combine
(
date
,
time
):
"""Return a new datetime object.
Its date members are equal to the given date object's, and whose time
and tzinfo members are equal to the given time object's. For any
datetime object d, d == datetime.combine(d.date(), d.timetz()).
If date is a datetime object, its time and tzinfo members are ignored.
"""
class
IDateTime
(
IDate
,
IDateTimeClass
):
"""Object contains all the information from a date object and a time object.
"""
year
=
Attribute
(
"Year between MINYEAR and MAXYEAR inclusive"
)
month
=
Attribute
(
"Month between 1 and 12 inclusive"
)
day
=
Attribute
(
"Day between 1 and the number of days in the given month of the year"
)
hour
=
Attribute
(
"Hour in range(24)"
)
minute
=
Attribute
(
"Minute in range(60)"
)
second
=
Attribute
(
"Second in range(60)"
)
microsecond
=
Attribute
(
"Microsecond in range(1000000)"
)
tzinfo
=
Attribute
(
"""The object passed as the tzinfo argument to the datetime constructor
or None if none was passed"""
)
def
date
():
"""Return date object with same year, month and day."""
def
time
():
"""Return time object with same hour, minute, second, microsecond.
tzinfo is None. See also method timetz().
"""
def
timetz
():
"""Return time object with same hour, minute, second, microsecond,
and tzinfo.
See also method time().
"""
def
replace
(
year
,
month
,
day
,
hour
,
minute
,
second
,
microsecond
,
tzinfo
):
"""Return a datetime with the same members, except for those members
given new values by whichever keyword arguments are specified.
Note that tzinfo=None can be specified to create a naive datetime from
an aware datetime with no conversion of date and time members.
"""
def
astimezone
(
tz
):
"""Return a datetime object with new tzinfo member tz, adjusting the
date and time members so the result is the same UTC time as self, but
in tz's local time.
tz must be an instance of a tzinfo subclass, and its utcoffset() and
dst() methods must not return None. self must be aware (self.tzinfo
must not be None, and self.utcoffset() must not return None).
If self.tzinfo is tz, self.astimezone(tz) is equal to self: no
adjustment of date or time members is performed. Else the result is
local time in time zone tz, representing the same UTC time as self:
after astz = dt.astimezone(tz), astz - astz.utcoffset()
will usually have the same date and time members as dt - dt.utcoffset().
The discussion of class tzinfo explains the cases at Daylight Saving
Time transition boundaries where this cannot be achieved (an issue only
if tz models both standard and daylight time).
If you merely want to attach a time zone object tz to a datetime dt
without adjustment of date and time members, use dt.replace(tzinfo=tz).
If you merely want to remove the time zone object from an aware
datetime dt without conversion of date and time members, use
dt.replace(tzinfo=None).
Note that the default tzinfo.fromutc() method can be overridden in a
tzinfo subclass to effect the result returned by astimezone().
"""
def
utcoffset
():
"""Return the timezone offset in minutes east of UTC (negative west of
UTC)."""
def
dst
():
"""Return 0 if DST is not in effect, or the DST offset (in minutes
eastward) if DST is in effect.
"""
def
tzname
():
"""Return the timezone name."""
def
timetuple
():
"""Return a 9-element tuple of the form returned by time.localtime()."""
def
utctimetuple
():
"""Return UTC time tuple compatilble with time.gmtimr()."""
def
toordinal
():
"""Return the proleptic Gregorian ordinal of the date.
The same as self.date().toordinal().
"""
def
weekday
():
"""Return the day of the week as an integer.
Monday is 0 and Sunday is 6. The same as self.date().weekday().
See also isoweekday().
"""
def
isoweekday
():
"""Return the day of the week as an integer.
Monday is 1 and Sunday is 7. The same as self.date().isoweekday.
See also weekday(), isocalendar().
"""
def
isocalendar
():
"""Return a 3-tuple, (ISO year, ISO week number, ISO weekday).
The same as self.date().isocalendar().
"""
def
isoformat
(
sep
=
'T'
):
"""Return a string representing the date and time in ISO 8601 format.
YYYY-MM-DDTHH:MM:SS.mmmmmm or YYYY-MM-DDTHH:MM:SS if microsecond is 0
If utcoffset() does not return None, a 6-character string is appended,
giving the UTC offset in (signed) hours and minutes:
YYYY-MM-DDTHH:MM:SS.mmmmmm+HH:MM or YYYY-MM-DDTHH:MM:SS+HH:MM
if microsecond is 0.
The optional argument sep (default 'T') is a one-character separator,
placed between the date and time portions of the result.
"""
def
__str__
():
"""For a datetime instance d, str(d) is equivalent to d.isoformat(' ').
"""
def
ctime
():
"""Return a string representing the date and time.
datetime(2002, 12, 4, 20, 30, 40).ctime() == 'Wed Dec 4 20:30:40 2002'.
d.ctime() is equivalent to time.ctime(time.mktime(d.timetuple())) on
platforms where the native C ctime() function (which time.ctime()
invokes, but which datetime.ctime() does not invoke) conforms to the
C standard.
"""
def
strftime
(
format
):
"""Return a string representing the date and time.
This is controlled by an explicit format string.
"""
class
ITimeClass
(
Interface
):
"""This is the time class interface."""
min
=
Attribute
(
"The earliest representable time"
)
max
=
Attribute
(
"The latest representable time"
)
resolution
=
Attribute
(
"The smallest possible difference between non-equal time objects"
)
class
ITime
(
ITimeClass
):
"""Represent time with time zone.
Operators:
__repr__, __str__
__cmp__, __hash__
"""
hour
=
Attribute
(
"Hour in range(24)"
)
minute
=
Attribute
(
"Minute in range(60)"
)
second
=
Attribute
(
"Second in range(60)"
)
microsecond
=
Attribute
(
"Microsecond in range(1000000)"
)
tzinfo
=
Attribute
(
"""The object passed as the tzinfo argument to the time constructor
or None if none was passed."""
)
def
replace
(
hour
,
minute
,
second
,
microsecond
,
tzinfo
):
"""Return a time with the same value.
Except for those members given new values by whichever keyword
arguments are specified. Note that tzinfo=None can be specified
to create a naive time from an aware time, without conversion of the
time members.
"""
def
isoformat
():
"""Return a string representing the time in ISO 8601 format.
That is HH:MM:SS.mmmmmm or, if self.microsecond is 0, HH:MM:SS
If utcoffset() does not return None, a 6-character string is appended,
giving the UTC offset in (signed) hours and minutes:
HH:MM:SS.mmmmmm+HH:MM or, if self.microsecond is 0, HH:MM:SS+HH:MM
"""
def
__str__
():
"""For a time t, str(t) is equivalent to t.isoformat()."""
def
strftime
(
format
):
"""Return a string representing the time.
This is controlled by an explicit format string.
"""
def
utcoffset
():
"""Return the timezone offset in minutes east of UTC (negative west of
UTC).
If tzinfo is None, returns None, else returns
self.tzinfo.utcoffset(None), and raises an exception if the latter
doesn't return None or a timedelta object representing a whole number
of minutes with magnitude less than one day.
"""
def
dst
():
"""Return 0 if DST is not in effect, or the DST offset (in minutes
eastward) if DST is in effect.
If tzinfo is None, returns None, else returns self.tzinfo.dst(None),
and raises an exception if the latter doesn't return None, or a
timedelta object representing a whole number of minutes with
magnitude less than one day.
"""
def
tzname
():
"""Return the timezone name.
If tzinfo is None, returns None, else returns self.tzinfo.tzname(None),
or raises an exception if the latter doesn't return None or a string
object.
"""
class
ITZInfo
(
Interface
):
"""Time zone info class.
"""
def
utcoffset
(
dt
):
"""Return offset of local time from UTC, in minutes east of UTC.
If local time is west of UTC, this should be negative.
Note that this is intended to be the total offset from UTC;
for example, if a tzinfo object represents both time zone and DST
adjustments, utcoffset() should return their sum. If the UTC offset
isn't known, return None. Else the value returned must be a timedelta
object specifying a whole number of minutes in the range -1439 to 1439
inclusive (1440 = 24*60; the magnitude of the offset must be less
than one day).
"""
def
dst
(
dt
):
"""Return the daylight saving time (DST) adjustment, in minutes east
of UTC, or None if DST information isn't known.
"""
def
tzname
(
dt
):
"""Return the time zone name corresponding to the datetime object as
a string.
"""
def
fromutc
(
dt
):
"""Return an equivalent datetime in self's local time."""
classImplements
(
timedelta
,
ITimeDelta
)
classImplements
(
date
,
IDate
)
classImplements
(
datetime
,
IDateTime
)
classImplements
(
time
,
ITime
)
classImplements
(
tzinfo
,
ITZInfo
)
## directlyProvides(timedelta, ITimeDeltaClass)
## directlyProvides(date, IDateClass)
## directlyProvides(datetime, IDateTimeClass)
## directlyProvides(time, ITimeClass)
trunk/src/zope/interface/common/interfaces.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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 for standard python exceptions
$Id$
"""
from
zope.interface
import
Interface
from
zope.interface
import
classImplements
class
IException
(
Interface
):
pass
class
IStandardError
(
IException
):
pass
class
IWarning
(
IException
):
pass
class
ISyntaxError
(
IStandardError
):
pass
class
ILookupError
(
IStandardError
):
pass
class
IValueError
(
IStandardError
):
pass
class
IRuntimeError
(
IStandardError
):
pass
class
IArithmeticError
(
IStandardError
):
pass
class
IAssertionError
(
IStandardError
):
pass
class
IAttributeError
(
IStandardError
):
pass
class
IDeprecationWarning
(
IWarning
):
pass
class
IEOFError
(
IStandardError
):
pass
class
IEnvironmentError
(
IStandardError
):
pass
class
IFloatingPointError
(
IArithmeticError
):
pass
class
IIOError
(
IEnvironmentError
):
pass
class
IImportError
(
IStandardError
):
pass
class
IIndentationError
(
ISyntaxError
):
pass
class
IIndexError
(
ILookupError
):
pass
class
IKeyError
(
ILookupError
):
pass
class
IKeyboardInterrupt
(
IStandardError
):
pass
class
IMemoryError
(
IStandardError
):
pass
class
INameError
(
IStandardError
):
pass
class
INotImplementedError
(
IRuntimeError
):
pass
class
IOSError
(
IEnvironmentError
):
pass
class
IOverflowError
(
IArithmeticError
):
pass
class
IOverflowWarning
(
IWarning
):
pass
class
IReferenceError
(
IStandardError
):
pass
class
IRuntimeWarning
(
IWarning
):
pass
class
IStopIteration
(
IException
):
pass
class
ISyntaxWarning
(
IWarning
):
pass
class
ISystemError
(
IStandardError
):
pass
class
ISystemExit
(
IException
):
pass
class
ITabError
(
IIndentationError
):
pass
class
ITypeError
(
IStandardError
):
pass
class
IUnboundLocalError
(
INameError
):
pass
class
IUnicodeError
(
IValueError
):
pass
class
IUserWarning
(
IWarning
):
pass
class
IZeroDivisionError
(
IArithmeticError
):
pass
classImplements
(
ArithmeticError
,
IArithmeticError
)
classImplements
(
AssertionError
,
IAssertionError
)
classImplements
(
AttributeError
,
IAttributeError
)
classImplements
(
DeprecationWarning
,
IDeprecationWarning
)
classImplements
(
EnvironmentError
,
IEnvironmentError
)
classImplements
(
EOFError
,
IEOFError
)
classImplements
(
Exception
,
IException
)
classImplements
(
FloatingPointError
,
IFloatingPointError
)
classImplements
(
ImportError
,
IImportError
)
classImplements
(
IndentationError
,
IIndentationError
)
classImplements
(
IndexError
,
IIndexError
)
classImplements
(
IOError
,
IIOError
)
classImplements
(
KeyboardInterrupt
,
IKeyboardInterrupt
)
classImplements
(
KeyError
,
IKeyError
)
classImplements
(
LookupError
,
ILookupError
)
classImplements
(
MemoryError
,
IMemoryError
)
classImplements
(
NameError
,
INameError
)
classImplements
(
NotImplementedError
,
INotImplementedError
)
classImplements
(
OSError
,
IOSError
)
classImplements
(
OverflowError
,
IOverflowError
)
classImplements
(
OverflowWarning
,
IOverflowWarning
)
classImplements
(
ReferenceError
,
IReferenceError
)
classImplements
(
RuntimeError
,
IRuntimeError
)
classImplements
(
RuntimeWarning
,
IRuntimeWarning
)
classImplements
(
StandardError
,
IStandardError
)
classImplements
(
StopIteration
,
IStopIteration
)
classImplements
(
SyntaxError
,
ISyntaxError
)
classImplements
(
SyntaxWarning
,
ISyntaxWarning
)
classImplements
(
SystemError
,
ISystemError
)
classImplements
(
SystemExit
,
ISystemExit
)
classImplements
(
TabError
,
ITabError
)
classImplements
(
TypeError
,
ITypeError
)
classImplements
(
UnboundLocalError
,
IUnboundLocalError
)
classImplements
(
UnicodeError
,
IUnicodeError
)
classImplements
(
UserWarning
,
IUserWarning
)
classImplements
(
ValueError
,
IValueError
)
classImplements
(
Warning
,
IWarning
)
classImplements
(
ZeroDivisionError
,
IZeroDivisionError
)
trunk/src/zope/interface/common/mapping.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# 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.1 (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.
#
##############################################################################
"""Mapping Interfaces
$Id$
"""
from
zope.interface
import
Interface
class
IItemMapping
(
Interface
):
"""Simplest readable mapping object
"""
def
__getitem__
(
key
):
"""Get a value for a key
A KeyError is raised if there is no value for the key.
"""
class
IReadMapping
(
IItemMapping
):
"""Basic mapping interface
"""
def
get
(
key
,
default
=
None
):
"""Get a value for a key
The default is returned if there is no value for the key.
"""
def
__contains__
(
key
):
"""Tell if a key exists in the mapping."""
class
IWriteMapping
(
Interface
):
"""Mapping methods for changing data"""
def
__delitem__
(
key
):
"""Delete a value from the mapping using the key."""
def
__setitem__
(
key
,
value
):
"""Set a new item in the mapping."""
class
IEnumerableMapping
(
IReadMapping
):
"""Mapping objects whose items can be enumerated.
"""
def
keys
():
"""Return the keys of the mapping object.
"""
def
__iter__
():
"""Return an iterator for 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.
"""
class
IMapping
(
IWriteMapping
,
IEnumerableMapping
):
''' Simple mapping interface '''
class
IIterableMapping
(
IEnumerableMapping
):
def
iterkeys
():
"iterate over keys; equivalent to __iter__"
def
itervalues
():
"iterate over values"
def
iteritems
():
"iterate over items"
class
IClonableMapping
(
Interface
):
def
copy
():
"return copy of dict"
class
IExtendedReadMapping
(
IIterableMapping
):
def
has_key
(
key
):
"""Tell if a key exists in the mapping; equivalent to __contains__"""
class
IExtendedWriteMapping
(
IWriteMapping
):
def
clear
():
"delete all items"
def
update
(
d
):
" Update D from E: for k in E.keys(): D[k] = E[k]"
def
setdefault
(
key
,
default
=
None
):
"D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D"
def
pop
(
k
,
*
args
):
"""remove specified key and return the corresponding value
*args may contain a single default value, or may not be supplied.
If key is not found, default is returned if given, otherwise
KeyError is raised"""
def
popitem
():
"""remove and return some (key, value) pair as a
2-tuple; but raise KeyError if mapping is empty"""
class
IFullMapping
(
IExtendedReadMapping
,
IExtendedWriteMapping
,
IClonableMapping
,
IMapping
):
''' Full mapping interface '''
# IMapping included so tests for IMapping
# succeed with IFullMapping
trunk/src/zope/interface/common/sequence.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# 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.1 (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.
#
##############################################################################
"""Sequence Interfaces
$Id$
"""
from
zope
import
interface
class
IReadSequence
(
interface
.
Interface
):
"read interface shared by tuple and list"
def
__getitem__
(
index
):
"x.__getitem__(index) <==> x[index]"
def
__iter__
():
"x.__iter__() <==> iter(x)"
def
__contains__
(
item
):
"x.__contains__(item) <==> item in x"
def
__lt__
(
other
):
"x.__lt__(other) <==> x<other"
def
__le__
(
other
):
"x.__le__(other) <==> x<=other"
def
__eq__
(
other
):
"x.__eq__(other) <==> x==other"
def
__ne__
(
other
):
"x.__ne__(other) <==> x!=other"
def
__gt__
(
other
):
"x.__gt__(other) <==> x>other"
def
__ge__
(
other
):
"x.__ge__(other) <==> x>=other"
def
__len__
():
"x.__len__() <==> len(x)"
def
__add__
(
other
):
"x.__add__(other) <==> x+other"
def
__mul__
(
n
):
"x.__mul__(n) <==> x*n"
def
__rmul__
(
n
):
"x.__rmul__(n) <==> n*x"
def
__getslice__
(
i
,
j
):
"""x.__getslice__(i, j) <==> x[i:j]
Use of negative indices is not supported.
"""
class
IExtendedReadSequence
(
IReadSequence
):
"Full read interface for lists"
def
count
(
item
):
"return number of occurrences of value"
def
index
(
item
,
*
args
):
"""return first index of value
L.index(value, [start, [stop]]) -> integer"""
class
IUniqueMemberWriteSequence
(
interface
.
Interface
):
"The write contract for a sequence that may enforce unique members"
def
__setitem__
(
index
,
item
):
"x.__setitem__(index, item) <==> x[index]=item"
def
__delitem__
(
index
):
"x.__delitem__(index) <==> del x[index]"
def
__setslice__
(
i
,
j
,
other
):
"""x.__setslice__(i, j, other) <==> x[i:j]=other
Use of negative indices is not supported."""
def
__delslice__
(
i
,
j
):
"""x.__delslice__(i, j) <==> del x[i:j]
Use of negative indices is not supported.
"""
def
__iadd__
(
y
):
"x.__iadd__(y) <==> x+=y"
def
append
(
item
):
"append item to end"
def
insert
(
index
,
item
):
"insert item before index"
def
pop
(
index
=-
1
):
"remove and return item at index (default last)"
def
remove
(
item
):
"remove first occurrence of value"
def
reverse
():
"reverse *IN PLACE*"
def
sort
(
cmpfunc
=
None
):
"""stable sort *IN PLACE*; cmpfunc(x, y) -> -1, 0, 1"""
def
extend
(
iterable
):
"extend list by appending elements from the iterable"
class
IWriteSequence
(
IUniqueMemberWriteSequence
):
"Full write contract for sequences"
def
__imul__
(
n
):
"x.__imul__(n) <==> x*=n"
class
ISequence
(
IReadSequence
,
IWriteSequence
):
"Full sequence contract"
trunk/src/zope/interface/common/tests/__init__.py
0 → 100644
View file @
a3c091cb
#
# This file is necessary to make this directory a package.
trunk/src/zope/interface/common/tests/basemapping.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# 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.1 (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.
#
##############################################################################
"""Base Mapping tests
$Id$
"""
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
(
key
in
inst
)
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_iter
(
self
,
inst
,
state
):
# Return the keys of the mapping object
inst_keys
=
list
(
inst
);
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
(
object
):
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_values
(
self
):
# Return the values of the mapping object
inst
=
self
.
_IEnumerableMapping__sample
()
state
=
self
.
_IEnumerableMapping__stateDict
()
test_iter
(
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
()
trunk/src/zope/interface/common/tests/test_idatetime.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Test for datetime interfaces
$Id$
"""
import
unittest
from
zope.interface.verify
import
verifyObject
,
verifyClass
from
zope.interface.common.idatetime
import
ITimeDelta
,
ITimeDeltaClass
from
zope.interface.common.idatetime
import
IDate
,
IDateClass
from
zope.interface.common.idatetime
import
IDateTime
,
IDateTimeClass
from
zope.interface.common.idatetime
import
ITime
,
ITimeClass
,
ITZInfo
from
datetime
import
timedelta
,
date
,
datetime
,
time
,
tzinfo
class
TestDateTimeInterfaces
(
unittest
.
TestCase
):
def
test_interfaces
(
self
):
verifyObject
(
ITimeDelta
,
timedelta
(
minutes
=
20
))
verifyObject
(
IDate
,
date
(
2000
,
1
,
2
))
verifyObject
(
IDateTime
,
datetime
(
2000
,
1
,
2
,
10
,
20
))
verifyObject
(
ITime
,
time
(
20
,
30
,
15
,
1234
))
verifyObject
(
ITZInfo
,
tzinfo
())
verifyClass
(
ITimeDeltaClass
,
timedelta
)
verifyClass
(
IDateClass
,
date
)
verifyClass
(
IDateTimeClass
,
datetime
)
verifyClass
(
ITimeClass
,
time
)
def
test_suite
():
suite
=
unittest
.
TestSuite
()
suite
.
addTest
(
unittest
.
makeSuite
(
TestDateTimeInterfaces
))
return
suite
if
__name__
==
'__main__'
:
unittest
.
main
()
trunk/src/zope/interface/declarations.py
0 → 100644
View file @
a3c091cb
##############################################################################
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
##############################################################################
"""Implementation of interface declarations
There are three flavors of declarations:
- Declarations are used to simply name declared interfaces.
- ImplementsDeclarations are used to express the interfaces that a
class implements (that instances of the class provides).
Implements specifications support inheriting interfaces.
- ProvidesDeclarations are used to express interfaces directly
provided by objects.
$Id$
"""
__docformat__
=
'restructuredtext'
import
sys
import
weakref
from
zope.interface.interface
import
InterfaceClass
,
Specification
from
ro
import
mergeOrderings
,
ro
import
exceptions
from
types
import
ClassType
from
zope.interface.advice
import
addClassAdvisor
# Registry of class-implementation specifications
BuiltinImplementationSpecifications
=
{}
class
Declaration
(
Specification
):
"""Interface declarations
"""
def
__init__
(
self
,
*
interfaces
):
Specification
.
__init__
(
self
,
_normalizeargs
(
interfaces
))
def
changed
(
self
):
Specification
.
changed
(
self
)
try
:
del
self
.
_v_attrs
except
AttributeError
:
pass
def
__contains__
(
self
,
interface
):
"""Test whether an interface is in the specification
for example::
>>> from zope.interface import Interface
>>> class I1(Interface): pass
...
>>> class I2(I1): pass
...
>>> class I3(Interface): pass
...
>>> class I4(I3): pass
...
>>> spec = Declaration(I2, I3)
>>> spec = Declaration(I4, spec)
>>> int(I1 in spec)
0
>>> int(I2 in spec)
1
>>> int(I3 in spec)
1
>>> int(I4 in spec)
1
"""
return
self
.
extends
(
interface
)
and
interface
in
self
.
interfaces
()
def
__iter__
(
self
):
"""Return an iterator for the interfaces in the specification
for example::
>>> from zope.interface import Interface
>>> class I1(Interface): pass
...
>>> class I2(I1): pass
...
>>> class I3(Interface): pass
...
>>> class I4(I3): pass
...
>>> spec = Declaration(I2, I3)
>>> spec = Declaration(I4, spec)
>>> i = iter(spec)
>>> i.next().getName()
'I4'
>>> i.next().getName()
'I2'
>>> i.next().getName()
'I3'
>>> list(i)
[]
"""
return
self
.
interfaces
()
def
flattened
(
self
):
"""Return an iterator of all included and extended interfaces
for example::
>>> from zope.interface import Interface
>>> class I1(Interface): pass
...
>>> class I2(I1): pass
...
>>> class I3(Interface): pass
...
>>> class I4(I3): pass
...
>>> spec = Declaration(I2, I3)
>>> spec = Declaration(I4, spec)
>>> i = spec.flattened()
>>> i.next().getName()
'I4'
>>> i.next().getName()
'I2'
>>> i.next().getName()
'I1'
>>> i.next().getName()
'I3'
>>> i.next().getName()
'Interface'
>>> list(i)
[]
"""
return
iter
(
self
.
__iro__
)
def
__sub__
(
self
,
other
):
"""Remove interfaces from a specification
Examples::
>>> from zope.interface import Interface
>>> class I1(Interface): pass
...
>>> class I2(I1): pass
...
>>> class I3(Interface): pass
...
>>> class I4(I3): pass
...
>>> spec = Declaration()
>>> [iface.getName() for iface in spec]
[]
>>> spec -= I1
>>> [iface.getName() for iface in spec]
[]
>>> spec -= Declaration(I1, I2)
>>> [iface.getName() for iface in spec]
[]
>>> spec = Declaration(I2, I4)
>>> [iface.getName() for iface in spec]
['I2', 'I4']
>>> [iface.getName() for iface in spec - I4]
['I2']
>>> [iface.getName() for iface in spec - I1]
['I4']
>>> [iface.getName() for iface
... in spec - Declaration(I3, I4)]
['I2']
"""
return
Declaration
(
*
[
i
for
i
in
self
.
interfaces
()
if
not
[
j
for
j
in
other
.
interfaces
()
if
i
.
extends
(
j
,
0
)]
]
)
def
__add__
(
self
,
other
):
"""Add two specifications or a specification and an interface
Examples::
>>> from zope.interface import Interface
>>> class I1(Interface): pass
...
>>> class I2(I1): pass
...
>>> class I3(Interface): pass
...
>>> class I4(I3): pass
...
>>> spec = Declaration()
>>> [iface.getName() for iface in spec]
[]
>>> [iface.getName() for iface in spec+I1]
['I1']
>>> [iface.getName() for iface in I1+spec]
['I1']
>>> spec2 = spec
>>> spec += I1
>>> [iface.getName() for iface in spec]
['I1']
>>> [iface.getName() for iface in spec2]
[]
>>> spec2 += Declaration(I3, I4)
>>> [iface.getName() for iface in spec2]
['I3', 'I4']
>>> [iface.getName() for iface in spec+spec2]
['I1', 'I3', 'I4']
>>> [iface.getName() for iface in spec2+spec]
['I3', 'I4', 'I1']
"""
seen
=
{}
result
=
[]
for
i
in
self
.
interfaces
():
if
i
not
in
seen
:
seen
[
i
]
=
1
result
.
append
(
i
)
for
i
in
other
.
interfaces
():
if
i
not
in
seen
:
seen
[
i
]
=
1
result
.
append
(
i
)
return
Declaration
(
*
result
)
__radd__
=
__add__
def
__nonzero__
(
self
):
"""Test whether there are any interfaces in a specification.
>>> from zope.interface import Interface
>>> class I1(Interface): pass
...
>>> spec = Declaration(I1)
>>> int(bool(spec))
1
>>> spec = Declaration()
>>> int(bool(spec))
0
"""
return
bool
(
self
.
__iro__
)
##############################################################################
#
# Implementation specifications
#
# These specify interfaces implemented by instances of classes
class
Implements
(
Declaration
):
inherit
=
None
declared
=
()
__name__
=
'?'
def
__repr__
(
self
):
return
'<implementedBy %s>'
%
(
self
.
__name__
)
def
implementedByFallback
(
cls
):
"""Return the interfaces implemented for a class' instances
The value returned is an IDeclaration.
for example:
>>> from zope.interface import Interface
>>> class I1(Interface): pass
...
>>> class I2(I1): pass
...
>>> class I3(Interface): pass
...
>>> class I4(I3): pass
...
>>> class C1(object):
... implements(I2)
>>> class C2(C1):
... implements(I3)
>>> [i.getName() for i in implementedBy(C2)]
['I3', 'I2']
"""
# This also manages storage of implementation specifications
try
:
spec
=
cls
.
__dict__
.
get
(
'__implemented__'
)
except
AttributeError
:
# we can't get the class dict. This is probably due to a
# security proxy. If this is the case, then probably no
# descriptor was installed for the class.
# We don't want to depend directly on zope.secury in
# zope.interface, but we'll try to make reasonable
# accommodations in an indirect way.
# We'll check to see if there's an implements:
spec
=
getattr
(
cls
,
'__implemented__'
,
None
)
if
spec
is
None
:
# There's no spec stred in the class. Maybe its a builtin:
spec
=
BuiltinImplementationSpecifications
.
get
(
cls
)
if
spec
is
not
None
:
return
spec
return
_empty
if
spec
.
__class__
==
Implements
:
# we defaulted to _empty or there was a spec. Good enough.
# Return it.
return
spec
# TODO: need old style __implements__ compatibility?
# Hm, there's an __implemented__, but it's not a spec. Must be
# an old-style declaration. Just compute a spec for it
return
Declaration
(
*
_normalizeargs
((
spec
,
)))
if
isinstance
(
spec
,
Implements
):
return
spec
if
spec
is
None
:
spec
=
BuiltinImplementationSpecifications
.
get
(
cls
)
if
spec
is
not
None
:
return
spec
# TODO: need old style __implements__ comptability?
if
spec
is
not
None
:
# old-style __implemented__ = foo declaration
spec
=
(
spec
,
)
# tuplefy, as it might be just an int
spec
=
Implements
(
*
_normalizeargs
(
spec
))
spec
.
inherit
=
None
# old-style implies no inherit
del
cls
.
__implemented__
# get rid of the old-style declaration
else
:
try
:
bases
=
cls
.
__bases__
except
AttributeError
:
if
not
callable
(
cls
):
raise
TypeError
(
"ImplementedBy called for non-factory"
,
cls
)
bases
=
()
spec
=
Implements
(
*
[
implementedBy
(
c
)
for
c
in
bases
])
spec
.
inherit
=
cls
spec
.
__name__
=
(
getattr
(
cls
,
'__module__'
,
'?'
)
or
'?'
)
+
\
'.'
+
cls
.
__name__
try
:
cls
.
__implemented__
=
spec
if
not
hasattr
(
cls
,
'__providedBy__'
):
cls
.
__providedBy__
=
objectSpecificationDescriptor
if
(
isinstance
(
cls
,
DescriptorAwareMetaClasses
)
and
'__provides__'
not
in
cls
.
__dict__
):
# Make sure we get a __provides__ descriptor
cls
.
__provides__
=
ClassProvides
(
cls
,
getattr
(
cls
,
'__class__'
,
type
(
cls
)),
)
except
TypeError
:
if
not
isinstance
(
cls
,
type
):
raise
TypeError
(
"ImplementedBy called for non-type"
,
cls
)
BuiltinImplementationSpecifications
[
cls
]
=
spec
return
spec
implementedBy
=
implementedByFallback
def
classImplementsOnly
(
cls
,
*
interfaces
):
"""Declare the only interfaces implemented by instances of a class
The arguments after the class are one or more interfaces or
interface specifications (IDeclaration objects).
The interfaces given (including the interfaces in the
specifications) replace any previous declarations.
Consider the following example::
>>> from zope.interface import Interface
>>> class I1(Interface): pass
...
>>> class I2(Interface): pass
...
>>> class I3(Interface): pass
...
>>> class I4(Interface): pass
...
>>> class A(object):
... implements(I3)
>>> class B(object):
... implements(I4)
>>> class C(A, B):
... pass
>>> classImplementsOnly(C, I1, I2)
>>> [i.getName() for i in implementedBy(C)]
['I1', 'I2']
Instances of ``C`` provide only ``I1``, ``I2``, and regardless of
whatever interfaces instances of ``A`` and ``B`` implement.
"""
spec
=
implementedBy
(
cls
)
spec
.
__bases__
=
tuple
(
_normalizeargs
(
interfaces
))
spec
.
inherit
=
None
def
classImplements
(
cls
,
*
interfaces
):
"""Declare additional interfaces implemented for instances of a class
The arguments after the class are one or more interfaces or
interface specifications (IDeclaration objects).
The interfaces given (including the interfaces in the
specifications) are added to any interfaces previously
declared.
Consider the following example::
for example:
>>> from zope.interface import Interface
>>> class I1(Interface): pass
...
>>> class I2(Interface): pass
...
>>> class I3(Interface): pass
...
>>> class I4(Interface): pass
...
>>> class I5(Interface): pass
...
>>> class A(object):
... implements(I3)
>>> class B(object):
... implements(I4)
>>> class C(A, B):
... pass
>>> classImplements(C, I1, I2)
>>> [i.getName() for i in implementedBy(C)]
['I1', 'I2', 'I3', 'I4']
>>> classImplements(C, I5)
>>> [i.getName() for i in implementedBy(C)]
['I1', 'I2', 'I5', 'I3', 'I4']
Instances of ``C`` provide ``I1``, ``I2``, ``I5``, and whatever
interfaces instances of ``A`` and ``B`` provide.
"""
spec
=
implementedBy
(
cls
)
spec
.
declared
+=
tuple
(
_normalizeargs
(
interfaces
))
# compute the bases
bases
=
[]
seen
=
{}
for
b
in
spec
.
declared
:
if
b
not
in
seen
:
seen
[
b
]
=
1
bases
.
append
(
b
)
if
spec
.
inherit
is
not
None
:
for
c
in
spec
.
inherit
.
__bases__
:
b
=
implementedBy
(
c
)
if
b
not
in
seen
:
seen
[
b
]
=
1
bases
.
append
(
b
)
spec
.
__bases__
=
tuple
(
bases
)
def
_implements_advice
(
cls
):
interfaces
,
classImplements
=
cls
.
__dict__
[
'__implements_advice_data__'
]
del
cls
.
__implements_advice_data__
classImplements
(
cls
,
*
interfaces
)
return
cls
class
implementer
:
def
__init__
(
self
,
*
interfaces
):
self
.
interfaces
=
interfaces
def
__call__
(
self
,
ob
):
if
isinstance
(
ob
,
DescriptorAwareMetaClasses
):
raise
TypeError
(
"Can't use implementer with classes. Use one of "
"the class-declaration functions instead."
)
spec
=
Implements
(
*
self
.
interfaces
)
try
:
ob
.
__implemented__
=
spec
except
AttributeError
:
raise
TypeError
(
"Can't declare implements"
,
ob
)
return
ob
def
_implements
(
name
,
interfaces
,
classImplements
):
frame
=
sys
.
_getframe
(
2
)
locals
=
frame
.
f_locals
# Try to make sure we were called from a class def. In 2.2.0 we can't
# check for __module__ since it doesn't seem to be added to the locals
# until later on.
if
(
locals
is
frame
.
f_globals
)
or
(
(
'__module__'
not
in
locals
)
and
sys
.
version_info
[:
3
]
>
(
2
,
2
,
0
)):
raise
TypeError
(
name
+
" can be used only from a class definition."
)
if
'__implements_advice_data__'
in
locals
:
raise
TypeError
(
name
+
" can be used only once in a class definition."
)
locals
[
'__implements_advice_data__'
]
=
interfaces
,
classImplements
addClassAdvisor
(
_implements_advice
,
depth
=
3
)
def
implements
(
*
interfaces
):
"""Declare interfaces implemented by instances of a class
This function is called in a class definition.
The arguments are one or more interfaces or interface
specifications (IDeclaration objects).
The interfaces given (including the interfaces in the
specifications) are added to any interfaces previously
declared.
Previous declarations include declarations for base classes
unless implementsOnly was used.
This function is provided for convenience. It provides a more
convenient way to call classImplements. For example::
implements(I1)
is equivalent to calling::
classImplements(C, I1)
after the class has been created.
Consider the following example::
>>> from zope.interface import Interface
>>> class IA1(Interface): pass
...
>>> class IA2(Interface): pass
...
>>> class IB(Interface): pass
...
>>> class IC(Interface): pass
...
>>> class A(object): implements(IA1, IA2)
...
>>> class B(object): implements(IB)
...
>>> class C(A, B):
... implements(IC)
>>> ob = C()
>>> int(IA1 in providedBy(ob))
1
>>> int(IA2 in providedBy(ob))
1
>>> int(IB in providedBy(ob))
1
>>> int(IC in providedBy(ob))
1
Instances of ``C`` implement ``I1``, ``I2``, and whatever interfaces
instances of ``A`` and ``B`` implement.
"""
_implements
(
"implements"
,
interfaces
,
classImplements
)
def
implementsOnly
(
*
interfaces
):
"""Declare the only interfaces implemented by instances of a class
This function is called in a class definition.
The arguments are one or more interfaces or interface
specifications (IDeclaration objects).
Previous declarations including declarations for base classes
are overridden.
This function is provided for convenience. It provides a more
convenient way to call classImplementsOnly. For example::
implementsOnly(I1)
is equivalent to calling::
classImplementsOnly(I1)
after the class has been created.
Consider the following example::
>>> from zope.interface import Interface
>>> class IA1(Interface): pass
...
>>> class IA2(Interface): pass
...
>>> class IB(Interface): pass
...
>>> class IC(Interface): pass
...
>>> class A(object): implements(IA1, IA2)
...
>>> class B(object): implements(IB)
...
>>> class C(A, B):
... implementsOnly(IC)
>>> ob = C()
>>> int(IA1 in providedBy(ob))
0
>>> int(IA2 in providedBy(ob))
0
>>> int(IB in providedBy(ob))
0
>>> int(IC in providedBy(ob))
1
Instances of ``C`` implement ``IC``, regardless of what
instances of ``A`` and ``B`` implement.
"""
_implements
(
"implementsOnly"
,
interfaces
,
classImplementsOnly
)
##############################################################################
#
# Instance declarations
class
Provides
(
Declaration
):
# Really named ProvidesClass
"""Implement __provides__, the instance-specific specification
When an object is pickled, we pickle the interfaces that it implements.
"""
def
__init__
(
self
,
cls
,
*
interfaces
):
self
.
__args
=
(
cls
,
)
+
interfaces
self
.
_cls
=
cls
Declaration
.
__init__
(
self
,
*
(
interfaces
+
(
implementedBy
(
cls
),
)))
def
__reduce__
(
self
):
return
Provides
,
self
.
__args
__module__
=
'zope.interface'
def
__get__
(
self
,
inst
,
cls
):
"""Make sure that a class __provides__ doesn't leak to an instance
For example::
>>> from zope.interface import Interface
>>> class IFooFactory(Interface): pass
...
>>> class C(object):
... pass
>>> C.__provides__ = ProvidesClass(C, IFooFactory)
>>> [i.getName() for i in C.__provides__]
['IFooFactory']
>>> getattr(C(), '__provides__', 0)
0
"""
if
inst
is
None
and
cls
is
self
.
_cls
:
# We were accessed through a class, so we are the class'
# provides spec. Just return this object, but only if we are
# being called on the same class that we were defined for:
return
self
raise
AttributeError
,
'__provides__'
ProvidesClass
=
Provides
# Registry of instance declarations
# This is a memory optimization to allow objects to share specifications.
InstanceDeclarations
=
weakref
.
WeakValueDictionary
()
def
Provides
(
*
interfaces
):
"""Cache instance declarations
Instance declarations are shared among instances that have the
same declaration. The declarations are cached in an weak value
dictionary.
(Note that, in the examples below, we are going to make
assertions about the size of the weakvalue dictionary. For the
assertions to be meaningful, we need to force garbage
collection to make sure garbage objects are, indeed, removed
from the system. Depending on how Python is run, we may need to
make multiple calls to be sure. We provide a collect function
to help with this:
>>> import gc
>>> def collect():
... for i in range(4):
... gc.collect()
)
>>> collect()
>>> before = len(InstanceDeclarations)
>>> class C(object):
... pass
>>> from zope.interface import Interface
>>> class I(Interface):
... pass
>>> c1 = C()
>>> c2 = C()
>>> len(InstanceDeclarations) == before
1
>>> directlyProvides(c1, I)
>>> len(InstanceDeclarations) == before + 1
1
>>> directlyProvides(c2, I)
>>> len(InstanceDeclarations) == before + 1
1
>>> del c1
>>> collect()
>>> len(InstanceDeclarations) == before + 1
1
>>> del c2
>>> collect()
>>> len(InstanceDeclarations) == before
1
"""
spec
=
InstanceDeclarations
.
get
(
interfaces
)
if
spec
is
None
:
spec
=
ProvidesClass
(
*
interfaces
)
InstanceDeclarations
[
interfaces
]
=
spec
return
spec
Provides
.
__safe_for_unpickling__
=
True
DescriptorAwareMetaClasses
=
ClassType
,
type
def
directlyProvides
(
object
,
*
interfaces
):
"""Declare interfaces declared directly for an object
The arguments after the object are one or more interfaces or
interface specifications (IDeclaration objects).
The interfaces given (including the interfaces in the
specifications) replace interfaces previously
declared for the object.
Consider the following example::
>>> from zope.interface import Interface
>>> class I1(Interface): pass
...
>>> class I2(Interface): pass
...
>>> class IA1(Interface): pass
...
>>> class IA2(Interface): pass
...
>>> class IB(Interface): pass
...
>>> class IC(Interface): pass
...
>>> class A(object): implements(IA1, IA2)
...
>>> class B(object): implements(IB)
...
>>> class C(A, B):
... implements(IC)
>>> ob = C()
>>> directlyProvides(ob, I1, I2)
>>> int(I1 in providedBy(ob))
1
>>> int(I2 in providedBy(ob))
1
>>> int(IA1 in providedBy(ob))
1
>>> int(IA2 in providedBy(ob))
1
>>> int(IB in providedBy(ob))
1
>>> int(IC in providedBy(ob))
1
The object, ``ob`` provides ``I1``, ``I2``, and whatever interfaces
instances have been declared for instances of ``C``.
To remove directly provided interfaces, use ``directlyProvidedBy`` and
subtract the unwanted interfaces. For example::
>>> directlyProvides(ob, directlyProvidedBy(ob)-I2)
>>> int(I1 in providedBy(ob))
1
>>> int(I2 in providedBy(ob))
0
removes I2 from the interfaces directly provided by
``ob``. The object, ``ob`` no longer directly provides ``I2``,
although it might still provide ``I2`` if it's class
implements ``I2``.
To add directly provided interfaces, use ``directlyProvidedBy`` and
include additional interfaces. For example::
>>> int(I2 in providedBy(ob))
0
>>> directlyProvides(ob, directlyProvidedBy(ob), I2)
adds I2 to the interfaces directly provided by ob::
>>> int(I2 in providedBy(ob))
1
"""
# We need to avoid setting this attribute on meta classes that
# don't support descriptors.
# We can do away with this check when we get rid of the old EC
cls
=
getattr
(
object
,
'__class__'
,
None
)
if
cls
is
not
None
and
getattr
(
cls
,
'__class__'
,
None
)
is
cls
:
# It's a meta class (well, at least it it could be an extension class)
if
not
isinstance
(
object
,
DescriptorAwareMetaClasses
):
raise
TypeError
(
"Attempt to make an interface declaration on a "
"non-descriptor-aware class"
)
interfaces
=
_normalizeargs
(
interfaces
)
if
cls
is
None
:
cls
=
type
(
object
)
issub
=
False
for
damc
in
DescriptorAwareMetaClasses
:
if
issubclass
(
cls
,
damc
):
issub
=
True
break
if
issub
:
# we have a class or type. We'll use a special descriptor
# that provides some extra caching
object
.
__provides__
=
ClassProvides
(
object
,
cls
,
*
interfaces
)
else
:
object
.
__provides__
=
Provides
(
cls
,
*
interfaces
)
def
alsoProvides
(
object
,
*
interfaces
):
"""Declare interfaces declared directly for an object
The arguments after the object are one or more interfaces or
interface specifications (IDeclaration objects).
The interfaces given (including the interfaces in the
specifications) are added to the interfaces previously
declared for the object.
Consider the following example::
>>> from zope.interface import Interface
>>> class I1(Interface): pass
...
>>> class I2(Interface): pass
...
>>> class IA1(Interface): pass
...
>>> class IA2(Interface): pass
...
>>> class IB(Interface): pass
...
>>> class IC(Interface): pass
...
>>> class A(object): implements(IA1, IA2)
...
>>> class B(object): implements(IB)
...
>>> class C(A, B):
... implements(IC)
>>> ob = C()
>>> directlyProvides(ob, I1)
>>> int(I1 in providedBy(ob))
1
>>> int(I2 in providedBy(ob))
0
>>> int(IA1 in providedBy(ob))
1
>>> int(IA2 in providedBy(ob))
1
>>> int(IB in providedBy(ob))
1
>>> int(IC in providedBy(ob))
1
>>> alsoProvides(ob, I2)
>>> int(I1 in providedBy(ob))
1
>>> int(I2 in providedBy(ob))
1
>>> int(IA1 in providedBy(ob))
1
>>> int(IA2 in providedBy(ob))
1
>>> int(IB in providedBy(ob))
1
>>> int(IC in providedBy(ob))
1
The object, ``ob`` provides ``I1``, ``I2``, and whatever interfaces
instances have been declared for instances of ``C``. Notice that the
alsoProvides just extends the provided interfaces.
"""
directlyProvides
(
object
,
directlyProvidedBy
(
object
),
*
interfaces
)
class
ClassProvidesBasePy
(
object
):
def
__get__
(
self
,
inst
,
cls
):
if
cls
is
self
.
_cls
:
# We only work if called on the class we were defined for
if
inst
is
None
:
# We were accessed through a class, so we are the class'
# provides spec. Just return this object as is:
return
self
return
self
.
_implements
raise
AttributeError
,
'__provides__'
ClassProvidesBase
=
ClassProvidesBasePy
# Try to get C base:
try
:
import
_zope_interface_coptimizations
except
ImportError
:
pass
else
:
from
_zope_interface_coptimizations
import
ClassProvidesBase
class
ClassProvides
(
Declaration
,
ClassProvidesBase
):
"""Special descriptor for class __provides__
The descriptor caches the implementedBy info, so that
we can get declarations for objects without instance-specific
interfaces a bit quicker.
For example::
>>> from zope.interface import Interface
>>> class IFooFactory(Interface):
... pass
>>> class IFoo(Interface):
... pass
>>> class C(object):
... implements(IFoo)
... classProvides(IFooFactory)
>>> [i.getName() for i in C.__provides__]
['IFooFactory']
>>> [i.getName() for i in C().__provides__]
['IFoo']
"""
def
__init__
(
self
,
cls
,
metacls
,
*
interfaces
):
self
.
_cls
=
cls
self
.
_implements
=
implementedBy
(
cls
)
self
.
__args
=
(
cls
,
metacls
,
)
+
interfaces
Declaration
.
__init__
(
self
,
*
(
interfaces
+
(
implementedBy
(
metacls
),
)))
def
__reduce__
(
self
):
return
self
.
__class__
,
self
.
__args
# Copy base-class method for speed
__get__
=
ClassProvidesBase
.
__get__
def
directlyProvidedBy
(
object
):
"""Return the interfaces directly provided by the given object
The value returned is an IDeclaration.
"""
provides
=
getattr
(
object
,
"__provides__"
,
None
)
if
(
provides
is
None
# no spec
or
# We might have gotten the implements spec, as an
# optimization. If so, it's like having only one base, that we
# lop off to exclude class-supplied declarations:
isinstance
(
provides
,
Implements
)
):
return
_empty
# Strip off the class part of the spec:
return
Declaration
(
provides
.
__bases__
[:
-
1
])
def
classProvides
(
*
interfaces
):
"""Declare interfaces provided directly by a class
This function is called in a class definition.
The arguments are one or more interfaces or interface
specifications (IDeclaration objects).
The given interfaces (including the interfaces in the
specifications) are used to create the class's direct-object
interface specification. An error will be raised if the module
class has an direct interface specification. In other words, it is
an error to call this function more than once in a class
definition.
Note that the given interfaces have nothing to do with the
interfaces implemented by instances of the class.
This function is provided for convenience. It provides a more
convenient way to call directlyProvidedByProvides for a class. For
example::
classProvides(I1)
is equivalent to calling::
directlyProvides(theclass, I1)
after the class has been created.
For example::
>>> from zope.interface import Interface
>>> class IFoo(Interface): pass
...
>>> class IFooFactory(Interface): pass
...
>>> class C(object):
... implements(IFoo)
... classProvides(IFooFactory)
>>> [i.getName() for i in C.__providedBy__]
['IFooFactory']
>>> [i.getName() for i in C().__providedBy__]
['IFoo']
if equivalent to::
>>> from zope.interface import Interface
>>> class IFoo(Interface): pass
...
>>> class IFooFactory(Interface): pass
...
>>> class C(object):
... implements(IFoo)
>>> directlyProvides(C, IFooFactory)
>>> [i.getName() for i in C.__providedBy__]
['IFooFactory']
>>> [i.getName() for i in C().__providedBy__]
['IFoo']
"""
frame
=
sys
.
_getframe
(
1
)
locals
=
frame
.
f_locals
# Try to make sure we were called from a class def
if
(
locals
is
frame
.
f_globals
)
or
(
'__module__'
not
in
locals
):
raise
TypeError
(
name
+
" can be used only from a class definition."
)
if
'__provides__'
in
locals
:
raise
TypeError
(
"classProvides can only be used once in a class definition."
)
locals
[
"__provides__"
]
=
_normalizeargs
(
interfaces
)
addClassAdvisor
(
_classProvides_advice
,
depth
=
2
)
def
_classProvides_advice
(
cls
):
interfaces
=
cls
.
__dict__
[
'__provides__'
]
del
cls
.
__provides__
directlyProvides
(
cls
,
*
interfaces
)
return
cls
def
moduleProvides
(
*
interfaces
):
"""Declare interfaces provided by a module
This function is used in a module definition.
The arguments are one or more interfaces or interface
specifications (IDeclaration objects).
The given interfaces (including the interfaces in the
specifications) are used to create the module's direct-object
interface specification. An error will be raised if the module
already has an interface specification. In other words, it is
an error to call this function more than once in a module
definition.
This function is provided for convenience. It provides a more
convenient way to call directlyProvides. For example::
moduleImplements(I1)
is equivalent to::
directlyProvides(sys.modules[__name__], I1)
"""
frame
=
sys
.
_getframe
(
1
)
locals
=
frame
.
f_locals
# Try to make sure we were called from a class def
if
(
locals
is
not
frame
.
f_globals
)
or
(
'__name__'
not
in
locals
):
raise
TypeError
(
"moduleProvides can only be used from a module definition."
)
if
'__provides__'
in
locals
:
raise
TypeError
(
"moduleProvides can only be used once in a module definition."
)
module
=
sys
.
modules
[
__name__
]
locals
[
"__provides__"
]
=
Provides
(
type
(
module
),
*
_normalizeargs
(
interfaces
))
##############################################################################
#
# Declaration querying support
def
ObjectSpecification
(
direct
,
cls
):
"""Provide object specifications
These combine information for the object and for it's classes.
For example::
>>> from zope.interface import Interface
>>> class I1(Interface): pass
...
>>> class I2(Interface): pass
...
>>> class I3(Interface): pass
...
>>> class I31(I3): pass
...
>>> class I4(Interface): pass
...
>>> class I5(Interface): pass
...
>>> class A(object): implements(I1)
...
>>> class B(object): __implemented__ = I2
...
>>> class C(A, B): implements(I31)
...
>>> c = C()
>>> directlyProvides(c, I4)
>>> [i.getName() for i in providedBy(c)]
['I4', 'I31', 'I1', 'I2']
>>> [i.getName() for i in providedBy(c).flattened()]
['I4', 'I31', 'I3', 'I1', 'I2', 'Interface']
>>> int(I1 in providedBy(c))
1
>>> int(I3 in providedBy(c))
0
>>> int(providedBy(c).extends(I3))
1
>>> int(providedBy(c).extends(I31))
1
>>> int(providedBy(c).extends(I5))
0
>>> class COnly(A, B): implementsOnly(I31)
...
>>> class D(COnly): implements(I5)
...
>>> c = D()
>>> directlyProvides(c, I4)
>>> [i.getName() for i in providedBy(c)]
['I4', 'I5', 'I31']
>>> [i.getName() for i in providedBy(c).flattened()]
['I4', 'I5', 'I31', 'I3', 'Interface']
>>> int(I1 in providedBy(c))
0
>>> int(I3 in providedBy(c))
0
>>> int(providedBy(c).extends(I3))
1
>>> int(providedBy(c).extends(I1))
0
>>> int(providedBy(c).extends(I31))
1
>>> int(providedBy(c).extends(I5))
1
nonzero:
>>> from zope.interface import Interface
>>> class I1(Interface):
... pass
>>> class I2(Interface):
... pass
>>> class C(object):
... implements(I1)
>>> c = C()
>>> int(bool(providedBy(c)))
1
>>> directlyProvides(c, I2)
>>> int(bool(providedBy(c)))
1
>>> class C(object):
... pass
>>> c = C()
>>> int(bool(providedBy(c)))
0
>>> directlyProvides(c, I2)
>>> int(bool(providedBy(c)))
1
"""
return
Provides
(
cls
,
direct
)
def
getObjectSpecification
(
ob
):
provides
=
getattr
(
ob
,
'__provides__'
,
None
)
if
provides
is
not
None
:
return
provides
try
:
cls
=
ob
.
__class__
except
AttributeError
:
# We can't get the class, so just consider provides
return
_empty
return
implementedBy
(
cls
)
def
providedBy
(
ob
):
# Here we have either a special object, an old-style declaration
# or a descriptor
# Try to get __providedBy__
try
:
r
=
ob
.
__providedBy__
except
AttributeError
:
# Not set yet. Fall back to lower-level thing that computes it
return
getObjectSpecification
(
ob
)
try
:
# We might have gotten a descriptor from an instance of a
# class (like an ExtensionClass) that doesn't support
# descriptors. We'll make sure we got one by trying to get
# the only attribute, which all specs have.
r
.
extends
except
AttributeError
:
# The object's class doesn't understand descriptors.
# Sigh. We need to get an object descriptor, but we have to be
# careful. We want to use the instance's __provides__, if
# there is one, but only if it didn't come from the class.
try
:
r
=
ob
.
__provides__
except
AttributeError
:
# No __provides__, so just fall back to implementedBy
return
implementedBy
(
ob
.
__class__
)
# We need to make sure we got the __provides__ from the
# instance. We'll do this by making sure we don't get the same
# thing from the class:
try
:
cp
=
ob
.
__class__
.
__provides__
except
AttributeError
:
# The ob doesn't have a class or the class has no
# provides, assume we're done:
return
r
if
r
is
cp
:
# Oops, we got the provides from the class. This means
# the object doesn't have it's own. We should use implementedBy
return
implementedBy
(
ob
.
__class__
)
return
r
class
ObjectSpecificationDescriptorPy
(
object
):
"""Implement the __providedBy__ attribute
The __providedBy__ attribute computes the interfaces peovided by
an object.
"""
def
__get__
(
self
,
inst
,
cls
):
"""Get an object specification for an object
For example::
>>> from zope.interface import Interface
>>> class IFoo(Interface): pass
...
>>> class IFooFactory(Interface): pass
...
>>> class C(object):
... implements(IFoo)
... classProvides(IFooFactory)
>>> [i.getName() for i in C.__providedBy__]
['IFooFactory']
>>> [i.getName() for i in C().__providedBy__]
['IFoo']
"""
# Get an ObjectSpecification bound to either an instance or a class,
# depending on how we were accessed.
if
inst
is
None
:
return
getObjectSpecification
(
cls
)
provides
=
getattr
(
inst
,
'__provides__'
,
None
)
if
provides
is
not
None
:
return
provides
return
implementedBy
(
cls
)
ObjectSpecificationDescriptor
=
ObjectSpecificationDescriptorPy
##############################################################################
def
_normalizeargs
(
sequence
,
output
=
None
):
"""Normalize declaration arguments
Normalization arguments might contain Declarions, tuples, or single
interfaces.
Anything but individial interfaces or implements specs will be expanded.
"""
if
output
is
None
:
output
=
[]
cls
=
sequence
.
__class__
if
InterfaceClass
in
cls
.
__mro__
or
Implements
in
cls
.
__mro__
:
output
.
append
(
sequence
)
else
:
for
v
in
sequence
:
_normalizeargs
(
v
,
output
)
return
output
_empty
=
Declaration
()
try
:
import
_zope_interface_coptimizations
except
ImportError
:
pass
else
:
from
_zope_interface_coptimizations
import
implementedBy
,
providedBy
from
_zope_interface_coptimizations
import
getObjectSpecification
from
_zope_interface_coptimizations
import
ObjectSpecificationDescriptor
objectSpecificationDescriptor
=
ObjectSpecificationDescriptor
()
trunk/src/zope/interface/document.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# 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.1 (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.
$Id$
"""
from
string
import
maketrans
import
zope.interface
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
"
)
bases
=
[
base
for
base
in
I
.
__bases__
if
base
is
not
zope
.
interface
.
Interface
]
if
bases
:
outp
((
" "
*
level
)
+
"This interface extends:
\
n
\
n
"
)
level
=
level
+
1
for
b
in
bases
:
item
=
"o %s"
%
b
.
getName
()
outp
(
_justify_and_indent
(
_trim_doc_string
(
item
),
level
,
munge
)
+
"
\
n
\
n
"
)
level
=
level
-
1
outp
(
_justify_and_indent
(
"Attributes:"
,
level
,
munge
)
+
'
\
n
\
n
'
)
level
=
level
+
1
namesAndDescriptions
=
I
.
namesAndDescriptions
()
namesAndDescriptions
.
sort
()
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
)
trunk/src/zope/interface/exceptions.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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-specific exceptions
$Id$
"""
class
Invalid
(
Exception
):
"""An specification is violated
"""
class
DoesNotImplement
(
Invalid
):
""" This object does not implement """
def
__init__
(
self
,
interface
):
self
.
interface
=
interface
def
__str__
(
self
):
return
"""An object does not implement interface %(interface)s
"""
%
self
.
__dict__
class
BrokenImplementation
(
Invalid
):
"""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
BrokenMethodImplementation
(
Invalid
):
"""An method is not completely implemented.
"""
def
__init__
(
self
,
method
,
mess
):
self
.
method
=
method
self
.
mess
=
mess
def
__str__
(
self
):
return
"""The implementation of %(method)s violates its contract
because %(mess)s.
"""
%
self
.
__dict__
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.
"""
trunk/src/zope/interface/human.txt
0 → 100644
View file @
a3c091cb
==========================
Using the Adapter Registry
==========================
This is a small demonstration of the zope.interface package including its
adapter registry. It is intended to provide a concrete but narrow example on
how to use interfaces and adapters outside of Zope 3.
First we have to import the interface package.
>>> import zope.interface
We now develop an interface for our object, which is a simple file in this
case. For now we simply support one attribute, the body, which contains the
actual file contents.
>>> class IFile(zope.interface.Interface):
...
... body = zope.interface.Attribute('Contents of the file.')
...
For statistical reasons we often want to know the size of a file. However, it
would be clumsy to implement the size directly in the file object, since the
size really represents meta-data. Thus we create another interface that
provides the size of something.
>>> class ISize(zope.interface.Interface):
...
... def getSize():
... 'Return the size of an object.'
...
Now we need to implement the file. It is essential that the object states
that it implements the `IFile` interface. We also provide a default body
value (just to make things simpler for this example).
>>> class File(object):
...
... zope.interface.implements(IFile)
... body = 'foo bar'
...
Next we implement an adapter that can provide the `ISize` interface given any
object providing `IFile`. By convention we use `__used_for__` to specify the
interface that we expect the adapted object to provide, in our case
`IFile`. However, this attribute is not used for anything. If you have
multiple interfaces for which an adapter is used, just specify the interfaces
via a tuple.
Again by convention, the constructor of an adapter takes one argument, the
context. The context in this case is an instance of `File` (providing `IFile`)
that is used to extract the size from. Also by convention the context is
stored in an attribute named `context` on the adapter. The twisted community
refers to the context as the `original` object. However, you may feel free to
use a specific argument name, such as `file`.
>>> class FileSize(object):
...
... zope.interface.implements(ISize)
... __used_for__ = IFile
...
... def __init__(self, context):
... self.context = context
...
... def getSize(self):
... return len(self.context.body)
...
Now that we have written our adapter, we have to register it with an adapter
registry, so that it can be looked up when needed. There is no such thing as a
global registry; thus we have to instantiate one for our example manually.
>>> from zope.interface.adapter import AdapterRegistry
>>> registry = AdapterRegistry()
The registry keeps a map of what adapters implement based on another
interface, the object already provides. Therefore, we next have to register an
adapter that adapts from `IFile` to `ISize`. The first argument to
the registry's `register()` method is a list of original interfaces.In our
cause we have only one original interface, `IFile`. A list makes sense, since
the interface package has the concept of multi-adapters, which are adapters
that require multiple objects to adapt to a new interface. In these
situations, your adapter constructor will require an argument for each
specified interface.
The second argument is the interface the adapter provides, in our case
`ISize`. The third argument in the name of the adapter. Since we do not care
about names, we simply leave it as an empty string. Names are commonly useful,
if you have adapters for the same set of interfaces, but they are useful in
different situations. The last argument is simply the adapter class.
>>> registry.register([IFile], ISize, '', FileSize)
You can now use the the registry to lookup the adapter.
>>> registry.lookup1(IFile, ISize, '')
<class '__main__.FileSize'>
Let's get a little bit more practical. Let's create a `File` instance and
create the adapter using a registry lookup. Then we see whether the adapter
returns the correct size by calling `getSize()`.
>>> file = File()
>>> size = registry.lookup1(IFile, ISize, '')(file)
>>> size.getSize()
7
However, this is not very practical, since I have to manually pass in the
arguments to the lookup method. There is some syntactic candy that will allow
us to get an adapter instance by simply calling `ISize(file)`. To make use of
this functionality, we need to add our registry to the adapter_hooks list,
which is a member of the adapters module. This list stores a collection of
callables that are automatically invoked when IFoo(obj) is called; their
purpose is to locate adapters that implement an interface for a certain
context instance.
You are required to implement your own adapter hook; this example covers one
of the simplest hooks that use the registry, but you could implement one that
used an adapter cache or persistent adapters, for instance. The helper hook is
required to expect as first argument the desired output interface (for us
`ISize`) and as the second argument the context of the adapter (here
`file`). The function returns an adapter, i.e. a `FileSize` instance.
>>> def hook(provided, object):
... adapter = registry.lookup1(zope.interface.providedBy(object),
... provided, '')
... return adapter(object)
...
We now just add the hook to an `adapter_hooks` list.
>>> from zope.interface.interface import adapter_hooks
>>> adapter_hooks.append(hook)
Once the hook is registered, you can use the desired syntax.
>>> size = ISize(file)
>>> size.getSize()
7
Now we have to cleanup after ourselves, so that others after us have a clean
`adapter_hooks` list.
>>> adapter_hooks.remove(hook)
That's it. I have intentionally left out a discussion of named adapters and
multi-adapters, since this text is intended as a practical and simple
introduction to Zope 3 interfaces and adapters. You might want to read the
`adapter.txt` in the `zope.interface` package for a more formal, referencial
and complete treatment of the package. Warning: People have reported that
`adapter.txt` makes their brain feel soft!
trunk/src/zope/interface/interface.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# 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.1 (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
$Id$
"""
from
__future__
import
generators
import
sys
import
warnings
import
weakref
from
types
import
FunctionType
from
ro
import
ro
from
zope.interface.exceptions
import
Invalid
CO_VARARGS
=
4
CO_VARKEYWORDS
=
8
TAGGED_DATA
=
'__interface_tagged_values__'
def
invariant
(
call
):
f_locals
=
sys
.
_getframe
(
1
).
f_locals
tags
=
f_locals
.
get
(
TAGGED_DATA
)
if
tags
is
None
:
tags
=
f_locals
[
TAGGED_DATA
]
=
{}
invariants
=
tags
.
get
(
'invariants'
)
if
invariants
is
None
:
invariants
=
tags
[
'invariants'
]
=
[]
invariants
.
append
(
call
)
class
Element
(
object
):
# We can't say this yet because we don't have enough
# infrastructure in place.
#
#implements(IElement)
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__
self
.
__tagged_values
=
{}
def
getName
(
self
):
""" Returns the name of the object. """
return
self
.
__name__
def
getDoc
(
self
):
""" Returns the documentation for the object. """
return
self
.
__doc__
def
getTaggedValue
(
self
,
tag
):
""" Returns the value associated with 'tag'. """
return
self
.
__tagged_values
[
tag
]
def
queryTaggedValue
(
self
,
tag
,
default
=
None
):
""" Returns the value associated with 'tag'. """
return
self
.
__tagged_values
.
get
(
tag
,
default
)
def
getTaggedValueTags
(
self
):
""" Returns a list of all tags. """
return
self
.
__tagged_values
.
keys
()
def
setTaggedValue
(
self
,
tag
,
value
):
""" Associates 'value' with 'key'. """
self
.
__tagged_values
[
tag
]
=
value
class
SpecificationBasePy
(
object
):
def
providedBy
(
self
,
ob
):
"""Is the interface implemented by an object
>>> from zope.interface import *
>>> class I1(Interface):
... pass
>>> class C(object):
... implements(I1)
>>> c = C()
>>> class X(object):
... pass
>>> x = X()
>>> I1.providedBy(x)
False
>>> I1.providedBy(C)
False
>>> I1.providedBy(c)
True
>>> directlyProvides(x, I1)
>>> I1.providedBy(x)
True
>>> directlyProvides(C, I1)
>>> I1.providedBy(C)
True
"""
spec
=
providedBy
(
ob
)
return
self
in
spec
.
_implied
def
implementedBy
(
self
,
cls
):
"""Do instances of the given class implement the interface?"""
spec
=
implementedBy
(
cls
)
return
self
in
spec
.
_implied
def
isOrExtends
(
self
,
interface
):
"""Is the interface the same as or extend the given interface
Examples::
>>> from zope.interface import Interface
>>> from zope.interface.declarations import Declaration
>>> class I1(Interface): pass
...
>>> class I2(I1): pass
...
>>> class I3(Interface): pass
...
>>> class I4(I3): pass
...
>>> spec = Declaration()
>>> int(spec.extends(Interface))
0
>>> spec = Declaration(I2)
>>> int(spec.extends(Interface))
1
>>> int(spec.extends(I1))
1
>>> int(spec.extends(I2))
1
>>> int(spec.extends(I3))
0
>>> int(spec.extends(I4))
0
"""
return
interface
in
self
.
_implied
SpecificationBase
=
SpecificationBasePy
try
:
from
_zope_interface_coptimizations
import
SpecificationBase
except
ImportError
:
pass
class
Specification
(
SpecificationBase
):
"""Specifications
An interface specification is used to track interface declarations
and component registrations.
This class is a base class for both interfaces themselves and for
interface specifications (declarations).
Specifications are mutable. If you reassign their cases, their
relations with other specifications are adjusted accordingly.
For example:
>>> from zope.interface import Interface
>>> class I1(Interface):
... pass
>>> class I2(I1):
... pass
>>> class I3(I2):
... pass
>>> [i.__name__ for i in I1.__bases__]
['Interface']
>>> [i.__name__ for i in I2.__bases__]
['I1']
>>> I3.extends(I1)
1
>>> I2.__bases__ = (Interface, )
>>> [i.__name__ for i in I2.__bases__]
['Interface']
>>> I3.extends(I1)
0
"""
# Copy some base class methods for speed
isOrExtends
=
SpecificationBase
.
isOrExtends
providedBy
=
SpecificationBase
.
providedBy
#########################################################################
# BBB 2004-07-13: Backward compatabilty. These methods have been
# deprecated in favour of providedBy and implementedBy.
def
isImplementedByInstancesOf
(
self
,
cls
):
warnings
.
warn
(
"isImplementedByInstancesOf has been renamed to implementedBy"
,
DeprecationWarning
,
stacklevel
=
2
,
)
return
self
.
implementedBy
(
cls
)
def
isImplementedBy
(
self
,
ob
):
warnings
.
warn
(
"isImplementedBy has been renamed to providedBy"
,
DeprecationWarning
,
stacklevel
=
2
,
)
return
self
.
providedBy
(
ob
)
#
#########################################################################
def
__init__
(
self
,
bases
=
()):
self
.
_implied
=
{}
self
.
dependents
=
weakref
.
WeakKeyDictionary
()
self
.
__bases__
=
tuple
(
bases
)
def
subscribe
(
self
,
dependent
):
self
.
dependents
[
dependent
]
=
1
def
unsubscribe
(
self
,
dependent
):
del
self
.
dependents
[
dependent
]
def
__setBases
(
self
,
bases
):
# Register ourselves as a dependent of our old bases
for
b
in
self
.
__bases__
:
b
.
unsubscribe
(
self
)
# Register ourselves as a dependent of our bases
self
.
__dict__
[
'__bases__'
]
=
bases
for
b
in
bases
:
b
.
subscribe
(
self
)
self
.
changed
()
__bases__
=
property
(
lambda
self
:
self
.
__dict__
.
get
(
'__bases__'
,
()),
__setBases
,
)
def
changed
(
self
):
"""We, or something we depend on, have changed
"""
implied
=
self
.
_implied
implied
.
clear
()
ancestors
=
ro
(
self
)
self
.
__sro__
=
tuple
(
ancestors
)
self
.
__iro__
=
tuple
([
ancestor
for
ancestor
in
ancestors
if
isinstance
(
ancestor
,
InterfaceClass
)
])
for
ancestor
in
ancestors
:
# We directly imply our ancestors:
implied
[
ancestor
]
=
()
# Now, advise our dependents of change:
for
dependent
in
self
.
dependents
.
keys
():
dependent
.
changed
()
def
interfaces
(
self
):
"""Return an iterator for the interfaces in the specification
for example::
>>> from zope.interface import Interface
>>> class I1(Interface): pass
...
>>> class I2(I1): pass
...
>>> class I3(Interface): pass
...
>>> class I4(I3): pass
...
>>> spec = Specification((I2, I3))
>>> spec = Specification((I4, spec))
>>> i = spec.interfaces()
>>> i.next().getName()
'I4'
>>> i.next().getName()
'I2'
>>> i.next().getName()
'I3'
>>> list(i)
[]
"""
seen
=
{}
for
base
in
self
.
__bases__
:
for
interface
in
base
.
interfaces
():
if
interface
not
in
seen
:
seen
[
interface
]
=
1
yield
interface
def
extends
(
self
,
interface
,
strict
=
True
):
"""Does the specification extend the given interface?
Test whether an interface in the specification extends the
given interface
Examples::
>>> from zope.interface import Interface
>>> from zope.interface.declarations import Declaration
>>> class I1(Interface): pass
...
>>> class I2(I1): pass
...
>>> class I3(Interface): pass
...
>>> class I4(I3): pass
...
>>> spec = Declaration()
>>> int(spec.extends(Interface))
0
>>> spec = Declaration(I2)
>>> int(spec.extends(Interface))
1
>>> int(spec.extends(I1))
1
>>> int(spec.extends(I2))
1
>>> int(spec.extends(I3))
0
>>> int(spec.extends(I4))
0
>>> I2.extends(I2)
0
>>> I2.extends(I2, False)
1
>>> I2.extends(I2, strict=False)
1
"""
return
((
interface
in
self
.
_implied
)
and
((
not
strict
)
or
(
self
!=
interface
))
)
def
weakref
(
self
,
callback
=
None
):
if
callback
is
None
:
return
weakref
.
ref
(
self
)
else
:
return
weakref
.
ref
(
self
,
callback
)
def
get
(
self
,
name
,
default
=
None
):
"""Query for an attribute description
"""
try
:
attrs
=
self
.
_v_attrs
except
AttributeError
:
attrs
=
self
.
_v_attrs
=
{}
attr
=
attrs
.
get
(
name
)
if
attr
is
None
:
for
iface
in
self
.
__iro__
:
attr
=
iface
.
direct
(
name
)
if
attr
is
not
None
:
attrs
[
name
]
=
attr
break
if
attr
is
None
:
return
default
else
:
return
attr
class
InterfaceClass
(
Element
,
Specification
):
"""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
(
'__module__'
in
attrs
)
and
isinstance
(
attrs
[
'__module__'
],
str
)
):
__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__
=
sys
.
_getframe
(
1
).
f_globals
[
'__name__'
]
except
(
AttributeError
,
KeyError
):
pass
self
.
__module__
=
__module__
if
attrs
is
None
:
attrs
=
{}
d
=
attrs
.
get
(
'__doc__'
)
if
d
is
not
None
:
if
not
isinstance
(
d
,
Attribute
):
if
__doc__
is
None
:
__doc__
=
d
del
attrs
[
'__doc__'
]
if
__doc__
is
None
:
__doc__
=
''
Element
.
__init__
(
self
,
name
,
__doc__
)
if
attrs
.
has_key
(
TAGGED_DATA
):
tagged_data
=
attrs
[
TAGGED_DATA
]
del
attrs
[
TAGGED_DATA
]
else
:
tagged_data
=
None
if
tagged_data
is
not
None
:
for
key
,
val
in
tagged_data
.
items
():
self
.
setTaggedValue
(
key
,
val
)
for
base
in
bases
:
if
not
isinstance
(
base
,
InterfaceClass
):
raise
TypeError
,
'Expected base interfaces'
Specification
.
__init__
(
self
,
bases
)
# Make sure that all recorded attributes (and methods) are of type
# `Attribute` and `Method`
for
name
,
attr
in
attrs
.
items
():
if
isinstance
(
attr
,
Attribute
):
attr
.
interface
=
self
if
not
attr
.
__name__
:
attr
.
__name__
=
name
elif
isinstance
(
attr
,
FunctionType
):
attrs
[
name
]
=
fromFunction
(
attr
,
self
,
name
=
name
)
else
:
raise
InvalidInterface
(
"Concrete attribute, %s"
%
name
)
self
.
__attrs
=
attrs
self
.
__identifier__
=
"%s.%s"
%
(
self
.
__module__
,
self
.
__name__
)
def
interfaces
(
self
):
"""Return an iterator for the interfaces in the specification
for example::
>>> from zope.interface import Interface
>>> class I1(Interface): pass
...
>>>
>>> i = I1.interfaces()
>>> i.next().getName()
'I1'
>>> list(i)
[]
"""
yield
self
def
getBases
(
self
):
return
self
.
__bases__
def
isEqualOrExtendedBy
(
self
,
other
):
"""Same interface or extends?"""
if
self
==
other
:
return
True
return
other
.
extends
(
self
)
def
names
(
self
,
all
=
False
):
"""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
(
all
):
r
[
name
]
=
1
return
r
.
keys
()
def
__iter__
(
self
):
return
iter
(
self
.
names
(
all
=
True
))
def
namesAndDescriptions
(
self
,
all
=
False
):
"""Return attribute names and descriptions defined by 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
(
all
):
if
name
not
in
r
:
r
[
name
]
=
d
return
r
.
items
()
def
getDescriptionFor
(
self
,
name
):
"""Return the attribute description for the given name."""
r
=
self
.
get
(
name
)
if
r
is
not
None
:
return
r
raise
KeyError
,
name
__getitem__
=
getDescriptionFor
def
__contains__
(
self
,
name
):
return
self
.
get
(
name
)
is
not
None
def
direct
(
self
,
name
):
return
self
.
__attrs
.
get
(
name
)
def
queryDescriptionFor
(
self
,
name
,
default
=
None
):
return
self
.
get
(
name
,
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
validateInvariants
(
self
,
obj
,
errors
=
None
):
"""validate object to defined invariants."""
for
call
in
self
.
queryTaggedValue
(
'invariants'
,
[]):
try
:
call
(
obj
)
except
Invalid
,
e
:
if
errors
is
None
:
raise
else
:
errors
.
append
(
e
)
for
base
in
self
.
__bases__
:
try
:
base
.
validateInvariants
(
obj
,
errors
)
except
Invalid
:
if
errors
is
None
:
raise
pass
if
errors
:
raise
Invalid
(
errors
)
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
(
k
in
dict
):
dict
[
k
]
=
v
for
b
in
self
.
__bases__
:
b
.
__d
(
dict
)
def
__repr__
(
self
):
r
=
getattr
(
self
,
'_v_repr'
,
self
)
if
r
is
self
:
name
=
self
.
__name__
m
=
self
.
__module__
if
m
:
name
=
'%s.%s'
%
(
m
,
name
)
r
=
"<%s %s>"
%
(
self
.
__class__
.
__name__
,
name
)
self
.
_v_repr
=
r
return
r
def
__call__
():
# TRICK! Create the call method
#
# An embedded function is used to allow an optional argument to
# __call__ without resorting to a global marker.
#
# The evility of this trick is a reflection of the underlying
# evility of "optional" arguments, arguments whose presense or
# absense changes the behavior of the methods.
#
# I think the evil is necessary, and perhaps desireable to
# provide some consistencey with the PEP 246 adapt method.
marker
=
object
()
def
__call__
(
self
,
obj
,
alternate
=
marker
):
"""Adapt an object to the interface
The sematics based on those of the PEP 246 adapt function.
If an object cannot be adapted, then a TypeError is raised::
>>> import zope.interface
>>> class I(zope.interface.Interface):
... pass
>>> I(0)
Traceback (most recent call last):
...
TypeError: ('Could not adapt', 0, """
\
"""<InterfaceClass zope.interface.interface.I>)
unless an alternate value is provided as a second
positional argument::
>>> I(0, 'bob')
'bob'
If an object already implements the interface, then it will be
returned::
>>> class C(object):
... zope.interface.implements(I)
>>> obj = C()
>>> I(obj) is obj
True
If an object implements __conform__, then it will be used::
>>> class C(object):
... zope.interface.implements(I)
... def __conform__(self, proto):
... return 0
>>> I(C())
0
Adapter hooks (see __adapt__) will also be used, if present:
>>> from zope.interface.interface import adapter_hooks
>>> def adapt_0_to_42(iface, obj):
... if obj == 0:
... return 42
>>> adapter_hooks.append(adapt_0_to_42)
>>> I(0)
42
>>> adapter_hooks.remove(adapt_0_to_42)
>>> I(0)
Traceback (most recent call last):
...
TypeError: ('Could not adapt', 0, """
\
"""<InterfaceClass zope.interface.interface.I>)
"""
conform
=
getattr
(
obj
,
'__conform__'
,
None
)
if
conform
is
not
None
:
try
:
adapter
=
conform
(
self
)
except
TypeError
:
# We got a TypeError. It might be an error raised by
# the __conform__ implementation, or *we* may have
# made the TypeError by calling an unbound method
# (object is a class). In the later case, we behave
# as though there is no __conform__ method. We can
# detect this case by checking whether there is more
# than one traceback object in the traceback chain:
if
sys
.
exc_info
()[
2
].
tb_next
is
not
None
:
# There is more than one entry in the chain, so
# reraise the error:
raise
# This clever trick is from Phillip Eby
else
:
if
adapter
is
not
None
:
return
adapter
adapter
=
self
.
__adapt__
(
obj
)
if
adapter
is
None
:
if
alternate
is
not
marker
:
return
alternate
raise
TypeError
(
"Could not adapt"
,
obj
,
self
)
return
adapter
return
__call__
__call__
=
__call__
()
# TRICK! Make the *real* __call__ method
def
__adapt__
(
self
,
obj
):
"""Adapt an object to the reciever
This method is normally not called directly. It is called by
the PEP 246 adapt framework and by the interface __call__
operator.
The adapt method is responsible for adapting an object to
the reciever.
The default version returns None::
>>> import zope.interface
>>> class I(zope.interface.Interface):
... pass
>>> I.__adapt__(0)
unless the object given provides the interface::
>>> class C(object):
... zope.interface.implements(I)
>>> obj = C()
>>> I.__adapt__(obj) is obj
True
Adapter hooks can be provided (or removed) to provide custom
adaptation. We'll install a silly hook that adapts 0 to 42.
We install a hook by simply adding it to the adapter_hooks
list::
>>> from zope.interface.interface import adapter_hooks
>>> def adapt_0_to_42(iface, obj):
... if obj == 0:
... return 42
>>> adapter_hooks.append(adapt_0_to_42)
>>> I.__adapt__(0)
42
Hooks must either return an adapter, or None if no adapter can
be found.
Hooks can be uninstalled by removing them from the list::
>>> adapter_hooks.remove(adapt_0_to_42)
>>> I.__adapt__(0)
"""
if
self
.
providedBy
(
obj
):
return
obj
for
hook
in
adapter_hooks
:
adapter
=
hook
(
self
,
obj
)
if
adapter
is
not
None
:
return
adapter
def
__reduce__
(
self
):
return
self
.
__name__
def
__cmp
(
self
,
o1
,
o2
):
# Yes, I did mean to name this __cmp, rather than __cmp__.
# It is a private method used by __lt__ and __gt__.
# I don't want to override __eq__ because I want the default
# __eq__, which is really fast.
"""Make interfaces sortable
TODO: It would ne nice if:
More specific interfaces should sort before less specific ones.
Otherwise, sort on name and module.
But this is too complicated, and we're going to punt on it
for now.
For now, sort on interface and module name.
None is treated as a pseudo interface that implies the loosest
contact possible, no contract. For that reason, all interfaces
sort before None.
"""
if
o1
==
o2
:
return
0
if
o1
is
None
:
return
1
if
o2
is
None
:
return
-
1
n1
=
(
getattr
(
o1
,
'__name__'
,
''
),
getattr
(
getattr
(
o1
,
'__module__'
,
None
),
'__name__'
,
''
))
n2
=
(
getattr
(
o2
,
'__name__'
,
''
),
getattr
(
getattr
(
o2
,
'__module__'
,
None
),
'__name__'
,
''
))
return
cmp
(
n1
,
n2
)
def
__lt__
(
self
,
other
):
c
=
self
.
__cmp
(
self
,
other
)
#print '<', self, other, c < 0, c
return
c
<
0
def
__gt__
(
self
,
other
):
c
=
self
.
__cmp
(
self
,
other
)
#print '>', self, other, c > 0, c
return
c
>
0
adapter_hooks
=
[]
Interface
=
InterfaceClass
(
"Interface"
,
__module__
=
'zope.interface'
)
class
Attribute
(
Element
):
"""Attribute descriptions
"""
# We can't say this yet because we don't have enough
# infrastructure in place.
#
# implements(IAttribute)
interface
=
None
class
Method
(
Attribute
):
"""Method interfaces
The idea here is that you have objects that describe methods.
This provides an opportunity for rich meta-data.
"""
# We can't say this yet because we don't have enough
# infrastructure in place.
#
# implements(IMethod)
def
__call__
(
self
,
*
args
,
**
kw
):
raise
BrokenImplementation
(
self
.
interface
,
self
.
__name__
)
def
getSignatureInfo
(
self
):
return
{
'positional'
:
self
.
positional
,
'required'
:
self
.
required
,
'optional'
:
self
.
optional
,
'varargs'
:
self
.
varargs
,
'kwargs'
:
self
.
kwargs
,
}
def
getSignatureString
(
self
):
sig
=
"("
for
v
in
self
.
positional
:
sig
=
sig
+
v
if
v
in
self
.
optional
.
keys
():
sig
=
sig
+
"=%s"
%
`self.optional[v]`
sig
=
sig
+
", "
if
self
.
varargs
:
sig
=
sig
+
(
"*%s, "
%
self
.
varargs
)
if
self
.
kwargs
:
sig
=
sig
+
(
"**%s, "
%
self
.
kwargs
)
# slice off the last comma and space
if
self
.
positional
or
self
.
varargs
or
self
.
kwargs
:
sig
=
sig
[:
-
2
]
sig
=
sig
+
")"
return
sig
def
fromFunction
(
func
,
interface
=
None
,
imlevel
=
0
,
name
=
None
):
name
=
name
or
func
.
__name__
method
=
Method
(
name
,
func
.
__doc__
)
defaults
=
func
.
func_defaults
or
()
code
=
func
.
func_code
# Number of positional arguments
na
=
code
.
co_argcount
-
imlevel
names
=
code
.
co_varnames
[
imlevel
:]
opt
=
{}
# Number of required arguments
nr
=
na
-
len
(
defaults
)
if
nr
<
0
:
defaults
=
defaults
[
-
nr
:]
nr
=
0
# Determine the optional arguments.
for
i
in
range
(
len
(
defaults
)):
opt
[
names
[
i
+
nr
]]
=
defaults
[
i
]
method
.
positional
=
names
[:
na
]
method
.
required
=
names
[:
nr
]
method
.
optional
=
opt
argno
=
na
# Determine the function's variable argument's name (i.e. *args)
if
code
.
co_flags
&
CO_VARARGS
:
method
.
varargs
=
names
[
argno
]
argno
=
argno
+
1
else
:
method
.
varargs
=
None
# Determine the function's keyword argument's name (i.e. **kw)
if
code
.
co_flags
&
CO_VARKEYWORDS
:
method
.
kwargs
=
names
[
argno
]
else
:
method
.
kwargs
=
None
method
.
interface
=
interface
for
key
,
value
in
func
.
__dict__
.
items
():
method
.
setTaggedValue
(
key
,
value
)
return
method
def
fromMethod
(
meth
,
interface
=
None
,
name
=
None
):
func
=
meth
.
im_func
return
fromFunction
(
func
,
interface
,
imlevel
=
1
,
name
=
name
)
# Now we can create the interesting interfaces and wire them up:
def
_wire
():
from
zope.interface.declarations
import
classImplements
from
zope.interface.interfaces
import
IAttribute
classImplements
(
Attribute
,
IAttribute
)
from
zope.interface.interfaces
import
IMethod
classImplements
(
Method
,
IMethod
)
from
zope.interface.interfaces
import
IInterface
classImplements
(
InterfaceClass
,
IInterface
)
# We import this here to deal with module dependencies.
from
zope.interface.declarations
import
providedBy
,
implementedBy
from
zope.interface.exceptions
import
InvalidInterface
from
zope.interface.exceptions
import
BrokenImplementation
trunk/src/zope/interface/interfaces.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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 Package Interfaces
$Id$
"""
__docformat__
=
'restructuredtext'
from
zope.interface
import
Interface
from
zope.interface.interface
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
getTaggedValue
(
tag
):
"""Returns the value associated with 'tag'.
Raise a KeyError of the tag isn't set
"""
def
queryTaggedValue
(
tag
,
default
=
None
):
"""Returns the value associated with 'tag'.
Return the default value of the tag isn't set.
"""
def
getTaggedValueTags
():
"""Returns a list of all tags."""
def
setTaggedValue
(
tag
,
value
):
"""Associates 'value' with 'key'."""
class
IAttribute
(
IElement
):
"""Attribute descriptors"""
interface
=
Attribute
(
'interface'
,
'Stores the interface instance in which the '
'attribute is located.'
)
class
IMethod
(
IAttribute
):
"""Method attributes
"""
def
getSignatureInfo
():
"""Returns the signature information.
This method returns a dictionary with the following keys:
o `positional` - All positional arguments.
o `required` - A list of all required arguments.
o `optional` - A list of all optional arguments.
o `varargs' - The name of the varargs argument.
o `kwargs` - The name of the kwargs argument.
"""
def
getSignatureString
():
"""Return a signature string suitable for inclusion in documentation.
This method returns the function signature string. For example, if you
have `func(a, b, c=1, d='f')`, then the signature string is `(a, b,
c=1, d='f')`.
"""
class
ISpecification
(
Interface
):
"""Object Behavioral specifications
"""
def
extends
(
other
,
strict
=
True
):
"""Test whether a specification extends another
The specification extends other if it has other as a base
interface or if one of it's bases extends other.
If strict is false, then the specification extends itself.
"""
def
isOrExtends
(
other
):
"""Test whether the specification is or extends another
"""
def
weakref
(
callback
=
None
):
"""Return a weakref to the specification
This method is, regrettably, needed to allow weakrefs to be
computed to security-proxied specifications. While the
zope.interface package does not require zope.security or
zope.proxy, it has to be able to coexist with it.
"""
__bases__
=
Attribute
(
"""Base specifications
A tuple if specifications from which this specification is
directly derived.
"""
)
__sro__
=
Attribute
(
"""Specification-resolution order
A tuple of the specification and all of it's ancestor
specifications from most specific to least specific.
(This is similar to the method-resolution order for new-style classes.)
"""
)
def
get
(
name
,
default
=
None
):
"""Look up the description for a name
If the named attribute is not defined, the default is
returned.
"""
class
IInterface
(
ISpecification
,
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 zope.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. Call zope.interface.implements in your class definition.
2. Call zope.interfaces.directlyProvides on your object.
3. Call 'zope.interface.classImplements' to assert that instances
of a class implement an interface.
For example::
from zope.interface import classImplements
classImplements(some_class, some_interface)
This 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 its instances
implement.
o You query interface meta-data. See the IInterface methods and
attributes for details.
"""
def
providedBy
(
object
):
"""Test whether the interface is implemented by the object
Return true of the object asserts that it implements the
interface, including asserting that it implements an extended
interface.
"""
def
implementedBy
(
class_
):
"""Test whether the interface is implemented by instances of the class
Return true of the class asserts that its instances implement the
interface, including asserting that they implement an extended
interface.
"""
def
names
(
all
=
False
):
"""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 base classes will be included.
"""
def
namesAndDescriptions
(
all
=
False
):
"""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 base classes will be included.
"""
def
__getitem__
(
name
):
"""Get the description for a name
If the named attribute is not defined, a KeyError is raised.
"""
def
direct
(
name
):
"""Get the description for the name if it was defined by the interface
If the interface doesn't define the name, returns None.
"""
def
validateInvariants
(
obj
,
errors
=
None
):
"""Validate invariants
Validate object to defined invariants. If errors is None,
raises first Invalid error; if errors is a list, appends all errors
to list, then raises Invalid with the errors as the first element
of the "args" tuple."""
def
__contains__
(
name
):
"""Test whether the name is defined by the interface"""
def
__iter__
():
"""Return an iterator over the names defined by the interface
The names iterated include all of the names defined by the
interface directly and indirectly by base interfaces.
"""
__module__
=
Attribute
(
"""The name of the module defining the interface"""
)
class
IDeclaration
(
ISpecification
):
"""Interface declaration
Declarations are used to express the interfaces implemented by
classes or provided by objects.
"""
def
__contains__
(
interface
):
"""Test whether an interface is in the specification
Return true if the given interface is one of the interfaces in
the specification and false otherwise.
"""
def
__iter__
():
"""Return an iterator for the interfaces in the specification
"""
def
flattened
():
"""Return an iterator of all included and extended interfaces
An iterator is returned for all interfaces either included in
or extended by interfaces included in the specifications
without duplicates. The interfaces are in "interface
resolution order". The interface resolution order is such that
base interfaces are listed after interfaces that extend them
and, otherwise, interfaces are included in the order that they
were defined in the specification.
"""
def
__sub__
(
interfaces
):
"""Create an interface specification with some interfaces excluded
The argument can be an interface or an interface
specifications. The interface or interfaces given in a
specification are subtracted from the interface specification.
Removing an interface that is not in the specification does
not raise an error. Doing so has no effect.
Removing an interface also removes sub-interfaces of the interface.
"""
def
__add__
(
interfaces
):
"""Create an interface specification with some interfaces added
The argument can be an interface or an interface
specifications. The interface or interfaces given in a
specification are added to the interface specification.
Adding an interface that is already in the specification does
not raise an error. Doing so has no effect.
"""
def
__nonzero__
():
"""Return a true value of the interface specification is non-empty
"""
class
IInterfaceDeclaration
(
Interface
):
"""Declare and check the interfaces of objects
The functions defined in this interface are used to declare the
interfaces that objects provide and to query the interfaces that have
been declared.
Interfaces can be declared for objects in two ways:
- Interfaces are declared for instances of the object's class
- Interfaces are declared for the object directly.
The interfaces declared for an object are, therefore, the union of
interfaces declared for the object directly and the interfaces
declared for instances of the object's class.
Note that we say that a class implements the interfaces provided
by it's instances. An instance can also provide interfaces
directly. The interfaces provided by an object are the union of
the interfaces provided directly and the interfaces implemented by
the class.
"""
def
providedBy
(
ob
):
"""Return the interfaces provided by an object
This is the union of the interfaces directly provided by an
object and interfaces implemented by it's class.
The value returned is an IDeclaration.
"""
def
implementedBy
(
class_
):
"""Return the interfaces implemented for a class' instances
The value returned is an IDeclaration.
"""
def
classImplements
(
class_
,
*
interfaces
):
"""Declare additional interfaces implemented for instances of a class
The arguments after the class are one or more interfaces or
interface specifications (IDeclaration objects).
The interfaces given (including the interfaces in the
specifications) are added to any interfaces previously
declared.
Consider the following example::
class C(A, B):
...
classImplements(C, I1, I2)
Instances of ``C`` provide ``I1``, ``I2``, and whatever interfaces
instances of ``A`` and ``B`` provide.
"""
def
implementer
(
*
interfaces
):
"""Create a decorator for declaring interfaces implemented by a facory
A callable is returned that makes an implements declaration on
objects passed to it.
"""
def
classImplementsOnly
(
class_
,
*
interfaces
):
"""Declare the only interfaces implemented by instances of a class
The arguments after the class are one or more interfaces or
interface specifications (IDeclaration objects).
The interfaces given (including the interfaces in the
specifications) replace any previous declarations.
Consider the following example::
class C(A, B):
...
classImplements(C, IA, IB. IC)
classImplementsOnly(C. I1, I2)
Instances of ``C`` provide only ``I1``, ``I2``, and regardless of
whatever interfaces instances of ``A`` and ``B`` implement.
"""
def
directlyProvidedBy
(
object
):
"""Return the interfaces directly provided by the given object
The value returned is an IDeclaration.
"""
def
directlyProvides
(
object
,
*
interfaces
):
"""Declare interfaces declared directly for an object
The arguments after the object are one or more interfaces or
interface specifications (IDeclaration objects).
The interfaces given (including the interfaces in the
specifications) replace interfaces previously
declared for the object.
Consider the following example::
class C(A, B):
...
ob = C()
directlyProvides(ob, I1, I2)
The object, ``ob`` provides ``I1``, ``I2``, and whatever interfaces
instances have been declared for instances of ``C``.
To remove directly provided interfaces, use ``directlyProvidedBy`` and
subtract the unwanted interfaces. For example::
directlyProvides(ob, directlyProvidedBy(ob)-I2)
removes I2 from the interfaces directly provided by
``ob``. The object, ``ob`` no longer directly provides ``I2``,
although it might still provide ``I2`` if it's class
implements ``I2``.
To add directly provided interfaces, use ``directlyProvidedBy`` and
include additional interfaces. For example::
directlyProvides(ob, directlyProvidedBy(ob), I2)
adds I2 to the interfaces directly provided by ob.
"""
def
implements
(
*
interfaces
):
"""Declare interfaces implemented by instances of a class
This function is called in a class definition.
The arguments are one or more interfaces or interface
specifications (IDeclaration objects).
The interfaces given (including the interfaces in the
specifications) are added to any interfaces previously
declared.
Previous declarations include declarations for base classes
unless implementsOnly was used.
This function is provided for convenience. It provides a more
convenient way to call classImplements. For example::
implements(I1)
is equivalent to calling::
classImplements(C, I1)
after the class has been created.
Consider the following example::
class C(A, B):
implements(I1, I2)
Instances of ``C`` implement ``I1``, ``I2``, and whatever interfaces
instances of ``A`` and ``B`` implement.
"""
def
implementsOnly
(
*
interfaces
):
"""Declare the only interfaces implemented by instances of a class
This function is called in a class definition.
The arguments are one or more interfaces or interface
specifications (IDeclaration objects).
Previous declarations including declarations for base classes
are overridden.
This function is provided for convenience. It provides a more
convenient way to call classImplementsOnly. For example::
implementsOnly(I1)
is equivalent to calling::
classImplementsOnly(I1)
after the class has been created.
Consider the following example::
class C(A, B):
implementsOnly(I1, I2)
Instances of ``C`` implement ``I1``, ``I2``, regardless of what
instances of ``A`` and ``B`` implement.
"""
def
classProvides
(
*
interfaces
):
"""Declare interfaces provided directly by a class
This function is called in a class definition.
The arguments are one or more interfaces or interface
specifications (IDeclaration objects).
The given interfaces (including the interfaces in the
specifications) are used to create the class's direct-object
interface specification. An error will be raised if the module
class has an direct interface specification. In other words, it is
an error to call this function more than once in a class
definition.
Note that the given interfaces have nothing to do with the
interfaces implemented by instances of the class.
This function is provided for convenience. It provides a more
convenient way to call directlyProvides for a class. For example::
classProvides(I1)
is equivalent to calling::
directlyProvides(theclass, I1)
after the class has been created.
"""
def
moduleProvides
(
*
interfaces
):
"""Declare interfaces provided by a module
This function is used in a module definition.
The arguments are one or more interfaces or interface
specifications (IDeclaration objects).
The given interfaces (including the interfaces in the
specifications) are used to create the module's direct-object
interface specification. An error will be raised if the module
already has an interface specification. In other words, it is
an error to call this function more than once in a module
definition.
This function is provided for convenience. It provides a more
convenient way to call directlyProvides for a module. For example::
moduleImplements(I1)
is equivalent to::
directlyProvides(sys.modules[__name__], I1)
"""
def
Declaration
(
*
interfaces
):
"""Create an interface specification
The arguments are one or more interfaces or interface
specifications (IDeclaration objects).
A new interface specification (IDeclaration) with
the given interfaces is returned.
"""
class
IAdapterRegistry
(
Interface
):
"""Provide an interface-based registry for adapters
This registry registers objects that are in some sense "from" a
sequence of specification to an interface and a name.
No specific semantics are assumed for the registered objects,
however, the most common application will be to register factories
that adapt objects providing required specifications to a provided
interface.
"""
def
register
(
required
,
provided
,
name
,
value
):
"""Register a value
A value is registered for a *sequence* of required specifications, a
provided interface, and a name.
"""
def
lookup
(
required
,
provided
,
name
,
default
=
None
):
"""Lookup a value
A value is looked up based on a *sequence* of required
specifications, a provided interface, and a name.
"""
def
lookupAll
(
required
,
provided
):
"""Find all adapters from the required to the provided interfaces
An iterable object is returned that provides name-value two-tuples.
"""
def
names
(
required
,
provided
):
"""Return the names for which there are registered objects
"""
def
subscribe
(
required
,
provided
,
subscriber
):
"""Register a subscriber
A subscriber is registered for a *sequence* of required
specifications, a provided interface, and a name.
Multiple subscribers may be registered for the same (or
equivalent) interfaces.
"""
def
subscriptions
(
required
,
provided
):
"""Get a sequence of subscribers
Subscribers for a *sequence* of required interfaces, and a provided
interface are returned.
"""
trunk/src/zope/interface/ro.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Compute a resolution order for an object and it's bases
$Id$
"""
def
ro
(
object
):
"""Compute a "resolution order" for an object
"""
return
mergeOrderings
([
_flatten
(
object
,
[])])
def
mergeOrderings
(
orderings
,
seen
=
None
):
"""Merge multiple orderings so that within-ordering order is preserved
Orderings are constrained in such a way that if an object appears
in two or more orderings, then the suffix that begins with the
object must be in both orderings.
For example:
>>> _mergeOrderings([
... ['x', 'y', 'z'],
... ['q', 'z'],
... [1, 3, 5],
... ['z']
... ])
['x', 'y', 'q', 1, 3, 5, 'z']
"""
if
seen
is
None
:
seen
=
{}
result
=
[]
orderings
.
reverse
()
for
ordering
in
orderings
:
ordering
=
list
(
ordering
)
ordering
.
reverse
()
for
o
in
ordering
:
if
o
not
in
seen
:
seen
[
o
]
=
1
result
.
append
(
o
)
result
.
reverse
()
return
result
def
_flatten
(
ob
,
result
):
result
.
append
(
ob
)
for
base
in
ob
.
__bases__
:
_flatten
(
base
,
result
)
return
result
trunk/src/zope/interface/tests/__init__.py
0 → 100644
View file @
a3c091cb
#
# This file is necessary to make this directory a package.
trunk/src/zope/interface/tests/dummy.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# 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.1 (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.
#
##############################################################################
"""Dummy Module
$Id$
"""
from
zope.interface
import
moduleProvides
from
zope.interface.tests.ifoo
import
IFoo
from
zope.interface
import
moduleProvides
moduleProvides
(
IFoo
)
def
bar
(
baz
):
pass
trunk/src/zope/interface/tests/foodforthought.txt
0 → 100644
View file @
a3c091cb
================================
Food-based subscription examples
================================
This file gives more subscription examples using a cooking-based example::
>>> from zope.interface.adapter import AdapterRegistry
>>> registry = AdapterRegistry()
>>> import zope.interface
>>> class IAnimal(zope.interface.Interface):
... pass
>>> class IPoultry(IAnimal):
... pass
>>> class IChicken(IPoultry):
... pass
>>> class ISeafood(IAnimal):
... pass
Adapting to some other interface for which there is no
subscription adapter returns an empty sequence::
>>> class IRecipe(zope.interface.Interface):
... pass
>>> class ISausages(IRecipe):
... pass
>>> class INoodles(IRecipe):
... pass
>>> class IKFC(IRecipe):
... pass
>>> list(registry.subscriptions([IPoultry], IRecipe))
[]
unless we define a subscription::
>>> registry.subscribe([IAnimal], ISausages, 'sausages')
>>> list(registry.subscriptions([IPoultry], ISausages))
['sausages']
And define another subscription adapter::
>>> registry.subscribe([IPoultry], INoodles, 'noodles')
>>> meals = list(registry.subscriptions([IPoultry], IRecipe))
>>> meals.sort()
>>> meals
['noodles', 'sausages']
>>> registry.subscribe([IChicken], IKFC, 'kfc')
>>> meals = list(registry.subscriptions([IChicken], IRecipe))
>>> meals.sort()
>>> meals
['kfc', 'noodles', 'sausages']
And the answer for poultry hasn't changed::
>>> meals = list(registry.subscriptions([IPoultry], IRecipe))
>>> meals.sort()
>>> meals
['noodles', 'sausages']
trunk/src/zope/interface/tests/ifoo.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# 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.1 (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.
#
##############################################################################
"""IFoo test module
$Id$
"""
from
zope.interface
import
Interface
class
IFoo
(
Interface
):
"""
Dummy interface for unit tests.
"""
def
bar
(
baz
):
"""
Just a note.
"""
trunk/src/zope/interface/tests/m1.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Test module that declares an interface
$Id$
"""
from
zope.interface
import
Interface
,
moduleProvides
class
I1
(
Interface
):
pass
class
I2
(
Interface
):
pass
moduleProvides
(
I1
,
I2
)
trunk/src/zope/interface/tests/m2.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Test module that doesn't declare an interface
$Id$
"""
trunk/src/zope/interface/tests/odd.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Odd meta class that doesn't subclass type.
This is used for testing support for ExtensionClass in new interfaces.
>>> class A(object):
... __metaclass__ = MetaClass
... a = 1
...
>>> A.__name__
'A'
>>> A.__bases__
()
>>> class B(object):
... __metaclass__ = MetaClass
... b = 1
...
>>> class C(A, B): pass
...
>>> C.__name__
'C'
>>> int(C.__bases__ == (A, B))
1
>>> a = A()
>>> aa = A()
>>> a.a
1
>>> aa.a
1
>>> aa.a = 2
>>> a.a
1
>>> aa.a
2
>>> c = C()
>>> c.a
1
>>> c.b
1
>>> c.b = 2
>>> c.b
2
>>> C.c = 1
>>> c.c
1
>>> from types import ClassType
>>> int(isinstance(C, (type, ClassType)))
0
>>> int(C.__class__.__class__ is C.__class__)
1
$Id$
"""
# class OddClass is an odd meta class
class
MetaMetaClass
(
type
):
def
__getattribute__
(
self
,
name
):
if
name
==
'__class__'
:
return
self
return
type
.
__getattribute__
(
self
,
name
)
class
MetaClass
(
object
):
"""Odd classes
"""
__metaclass__
=
MetaMetaClass
def
__init__
(
self
,
name
,
bases
,
dict
):
self
.
__name__
=
name
self
.
__bases__
=
bases
self
.
__dict__
.
update
(
dict
)
def
__call__
(
self
):
return
OddInstance
(
self
)
def
__getattr__
(
self
,
name
):
for
b
in
self
.
__bases__
:
v
=
getattr
(
b
,
name
,
self
)
if
v
is
not
self
:
return
v
raise
AttributeError
,
name
def
__repr__
(
self
):
return
"<odd class %s at %s>"
%
(
self
.
__name__
,
hex
(
id
(
self
)))
class
OddInstance
(
object
):
def
__init__
(
self
,
cls
):
self
.
__dict__
[
'__class__'
]
=
cls
def
__getattribute__
(
self
,
name
):
dict
=
object
.
__getattribute__
(
self
,
'__dict__'
)
if
name
==
'__dict__'
:
return
dict
v
=
dict
.
get
(
name
,
self
)
if
v
is
not
self
:
return
v
return
getattr
(
dict
[
'__class__'
],
name
)
def
__setattr__
(
self
,
name
,
v
):
self
.
__dict__
[
name
]
=
v
def
__delattr__
(
self
,
name
):
del
self
.
__dict__
[
name
]
def
__repr__
(
self
):
return
"<odd %s instance at %s>"
%
(
self
.
__class__
.
__name__
,
hex
(
id
(
self
)))
# DocTest:
if
__name__
==
"__main__"
:
import
doctest
,
__main__
doctest
.
testmod
(
__main__
,
isprivate
=
lambda
*
a
:
False
)
trunk/src/zope/interface/tests/test_adapter.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Adapter registry tests
$Id$
"""
import
unittest
import
zope.interface
from
zope.interface.adapter
import
AdapterRegistry
import
zope.interface
class
IF0
(
zope
.
interface
.
Interface
):
pass
class
IF1
(
IF0
):
pass
class
IB0
(
zope
.
interface
.
Interface
):
pass
class
IB1
(
IB0
):
pass
class
IR0
(
zope
.
interface
.
Interface
):
pass
class
IR1
(
IR0
):
pass
def
test_orderwith
():
"""
>>> Interface = zope.interface.Interface
>>> bywith = {(Interface, Interface): 'A0',
... (IF0, Interface): 'A1',
... (Interface, IB0): 'A2',
... (IF0, IB0): 'A3',
... (IF1, IB0): 'A4',
... (IF0, IB1): 'A5',
... (IF1, IB1): 'A6',
... }
>>> [value for spec, value in zope.interface.adapter.orderwith(bywith)]
['A6', 'A4', 'A5', 'A3', 'A1', 'A2', 'A0']
"""
def
test_multi_adapter_get_best_match
():
"""
>>> registry = AdapterRegistry()
>>> class IB2(IB0):
... pass
>>> class IB3(IB2, IB1):
... pass
>>> class IB4(IB1, IB2):
... pass
>>> registry.register([None, IB1], IR0, '', 'A1')
>>> registry.register([None, IB0], IR0, '', 'A0')
>>> registry.register([None, IB2], IR0, '', 'A2')
>>> registry.lookup((IF1, IB1), IR0, '')
'A1'
>>> registry.lookup((IF1, IB2), IR0, '')
'A2'
>>> registry.lookup((IF1, IB0), IR0, '')
'A0'
>>> registry.lookup((IF1, IB3), IR0, '')
'A2'
>>> registry.lookup((IF1, IB4), IR0, '')
'A1'
"""
def
test_multi_adapter_lookupAll_get_best_matches
():
"""
>>> registry = AdapterRegistry()
>>> class IB2(IB0):
... pass
>>> class IB3(IB2, IB1):
... pass
>>> class IB4(IB1, IB2):
... pass
>>> registry.register([None, IB1], IR0, '', 'A1')
>>> registry.register([None, IB0], IR0, '', 'A0')
>>> registry.register([None, IB2], IR0, '', 'A2')
>>> registry.lookupAll((IF1, IB1), IR0).next()[1]
'A1'
>>> registry.lookupAll((IF1, IB2), IR0).next()[1]
'A2'
>>> registry.lookupAll((IF1, IB0), IR0).next()[1]
'A0'
>>> registry.lookupAll((IF1, IB3), IR0).next()[1]
'A2'
>>> registry.lookupAll((IF1, IB4), IR0).next()[1]
'A1'
"""
def
test_multi_adapter_w_default
():
"""
>>> registry = AdapterRegistry()
>>> registry.register([None, None], IB1, 'bob', 'A0')
>>> registry.lookup((IF1, IR1), IB0, 'bob')
'A0'
>>> registry.register([None, IR0], IB1, 'bob', 'A1')
>>> registry.lookup((IF1, IR1), IB0, 'bob')
'A1'
>>> registry.lookup((IF1, IR1), IB0, 'bruce')
>>> registry.register([None, IR1], IB1, 'bob', 'A2')
>>> registry.lookup((IF1, IR1), IB0, 'bob')
'A2'
"""
def
test_multi_adapter_w_inherited_and_multiple_registrations
():
"""
>>> registry = AdapterRegistry()
>>> class IX(zope.interface.Interface):
... pass
>>> registry.register([IF0, IR0], IB1, 'bob', 'A1')
>>> registry.register([IF1, IX], IB1, 'bob', 'AX')
>>> registry.lookup((IF1, IR1), IB0, 'bob')
'A1'
"""
def
test_named_adapter_with_default
():
"""Query a named simple adapter
>>> registry = AdapterRegistry()
If we ask for a named adapter, we won't get a result unless there
is a named adapter, even if the object implements the interface:
>>> registry.lookup([IF1], IF0, 'bob')
>>> registry.register([None], IB1, 'bob', 'A1')
>>> registry.lookup([IF1], IB0, 'bob')
'A1'
>>> registry.lookup([IF1], IB0, 'bruce')
>>> registry.register([None], IB0, 'bob', 'A2')
>>> registry.lookup([IF1], IB0, 'bob')
'A2'
"""
def
test_multi_adapter_gets_closest_provided
():
"""
>>> registry = AdapterRegistry()
>>> registry.register([IF1, IR0], IB0, 'bob', 'A1')
>>> registry.register((IF1, IR0), IB1, 'bob', 'A2')
>>> registry.lookup((IF1, IR1), IB0, 'bob')
'A1'
>>> registry = AdapterRegistry()
>>> registry.register([IF1, IR0], IB1, 'bob', 'A2')
>>> registry.register([IF1, IR0], IB0, 'bob', 'A1')
>>> registry.lookup([IF1, IR0], IB0, 'bob')
'A1'
>>> registry = AdapterRegistry()
>>> registry.register([IF1, IR0], IB0, 'bob', 'A1')
>>> registry.register([IF1, IR1], IB1, 'bob', 'A2')
>>> registry.lookup([IF1, IR1], IB0, 'bob')
'A2'
>>> registry = AdapterRegistry()
>>> registry.register([IF1, IR1], IB1, 'bob', 2)
>>> registry.register([IF1, IR0], IB0, 'bob', 1)
>>> registry.lookup([IF1, IR1], IB0, 'bob')
2
"""
def
test_multi_adapter_check_non_default_dont_hide_default
():
"""
>>> registry = AdapterRegistry()
>>> class IX(zope.interface.Interface):
... pass
>>> registry.register([None, IR0], IB0, 'bob', 1)
>>> registry.register([IF1, IX], IB0, 'bob', 2)
>>> registry.lookup([IF1, IR1], IB0, 'bob')
1
"""
def
test_adapter_hook_with_factory_producing_None
():
"""
>>> registry = AdapterRegistry()
>>> default = object()
>>> class Object1(object):
... zope.interface.implements(IF0)
>>> class Object2(object):
... zope.interface.implements(IF0)
>>> def factory(context):
... if isinstance(context, Object1):
... return 'adapter'
... return None
>>> registry.register([IF0], IB0, '', factory)
>>> registry.adapter_hook(IB0, Object1())
'adapter'
>>> registry.adapter_hook(IB0, Object2()) is None
True
>>> registry.adapter_hook(IB0, Object2(), default=default) is default
True
"""
def
test_adapter_registry_update_upon_interface_bases_change
():
"""
Let's first create a adapter registry and a simple adaptation hook:
>>> globalRegistry = AdapterRegistry()
>>> def _hook(iface, ob, lookup=globalRegistry.lookup1):
... factory = lookup(zope.interface.providedBy(ob), iface)
... if factory is None:
... return None
... else:
... return factory(ob)
>>> zope.interface.interface.adapter_hooks.append(_hook)
Now we create some interfaces and an implementation:
>>> class IX(zope.interface.Interface):
... pass
>>> class IY(zope.interface.Interface):
... pass
>>> class X(object):
... pass
>>> class Y(object):
... zope.interface.implements(IY)
... def __init__(self, original):
... self.original=original
and register an adapter:
>>> globalRegistry.register((IX,), IY, '', Y)
at first, we still expect the adapter lookup from `X` to `IY` to fail:
>>> IY(X()) #doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
Traceback (most recent call last):
...
TypeError: ('Could not adapt',
<zope.interface.tests.test_adapter.X object at ...>,
<InterfaceClass zope.interface.tests.test_adapter.IY>)
But after we declare an interface on the class `X`, it should pass:
>>> zope.interface.classImplementsOnly(X, IX)
>>> IY(X()) #doctest: +ELLIPSIS
<zope.interface.tests.test_adapter.Y object at ...>
>>> hook = zope.interface.interface.adapter_hooks.pop()
"""
def
test_suite
():
from
zope.testing
import
doctest
,
doctestunit
return
unittest
.
TestSuite
((
doctestunit
.
DocFileSuite
(
'../adapter.txt'
,
'../human.txt'
,
'foodforthought.txt'
,
globs
=
{
'__name__'
:
'__main__'
}),
doctest
.
DocTestSuite
(),
))
if
__name__
==
'__main__'
:
unittest
.
main
(
defaultTest
=
'test_suite'
)
trunk/src/zope/interface/tests/test_advice.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Tests for advice
This module was adapted from 'protocols.tests.advice', part of the Python
Enterprise Application Kit (PEAK). Please notify the PEAK authors
(pje@telecommunity.com and tsarna@sarna.org) if bugs are found or
Zope-specific changes are required, so that the PEAK version of this module
can be kept in sync.
PEAK is a Python application framework that interoperates with (but does
not require) Zope 3 and Twisted. It provides tools for manipulating UML
models, object-relational persistence, aspect-oriented programming, and more.
Visit the PEAK home page at http://peak.telecommunity.com for more information.
$Id$
"""
from
unittest
import
TestCase
,
makeSuite
,
TestSuite
from
zope.interface.advice
import
*
from
types
import
ClassType
import
sys
def
ping
(
log
,
value
):
def
pong
(
klass
):
log
.
append
((
value
,
klass
))
return
[
klass
]
addClassAdvisor
(
pong
)
class
ClassicClass
:
__metaclass__
=
ClassType
classLevelFrameInfo
=
getFrameInfo
(
sys
.
_getframe
())
class
NewStyleClass
:
__metaclass__
=
type
classLevelFrameInfo
=
getFrameInfo
(
sys
.
_getframe
())
moduleLevelFrameInfo
=
getFrameInfo
(
sys
.
_getframe
())
class
FrameInfoTest
(
TestCase
):
classLevelFrameInfo
=
getFrameInfo
(
sys
.
_getframe
())
def
checkModuleInfo
(
self
):
kind
,
module
,
f_locals
,
f_globals
=
moduleLevelFrameInfo
self
.
assertEquals
(
kind
,
"module"
)
for
d
in
module
.
__dict__
,
f_locals
,
f_globals
:
self
.
assert_
(
d
is
globals
())
def
checkClassicClassInfo
(
self
):
kind
,
module
,
f_locals
,
f_globals
=
ClassicClass
.
classLevelFrameInfo
self
.
assertEquals
(
kind
,
"class"
)
self
.
assert_
(
f_locals
is
ClassicClass
.
__dict__
)
# ???
for
d
in
module
.
__dict__
,
f_globals
:
self
.
assert_
(
d
is
globals
())
def
checkNewStyleClassInfo
(
self
):
kind
,
module
,
f_locals
,
f_globals
=
NewStyleClass
.
classLevelFrameInfo
self
.
assertEquals
(
kind
,
"class"
)
for
d
in
module
.
__dict__
,
f_globals
:
self
.
assert_
(
d
is
globals
())
def
checkCallInfo
(
self
):
kind
,
module
,
f_locals
,
f_globals
=
getFrameInfo
(
sys
.
_getframe
())
self
.
assertEquals
(
kind
,
"function call"
)
self
.
assert_
(
f_locals
is
locals
())
# ???
for
d
in
module
.
__dict__
,
f_globals
:
self
.
assert_
(
d
is
globals
())
class
AdviceTests
(
TestCase
):
def
checkOrder
(
self
):
log
=
[]
class
Foo
(
object
):
ping
(
log
,
1
)
ping
(
log
,
2
)
ping
(
log
,
3
)
# Strip the list nesting
for
i
in
1
,
2
,
3
:
self
.
assert_
(
isinstance
(
Foo
,
list
))
Foo
,
=
Foo
self
.
assertEquals
(
log
,
[(
1
,
Foo
),
(
2
,
[
Foo
]),
(
3
,
[[
Foo
]])])
def
XXXcheckOutside
(
self
):
# Disabled because the check does not work with doctest tests.
try
:
ping
([],
1
)
except
SyntaxError
:
pass
else
:
raise
AssertionError
(
"Should have detected advice outside class body"
)
def
checkDoubleType
(
self
):
if
sys
.
hexversion
>=
0x02030000
:
return
# you can't duplicate bases in 2.3
class
aType
(
type
,
type
):
ping
([],
1
)
aType
,
=
aType
self
.
assert_
(
aType
.
__class__
is
type
)
def
checkSingleExplicitMeta
(
self
):
class
M
(
type
):
pass
class
C
(
M
):
__metaclass__
=
M
ping
([],
1
)
C
,
=
C
self
.
assert_
(
C
.
__class__
is
M
)
def
checkMixedMetas
(
self
):
class
M1
(
type
):
pass
class
M2
(
type
):
pass
class
B1
:
__metaclass__
=
M1
class
B2
:
__metaclass__
=
M2
try
:
class
C
(
B1
,
B2
):
ping
([],
1
)
except
TypeError
:
pass
else
:
raise
AssertionError
(
"Should have gotten incompatibility error"
)
class
M3
(
M1
,
M2
):
pass
class
C
(
B1
,
B2
):
__metaclass__
=
M3
ping
([],
1
)
self
.
assert_
(
isinstance
(
C
,
list
))
C
,
=
C
self
.
assert_
(
isinstance
(
C
,
M3
))
def
checkMetaOfClass
(
self
):
class
metameta
(
type
):
pass
class
meta
(
type
):
__metaclass__
=
metameta
self
.
assertEquals
(
determineMetaclass
((
meta
,
type
)),
metameta
)
TestClasses
=
(
AdviceTests
,
FrameInfoTest
)
def
test_suite
():
return
TestSuite
([
makeSuite
(
t
,
'check'
)
for
t
in
TestClasses
])
if
__name__
==
'__main__'
:
unittest
.
main
(
defaultTest
=
test_suite
)
trunk/src/zope/interface/tests/test_declarations.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Test the new API for making and checking interface declarations
$Id$
"""
import
unittest
from
zope.interface
import
*
from
zope.testing.doctestunit
import
DocTestSuite
from
zope.interface
import
Interface
class
I1
(
Interface
):
pass
class
I2
(
Interface
):
pass
class
I3
(
Interface
):
pass
class
I4
(
Interface
):
pass
class
I5
(
Interface
):
pass
class
A
(
object
):
implements
(
I1
)
class
B
(
object
):
implements
(
I2
)
class
C
(
A
,
B
):
implements
(
I3
)
class
COnly
(
A
,
B
):
implementsOnly
(
I3
)
class
COnly_old
(
A
,
B
):
__implemented__
=
I3
class
D
(
COnly
):
implements
(
I5
)
def
test_ObjectSpecification_Simple
():
"""
>>> c = C()
>>> directlyProvides(c, I4)
>>> [i.__name__ for i in providedBy(c)]
['I4', 'I3', 'I1', 'I2']
"""
def
test_ObjectSpecification_Simple_w_only
():
"""
>>> c = COnly()
>>> directlyProvides(c, I4)
>>> [i.__name__ for i in providedBy(c)]
['I4', 'I3']
"""
def
test_ObjectSpecification_Simple_old_style
():
"""
>>> c = COnly_old()
>>> directlyProvides(c, I4)
>>> [i.__name__ for i in providedBy(c)]
['I4', 'I3']
"""
class
Test
(
unittest
.
TestCase
):
# Note that most of the tests are in the doc strings of the
# declarations module.
def
test_backward_compat
(
self
):
class
C1
(
object
):
__implemented__
=
I1
class
C2
(
C1
):
__implemented__
=
I2
,
I5
class
C3
(
C2
):
__implemented__
=
I3
,
C2
.
__implemented__
self
.
assert_
(
C3
.
__implemented__
.
__class__
is
tuple
)
self
.
assertEqual
(
[
i
.
getName
()
for
i
in
providedBy
(
C3
())],
[
'I3'
,
'I2'
,
'I5'
],
)
class
C4
(
C3
):
implements
(
I4
)
self
.
assertEqual
(
[
i
.
getName
()
for
i
in
providedBy
(
C4
())],
[
'I4'
,
'I3'
,
'I2'
,
'I5'
],
)
self
.
assertEqual
(
[
i
.
getName
()
for
i
in
C4
.
__implemented__
],
[
'I4'
,
'I3'
,
'I2'
,
'I5'
],
)
# Note that C3.__implemented__ should now be a sequence of interfaces
self
.
assertEqual
(
[
i
.
getName
()
for
i
in
C3
.
__implemented__
],
[
'I3'
,
'I2'
,
'I5'
],
)
self
.
failIf
(
C3
.
__implemented__
.
__class__
is
tuple
)
def
test_module
(
self
):
import
zope.interface.tests.m1
import
zope.interface.tests.m2
directlyProvides
(
zope
.
interface
.
tests
.
m2
,
zope
.
interface
.
tests
.
m1
.
I1
,
zope
.
interface
.
tests
.
m1
.
I2
,
)
self
.
assertEqual
(
list
(
providedBy
(
zope
.
interface
.
tests
.
m1
)),
list
(
providedBy
(
zope
.
interface
.
tests
.
m2
)),
)
def
test_builtins
(
self
):
# Setup
intspec
=
implementedBy
(
int
)
olddeclared
=
intspec
.
declared
classImplements
(
int
,
I1
)
class
myint
(
int
):
implements
(
I2
)
x
=
42
self
.
assertEqual
([
i
.
getName
()
for
i
in
providedBy
(
x
)],
[
'I1'
])
x
=
myint
(
42
)
directlyProvides
(
x
,
I3
)
self
.
assertEqual
([
i
.
getName
()
for
i
in
providedBy
(
x
)],
[
'I3'
,
'I2'
,
'I1'
])
# cleanup
intspec
.
declared
=
olddeclared
classImplements
(
int
)
x
=
42
self
.
assertEqual
([
i
.
getName
()
for
i
in
providedBy
(
x
)],
[])
def
test_signature_w_no_class_interfaces
():
"""
>>> from zope.interface import *
>>> class C(object):
... pass
>>> c = C()
>>> list(providedBy(c))
[]
>>> class I(Interface):
... pass
>>> directlyProvides(c, I)
>>> list(providedBy(c)) == list(directlyProvidedBy(c))
1
"""
def
test_classImplement_on_deeply_nested_classes
():
"""This test is in response to a bug found, which is why it's a bit
contrived
>>> from zope.interface import *
>>> class B1(object):
... pass
>>> class B2(B1):
... pass
>>> class B3(B2):
... pass
>>> class D(object):
... implements()
>>> class S(B3, D):
... implements()
This failed due to a bug in the code for finding __providedBy__
descriptors for old-style classes.
"""
def
test_pickle_provides_specs
():
"""
>>> from pickle import dumps, loads
>>> a = A()
>>> I2.providedBy(a)
0
>>> directlyProvides(a, I2)
>>> I2.providedBy(a)
1
>>> a2 = loads(dumps(a))
>>> I2.providedBy(a2)
1
"""
def
test_that_we_dont_inherit_class_provides
():
"""
>>> class X(object):
... classProvides(I1)
>>> class Y(X):
... pass
>>> [i.__name__ for i in X.__provides__]
['I1']
>>> Y.__provides__
Traceback (most recent call last):
...
AttributeError: __provides__
"""
def
test_that_we_dont_inherit_provides_optimizations
():
"""
When we make a declaration for a class, we install a __provides__
descriptors that provides a default for instances that don't have
instance-specific declarations:
>>> class A(object):
... implements(I1)
>>> class B(object):
... implements(I2)
>>> [i.__name__ for i in A().__provides__]
['I1']
>>> [i.__name__ for i in B().__provides__]
['I2']
But it's important that we don't use this for subclasses without
declarations. This would cause incorrect results:
>>> class X(A, B):
... pass
>>> X().__provides__
Traceback (most recent call last):
...
AttributeError: __provides__
However, if we "induce" a declaration, by calling implementedBy
(even indirectly through providedBy):
>>> [i.__name__ for i in providedBy(X())]
['I1', 'I2']
then the optimization will work:
>>> [i.__name__ for i in X().__provides__]
['I1', 'I2']
"""
def
test_classProvides_before_implements
():
"""Special descriptor for class __provides__
The descriptor caches the implementedBy info, so that
we can get declarations for objects without instance-specific
interfaces a bit quicker.
For example::
>>> from zope.interface import Interface
>>> class IFooFactory(Interface):
... pass
>>> class IFoo(Interface):
... pass
>>> class C(object):
... classProvides(IFooFactory)
... implements(IFoo)
>>> [i.getName() for i in C.__provides__]
['IFooFactory']
>>> [i.getName() for i in C().__provides__]
['IFoo']
"""
def
test_getting_spec_for_proxied_builtin_class
():
"""
In general, we should be able to get a spec
for a proxied class if someone has declared or
asked for a spec before.
We don't want to depend on proxies in this (zope.interface)
package, but we do want to work with proxies. Proxies have the
effect that a class's __dict__ cannot be gotten. Further, for
built-in classes, we can't save, and thus, cannot get, any class
attributes. We'll emulate this by treating a plain object as a class:
>>> cls = object()
We'll create an implements specification:
>>> import zope.interface.declarations
>>> impl = zope.interface.declarations.Implements(I1, I2)
Now, we'll emulate a declaration for a built-in type by putting
it in BuiltinImplementationSpecifications:
>>> zope.interface.declarations.BuiltinImplementationSpecifications[
... cls] = impl
Now, we should be able to get it back:
>>> implementedBy(cls) is impl
True
"""
def
test_declaration_get
():
"""
We can get definitions from a declaration:
>>> import zope.interface
>>> class I1(zope.interface.Interface):
... a11 = zope.interface.Attribute('a11')
... a12 = zope.interface.Attribute('a12')
>>> class I2(zope.interface.Interface):
... a21 = zope.interface.Attribute('a21')
... a22 = zope.interface.Attribute('a22')
... a12 = zope.interface.Attribute('a212')
>>> class I11(I1):
... a11 = zope.interface.Attribute('a111')
>>> decl = Declaration(I11, I2)
>>> decl.get('a11') is I11.get('a11')
True
>>> decl.get('a12') is I1.get('a12')
True
>>> decl.get('a21') is I2.get('a21')
True
>>> decl.get('a22') is I2.get('a22')
True
>>> decl.get('a')
>>> decl.get('a', 42)
42
We get None even with no interfaces:
>>> decl = Declaration()
>>> decl.get('a11')
>>> decl.get('a11', 42)
42
We get new data if e change interface bases:
>>> decl.__bases__ = I11, I2
>>> decl.get('a11') is I11.get('a11')
True
"""
def
test_suite
():
suite
=
unittest
.
TestSuite
()
suite
.
addTest
(
unittest
.
makeSuite
(
Test
))
suite
.
addTest
(
DocTestSuite
(
"zope.interface.declarations"
))
suite
.
addTest
(
DocTestSuite
())
return
suite
if
__name__
==
'__main__'
:
unittest
.
main
()
trunk/src/zope/interface/tests/test_document.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# 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.1 (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.
#
##############################################################################
"""Documentation tests.
$Id$
"""
from
unittest
import
TestCase
,
main
,
makeSuite
from
zope.interface
import
Interface
,
Attribute
class
Test
(
TestCase
):
def
testBlech
(
self
):
from
zope.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
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'
)
trunk/src/zope/interface/tests/test_element.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Test Element meta-class.
$Id$
"""
import
unittest
from
zope.interface.interface
import
Element
class
TestElement
(
unittest
.
TestCase
):
def
test_taggedValues
(
self
):
"""Test that we can update tagged values of more than one element
"""
e1
=
Element
(
"foo"
)
e2
=
Element
(
"bar"
)
e1
.
setTaggedValue
(
"x"
,
1
)
e2
.
setTaggedValue
(
"x"
,
2
)
self
.
assertEqual
(
e1
.
getTaggedValue
(
"x"
),
1
)
self
.
assertEqual
(
e2
.
getTaggedValue
(
"x"
),
2
)
def
test_suite
():
suite
=
unittest
.
TestSuite
()
suite
.
addTest
(
unittest
.
makeSuite
(
TestElement
))
return
suite
if
__name__
==
'__main__'
:
unittest
.
main
(
defaultTest
=
test_suite
)
trunk/src/zope/interface/tests/test_interface.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# 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.1 (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.
#
##############################################################################
"""Test Interface implementation
$Id$
"""
import
unittest
from
zope.testing.doctestunit
import
DocTestSuite
from
zope.interface.tests.unitfixtures
import
*
# hehehe
from
zope.interface.exceptions
import
BrokenImplementation
,
Invalid
from
zope.interface
import
implementedBy
,
providedBy
from
zope.interface
import
Interface
,
directlyProvides
,
Attribute
class
InterfaceTests
(
unittest
.
TestCase
):
def
testInterfaceSetOnAttributes
(
self
):
self
.
assertEqual
(
FooInterface
[
'foobar'
].
interface
,
FooInterface
)
self
.
assertEqual
(
FooInterface
[
'aMethod'
].
interface
,
FooInterface
)
def
testClassImplements
(
self
):
self
.
assert_
(
IC
.
implementedBy
(
C
))
self
.
assert_
(
I1
.
implementedBy
(
A
))
self
.
assert_
(
I1
.
implementedBy
(
B
))
self
.
assert_
(
not
I1
.
implementedBy
(
C
))
self
.
assert_
(
I1
.
implementedBy
(
D
))
self
.
assert_
(
I1
.
implementedBy
(
E
))
self
.
assert_
(
not
I2
.
implementedBy
(
A
))
self
.
assert_
(
I2
.
implementedBy
(
B
))
self
.
assert_
(
not
I2
.
implementedBy
(
C
))
# No longer after interfacegeddon
# self.assert_(not I2.implementedBy(D))
self
.
assert_
(
not
I2
.
implementedBy
(
E
))
def
testUtil
(
self
):
self
.
assert_
(
IC
in
implementedBy
(
C
))
self
.
assert_
(
I1
in
implementedBy
(
A
))
self
.
assert_
(
not
I1
in
implementedBy
(
C
))
self
.
assert_
(
I2
in
implementedBy
(
B
))
self
.
assert_
(
not
I2
in
implementedBy
(
C
))
self
.
assert_
(
IC
in
providedBy
(
C
()))
self
.
assert_
(
I1
in
providedBy
(
A
()))
self
.
assert_
(
not
I1
in
providedBy
(
C
()))
self
.
assert_
(
I2
in
providedBy
(
B
()))
self
.
assert_
(
not
I2
in
providedBy
(
C
()))
def
testObjectImplements
(
self
):
self
.
assert_
(
IC
.
providedBy
(
C
()))
self
.
assert_
(
I1
.
providedBy
(
A
()))
self
.
assert_
(
I1
.
providedBy
(
B
()))
self
.
assert_
(
not
I1
.
providedBy
(
C
()))
self
.
assert_
(
I1
.
providedBy
(
D
()))
self
.
assert_
(
I1
.
providedBy
(
E
()))
self
.
assert_
(
not
I2
.
providedBy
(
A
()))
self
.
assert_
(
I2
.
providedBy
(
B
()))
self
.
assert_
(
not
I2
.
providedBy
(
C
()))
# Not after interface geddon
# self.assert_(not I2.providedBy(D()))
self
.
assert_
(
not
I2
.
providedBy
(
E
()))
def
testDeferredClass
(
self
):
a
=
A
()
self
.
assertRaises
(
BrokenImplementation
,
a
.
ma
)
def
testInterfaceExtendsInterface
(
self
):
self
.
assert_
(
BazInterface
.
extends
(
BobInterface
))
self
.
assert_
(
BazInterface
.
extends
(
BarInterface
))
self
.
assert_
(
BazInterface
.
extends
(
FunInterface
))
self
.
assert_
(
not
BobInterface
.
extends
(
FunInterface
))
self
.
assert_
(
not
BobInterface
.
extends
(
BarInterface
))
self
.
assert_
(
BarInterface
.
extends
(
FunInterface
))
self
.
assert_
(
not
BarInterface
.
extends
(
BazInterface
))
def
testVerifyImplementation
(
self
):
from
zope.interface.verify
import
verifyClass
self
.
assert_
(
verifyClass
(
FooInterface
,
Foo
))
self
.
assert_
(
Interface
.
providedBy
(
I1
))
def
test_names
(
self
):
names
=
list
(
_I2
.
names
());
names
.
sort
()
self
.
assertEqual
(
names
,
[
'f21'
,
'f22'
,
'f23'
])
names
=
list
(
_I2
.
names
(
all
=
True
));
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
test___getitem__
(
self
):
self
.
assertEqual
(
_I2
[
'f11'
].
__name__
,
'f11'
)
self
.
assertEqual
(
_I2
[
'f22'
].
__name__
,
'f22'
)
self
.
assertEqual
(
_I2
.
get
(
'f33'
,
self
),
self
)
self
.
assertRaises
(
KeyError
,
_I2
.
__getitem__
,
'f33'
)
def
test___contains__
(
self
):
self
.
failUnless
(
'f11'
in
_I2
)
self
.
failIf
(
'f33'
in
_I2
)
def
test___iter__
(
self
):
names
=
list
(
iter
(
_I2
))
names
.
sort
()
self
.
assertEqual
(
names
,
[
'a1'
,
'f11'
,
'f12'
,
'f21'
,
'f22'
,
'f23'
])
def
testAttr
(
self
):
description
=
_I2
.
getDescriptionFor
(
'a1'
)
self
.
assertEqual
(
description
.
__name__
,
'a1'
)
self
.
assertEqual
(
description
.
__doc__
,
'This is an attribute'
)
def
testFunctionAttributes
(
self
):
# Make sure function attributes become tagged values.
meth
=
_I1
[
'f12'
]
self
.
assertEqual
(
meth
.
getTaggedValue
(
'optional'
),
1
)
def
testInvariant
(
self
):
# set up
o
=
InvariantC
()
directlyProvides
(
o
,
IInvariant
)
# a helper
def
errorsEqual
(
self
,
o
,
error_len
,
error_msgs
,
interface
=
None
):
if
interface
is
None
:
interface
=
IInvariant
self
.
assertRaises
(
Invalid
,
interface
.
validateInvariants
,
o
)
e
=
[]
try
:
interface
.
validateInvariants
(
o
,
e
)
except
Invalid
,
error
:
self
.
assertEquals
(
error
.
args
[
0
],
e
)
else
:
self
.
_assert
(
0
)
# validateInvariants should always raise
# Invalid
self
.
assertEquals
(
len
(
e
),
error_len
)
msgs
=
[
error
.
args
[
0
]
for
error
in
e
]
msgs
.
sort
()
for
msg
in
msgs
:
self
.
assertEquals
(
msg
,
error_msgs
.
pop
(
0
))
# the tests
self
.
assertEquals
(
IInvariant
.
getTaggedValue
(
'invariants'
),
[
ifFooThenBar
])
self
.
assertEquals
(
IInvariant
.
validateInvariants
(
o
),
None
)
o
.
bar
=
27
self
.
assertEquals
(
IInvariant
.
validateInvariants
(
o
),
None
)
o
.
foo
=
42
self
.
assertEquals
(
IInvariant
.
validateInvariants
(
o
),
None
)
del
o
.
bar
errorsEqual
(
self
,
o
,
1
,
[
'If Foo, then Bar!'
])
# nested interfaces with invariants:
self
.
assertEquals
(
ISubInvariant
.
getTaggedValue
(
'invariants'
),
[
BarGreaterThanFoo
])
o
=
InvariantC
()
directlyProvides
(
o
,
ISubInvariant
)
o
.
foo
=
42
# even though the interface has changed, we should still only have one
# error.
errorsEqual
(
self
,
o
,
1
,
[
'If Foo, then Bar!'
],
ISubInvariant
)
# however, if we set foo to 0 (Boolean False) and bar to a negative
# number then we'll get the new error
o
.
foo
=
2
o
.
bar
=
1
errorsEqual
(
self
,
o
,
1
,
[
'Please, Boo MUST be greater than Foo!'
],
ISubInvariant
)
# and if we set foo to a positive number and boo to 0, we'll
# get both errors!
o
.
foo
=
1
o
.
bar
=
0
errorsEqual
(
self
,
o
,
2
,
[
'If Foo, then Bar!'
,
'Please, Boo MUST be greater than Foo!'
],
ISubInvariant
)
# for a happy ending, we'll make the invariants happy
o
.
foo
=
1
o
.
bar
=
2
self
.
assertEquals
(
IInvariant
.
validateInvariants
(
o
),
None
)
# woohoo
# now we'll do two invariants on the same interface,
# just to make sure that a small
# multi-invariant interface is at least minimally tested.
o
=
InvariantC
()
directlyProvides
(
o
,
IInvariant
)
o
.
foo
=
42
old_invariants
=
IInvariant
.
getTaggedValue
(
'invariants'
)
invariants
=
old_invariants
[:]
invariants
.
append
(
BarGreaterThanFoo
)
# if you really need to mutate,
# then this would be the way to do it. Probably a bad idea, though. :-)
IInvariant
.
setTaggedValue
(
'invariants'
,
invariants
)
#
# even though the interface has changed, we should still only have one
# error.
errorsEqual
(
self
,
o
,
1
,
[
'If Foo, then Bar!'
])
# however, if we set foo to 0 (Boolean False) and bar to a negative
# number then we'll get the new error
o
.
foo
=
2
o
.
bar
=
1
errorsEqual
(
self
,
o
,
1
,
[
'Please, Boo MUST be greater than Foo!'
])
# and if we set foo to a positive number and boo to 0, we'll
# get both errors!
o
.
foo
=
1
o
.
bar
=
0
errorsEqual
(
self
,
o
,
2
,
[
'If Foo, then Bar!'
,
'Please, Boo MUST be greater than Foo!'
])
# for another happy ending, we'll make the invariants happy again
o
.
foo
=
1
o
.
bar
=
2
self
.
assertEquals
(
IInvariant
.
validateInvariants
(
o
),
None
)
# bliss
# clean up
IInvariant
.
setTaggedValue
(
'invariants'
,
old_invariants
)
def
test___doc___element
(
self
):
class
I
(
Interface
):
"xxx"
self
.
assertEqual
(
I
.
__doc__
,
"xxx"
)
self
.
assertEqual
(
list
(
I
),
[])
class
I
(
Interface
):
"xxx"
__doc__
=
Attribute
(
'the doc'
)
self
.
assertEqual
(
I
.
__doc__
,
""
)
self
.
assertEqual
(
list
(
I
),
[
'__doc__'
])
def
testIssue228
(
self
):
# Test for http://collector.zope.org/Zope3-dev/228
class
I
(
Interface
):
"xxx"
class
Bad
:
__providedBy__
=
None
# Old style classes don't have a '__class__' attribute
self
.
failUnlessRaises
(
AttributeError
,
I
.
providedBy
,
Bad
)
class
_I1
(
Interface
):
a1
=
Attribute
(
"This is an attribute"
)
def
f11
():
pass
def
f12
():
pass
f12
.
optional
=
1
class
_I1_
(
_I1
):
pass
class
_I1__
(
_I1_
):
pass
class
_I2
(
_I1__
):
def
f21
():
pass
def
f22
():
pass
f23
=
f22
def
test_suite
():
from
zope.testing
import
doctest
suite
=
unittest
.
makeSuite
(
InterfaceTests
)
suite
.
addTest
(
doctest
.
DocTestSuite
(
"zope.interface.interface"
))
suite
.
addTest
(
doctest
.
DocFileSuite
(
'../README.txt'
,
globs
=
{
'__name__'
:
'__main__'
},
optionflags
=
doctest
.
NORMALIZE_WHITESPACE
,
))
return
suite
def
main
():
unittest
.
TextTestRunner
().
run
(
test_suite
())
if
__name__
==
"__main__"
:
main
()
trunk/src/zope/interface/tests/test_odd_declarations.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Test interface declarations against ExtensionClass-like classes.
These tests are to make sure we do something sane in the presense of
classic ExtensionClass classes and instances.
$Id$
"""
import
unittest
,
odd
from
zope.interface
import
Interface
,
implements
,
implementsOnly
from
zope.interface
import
directlyProvides
,
providedBy
,
directlyProvidedBy
from
zope.interface
import
classImplements
,
classImplementsOnly
,
implementedBy
class
I1
(
Interface
):
pass
class
I2
(
Interface
):
pass
class
I3
(
Interface
):
pass
class
I31
(
I3
):
pass
class
I4
(
Interface
):
pass
class
I5
(
Interface
):
pass
class
Odd
(
object
):
__metaclass__
=
odd
.
MetaClass
class
B
(
Odd
):
__implemented__
=
I2
# TODO: We are going to need more magic to make classProvides work with odd
# classes. This will work in the next iteration. For now, we'll use
# a different mechanism.
# from zope.interface import classProvides
class
A
(
Odd
):
implements
(
I1
)
class
C
(
A
,
B
):
implements
(
I31
)
class
Test
(
unittest
.
TestCase
):
def
test_ObjectSpecification
(
self
):
c
=
C
()
directlyProvides
(
c
,
I4
)
self
.
assertEqual
([
i
.
getName
()
for
i
in
providedBy
(
c
)],
[
'I4'
,
'I31'
,
'I1'
,
'I2'
]
)
self
.
assertEqual
([
i
.
getName
()
for
i
in
providedBy
(
c
).
flattened
()],
[
'I4'
,
'I31'
,
'I3'
,
'I1'
,
'I2'
,
'Interface'
]
)
self
.
assert_
(
I1
in
providedBy
(
c
))
self
.
failIf
(
I3
in
providedBy
(
c
))
self
.
assert_
(
providedBy
(
c
).
extends
(
I3
))
self
.
assert_
(
providedBy
(
c
).
extends
(
I31
))
self
.
failIf
(
providedBy
(
c
).
extends
(
I5
))
class
COnly
(
A
,
B
):
implementsOnly
(
I31
)
class
D
(
COnly
):
implements
(
I5
)
classImplements
(
D
,
I5
)
c
=
D
()
directlyProvides
(
c
,
I4
)
self
.
assertEqual
([
i
.
getName
()
for
i
in
providedBy
(
c
)],
[
'I4'
,
'I5'
,
'I31'
])
self
.
assertEqual
([
i
.
getName
()
for
i
in
providedBy
(
c
).
flattened
()],
[
'I4'
,
'I5'
,
'I31'
,
'I3'
,
'Interface'
])
self
.
failIf
(
I1
in
providedBy
(
c
))
self
.
failIf
(
I3
in
providedBy
(
c
))
self
.
assert_
(
providedBy
(
c
).
extends
(
I3
))
self
.
failIf
(
providedBy
(
c
).
extends
(
I1
))
self
.
assert_
(
providedBy
(
c
).
extends
(
I31
))
self
.
assert_
(
providedBy
(
c
).
extends
(
I5
))
class
COnly
(
A
,
B
):
__implemented__
=
I31
class
D
(
COnly
):
implements
(
I5
)
classImplements
(
D
,
I5
)
c
=
D
()
directlyProvides
(
c
,
I4
)
self
.
assertEqual
([
i
.
getName
()
for
i
in
providedBy
(
c
)],
[
'I4'
,
'I5'
,
'I31'
])
self
.
assertEqual
([
i
.
getName
()
for
i
in
providedBy
(
c
).
flattened
()],
[
'I4'
,
'I5'
,
'I31'
,
'I3'
,
'Interface'
])
self
.
failIf
(
I1
in
providedBy
(
c
))
self
.
failIf
(
I3
in
providedBy
(
c
))
self
.
assert_
(
providedBy
(
c
).
extends
(
I3
))
self
.
failIf
(
providedBy
(
c
).
extends
(
I1
))
self
.
assert_
(
providedBy
(
c
).
extends
(
I31
))
self
.
assert_
(
providedBy
(
c
).
extends
(
I5
))
def
test_classImplements
(
self
):
class
A
(
Odd
):
implements
(
I3
)
class
B
(
Odd
):
implements
(
I4
)
class
C
(
A
,
B
):
pass
classImplements
(
C
,
I1
,
I2
)
self
.
assertEqual
([
i
.
getName
()
for
i
in
implementedBy
(
C
)],
[
'I1'
,
'I2'
,
'I3'
,
'I4'
])
classImplements
(
C
,
I5
)
self
.
assertEqual
([
i
.
getName
()
for
i
in
implementedBy
(
C
)],
[
'I1'
,
'I2'
,
'I5'
,
'I3'
,
'I4'
])
def
test_classImplementsOnly
(
self
):
class
A
(
Odd
):
implements
(
I3
)
class
B
(
Odd
):
implements
(
I4
)
class
C
(
A
,
B
):
pass
classImplementsOnly
(
C
,
I1
,
I2
)
self
.
assertEqual
([
i
.
__name__
for
i
in
implementedBy
(
C
)],
[
'I1'
,
'I2'
])
def
test_directlyProvides
(
self
):
class
IA1
(
Interface
):
pass
class
IA2
(
Interface
):
pass
class
IB
(
Interface
):
pass
class
IC
(
Interface
):
pass
class
A
(
Odd
):
implements
(
IA1
,
IA2
)
class
B
(
Odd
):
implements
(
IB
)
class
C
(
A
,
B
):
implements
(
IC
)
ob
=
C
()
directlyProvides
(
ob
,
I1
,
I2
)
self
.
assert_
(
I1
in
providedBy
(
ob
))
self
.
assert_
(
I2
in
providedBy
(
ob
))
self
.
assert_
(
IA1
in
providedBy
(
ob
))
self
.
assert_
(
IA2
in
providedBy
(
ob
))
self
.
assert_
(
IB
in
providedBy
(
ob
))
self
.
assert_
(
IC
in
providedBy
(
ob
))
directlyProvides
(
ob
,
directlyProvidedBy
(
ob
)
-
I2
)
self
.
assert_
(
I1
in
providedBy
(
ob
))
self
.
failIf
(
I2
in
providedBy
(
ob
))
self
.
failIf
(
I2
in
providedBy
(
ob
))
directlyProvides
(
ob
,
directlyProvidedBy
(
ob
),
I2
)
self
.
assert_
(
I2
in
providedBy
(
ob
))
def
test_directlyProvides_fails_for_odd_class
(
self
):
self
.
assertRaises
(
TypeError
,
directlyProvides
,
C
,
I5
)
# XXX see above
def
XXX_test_classProvides_fails_for_odd_class
(
self
):
try
:
class
A
(
Odd
):
classProvides
(
I1
)
except
TypeError
:
pass
# Sucess
self
.
assert_
(
False
,
"Shouldn't be able to use directlyProvides on odd class."
)
def
test_implementedBy
(
self
):
class
I2
(
I1
):
pass
class
C1
(
Odd
):
implements
(
I2
)
class
C2
(
C1
):
implements
(
I3
)
self
.
assertEqual
([
i
.
getName
()
for
i
in
implementedBy
(
C2
)],
[
'I3'
,
'I2'
])
def
test_suite
():
suite
=
unittest
.
TestSuite
()
suite
.
addTest
(
unittest
.
makeSuite
(
Test
))
return
suite
if
__name__
==
'__main__'
:
unittest
.
main
()
trunk/src/zope/interface/tests/test_sorting.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# 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.1 (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.
#
##############################################################################
"""Test interface sorting
$Id$
"""
from
unittest
import
TestCase
,
TestSuite
,
main
,
makeSuite
from
zope.interface
import
Interface
class
I1
(
Interface
):
pass
class
I2
(
I1
):
pass
class
I3
(
I1
):
pass
class
I4
(
Interface
):
pass
class
I5
(
I4
):
pass
class
I6
(
I2
):
pass
class
Test
(
TestCase
):
def
test
(
self
):
l
=
[
I1
,
I3
,
I5
,
I6
,
I4
,
I2
]
l
.
sort
()
self
.
assertEqual
(
l
,
[
I1
,
I2
,
I3
,
I4
,
I5
,
I6
])
def
test_w_None
(
self
):
l
=
[
I1
,
None
,
I3
,
I5
,
None
,
I6
,
I4
,
I2
]
l
.
sort
()
self
.
assertEqual
(
l
,
[
I1
,
I2
,
I3
,
I4
,
I5
,
I6
,
None
,
None
])
def
test_suite
():
return
TestSuite
((
makeSuite
(
Test
),
))
if
__name__
==
'__main__'
:
main
(
defaultTest
=
'test_suite'
)
trunk/src/zope/interface/tests/test_verify.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# 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.1 (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 Verify tests
$Id$
"""
from
zope.interface
import
Interface
,
implements
,
classImplements
,
Attribute
from
zope.interface.verify
import
verifyClass
,
verifyObject
from
zope.interface.exceptions
import
DoesNotImplement
,
BrokenImplementation
from
zope.interface.exceptions
import
BrokenMethodImplementation
import
unittest
class
Test
(
unittest
.
TestCase
):
def
testNotImplemented
(
self
):
class
C
(
object
):
pass
class
I
(
Interface
):
pass
self
.
assertRaises
(
DoesNotImplement
,
verifyClass
,
I
,
C
)
classImplements
(
C
,
I
)
verifyClass
(
I
,
C
)
def
testMissingAttr
(
self
):
class
I
(
Interface
):
def
f
():
pass
class
C
(
object
):
implements
(
I
)
self
.
assertRaises
(
BrokenImplementation
,
verifyClass
,
I
,
C
)
C
.
f
=
lambda
self
:
None
verifyClass
(
I
,
C
)
def
testMissingAttr_with_Extended_Interface
(
self
):
class
II
(
Interface
):
def
f
():
pass
class
I
(
II
):
pass
class
C
(
object
):
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
(
object
):
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
(
object
):
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
(
object
):
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
(
object
):
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
zope.interface.tests.ifoo
import
IFoo
from
zope.interface.tests
import
dummy
verifyObject
(
IFoo
,
dummy
)
def
testMethodForAttr
(
self
):
class
IFoo
(
Interface
):
foo
=
Attribute
(
"The foo Attribute"
)
class
Foo
:
implements
(
IFoo
)
def
foo
(
self
):
pass
verifyClass
(
IFoo
,
Foo
)
def
testNonMethodForMethod
(
self
):
class
IBar
(
Interface
):
def
foo
():
pass
class
Bar
:
implements
(
IBar
)
foo
=
1
self
.
assertRaises
(
BrokenMethodImplementation
,
verifyClass
,
IBar
,
Bar
)
def
test_suite
():
loader
=
unittest
.
TestLoader
()
return
loader
.
loadTestsFromTestCase
(
Test
)
if
__name__
==
'__main__'
:
unittest
.
TextTestRunner
().
run
(
test_suite
())
trunk/src/zope/interface/tests/unitfixtures.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# 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.1 (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.
#
##############################################################################
"""Unit Test Fixtures
$Id$
"""
from
zope.interface
import
Interface
,
invariant
from
zope.interface.interface
import
Attribute
from
zope.interface.exceptions
import
Invalid
class
mytest
(
Interface
):
pass
class
C
(
object
):
def
m1
(
self
,
a
,
b
):
"return 1"
return
1
def
m2
(
self
,
a
,
b
):
"return 2"
return
2
# testInstancesOfClassImplements
# YAGNI IC=Interface.impliedInterface(C)
class
IC
(
Interface
):
def
m1
(
a
,
b
):
"return 1"
def
m2
(
a
,
b
):
"return 2"
C
.
__implemented__
=
IC
class
I1
(
Interface
):
def
ma
():
"blah"
class
I2
(
I1
):
pass
class
I3
(
Interface
):
pass
class
I4
(
Interface
):
pass
class
A
(
I1
.
deferred
()):
__implemented__
=
I1
class
B
(
object
):
__implemented__
=
I2
,
I3
class
D
(
A
,
B
):
pass
class
E
(
A
,
B
):
__implemented__
=
A
.
__implemented__
,
C
.
__implemented__
class
FooInterface
(
Interface
):
""" This is an Abstract Base Class """
foobar
=
Attribute
(
"fuzzed over beyond all recognition"
)
def
aMethod
(
foo
,
bar
,
bingo
):
""" This is aMethod """
def
anotherMethod
(
foo
=
6
,
bar
=
"where you get sloshed"
,
bingo
=
(
1
,
3
,)):
""" This is anotherMethod """
def
wammy
(
zip
,
*
argues
):
""" yadda yadda """
def
useless
(
**
keywords
):
""" useless code is fun! """
class
Foo
(
object
):
""" A concrete class """
__implemented__
=
FooInterface
,
foobar
=
"yeah"
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!"
foo_instance
=
Foo
()
class
Blah
(
object
):
pass
new
=
Interface
.
__class__
FunInterface
=
new
(
'FunInterface'
)
BarInterface
=
new
(
'BarInterface'
,
[
FunInterface
])
BobInterface
=
new
(
'BobInterface'
)
BazInterface
=
new
(
'BazInterface'
,
[
BobInterface
,
BarInterface
])
# fixtures for invariant tests
def
ifFooThenBar
(
obj
):
if
getattr
(
obj
,
'foo'
,
None
)
and
not
getattr
(
obj
,
'bar'
,
None
):
raise
Invalid
(
'If Foo, then Bar!'
)
class
IInvariant
(
Interface
):
foo
=
Attribute
(
'foo'
)
bar
=
Attribute
(
'bar; must eval to Boolean True if foo does'
)
invariant
(
ifFooThenBar
)
def
BarGreaterThanFoo
(
obj
):
foo
=
getattr
(
obj
,
'foo'
,
None
)
bar
=
getattr
(
obj
,
'bar'
,
None
)
if
foo
is
not
None
and
isinstance
(
foo
,
type
(
bar
)):
# type checking should be handled elsewhere (like, say,
# schema); these invariants should be intra-interface
# constraints. This is a hacky way to do it, maybe, but you
# get the idea
if
not
bar
>
foo
:
raise
Invalid
(
'Please, Boo MUST be greater than Foo!'
)
class
ISubInvariant
(
IInvariant
):
invariant
(
BarGreaterThanFoo
)
class
InvariantC
(
object
):
pass
trunk/src/zope/interface/verify.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# 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.1 (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.
#
##############################################################################
"""Verify interface implementations
$Id$
"""
from
zope.interface.exceptions
import
BrokenImplementation
,
DoesNotImplement
from
zope.interface.exceptions
import
BrokenMethodImplementation
from
types
import
FunctionType
,
MethodType
from
zope.interface.interface
import
fromMethod
,
fromFunction
,
Method
# This will be monkey-patched when running under Zope 2, so leave this
# here:
MethodTypes
=
(
MethodType
,
)
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
==
'c'
:
tester
=
iface
.
implementedBy
else
:
tester
=
iface
.
providedBy
if
not
tentative
and
not
tester
(
candidate
):
raise
DoesNotImplement
(
iface
)
# Here the `desc` is either an `Attribute` or `Method` instance
for
name
,
desc
in
iface
.
namesAndDescriptions
(
1
):
if
not
hasattr
(
candidate
,
name
):
if
(
not
isinstance
(
desc
,
Method
))
and
vtype
==
'c'
:
# We can't verify non-methods on classes, since the
# class may provide attrs in it's __init__.
continue
raise
BrokenImplementation
(
iface
,
name
)
attr
=
getattr
(
candidate
,
name
)
if
not
isinstance
(
desc
,
Method
):
# If it's not a method, there's nothing else we can test
continue
if
isinstance
(
attr
,
FunctionType
):
# should never get here, since classes should not provide functions
meth
=
fromFunction
(
attr
,
iface
,
name
=
name
)
elif
(
isinstance
(
attr
,
MethodTypes
)
and
type
(
attr
.
im_func
)
is
FunctionType
):
meth
=
fromMethod
(
attr
,
iface
,
name
)
else
:
if
not
callable
(
attr
):
raise
BrokenMethodImplementation
(
name
,
"Not a method"
)
# sigh, it's callable, but we don't know how to intrspect it, so
# we have to give it a pass.
continue
# Make sure that the required and implemented method signatures are
# the same.
desc
=
desc
.
getSignatureInfo
()
meth
=
meth
.
getSignatureInfo
()
mess
=
_incompat
(
desc
,
meth
)
if
mess
:
raise
BrokenMethodImplementation
(
name
,
mess
)
return
True
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"
trunk/src/zope/testing/DEPENDENCIES.cfg
0 → 100644
View file @
a3c091cb
zope.exceptions
trunk/src/zope/testing/__init__.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# 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.1 (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.
#
##############################################################################
"""Set up testing environment
$Id$
"""
import
os
def
patchTracebackModule
():
"""Use the ExceptionFormatter to show more info in tracebacks.
"""
from
zope.exceptions.exceptionformatter
import
format_exception
import
traceback
traceback
.
format_exception
=
format_exception
# Don't use the new exception formatter by default, since it
# doesn't show filenames.
if
os
.
environ
.
get
(
'NEW_ZOPE_EXCEPTION_FORMATTER'
,
0
):
patchTracebackModule
()
trunk/src/zope/testing/cleanup.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# 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.1 (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 standard cleanup registry
Unit tests that change global data should include the CleanUp base
class, which provides simpler setUp and tearDown methods that call
global-data cleanup routines::
class Test(CleanUp, unittest.TestCase):
....
If custom setUp or tearDown are needed, then the base routines should
be called, as in::
def tearDown(self):
super(Test, self).tearDown()
....
Cleanup routines for global data should be registered by passing them to
addCleanup::
addCleanUp(pigRegistry._clear)
$Id$
"""
_cleanups
=
[]
def
addCleanUp
(
func
,
args
=
(),
kw
=
{}):
"""Register a cleanup routines
Pass a function to be called to cleanup global data.
Optional argument tuple and keyword arguments may be passed.
"""
_cleanups
.
append
((
func
,
args
,
kw
))
class
CleanUp
(
object
):
"""Mix-in class providing clean-up setUp and tearDown routines."""
def
cleanUp
(
self
):
"""Clean up global data."""
cleanUp
()
setUp
=
tearDown
=
cleanUp
def
cleanUp
():
"""Clean up global data."""
for
func
,
args
,
kw
in
_cleanups
:
func
(
*
args
,
**
kw
)
setUp
=
tearDown
=
cleanUp
trunk/src/zope/testing/doctest.py
0 → 100644
View file @
a3c091cb
This source diff could not be displayed because it is too large. You can
view the blob
instead.
trunk/src/zope/testing/doctestunit.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Extension to use doctest tests as unit tests
This module provides a DocTestSuite contructor for converting doctest
tests to unit tests.
$Id$
"""
from
doctest
import
DocFileSuite
,
DocTestSuite
from
doctest
import
debug_src
,
debug
def
pprint
():
from
pprint
import
PrettyPrinter
def
pprint
(
ob
,
**
opts
):
if
'width'
not
in
opts
:
opts
[
'width'
]
=
1
return
PrettyPrinter
(
**
opts
).
pprint
(
ob
)
return
pprint
pprint
=
pprint
()
trunk/src/zope/testing/formparser.py
0 → 100644
View file @
a3c091cb
"""HTML parser that extracts form information.
This is intended to support functional tests that need to extract
information from HTML forms returned by the publisher.
See *formparser.txt* for documentation.
"""
__docformat__
=
"reStructuredText"
import
HTMLParser
import
urlparse
def
parse
(
data
,
base
=
None
):
"""Return a form collection parsed from `data`.
`base` should be the URL from which `data` was retrieved.
"""
parser
=
FormParser
(
data
,
base
)
return
parser
.
parse
()
class
FormParser
(
object
):
def
__init__
(
self
,
data
,
base
=
None
):
self
.
data
=
data
self
.
base
=
base
self
.
_parser
=
HTMLParser
.
HTMLParser
()
self
.
_parser
.
handle_data
=
self
.
_handle_data
self
.
_parser
.
handle_endtag
=
self
.
_handle_endtag
self
.
_parser
.
handle_starttag
=
self
.
_handle_starttag
self
.
_parser
.
handle_startendtag
=
self
.
_handle_starttag
self
.
_buffer
=
[]
self
.
current
=
None
self
.
forms
=
FormCollection
()
def
parse
(
self
):
"""Parse the document, returning the collection of forms."""
self
.
_parser
.
feed
(
self
.
data
)
self
.
_parser
.
close
()
return
self
.
forms
# HTMLParser handlers
def
_handle_data
(
self
,
data
):
self
.
_buffer
.
append
(
data
)
def
_handle_endtag
(
self
,
tag
):
if
tag
==
"textarea"
:
self
.
textarea
.
value
=
""
.
join
(
self
.
_buffer
)
self
.
textarea
=
None
elif
tag
==
"select"
:
self
.
select
=
None
elif
tag
==
"option"
:
option
=
self
.
select
.
options
[
-
1
]
label
=
""
.
join
(
self
.
_buffer
)
if
not
option
.
label
:
option
.
label
=
label
if
not
option
.
value
:
option
.
value
=
label
if
option
.
selected
:
if
self
.
select
.
multiple
:
self
.
select
.
value
.
append
(
option
.
value
)
else
:
self
.
select
.
value
=
option
.
value
def
_handle_starttag
(
self
,
tag
,
attrs
):
del
self
.
_buffer
[:]
d
=
{}
for
name
,
value
in
attrs
:
d
[
name
]
=
value
name
=
d
.
get
(
"name"
)
id
=
d
.
get
(
"id"
)
or
d
.
get
(
"xml:id"
)
if
tag
==
"form"
:
method
=
kwattr
(
d
,
"method"
,
"get"
)
action
=
d
.
get
(
"action"
,
""
).
strip
()
or
None
if
self
.
base
and
action
:
action
=
urlparse
.
urljoin
(
self
.
base
,
action
)
enctype
=
kwattr
(
d
,
"enctype"
,
"application/x-www-form-urlencoded"
)
self
.
current
=
Form
(
name
,
id
,
method
,
action
,
enctype
)
self
.
forms
.
append
(
self
.
current
)
elif
tag
==
"input"
:
type
=
kwattr
(
d
,
"type"
,
"text"
)
checked
=
"checked"
in
d
disabled
=
"disabled"
in
d
readonly
=
"readonly"
in
d
src
=
d
.
get
(
"src"
,
""
).
strip
()
or
None
if
self
.
base
and
src
:
src
=
urlparse
.
urljoin
(
self
.
base
,
src
)
value
=
d
.
get
(
"value"
)
size
=
intattr
(
d
,
"size"
)
maxlength
=
intattr
(
d
,
"maxlength"
)
self
.
current
[
name
]
=
Input
(
name
,
id
,
type
,
value
,
checked
,
disabled
,
readonly
,
src
,
size
,
maxlength
)
elif
tag
==
"button"
:
pass
elif
tag
==
"textarea"
:
disabled
=
"disabled"
in
d
readonly
=
"readonly"
in
d
self
.
textarea
=
Input
(
name
,
id
,
"textarea"
,
None
,
None
,
disabled
,
readonly
,
None
,
None
,
None
)
self
.
textarea
.
rows
=
intattr
(
d
,
"rows"
)
self
.
textarea
.
cols
=
intattr
(
d
,
"cols"
)
self
.
current
[
name
]
=
self
.
textarea
# The value will be set when the </textarea> is seen.
elif
tag
==
"base"
:
href
=
d
.
get
(
"href"
,
""
).
strip
()
if
href
and
self
.
base
:
href
=
urlparse
.
urljoin
(
self
.
base
,
href
)
self
.
base
=
href
elif
tag
==
"select"
:
disabled
=
"disabled"
in
d
multiple
=
"multiple"
in
d
size
=
intattr
(
d
,
"size"
)
self
.
select
=
Select
(
name
,
id
,
disabled
,
multiple
,
size
)
self
.
current
[
name
]
=
self
.
select
elif
tag
==
"option"
:
disabled
=
"disabled"
in
d
selected
=
"selected"
in
d
value
=
d
.
get
(
"value"
)
label
=
d
.
get
(
"label"
)
option
=
Option
(
id
,
value
,
selected
,
label
,
disabled
)
self
.
select
.
options
.
append
(
option
)
def
kwattr
(
d
,
name
,
default
=
None
):
"""Return attribute, converted to lowercase."""
v
=
d
.
get
(
name
,
default
)
if
v
!=
default
and
v
is
not
None
:
v
=
v
.
strip
().
lower
()
v
=
v
or
default
return
v
def
intattr
(
d
,
name
):
"""Return attribute as an integer, or None."""
if
name
in
d
:
v
=
d
[
name
].
strip
()
return
int
(
v
)
else
:
return
None
class
FormCollection
(
list
):
"""Collection of all forms from a page."""
def
__getattr__
(
self
,
name
):
for
form
in
self
:
if
form
.
name
==
name
:
return
form
raise
AttributeError
,
name
class
Form
(
dict
):
"""A specific form within a page."""
def
__init__
(
self
,
name
,
id
,
method
,
action
,
enctype
):
super
(
Form
,
self
).
__init__
()
self
.
name
=
name
self
.
id
=
id
self
.
method
=
method
self
.
action
=
action
self
.
enctype
=
enctype
class
Input
(
object
):
"""Input element."""
rows
=
None
cols
=
None
def
__init__
(
self
,
name
,
id
,
type
,
value
,
checked
,
disabled
,
readonly
,
src
,
size
,
maxlength
):
super
(
Input
,
self
).
__init__
()
self
.
name
=
name
self
.
id
=
id
self
.
type
=
type
self
.
value
=
value
self
.
checked
=
checked
self
.
disabled
=
disabled
self
.
readonly
=
readonly
self
.
src
=
src
self
.
size
=
size
self
.
maxlength
=
maxlength
class
Select
(
Input
):
"""Select element."""
def
__init__
(
self
,
name
,
id
,
disabled
,
multiple
,
size
):
super
(
Select
,
self
).
__init__
(
name
,
id
,
"select"
,
None
,
None
,
disabled
,
None
,
None
,
size
,
None
)
self
.
options
=
[]
self
.
multiple
=
multiple
if
multiple
:
self
.
value
=
[]
class
Option
(
object
):
"""Individual value representation for a select element."""
def
__init__
(
self
,
id
,
value
,
selected
,
label
,
disabled
):
super
(
Option
,
self
).
__init__
()
self
.
id
=
id
self
.
value
=
value
self
.
selected
=
selected
self
.
label
=
label
self
.
disabled
=
disabled
trunk/src/zope/testing/formparser.txt
0 → 100644
View file @
a3c091cb
==================
Parsing HTML Forms
==================
Sometimes in functional tests, information from a generated form must
be extracted in order to re-submit it as part of a subsequent request.
The `zope.testing.formparser` module can be used for this purpose.
The scanner is implemented using the `FormParser` class. The
constructor arguments are the page data containing the form and
(optionally) the URL from which the page was retrieved:
>>> import zope.testing.formparser
>>> page_text = '''\
... <html><body>
... <form name="form1" action="/cgi-bin/foobar.py" method="POST">
... <input type="hidden" name="f1" value="today" />
... <input type="submit" name="do-it-now" value="Go for it!" />
... <input type="IMAGE" name="not-really" value="Don't."
... src="dont.png" />
... <select name="pick-two" size="3" multiple>
... <option value="one" selected>First</option>
... <option value="two" label="Second">Another</option>
... <optgroup>
... <option value="three">Third</option>
... <option selected="selected">Fourth</option>
... </optgroup>
... </select>
... </form>
...
... Just for fun, a second form, after specifying a base:
... <base href="http://www.example.com/base/" />
... <form action = 'sproing/sprung.html' enctype="multipart/form">
... <textarea name="sometext" rows="5">Some text.</textarea>
... <input type="Image" name="action" value="Do something."
... src="else.png" />
... </form>
... </body></html>
... '''
>>> parser = zope.testing.formparser.FormParser(page_text)
>>> forms = parser.parse()
>>> len(forms)
2
>>> forms.form1 is forms[0]
True
>>> forms.form1 is forms[1]
False
More often, the `parse()` convenience function is all that's needed:
>>> forms = zope.testing.formparser.parse(
... page_text, "http://cgi.example.com/somewhere/form.html")
>>> len(forms)
2
>>> forms.form1 is forms[0]
True
>>> forms.form1 is forms[1]
False
Once we have the form we're interested in, we can check form
attributes and individual field values:
>>> form = forms.form1
>>> form.enctype
'application/x-www-form-urlencoded'
>>> form.method
'post'
>>> keys = form.keys()
>>> keys.sort()
>>> keys
['do-it-now', 'f1', 'not-really', 'pick-two']
>>> not_really = form["not-really"]
>>> not_really.type
'image'
>>> not_really.value
"Don't."
>>> not_really.readonly
False
>>> not_really.disabled
False
Note that relative URLs are converted to absolute URLs based on the
``<base>`` element (if present) or using the base passed in to the
constructor.
>>> form.action
'http://cgi.example.com/cgi-bin/foobar.py'
>>> not_really.src
'http://cgi.example.com/somewhere/dont.png'
>>> forms[1].action
'http://www.example.com/base/sproing/sprung.html'
>>> forms[1]["action"].src
'http://www.example.com/base/else.png'
The ``<textarea>`` element provides some additional attributes:
>>> ta = forms[1]["sometext"]
>>> print ta.rows
5
>>> print ta.cols
None
>>> ta.value
'Some text.'
The ``<select>`` element provides access to the options as well:
>>> select = form["pick-two"]
>>> select.multiple
True
>>> select.size
3
>>> select.type
'select'
>>> select.value
['one', 'Fourth']
>>> options = select.options
>>> len(options)
4
>>> [opt.label for opt in options]
['First', 'Second', 'Third', 'Fourth']
>>> [opt.value for opt in options]
['one', 'two', 'three', 'Fourth']
trunk/src/zope/testing/loggingsupport.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Support for testing logging code
If you want to test that your code generates proper log output, you
can create and install a handler that collects output:
>>> handler = InstalledHandler('foo.bar')
The handler is installed into loggers for all of the names passed. In
addition, the logger level is set to 1, which means, log
everything. If you want to log less than everything, you can provide a
level keyword argument. The level setting effects only the named
loggers.
Then, any log output is collected in the handler:
>>> logging.getLogger('foo.bar').exception('eek')
>>> logging.getLogger('foo.bar').info('blah blah')
>>> for record in handler.records:
... print record.name, record.levelname
... print ' ', record.getMessage()
foo.bar ERROR
eek
foo.bar INFO
blah blah
A similar effect can be gotten by just printing the handler:
>>> print handler
foo.bar ERROR
eek
foo.bar INFO
blah blah
After checking the log output, you need to uninstall the handler:
>>> handler.uninstall()
At which point, the handler won't get any more log output.
Let's clear the handler:
>>> handler.clear()
>>> handler.records
[]
And then log something:
>>> logging.getLogger('foo.bar').info('blah')
and, sure enough, we still have no output:
>>> handler.records
[]
$Id$
"""
import
logging
class
Handler
(
logging
.
Handler
):
def
__init__
(
self
,
*
names
,
**
kw
):
logging
.
Handler
.
__init__
(
self
)
self
.
names
=
names
self
.
records
=
[]
self
.
setLoggerLevel
(
**
kw
)
def
setLoggerLevel
(
self
,
level
=
1
):
self
.
level
=
level
self
.
oldlevels
=
{}
def
emit
(
self
,
record
):
self
.
records
.
append
(
record
)
def
clear
(
self
):
del
self
.
records
[:]
def
install
(
self
):
for
name
in
self
.
names
:
logger
=
logging
.
getLogger
(
name
)
self
.
oldlevels
[
name
]
=
logger
.
level
logger
.
setLevel
(
self
.
level
)
logger
.
addHandler
(
self
)
def
uninstall
(
self
):
for
name
in
self
.
names
:
logger
=
logging
.
getLogger
(
name
)
logger
.
setLevel
(
self
.
oldlevels
[
name
])
logger
.
removeHandler
(
self
)
def
__str__
(
self
):
return
'
\
n
'
.
join
(
[(
"%s %s
\
n
%s"
%
(
record
.
name
,
record
.
levelname
,
'
\
n
'
.
join
([
line
for
line
in
record
.
getMessage
().
split
(
'
\
n
'
)
if
line
.
strip
()])
)
)
for
record
in
self
.
records
]
)
class
InstalledHandler
(
Handler
):
def
__init__
(
self
,
*
names
):
Handler
.
__init__
(
self
,
*
names
)
self
.
install
()
trunk/src/zope/testing/loghandler.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""logging handler for tests that check logging output.
$Id$
"""
import
logging
class
Handler
(
logging
.
Handler
):
"""Handler for use with unittest.TestCase objects.
The handler takes a TestCase instance as a constructor argument.
It can be registered with one or more loggers and collects log
records they generate.
The assertLogsMessage() and failIfLogsMessage() methods can be
used to check the logger output and causes the test to fail as
appropriate.
"""
def
__init__
(
self
,
testcase
,
propagate
=
False
):
logging
.
Handler
.
__init__
(
self
)
self
.
records
=
[]
# loggers stores (logger, propagate) tuples
self
.
loggers
=
[]
self
.
closed
=
False
self
.
propagate
=
propagate
self
.
testcase
=
testcase
def
close
(
self
):
"""Remove handler from any loggers it was added to."""
if
self
.
closed
:
return
for
logger
,
propagate
in
self
.
loggers
:
logger
.
removeHandler
(
self
)
logger
.
propagate
=
propagate
self
.
closed
=
True
def
add
(
self
,
name
):
"""Add handler to logger named name."""
logger
=
logging
.
getLogger
(
name
)
old_prop
=
logger
.
propagate
logger
.
addHandler
(
self
)
if
self
.
propagate
:
logger
.
propagate
=
1
else
:
logger
.
propagate
=
0
self
.
loggers
.
append
((
logger
,
old_prop
))
def
emit
(
self
,
record
):
self
.
records
.
append
(
record
)
def
assertLogsMessage
(
self
,
msg
,
level
=
None
):
for
r
in
self
.
records
:
if
r
.
getMessage
()
==
msg
:
if
level
is
not
None
and
r
.
levelno
==
level
:
return
msg
=
"No log message contained %r"
%
msg
if
level
is
not
None
:
msg
+=
" at level %d"
%
level
self
.
testcase
.
fail
(
msg
)
def
failIfLogsMessage
(
self
,
msg
):
for
r
in
self
.
records
:
if
r
.
getMessage
()
==
msg
:
self
.
testcase
.
fail
(
"Found log message %r"
%
msg
)
trunk/src/zope/testing/module.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Fake module support
$Id$
"""
import
sys
class
FakeModule
:
def
__init__
(
self
,
dict
):
self
.
__dict
=
dict
def
__getattr__
(
self
,
name
):
try
:
return
self
.
__dict
[
name
]
except
KeyError
:
raise
AttributeError
,
name
def
setUp
(
test
,
name
=
'README.txt'
):
dict
=
test
.
globs
dict
[
'__name__'
]
=
name
sys
.
modules
[
name
]
=
FakeModule
(
dict
)
def
tearDown
(
test
,
name
=
'README.txt'
):
del
sys
.
modules
[
name
]
trunk/src/zope/testing/tests.py
0 → 100644
View file @
a3c091cb
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Tests for the testing framework.
$Id$
"""
import
unittest
from
zope.testing.doctestunit
import
DocTestSuite
,
DocFileSuite
def
test_suite
():
return
unittest
.
TestSuite
((
DocFileSuite
(
'formparser.txt'
),
DocTestSuite
(
'zope.testing.loggingsupport'
),
))
if
__name__
==
'__main__'
:
unittest
.
main
(
defaultTest
=
'test_suite'
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment