[Zope3-checkins] CVS: Zope3/src/zope/interface - ro.py:1.1.2.1
surrogate.py:1.1.2.1 declarations.py:1.17.10.1 interface.py:1.13.4.1
Jim Fulton
jim at zope.com
Fri Oct 10 07:17:09 EDT 2003
Update of /cvs-repository/Zope3/src/zope/interface
In directory cvs.zope.org:/tmp/cvs-serv8781/zope/interface
Modified Files:
Tag: adaptergeddon-branch
declarations.py interface.py
Added Files:
Tag: adaptergeddon-branch
ro.py surrogate.py
Log Message:
Committing some initial work on the adaptergeddon branch to facilitate
colaboration.
=== Added File Zope3/src/zope/interface/ro.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Compute a resolution order for an object and it's bases
$Id: ro.py,v 1.1.2.1 2003/10/10 11:16:38 jim Exp $
"""
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
=== Added File Zope3/src/zope/interface/surrogate.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Surrogate-interface registry implementation
$Id: surrogate.py,v 1.1.2.1 2003/10/10 11:16:38 jim Exp $
"""
# Optimization notes
# For each surrogate, we store a dict {key -> factories}
# where key is one of
# provides,
# provided, name
# provided, name, order
# Currently, we stora all of the keys in a single dict:
# consider named adapters. Each key is a two-tuple.
# That's 5 extra words per key for the tuple.
# suppose that the dict was of the form:
# {name -> {provided -> factroies}
# This saves n-1 * 5 words, where n is the number of entries for a
# given name. There is the overhead of an extra dictionary per
# word. Given that many interfaces only have a single entry, this
# extra overhead could be significant, however, there are some extra
# opportunities for savings. See below. There are two dict lookups,
# but, possibly, each lookup is cheaper, because the cache and
# comparisons are cheaper.
# It will often be the case that an implied dict (for a required
# interface and name (and order), will be equal to the implied dict
# for one of the interfaces bases. This will happen, for example, when
# there aren't adapters registered for an interface and the interface
# has one base. In a case like this, the interface can share the
# implied dict with the base.
# Additional savings will be permitted for local surrogates. For
# example, it will often be the case that the implied dictionary
# for a local surrogate will be the same as the implied dictionary for
# it's base (less local) surogate. In this case too, the dictionary
# can be saved.
import weakref
from zope.interface.ro import ro
from zope.interface.declarations import providedBy
class Surrogate(object):
def __init__(self, interface, registry):
self._adapters = {}
self._implied = {}
self._interface = interface
interface.addDependent(self)
self._registry = registry
self.dependents = weakref.WeakKeyDictionary()
self.__bases__ = [registry.get(base) for base in interface.__bases__]
for base in self.__bases__:
base.dependents[self] = 1
self._computeImplied()
def extends(self, other, strict=True):
if other is self and strict:
return False
i = self._implied.get(other)
if i is not None and not i:
return True
return False
def isImplementedBy(self, ob):
return self._interface.isImplementedBy(ob)
def _adaptTo(self, specification, factory, name=None, with=()):
self._adapters[with, name, specification] = factory
self.changed()
def changed(self):
self._computeImplied()
for dependent in self.dependents:
dependent.changed()
def _computeImplied(self):
implied = self._implied
implied.clear()
ancestors = ro(self)
for ancestor in ancestors:
# We directly imply our ancestors:
implied[ancestor] = ()
# Work dict used to keep track of the registered interface
# leading to an implied entry. This is so that we can can
# override an implied entry of we get a closer "provided"
# fit.
registered = {}
# Add adapters and interfaces directly implied by same:
items = ancestor._adapters.iteritems()
for (with, name, target), factory in items:
if with:
self.__add_multi_adapter(with, name, target, target,
implied, registered,
(factory, ))
elif name is not None:
self.__add_named_adapter(target, target, name,
implied, registered,
(factory, ))
else:
self.__add_adapter(target, target,
implied, registered,
(factory, ))
def __add_adapter(self, target, provided, implied, registered, factories):
if (target not in implied
or
(target in registered and registered[target].extends(provided))
):
registered[target] = provided
implied[target] = factories
for b in target.__bases__:
self.__add_adapter(b, provided, implied, registered, factories)
def __add_named_adapter(self, target, provided, name,
implied, registered, factories):
key = target, name
if (key not in implied
or
(key in registered and registered[key].extends(provided))
):
registered[key] = provided
implied[key] = factories
for b in target.__bases__:
self.__add_named_adapter(b, provided, name,
implied, registered, factories)
def __add_multi_adapter(self, interfaces, name, target, provided,
implied, registered, factories):
order = len(interfaces)+1
adapters = implied.get((target, name, order))
if adapters is None:
adapters = {}
implied[(target, name, order)] = adapters
adapters.setdefault(interfaces, factories)
for b in target.__bases__:
self.__add_multi_adapter(interfaces, name, b, provided,
implied, registered, factories)
class SurrogateRegistry(object):
def __init__(self):
self._surrogates = weakref.WeakKeyDictionary()
def get(self, declaration):
surrogate = self._surrogates.get(declaration)
if surrogate is None:
surrogate = Surrogate(declaration, self)
self._surrogates[declaration] = surrogate
return surrogate
def provideAdapter(self, required, provided, factory,
name=None, with=()):
required = self.get(required)
provided = self.get(provided)
if with:
with = tuple(map(self.get, with))
else:
with = ()
required._adaptTo(provided, factory, name, with)
def queryAdapter(self, ob, interface, default=None):
"""Query a simple adapter
>>> import zope.interface
>>> class F0(zope.interface.Interface):
... pass
>>> class F1(F0):
... pass
>>> class C:
... zope.interface.implements(F1)
>>> c = C()
>>> registry = SurrogateRegistry()
When we adapt an object to an interface it implements, we get
the object back:
>>> registry.queryAdapter(c, F0) is c
1
But adapting to some other interface returns the default:
>>> class B0(zope.interface.Interface):
... pass
>>> class B1(B0):
... pass
>>> registry.queryAdapter(c, B0)
>>> registry.queryAdapter(c, B0, 42)
42
Unless we define an adapter:
>>> def f1(ob):
... return 1
>>> registry.provideAdapter(F0, B1, f1)
>>> registry.queryAdapter(c, B0)
1
If we define a more specific adapter (for F1), we'll get that:
>>> def f2(ob):
... return 2
>>> registry.provideAdapter(F1, B1, f2)
>>> registry.queryAdapter(c, B0)
2
>>> def f3(ob):
... return 3
>>> registry.provideAdapter(F1, B0, f3)
>>> registry.queryAdapter(c, B0)
3
"""
surrogates = self._surrogates
surrogate = surrogates.get(interface)
if surrogate is None:
# If there is no surrogate for the interface, then we can't
# have an adapter for it.
# But we may implement it
if interface.isImplementedBy(ob):
return ob
return default
interface = surrogate
for declaration in providedBy(ob):
s = surrogates.get(declaration)
if s is None:
s = self.get(declaration)
factories = s._implied.get(interface)
if factories is not None:
for factory in factories:
ob = factory(ob)
return ob
return default
def queryNamedAdapter(self, ob, interface, name, default=None):
"""Query a named simple adapter
>>> import zope.interface
>>> class F0(zope.interface.Interface):
... pass
>>> class F1(F0):
... pass
>>> class C:
... zope.interface.implements(F1)
>>> c = C()
>>> registry = SurrogateRegistry()
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.queryNamedAdapter(c, F0, 'bob')
>>> class B0(zope.interface.Interface):
... pass
>>> class B1(B0):
... pass
>>> def f1(ob):
... return 1
>>> registry.provideAdapter(F0, B1, f1, name='bob')
>>> registry.queryNamedAdapter(c, B0, 'bob')
1
>>> registry.queryNamedAdapter(c, B0, 'bruce')
>>> def f2(ob):
... return 2
>>> registry.provideAdapter(F1, B1, f2, name='bob')
>>> registry.queryNamedAdapter(c, B0, 'bob')
2
>>> def f3(ob):
... return 3
>>> registry.provideAdapter(F1, B0, f3, name='bob')
>>> registry.queryNamedAdapter(c, B0, 'bob')
3
"""
surrogates = self._surrogates
interface = surrogates.get(interface)
if interface is None:
# If there is no surrogate for the interface, then we can't
# have an adapter for it.
return default
for declaration in providedBy(ob):
s = surrogates.get(declaration)
if s is None:
s = self.get(declaration)
factories = s._implied.get((interface, name))
if factories is not None:
for factory in factories:
ob = factory(ob)
return ob
return default
def queryMultiAdapter(self, objects, interface, name):
"""
>>> import zope.interface
>>> class IF0(zope.interface.Interface):
... pass
>>> class IF1(IF0):
... pass
>>> class IR0(zope.interface.Interface):
... pass
>>> class IR1(IR0):
... pass
>>> class F1:
... zope.interface.implements(IF1)
>>> c = F1()
>>> class R1:
... zope.interface.implements(IR1)
>>> r = R1()
>>> registry = SurrogateRegistry()
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.queryMultiAdapter((c, r), IF0, 'bob')
>>> class IB0(zope.interface.Interface):
... pass
>>> class IB1(IB0):
... pass
>>> def f1(ob):
... return 1
>>> registry.provideAdapter(IF0, IB1, f1, name='bob', with=[IR0])
>>> registry.queryMultiAdapter((c, r), IB0, 'bob')
1
>>> registry.queryMultiAdapter((c, r), IB0, 'bruce')
>>> def f2(ob):
... return 2
>>> registry.provideAdapter(IF1, IB1, f2, name='bob', with=[IR1])
>>> registry.queryMultiAdapter((c, r), IB0, 'bob')
2
>>> def f3(ob):
... return 3
"""
surrogates = self._surrogates
interface = surrogates.get(interface)
if interface is None:
# If there is no surrogate for the interface, then we can't
# have an adapter for it.
return None
ob = objects[0]
order = len(objects)
objects = objects[1:]
for declaration in providedBy(ob):
s = surrogates.get(declaration)
if s is None:
s = self.get(declaration)
adapters = s._implied.get((interface, name, order))
if adapters:
matched = None
matched_factories = None
for interfaces, factories in adapters.iteritems():
for iface, ob in zip(interfaces, objects):
if not iface.isImplementedBy(ob):
break # This one is no good
else:
# we didn't break, so we have a match
if matched is None:
matched = interfaces
matched_factories = factories
else:
for iface, m in zip(interfaces, matched):
if iface.extends(m):
# new is better than old
matched = interfaces
matched_factories = factories
break
elif m.extends(iface):
# old is better than new
break
for factory in matched_factories:
ob = factory(ob)
return ob
return None
=== Zope3/src/zope/interface/declarations.py 1.17 => 1.17.10.1 ===
--- Zope3/src/zope/interface/declarations.py:1.17 Fri Aug 15 20:44:44 2003
+++ Zope3/src/zope/interface/declarations.py Fri Oct 10 07:16:38 2003
@@ -15,7 +15,8 @@
"""
import sys
-from zope.interface.interface import InterfaceClass, mergeOrderings
+from zope.interface.interface import InterfaceClass
+from ro import mergeOrderings
import exceptions
from types import ClassType
from zope.interface.advice import addClassAdvisor
=== Zope3/src/zope/interface/interface.py 1.13 => 1.13.4.1 ===
--- Zope3/src/zope/interface/interface.py:1.13 Fri Oct 3 16:36:30 2003
+++ Zope3/src/zope/interface/interface.py Fri Oct 10 07:16:38 2003
@@ -18,7 +18,9 @@
"""
import sys
+import weakref
from types import FunctionType
+from ro import ro
CO_VARARGS = 4
CO_VARKEYWORDS = 8
@@ -113,7 +115,7 @@
Element.__init__(self, name, __doc__)
- self.__iro__ = mergeOrderings([_flattenInterface(self, [])])
+ self.__iro__ = ro(self)
for k, v in attrs.items():
if isinstance(v, Attribute):
@@ -129,6 +131,10 @@
self.__identifier__ = "%s.%s" % (self.__module__, self.__name__)
+ self._dependents = weakref.WeakKeyDictionary()
+
+ def addDependent(self, ob):
+ self._dependents[ob] = 1
def getBases(self):
return self.__bases__
@@ -328,48 +334,6 @@
c = self.__cmp(self, other)
#print '>', self, other, c > 0, c
return c > 0
-
-
-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 _flattenInterface(iface, result):
- result.append(iface)
- for base in iface.__bases__:
- _flattenInterface(base, result)
-
- return result
Interface = InterfaceClass("Interface", __module__ = 'zope.interface')
More information about the Zope3-Checkins
mailing list