17.12
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2002 Zope Foundation 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.
|
||||
"""
|
||||
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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,712 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2004 Zope Foundation 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 management
|
||||
"""
|
||||
import weakref
|
||||
|
||||
from zope.interface import implementer
|
||||
from zope.interface import providedBy
|
||||
from zope.interface import Interface
|
||||
from zope.interface import ro
|
||||
from zope.interface.interfaces import IAdapterRegistry
|
||||
|
||||
from zope.interface._compat import _normalize_name
|
||||
from zope.interface._compat import STRING_TYPES
|
||||
|
||||
_BLANK = u''
|
||||
|
||||
class BaseAdapterRegistry(object):
|
||||
|
||||
# List of methods copied from lookup sub-objects:
|
||||
_delegated = ('lookup', 'queryMultiAdapter', 'lookup1', 'queryAdapter',
|
||||
'adapter_hook', 'lookupAll', 'names',
|
||||
'subscriptions', 'subscribers')
|
||||
|
||||
# All registries maintain a generation that can be used by verifying
|
||||
# registries
|
||||
_generation = 0
|
||||
|
||||
def __init__(self, bases=()):
|
||||
|
||||
# The comments here could be improved. Possibly this bit needs
|
||||
# explaining in a separate document, as the comments here can
|
||||
# be quite confusing. /regebro
|
||||
|
||||
# {order -> {required -> {provided -> {name -> value}}}}
|
||||
# Here "order" is actually an index in a list, "required" and
|
||||
# "provided" are interfaces, and "required" is really a nested
|
||||
# key. So, for example:
|
||||
# for order == 0 (that is, self._adapters[0]), we have:
|
||||
# {provided -> {name -> value}}
|
||||
# but for order == 2 (that is, self._adapters[2]), we have:
|
||||
# {r1 -> {r2 -> {provided -> {name -> value}}}}
|
||||
#
|
||||
self._adapters = []
|
||||
|
||||
# {order -> {required -> {provided -> {name -> [value]}}}}
|
||||
# where the remarks about adapters above apply
|
||||
self._subscribers = []
|
||||
|
||||
# Set, with a reference count, keeping track of the interfaces
|
||||
# for which we have provided components:
|
||||
self._provided = {}
|
||||
|
||||
# Create ``_v_lookup`` object to perform lookup. We make this a
|
||||
# separate object to to make it easier to implement just the
|
||||
# lookup functionality in C. This object keeps track of cache
|
||||
# invalidation data in two kinds of registries.
|
||||
|
||||
# Invalidating registries have caches that are invalidated
|
||||
# when they or their base registies change. An invalidating
|
||||
# registry can only have invalidating registries as bases.
|
||||
# See LookupBaseFallback below for the pertinent logic.
|
||||
|
||||
# Verifying registies can't rely on getting invalidation messages,
|
||||
# so have to check the generations of base registries to determine
|
||||
# if their cache data are current. See VerifyingBasePy below
|
||||
# for the pertinent object.
|
||||
self._createLookup()
|
||||
|
||||
# Setting the bases causes the registries described above
|
||||
# to be initialized (self._setBases -> self.changed ->
|
||||
# self._v_lookup.changed).
|
||||
|
||||
self.__bases__ = bases
|
||||
|
||||
def _setBases(self, bases):
|
||||
self.__dict__['__bases__'] = bases
|
||||
self.ro = ro.ro(self)
|
||||
self.changed(self)
|
||||
|
||||
__bases__ = property(lambda self: self.__dict__['__bases__'],
|
||||
lambda self, bases: self._setBases(bases),
|
||||
)
|
||||
|
||||
def _createLookup(self):
|
||||
self._v_lookup = self.LookupClass(self)
|
||||
for name in self._delegated:
|
||||
self.__dict__[name] = getattr(self._v_lookup, name)
|
||||
|
||||
def changed(self, originally_changed):
|
||||
self._generation += 1
|
||||
self._v_lookup.changed(originally_changed)
|
||||
|
||||
def register(self, required, provided, name, value):
|
||||
if not isinstance(name, STRING_TYPES):
|
||||
raise ValueError('name is not a string')
|
||||
if value is None:
|
||||
self.unregister(required, provided, name, value)
|
||||
return
|
||||
|
||||
required = tuple(map(_convert_None_to_Interface, required))
|
||||
name = _normalize_name(name)
|
||||
order = len(required)
|
||||
byorder = self._adapters
|
||||
while len(byorder) <= order:
|
||||
byorder.append({})
|
||||
components = byorder[order]
|
||||
key = required + (provided,)
|
||||
|
||||
for k in key:
|
||||
d = components.get(k)
|
||||
if d is None:
|
||||
d = {}
|
||||
components[k] = d
|
||||
components = d
|
||||
|
||||
if components.get(name) is value:
|
||||
return
|
||||
|
||||
components[name] = value
|
||||
|
||||
n = self._provided.get(provided, 0) + 1
|
||||
self._provided[provided] = n
|
||||
if n == 1:
|
||||
self._v_lookup.add_extendor(provided)
|
||||
|
||||
self.changed(self)
|
||||
|
||||
def registered(self, required, provided, name=_BLANK):
|
||||
required = tuple(map(_convert_None_to_Interface, required))
|
||||
name = _normalize_name(name)
|
||||
order = len(required)
|
||||
byorder = self._adapters
|
||||
if len(byorder) <= order:
|
||||
return None
|
||||
|
||||
components = byorder[order]
|
||||
key = required + (provided,)
|
||||
|
||||
for k in key:
|
||||
d = components.get(k)
|
||||
if d is None:
|
||||
return None
|
||||
components = d
|
||||
|
||||
return components.get(name)
|
||||
|
||||
def unregister(self, required, provided, name, value=None):
|
||||
required = tuple(map(_convert_None_to_Interface, required))
|
||||
order = len(required)
|
||||
byorder = self._adapters
|
||||
if order >= len(byorder):
|
||||
return False
|
||||
components = byorder[order]
|
||||
key = required + (provided,)
|
||||
|
||||
# Keep track of how we got to `components`:
|
||||
lookups = []
|
||||
for k in key:
|
||||
d = components.get(k)
|
||||
if d is None:
|
||||
return
|
||||
lookups.append((components, k))
|
||||
components = d
|
||||
|
||||
old = components.get(name)
|
||||
if old is None:
|
||||
return
|
||||
if (value is not None) and (old is not value):
|
||||
return
|
||||
|
||||
del components[name]
|
||||
if not components:
|
||||
# Clean out empty containers, since we don't want our keys
|
||||
# to reference global objects (interfaces) unnecessarily.
|
||||
# This is often a problem when an interface is slated for
|
||||
# removal; a hold-over entry in the registry can make it
|
||||
# difficult to remove such interfaces.
|
||||
for comp, k in reversed(lookups):
|
||||
d = comp[k]
|
||||
if d:
|
||||
break
|
||||
else:
|
||||
del comp[k]
|
||||
while byorder and not byorder[-1]:
|
||||
del byorder[-1]
|
||||
n = self._provided[provided] - 1
|
||||
if n == 0:
|
||||
del self._provided[provided]
|
||||
self._v_lookup.remove_extendor(provided)
|
||||
else:
|
||||
self._provided[provided] = n
|
||||
|
||||
self.changed(self)
|
||||
|
||||
def subscribe(self, required, provided, value):
|
||||
required = tuple(map(_convert_None_to_Interface, required))
|
||||
name = _BLANK
|
||||
order = len(required)
|
||||
byorder = self._subscribers
|
||||
while len(byorder) <= order:
|
||||
byorder.append({})
|
||||
components = byorder[order]
|
||||
key = required + (provided,)
|
||||
|
||||
for k in key:
|
||||
d = components.get(k)
|
||||
if d is None:
|
||||
d = {}
|
||||
components[k] = d
|
||||
components = d
|
||||
|
||||
components[name] = components.get(name, ()) + (value, )
|
||||
|
||||
if provided is not None:
|
||||
n = self._provided.get(provided, 0) + 1
|
||||
self._provided[provided] = n
|
||||
if n == 1:
|
||||
self._v_lookup.add_extendor(provided)
|
||||
|
||||
self.changed(self)
|
||||
|
||||
def unsubscribe(self, required, provided, value=None):
|
||||
required = tuple(map(_convert_None_to_Interface, required))
|
||||
order = len(required)
|
||||
byorder = self._subscribers
|
||||
if order >= len(byorder):
|
||||
return
|
||||
components = byorder[order]
|
||||
key = required + (provided,)
|
||||
|
||||
# Keep track of how we got to `components`:
|
||||
lookups = []
|
||||
for k in key:
|
||||
d = components.get(k)
|
||||
if d is None:
|
||||
return
|
||||
lookups.append((components, k))
|
||||
components = d
|
||||
|
||||
old = components.get(_BLANK)
|
||||
if not old:
|
||||
# this is belt-and-suspenders against the failure of cleanup below
|
||||
return # pragma: no cover
|
||||
|
||||
if value is None:
|
||||
new = ()
|
||||
else:
|
||||
new = tuple([v for v in old if v != value])
|
||||
|
||||
if new == old:
|
||||
return
|
||||
|
||||
if new:
|
||||
components[_BLANK] = new
|
||||
else:
|
||||
# Instead of setting components[_BLANK] = new, we clean out
|
||||
# empty containers, since we don't want our keys to
|
||||
# reference global objects (interfaces) unnecessarily. This
|
||||
# is often a problem when an interface is slated for
|
||||
# removal; a hold-over entry in the registry can make it
|
||||
# difficult to remove such interfaces.
|
||||
del components[_BLANK]
|
||||
for comp, k in reversed(lookups):
|
||||
d = comp[k]
|
||||
if d:
|
||||
break
|
||||
else:
|
||||
del comp[k]
|
||||
while byorder and not byorder[-1]:
|
||||
del byorder[-1]
|
||||
|
||||
if provided is not None:
|
||||
n = self._provided[provided] + len(new) - len(old)
|
||||
if n == 0:
|
||||
del self._provided[provided]
|
||||
self._v_lookup.remove_extendor(provided)
|
||||
|
||||
self.changed(self)
|
||||
|
||||
# XXX hack to fake out twisted's use of a private api. We need to get them
|
||||
# to use the new registed method.
|
||||
def get(self, _): # pragma: no cover
|
||||
class XXXTwistedFakeOut:
|
||||
selfImplied = {}
|
||||
return XXXTwistedFakeOut
|
||||
|
||||
|
||||
_not_in_mapping = object()
|
||||
class LookupBaseFallback(object):
|
||||
|
||||
def __init__(self):
|
||||
self._cache = {}
|
||||
self._mcache = {}
|
||||
self._scache = {}
|
||||
|
||||
def changed(self, ignored=None):
|
||||
self._cache.clear()
|
||||
self._mcache.clear()
|
||||
self._scache.clear()
|
||||
|
||||
def _getcache(self, provided, name):
|
||||
cache = self._cache.get(provided)
|
||||
if cache is None:
|
||||
cache = {}
|
||||
self._cache[provided] = cache
|
||||
if name:
|
||||
c = cache.get(name)
|
||||
if c is None:
|
||||
c = {}
|
||||
cache[name] = c
|
||||
cache = c
|
||||
return cache
|
||||
|
||||
def lookup(self, required, provided, name=_BLANK, default=None):
|
||||
if not isinstance(name, STRING_TYPES):
|
||||
raise ValueError('name is not a string')
|
||||
cache = self._getcache(provided, name)
|
||||
required = tuple(required)
|
||||
if len(required) == 1:
|
||||
result = cache.get(required[0], _not_in_mapping)
|
||||
else:
|
||||
result = cache.get(tuple(required), _not_in_mapping)
|
||||
|
||||
if result is _not_in_mapping:
|
||||
result = self._uncached_lookup(required, provided, name)
|
||||
if len(required) == 1:
|
||||
cache[required[0]] = result
|
||||
else:
|
||||
cache[tuple(required)] = result
|
||||
|
||||
if result is None:
|
||||
return default
|
||||
|
||||
return result
|
||||
|
||||
def lookup1(self, required, provided, name=_BLANK, default=None):
|
||||
if not isinstance(name, STRING_TYPES):
|
||||
raise ValueError('name is not a string')
|
||||
cache = self._getcache(provided, name)
|
||||
result = cache.get(required, _not_in_mapping)
|
||||
if result is _not_in_mapping:
|
||||
return self.lookup((required, ), provided, name, default)
|
||||
|
||||
if result is None:
|
||||
return default
|
||||
|
||||
return result
|
||||
|
||||
def queryAdapter(self, object, provided, name=_BLANK, default=None):
|
||||
return self.adapter_hook(provided, object, name, default)
|
||||
|
||||
def adapter_hook(self, provided, object, name=_BLANK, default=None):
|
||||
if not isinstance(name, STRING_TYPES):
|
||||
raise ValueError('name is not a string')
|
||||
required = providedBy(object)
|
||||
cache = self._getcache(provided, name)
|
||||
factory = cache.get(required, _not_in_mapping)
|
||||
if factory is _not_in_mapping:
|
||||
factory = self.lookup((required, ), provided, name)
|
||||
|
||||
if factory is not None:
|
||||
result = factory(object)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
return default
|
||||
|
||||
def lookupAll(self, required, provided):
|
||||
cache = self._mcache.get(provided)
|
||||
if cache is None:
|
||||
cache = {}
|
||||
self._mcache[provided] = cache
|
||||
|
||||
required = tuple(required)
|
||||
result = cache.get(required, _not_in_mapping)
|
||||
if result is _not_in_mapping:
|
||||
result = self._uncached_lookupAll(required, provided)
|
||||
cache[required] = result
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def subscriptions(self, required, provided):
|
||||
cache = self._scache.get(provided)
|
||||
if cache is None:
|
||||
cache = {}
|
||||
self._scache[provided] = cache
|
||||
|
||||
required = tuple(required)
|
||||
result = cache.get(required, _not_in_mapping)
|
||||
if result is _not_in_mapping:
|
||||
result = self._uncached_subscriptions(required, provided)
|
||||
cache[required] = result
|
||||
|
||||
return result
|
||||
|
||||
LookupBasePy = LookupBaseFallback # BBB
|
||||
|
||||
try:
|
||||
from zope.interface._zope_interface_coptimizations import LookupBase
|
||||
except ImportError:
|
||||
LookupBase = LookupBaseFallback
|
||||
|
||||
|
||||
class VerifyingBaseFallback(LookupBaseFallback):
|
||||
# Mixin for lookups against registries which "chain" upwards, and
|
||||
# whose lookups invalidate their own caches whenever a parent registry
|
||||
# bumps its own '_generation' counter. E.g., used by
|
||||
# zope.component.persistentregistry
|
||||
|
||||
def changed(self, originally_changed):
|
||||
LookupBaseFallback.changed(self, originally_changed)
|
||||
self._verify_ro = self._registry.ro[1:]
|
||||
self._verify_generations = [r._generation for r in self._verify_ro]
|
||||
|
||||
def _verify(self):
|
||||
if ([r._generation for r in self._verify_ro]
|
||||
!= self._verify_generations):
|
||||
self.changed(None)
|
||||
|
||||
def _getcache(self, provided, name):
|
||||
self._verify()
|
||||
return LookupBaseFallback._getcache(self, provided, name)
|
||||
|
||||
def lookupAll(self, required, provided):
|
||||
self._verify()
|
||||
return LookupBaseFallback.lookupAll(self, required, provided)
|
||||
|
||||
def subscriptions(self, required, provided):
|
||||
self._verify()
|
||||
return LookupBaseFallback.subscriptions(self, required, provided)
|
||||
|
||||
VerifyingBasePy = VerifyingBaseFallback #BBB
|
||||
|
||||
try:
|
||||
from zope.interface._zope_interface_coptimizations import VerifyingBase
|
||||
except ImportError:
|
||||
VerifyingBase = VerifyingBaseFallback
|
||||
|
||||
|
||||
class AdapterLookupBase(object):
|
||||
|
||||
def __init__(self, registry):
|
||||
self._registry = registry
|
||||
self._required = {}
|
||||
self.init_extendors()
|
||||
super(AdapterLookupBase, self).__init__()
|
||||
|
||||
def changed(self, ignored=None):
|
||||
super(AdapterLookupBase, self).changed(None)
|
||||
for r in self._required.keys():
|
||||
r = r()
|
||||
if r is not None:
|
||||
r.unsubscribe(self)
|
||||
self._required.clear()
|
||||
|
||||
|
||||
# Extendors
|
||||
# ---------
|
||||
|
||||
# When given an target interface for an adapter lookup, we need to consider
|
||||
# adapters for interfaces that extend the target interface. This is
|
||||
# what the extendors dictionary is about. It tells us all of the
|
||||
# interfaces that extend an interface for which there are adapters
|
||||
# registered.
|
||||
|
||||
# We could separate this by order and name, thus reducing the
|
||||
# number of provided interfaces to search at run time. The tradeoff,
|
||||
# however, is that we have to store more information. For example,
|
||||
# if the same interface is provided for multiple names and if the
|
||||
# interface extends many interfaces, we'll have to keep track of
|
||||
# a fair bit of information for each name. It's better to
|
||||
# be space efficient here and be time efficient in the cache
|
||||
# implementation.
|
||||
|
||||
# TODO: add invalidation when a provided interface changes, in case
|
||||
# the interface's __iro__ has changed. This is unlikely enough that
|
||||
# we'll take our chances for now.
|
||||
|
||||
def init_extendors(self):
|
||||
self._extendors = {}
|
||||
for p in self._registry._provided:
|
||||
self.add_extendor(p)
|
||||
|
||||
def add_extendor(self, provided):
|
||||
_extendors = self._extendors
|
||||
for i in provided.__iro__:
|
||||
extendors = _extendors.get(i, ())
|
||||
_extendors[i] = (
|
||||
[e for e in extendors if provided.isOrExtends(e)]
|
||||
+
|
||||
[provided]
|
||||
+
|
||||
[e for e in extendors if not provided.isOrExtends(e)]
|
||||
)
|
||||
|
||||
def remove_extendor(self, provided):
|
||||
_extendors = self._extendors
|
||||
for i in provided.__iro__:
|
||||
_extendors[i] = [e for e in _extendors.get(i, ())
|
||||
if e != provided]
|
||||
|
||||
|
||||
def _subscribe(self, *required):
|
||||
_refs = self._required
|
||||
for r in required:
|
||||
ref = r.weakref()
|
||||
if ref not in _refs:
|
||||
r.subscribe(self)
|
||||
_refs[ref] = 1
|
||||
|
||||
def _uncached_lookup(self, required, provided, name=_BLANK):
|
||||
required = tuple(required)
|
||||
result = None
|
||||
order = len(required)
|
||||
for registry in self._registry.ro:
|
||||
byorder = registry._adapters
|
||||
if order >= len(byorder):
|
||||
continue
|
||||
|
||||
extendors = registry._v_lookup._extendors.get(provided)
|
||||
if not extendors:
|
||||
continue
|
||||
|
||||
components = byorder[order]
|
||||
result = _lookup(components, required, extendors, name, 0,
|
||||
order)
|
||||
if result is not None:
|
||||
break
|
||||
|
||||
self._subscribe(*required)
|
||||
|
||||
return result
|
||||
|
||||
def queryMultiAdapter(self, objects, provided, name=_BLANK, default=None):
|
||||
factory = self.lookup(map(providedBy, objects), provided, name)
|
||||
if factory is None:
|
||||
return default
|
||||
|
||||
result = factory(*objects)
|
||||
if result is None:
|
||||
return default
|
||||
|
||||
return result
|
||||
|
||||
def _uncached_lookupAll(self, required, provided):
|
||||
required = tuple(required)
|
||||
order = len(required)
|
||||
result = {}
|
||||
for registry in reversed(self._registry.ro):
|
||||
byorder = registry._adapters
|
||||
if order >= len(byorder):
|
||||
continue
|
||||
extendors = registry._v_lookup._extendors.get(provided)
|
||||
if not extendors:
|
||||
continue
|
||||
components = byorder[order]
|
||||
_lookupAll(components, required, extendors, result, 0, order)
|
||||
|
||||
self._subscribe(*required)
|
||||
|
||||
return tuple(result.items())
|
||||
|
||||
def names(self, required, provided):
|
||||
return [c[0] for c in self.lookupAll(required, provided)]
|
||||
|
||||
def _uncached_subscriptions(self, required, provided):
|
||||
required = tuple(required)
|
||||
order = len(required)
|
||||
result = []
|
||||
for registry in reversed(self._registry.ro):
|
||||
byorder = registry._subscribers
|
||||
if order >= len(byorder):
|
||||
continue
|
||||
|
||||
if provided is None:
|
||||
extendors = (provided, )
|
||||
else:
|
||||
extendors = registry._v_lookup._extendors.get(provided)
|
||||
if extendors is None:
|
||||
continue
|
||||
|
||||
_subscriptions(byorder[order], required, extendors, _BLANK,
|
||||
result, 0, order)
|
||||
|
||||
self._subscribe(*required)
|
||||
|
||||
return result
|
||||
|
||||
def subscribers(self, objects, provided):
|
||||
subscriptions = self.subscriptions(map(providedBy, objects), provided)
|
||||
if provided is None:
|
||||
result = ()
|
||||
for subscription in subscriptions:
|
||||
subscription(*objects)
|
||||
else:
|
||||
result = []
|
||||
for subscription in subscriptions:
|
||||
subscriber = subscription(*objects)
|
||||
if subscriber is not None:
|
||||
result.append(subscriber)
|
||||
return result
|
||||
|
||||
class AdapterLookup(AdapterLookupBase, LookupBase):
|
||||
pass
|
||||
|
||||
@implementer(IAdapterRegistry)
|
||||
class AdapterRegistry(BaseAdapterRegistry):
|
||||
|
||||
LookupClass = AdapterLookup
|
||||
|
||||
def __init__(self, bases=()):
|
||||
# AdapterRegisties are invalidating registries, so
|
||||
# we need to keep track of out invalidating subregistries.
|
||||
self._v_subregistries = weakref.WeakKeyDictionary()
|
||||
|
||||
super(AdapterRegistry, self).__init__(bases)
|
||||
|
||||
def _addSubregistry(self, r):
|
||||
self._v_subregistries[r] = 1
|
||||
|
||||
def _removeSubregistry(self, r):
|
||||
if r in self._v_subregistries:
|
||||
del self._v_subregistries[r]
|
||||
|
||||
def _setBases(self, bases):
|
||||
old = self.__dict__.get('__bases__', ())
|
||||
for r in old:
|
||||
if r not in bases:
|
||||
r._removeSubregistry(self)
|
||||
for r in bases:
|
||||
if r not in old:
|
||||
r._addSubregistry(self)
|
||||
|
||||
super(AdapterRegistry, self)._setBases(bases)
|
||||
|
||||
def changed(self, originally_changed):
|
||||
super(AdapterRegistry, self).changed(originally_changed)
|
||||
|
||||
for sub in self._v_subregistries.keys():
|
||||
sub.changed(originally_changed)
|
||||
|
||||
|
||||
class VerifyingAdapterLookup(AdapterLookupBase, VerifyingBase):
|
||||
pass
|
||||
|
||||
@implementer(IAdapterRegistry)
|
||||
class VerifyingAdapterRegistry(BaseAdapterRegistry):
|
||||
|
||||
LookupClass = VerifyingAdapterLookup
|
||||
|
||||
def _convert_None_to_Interface(x):
|
||||
if x is None:
|
||||
return Interface
|
||||
else:
|
||||
return x
|
||||
|
||||
def _lookup(components, specs, provided, name, i, l):
|
||||
if i < l:
|
||||
for spec in specs[i].__sro__:
|
||||
comps = components.get(spec)
|
||||
if comps:
|
||||
r = _lookup(comps, specs, provided, name, i+1, l)
|
||||
if r is not None:
|
||||
return r
|
||||
else:
|
||||
for iface in provided:
|
||||
comps = components.get(iface)
|
||||
if comps:
|
||||
r = comps.get(name)
|
||||
if r is not None:
|
||||
return r
|
||||
|
||||
return None
|
||||
|
||||
def _lookupAll(components, specs, provided, result, i, l):
|
||||
if i < l:
|
||||
for spec in reversed(specs[i].__sro__):
|
||||
comps = components.get(spec)
|
||||
if comps:
|
||||
_lookupAll(comps, specs, provided, result, i+1, l)
|
||||
else:
|
||||
for iface in reversed(provided):
|
||||
comps = components.get(iface)
|
||||
if comps:
|
||||
result.update(comps)
|
||||
|
||||
def _subscriptions(components, specs, provided, name, result, i, l):
|
||||
if i < l:
|
||||
for spec in reversed(specs[i].__sro__):
|
||||
comps = components.get(spec)
|
||||
if comps:
|
||||
_subscriptions(comps, specs, provided, name, result, i+1, l)
|
||||
else:
|
||||
for iface in reversed(provided):
|
||||
comps = components.get(iface)
|
||||
if comps:
|
||||
comps = comps.get(name)
|
||||
if comps:
|
||||
result.extend(comps)
|
||||
@@ -0,0 +1,2 @@
|
||||
#
|
||||
# This file is necessary to make this directory a package.
|
||||
@@ -0,0 +1,606 @@
|
||||
##############################################################################
|
||||
# Copyright (c) 2002 Zope Foundation 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.
|
||||
"""
|
||||
from datetime import timedelta, date, datetime, time, tzinfo
|
||||
|
||||
from zope.interface import Interface, Attribute
|
||||
from zope.interface import classImplements
|
||||
|
||||
|
||||
class ITimeDeltaClass(Interface):
|
||||
"""This is the timedelta class interface.
|
||||
|
||||
This is symbolic; this module does **not** make
|
||||
`datetime.timedelta` provide this 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.
|
||||
|
||||
Implemented by `datetime.timedelta`.
|
||||
|
||||
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.
|
||||
|
||||
This is symbolic; this module does **not** make
|
||||
`datetime.date` provide this 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.
|
||||
|
||||
Implemented by `datetime.date`.
|
||||
|
||||
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.
|
||||
|
||||
.. seealso:: `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.
|
||||
|
||||
.. seealso:: `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.
|
||||
|
||||
This is symbolic; this module does **not** make
|
||||
`datetime.datetime` provide this 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())``.
|
||||
|
||||
.. seealso:: `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)).
|
||||
|
||||
.. seealso:: `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.
|
||||
|
||||
.. seealso:: `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.
|
||||
|
||||
.. seealso:: `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.
|
||||
|
||||
.. seealso:: `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.
|
||||
|
||||
Implemented by `datetime.datetime`.
|
||||
"""
|
||||
|
||||
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.
|
||||
|
||||
.. seealso:: Method :meth:`timetz`.
|
||||
"""
|
||||
|
||||
def timetz():
|
||||
"""Return time object with same hour, minute, second, microsecond,
|
||||
and tzinfo.
|
||||
|
||||
.. seealso:: Method :meth:`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 `datetime.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.gmtime`."""
|
||||
|
||||
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.
|
||||
|
||||
.. seealso:: `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.
|
||||
|
||||
This is symbolic; this module does **not** make
|
||||
`datetime.time` provide this 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.
|
||||
|
||||
Implemented by `datetime.time`.
|
||||
|
||||
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)
|
||||
@@ -0,0 +1,165 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation 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
|
||||
|
||||
Importing this module does *not* mark any standard classes
|
||||
as implementing any of these interfaces.
|
||||
"""
|
||||
|
||||
__docformat__ = 'restructuredtext'
|
||||
from zope.interface import Interface
|
||||
|
||||
class IMinimalSequence(Interface):
|
||||
"""Most basic sequence interface.
|
||||
|
||||
All sequences are iterable. This requires at least one of the
|
||||
following:
|
||||
|
||||
- a `__getitem__()` method that takes a single argument; integer
|
||||
values starting at 0 must be supported, and `IndexError` should
|
||||
be raised for the first index for which there is no value, or
|
||||
|
||||
- an `__iter__()` method that returns an iterator as defined in
|
||||
the Python documentation (http://docs.python.org/lib/typeiter.html).
|
||||
|
||||
"""
|
||||
|
||||
def __getitem__(index):
|
||||
"""``x.__getitem__(index) <==> x[index]``
|
||||
|
||||
Declaring this interface does not specify whether `__getitem__`
|
||||
supports slice objects."""
|
||||
|
||||
class IFiniteSequence(IMinimalSequence):
|
||||
|
||||
def __len__():
|
||||
"""``x.__len__() <==> len(x)``"""
|
||||
|
||||
class IReadSequence(IFiniteSequence):
|
||||
"""read interface shared by tuple and list"""
|
||||
|
||||
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 __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.
|
||||
|
||||
Deprecated since Python 2.0 but still a part of `UserList`.
|
||||
"""
|
||||
|
||||
class IExtendedReadSequence(IReadSequence):
|
||||
"""Full read interface for lists"""
|
||||
|
||||
def count(item):
|
||||
"""Return number of occurrences of value"""
|
||||
|
||||
def index(item, *args):
|
||||
"""index(value, [start, [stop]]) -> int
|
||||
|
||||
Return first index of *value*
|
||||
"""
|
||||
|
||||
class IUniqueMemberWriteSequence(Interface):
|
||||
"""The write contract for a sequence that may enforce unique members"""
|
||||
|
||||
def __setitem__(index, item):
|
||||
"""``x.__setitem__(index, item) <==> x[index] = item``
|
||||
|
||||
Declaring this interface does not specify whether `__setitem__`
|
||||
supports slice objects.
|
||||
"""
|
||||
|
||||
def __delitem__(index):
|
||||
"""``x.__delitem__(index) <==> del x[index]``
|
||||
|
||||
Declaring this interface does not specify whether `__delitem__`
|
||||
supports slice objects.
|
||||
"""
|
||||
|
||||
def __setslice__(i, j, other):
|
||||
"""``x.__setslice__(i, j, other) <==> x[i:j] = other``
|
||||
|
||||
Use of negative indices is not supported.
|
||||
|
||||
Deprecated since Python 2.0 but still a part of `UserList`.
|
||||
"""
|
||||
|
||||
def __delslice__(i, j):
|
||||
"""``x.__delslice__(i, j) <==> del x[i:j]``
|
||||
|
||||
Use of negative indices is not supported.
|
||||
|
||||
Deprecated since Python 2.0 but still a part of `UserList`.
|
||||
"""
|
||||
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"""
|
||||
@@ -0,0 +1,2 @@
|
||||
#
|
||||
# This file is necessary to make this directory a package.
|
||||
@@ -0,0 +1,107 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation 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
|
||||
"""
|
||||
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.assertTrue(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_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()
|
||||
@@ -0,0 +1,64 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2003 Zope Foundation 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 its bases
|
||||
"""
|
||||
__docformat__ = 'restructuredtext'
|
||||
|
||||
def _mergeOrderings(orderings):
|
||||
"""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']
|
||||
|
||||
"""
|
||||
|
||||
seen = {}
|
||||
result = []
|
||||
for ordering in reversed(orderings):
|
||||
for o in reversed(ordering):
|
||||
if o not in seen:
|
||||
seen[o] = 1
|
||||
result.insert(0, o)
|
||||
|
||||
return result
|
||||
|
||||
def _flatten(ob):
|
||||
result = [ob]
|
||||
i = 0
|
||||
for ob in iter(result):
|
||||
i += 1
|
||||
# The recursive calls can be avoided by inserting the base classes
|
||||
# into the dynamically growing list directly after the currently
|
||||
# considered object; the iterator makes sure this will keep working
|
||||
# in the future, since it cannot rely on the length of the list
|
||||
# by definition.
|
||||
result[i:i] = ob.__bases__
|
||||
return result
|
||||
|
||||
|
||||
def ro(object):
|
||||
"""Compute a "resolution order" for an object
|
||||
"""
|
||||
return _mergeOrderings([_flatten(object)])
|
||||
@@ -0,0 +1,23 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation 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 describing API of zope.interface.tests.dummy test module
|
||||
"""
|
||||
from zope.interface import Interface
|
||||
|
||||
class IDummyModule(Interface):
|
||||
""" Dummy interface for unit tests.
|
||||
"""
|
||||
def bar(baz):
|
||||
""" Just a note.
|
||||
"""
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,31 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2003 Zope Foundation 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.
|
||||
"""
|
||||
|
||||
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)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Foundation 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
|
||||
"""
|
||||
|
||||
import unittest
|
||||
|
||||
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(unittest.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, I6, I4, I2]
|
||||
l.sort()
|
||||
self.assertEqual(l, [I1, I2, I3, I4, I5, I6, None])
|
||||
|
||||
def test_w_equal_names(self):
|
||||
# interfaces with equal names but different modules should sort by
|
||||
# module name
|
||||
from zope.interface.tests.m1 import I1 as m1_I1
|
||||
l = [I1, m1_I1]
|
||||
l.sort()
|
||||
self.assertEqual(l, [m1_I1, I1])
|
||||
Reference in New Issue
Block a user