mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-04-19 00:21:47 +00:00
Added printing requirements
This commit is contained in:
1
zope/__init__.py
Normal file
1
zope/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
__import__('pkg_resources').declare_namespace(__name__)
|
||||
31
zope/event/__init__.py
Normal file
31
zope/event/__init__.py
Normal file
@@ -0,0 +1,31 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
""" Base event system implementation
|
||||
|
||||
"""
|
||||
|
||||
#: Applications may register for notification of events by appending a
|
||||
#: callable to the ``subscribers`` list.
|
||||
#:
|
||||
#: Each subscriber takes a single argument, which is the event object
|
||||
#: being published.
|
||||
#:
|
||||
#: Exceptions raised by subscribers will be propagated.
|
||||
subscribers = []
|
||||
|
||||
def notify(event):
|
||||
""" Notify all subscribers of ``event``.
|
||||
"""
|
||||
for subscriber in subscribers:
|
||||
subscriber(event)
|
||||
48
zope/event/tests.py
Normal file
48
zope/event/tests.py
Normal file
@@ -0,0 +1,48 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
""" Test the event system
|
||||
"""
|
||||
import unittest
|
||||
|
||||
class Test_notify(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
from zope.event import subscribers
|
||||
self._old_subscribers = subscribers[:]
|
||||
subscribers[:] = []
|
||||
|
||||
def tearDown(self):
|
||||
from zope.event import subscribers
|
||||
subscribers[:] = self._old_subscribers
|
||||
|
||||
def _callFUT(self, event):
|
||||
from zope.event import notify
|
||||
notify(event)
|
||||
|
||||
def test_empty(self):
|
||||
event = object()
|
||||
self._callFUT(event)
|
||||
|
||||
def test_not_empty(self):
|
||||
from zope.event import subscribers
|
||||
dummy = []
|
||||
subscribers.append(dummy.append)
|
||||
event = object()
|
||||
self._callFUT(event)
|
||||
self.assertEqual(dummy, [event])
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(Test_notify),
|
||||
))
|
||||
89
zope/interface/__init__.py
Normal file
89
zope/interface/__init__.py
Normal file
@@ -0,0 +1,89 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""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.
|
||||
"""
|
||||
__docformat__ = 'restructuredtext'
|
||||
|
||||
from zope.interface.interface import Interface
|
||||
from zope.interface.interface import _wire
|
||||
|
||||
# Need to actually get the interface elements to implement the right interfaces
|
||||
_wire()
|
||||
del _wire
|
||||
|
||||
from zope.interface.declarations import Declaration
|
||||
from zope.interface.declarations import alsoProvides
|
||||
from zope.interface.declarations import classImplements
|
||||
from zope.interface.declarations import classImplementsOnly
|
||||
from zope.interface.declarations import classProvides
|
||||
from zope.interface.declarations import directlyProvidedBy
|
||||
from zope.interface.declarations import directlyProvides
|
||||
from zope.interface.declarations import implementedBy
|
||||
from zope.interface.declarations import implementer
|
||||
from zope.interface.declarations import implementer_only
|
||||
from zope.interface.declarations import implements
|
||||
from zope.interface.declarations import implementsOnly
|
||||
from zope.interface.declarations import moduleProvides
|
||||
from zope.interface.declarations import noLongerProvides
|
||||
from zope.interface.declarations import providedBy
|
||||
from zope.interface.declarations import provider
|
||||
from zope.interface.exceptions import Invalid
|
||||
from zope.interface.interface import Attribute
|
||||
from zope.interface.interface import invariant
|
||||
from zope.interface.interface import taggedValue
|
||||
|
||||
# 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)
|
||||
69
zope/interface/_compat.py
Normal file
69
zope/interface/_compat.py
Normal file
@@ -0,0 +1,69 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2006 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Basic components support
|
||||
"""
|
||||
import sys
|
||||
import types
|
||||
|
||||
if sys.version_info[0] < 3: #pragma NO COVER
|
||||
|
||||
def _u(s):
|
||||
return unicode(s, 'unicode_escape')
|
||||
|
||||
def _normalize_name(name):
|
||||
if isinstance(name, basestring):
|
||||
return unicode(name)
|
||||
raise TypeError("name must be a regular or unicode string")
|
||||
|
||||
CLASS_TYPES = (type, types.ClassType)
|
||||
STRING_TYPES = (basestring,)
|
||||
|
||||
_BUILTINS = '__builtin__'
|
||||
|
||||
PYTHON3 = False
|
||||
PYTHON2 = True
|
||||
|
||||
else: #pragma NO COVER
|
||||
|
||||
def _u(s):
|
||||
return s
|
||||
|
||||
def _normalize_name(name):
|
||||
if isinstance(name, bytes):
|
||||
name = str(name, 'ascii')
|
||||
if isinstance(name, str):
|
||||
return name
|
||||
raise TypeError("name must be a string or ASCII-only bytes")
|
||||
|
||||
CLASS_TYPES = (type,)
|
||||
STRING_TYPES = (str,)
|
||||
|
||||
_BUILTINS = 'builtins'
|
||||
|
||||
PYTHON3 = True
|
||||
PYTHON2 = False
|
||||
|
||||
def _skip_under_py3k(test_method): #pragma NO COVER
|
||||
if sys.version_info[0] < 3:
|
||||
return test_method
|
||||
def _dummy(*args):
|
||||
pass
|
||||
return _dummy
|
||||
|
||||
def _skip_under_py2(test_method): #pragma NO COVER
|
||||
if sys.version_info[0] > 2:
|
||||
return test_method
|
||||
def _dummy(*args):
|
||||
pass
|
||||
return _dummy
|
||||
35
zope/interface/_flatten.py
Normal file
35
zope/interface/_flatten.py
Normal file
@@ -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
|
||||
1688
zope/interface/_zope_interface_coptimizations.c
Normal file
1688
zope/interface/_zope_interface_coptimizations.c
Normal file
File diff suppressed because it is too large
Load Diff
704
zope/interface/adapter.py
Normal file
704
zope/interface/adapter.py
Normal file
@@ -0,0 +1,704 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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 providedBy
|
||||
from zope.interface import Interface
|
||||
from zope.interface import ro
|
||||
from zope.interface._compat import _u
|
||||
from zope.interface._compat import _normalize_name
|
||||
|
||||
_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 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 = []
|
||||
# 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 = []
|
||||
# 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 COVERAGE
|
||||
|
||||
if value is None:
|
||||
new = ()
|
||||
else:
|
||||
new = tuple([v for v in old if v is not 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.
|
||||
if _BLANK in components:
|
||||
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):
|
||||
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):
|
||||
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):
|
||||
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_coptimizations import LookupBase
|
||||
except ImportError: #pragma NO COVER
|
||||
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_coptimizations import VerifyingBase
|
||||
except ImportError: #pragma NO COVER
|
||||
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,
|
||||
# is 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
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
206
zope/interface/advice.py
Normal file
206
zope/interface/advice.py
Normal file
@@ -0,0 +1,206 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""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.
|
||||
"""
|
||||
|
||||
from types import FunctionType
|
||||
try:
|
||||
from types import ClassType
|
||||
except ImportError: #pragma NO COVER Python > 3.x
|
||||
__python3 = True
|
||||
else: #pragma NO COVER Python < 3.x
|
||||
__python3 = False
|
||||
|
||||
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: # pragma NO COVER
|
||||
# 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."""
|
||||
# This entire approach is invalid under Py3K. Don't even try to fix
|
||||
# the coverage for this block there. :(
|
||||
if __python3: #pragma NO COVER
|
||||
raise TypeError('Class advice impossible in Python3')
|
||||
|
||||
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__')
|
||||
if __python3: # pragma NO COVER
|
||||
defaultMetaclass = caller_globals.get('__metaclass__', type)
|
||||
else:
|
||||
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: #pragma NO COVER
|
||||
# they're all "classic" classes
|
||||
assert(not __python3) # This should not happen under Python 3
|
||||
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"""
|
||||
|
||||
if not __python3: #pragma NO COVER
|
||||
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
|
||||
|
||||
2
zope/interface/common/__init__.py
Normal file
2
zope/interface/common/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
#
|
||||
# This file is necessary to make this directory a package.
|
||||
575
zope/interface/common/idatetime.py
Normal file
575
zope/interface/common/idatetime.py
Normal file
@@ -0,0 +1,575 @@
|
||||
##############################################################################
|
||||
# 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 zope.interface import Interface, Attribute
|
||||
from zope.interface import classImplements
|
||||
|
||||
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)
|
||||
102
zope/interface/common/interfaces.py
Normal file
102
zope/interface/common/interfaces.py
Normal file
@@ -0,0 +1,102 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Interfaces for standard python exceptions
|
||||
"""
|
||||
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)
|
||||
try:
|
||||
classImplements(OverflowWarning, IOverflowWarning)
|
||||
except NameError: #pragma NO COVER
|
||||
pass # OverflowWarning was removed in Python 2.5
|
||||
classImplements(ReferenceError, IReferenceError)
|
||||
classImplements(RuntimeError, IRuntimeError)
|
||||
classImplements(RuntimeWarning, IRuntimeWarning)
|
||||
try:
|
||||
classImplements(StandardError, IStandardError)
|
||||
except NameError: #pragma NO COVER
|
||||
pass # StandardError does not exist in Python 3
|
||||
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)
|
||||
|
||||
125
zope/interface/common/mapping.py
Normal file
125
zope/interface/common/mapping.py
Normal file
@@ -0,0 +1,125 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Mapping Interfaces
|
||||
"""
|
||||
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
|
||||
160
zope/interface/common/sequence.py
Normal file
160
zope/interface/common/sequence.py
Normal file
@@ -0,0 +1,160 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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
|
||||
"""
|
||||
__docformat__ = 'restructuredtext'
|
||||
from zope import interface
|
||||
|
||||
class IMinimalSequence(interface.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; interger
|
||||
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):
|
||||
"""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`
|
||||
|
||||
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"""
|
||||
2
zope/interface/common/tests/__init__.py
Normal file
2
zope/interface/common/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
#
|
||||
# This file is necessary to make this directory a package.
|
||||
107
zope/interface/common/tests/basemapping.py
Normal file
107
zope/interface/common/tests/basemapping.py
Normal file
@@ -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.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_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()
|
||||
47
zope/interface/common/tests/test_idatetime.py
Normal file
47
zope/interface/common/tests/test_idatetime.py
Normal file
@@ -0,0 +1,47 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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 for datetime interfaces
|
||||
"""
|
||||
|
||||
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()
|
||||
29
zope/interface/common/tests/test_import_interfaces.py
Normal file
29
zope/interface/common/tests/test_import_interfaces.py
Normal file
@@ -0,0 +1,29 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2006 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.
|
||||
#
|
||||
##############################################################################
|
||||
import doctest
|
||||
import unittest
|
||||
|
||||
def test_interface_import():
|
||||
"""
|
||||
>>> import zope.interface.common.interfaces
|
||||
"""
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
doctest.DocTestSuite(),
|
||||
))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(defaultTest='test_suite')
|
||||
|
||||
848
zope/interface/declarations.py
Normal file
848
zope/interface/declarations.py
Normal file
@@ -0,0 +1,848 @@
|
||||
##############################################################################
|
||||
# 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.
|
||||
##############################################################################
|
||||
"""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.
|
||||
|
||||
"""
|
||||
__docformat__ = 'restructuredtext'
|
||||
|
||||
import sys
|
||||
from types import FunctionType
|
||||
from types import MethodType
|
||||
from types import ModuleType
|
||||
import warnings
|
||||
import weakref
|
||||
|
||||
from zope.interface.advice import addClassAdvisor
|
||||
from zope.interface.interface import InterfaceClass
|
||||
from zope.interface.interface import SpecificationBase
|
||||
from zope.interface.interface import Specification
|
||||
from zope.interface._compat import CLASS_TYPES as DescriptorAwareMetaClasses
|
||||
from zope.interface._compat import PYTHON3
|
||||
|
||||
# Registry of class-implementation specifications
|
||||
BuiltinImplementationSpecifications = {}
|
||||
|
||||
_ADVICE_ERROR = ('Class advice impossible in Python3. '
|
||||
'Use the @%s class decorator instead.')
|
||||
|
||||
_ADVICE_WARNING = ('The %s API is deprecated, and will not work in Python3 '
|
||||
'Use the @%s class decorator instead.')
|
||||
|
||||
class Declaration(Specification):
|
||||
"""Interface declarations"""
|
||||
|
||||
def __init__(self, *interfaces):
|
||||
Specification.__init__(self, _normalizeargs(interfaces))
|
||||
|
||||
def changed(self, originally_changed):
|
||||
Specification.changed(self, originally_changed)
|
||||
try:
|
||||
del self._v_attrs
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def __contains__(self, interface):
|
||||
"""Test whether an interface is in the specification
|
||||
"""
|
||||
|
||||
return self.extends(interface) and interface in self.interfaces()
|
||||
|
||||
def __iter__(self):
|
||||
"""Return an iterator for the interfaces in the specification
|
||||
"""
|
||||
return self.interfaces()
|
||||
|
||||
def flattened(self):
|
||||
"""Return an iterator of all included and extended interfaces
|
||||
"""
|
||||
return iter(self.__iro__)
|
||||
|
||||
def __sub__(self, other):
|
||||
"""Remove interfaces from a specification
|
||||
"""
|
||||
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
|
||||
"""
|
||||
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__
|
||||
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Implementation specifications
|
||||
#
|
||||
# These specify interfaces implemented by instances of classes
|
||||
|
||||
class Implements(Declaration):
|
||||
|
||||
# class whose specification should be used as additional base
|
||||
inherit = None
|
||||
|
||||
# interfaces actually declared for a class
|
||||
declared = ()
|
||||
|
||||
__name__ = '?'
|
||||
|
||||
def __repr__(self):
|
||||
return '<implementedBy %s>' % (self.__name__)
|
||||
|
||||
def __reduce__(self):
|
||||
return implementedBy, (self.inherit, )
|
||||
|
||||
def implementedByFallback(cls):
|
||||
"""Return the interfaces implemented for a class' instances
|
||||
|
||||
The value returned is an IDeclaration.
|
||||
"""
|
||||
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.security 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__ compatibility?
|
||||
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 '?') + \
|
||||
'.' + (getattr(cls, '__name__', '?') or '?')
|
||||
|
||||
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.
|
||||
"""
|
||||
spec = implementedBy(cls)
|
||||
spec.declared = ()
|
||||
spec.inherit = None
|
||||
classImplements(cls, *interfaces)
|
||||
|
||||
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.
|
||||
"""
|
||||
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:
|
||||
"""Declare the interfaces implemented by instances of a class.
|
||||
|
||||
This function is called as a class decorator.
|
||||
|
||||
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::
|
||||
|
||||
@implementer(I1)
|
||||
class C(object):
|
||||
pass
|
||||
|
||||
is equivalent to calling::
|
||||
|
||||
classImplements(C, I1)
|
||||
|
||||
after the class has been created.
|
||||
"""
|
||||
|
||||
def __init__(self, *interfaces):
|
||||
self.interfaces = interfaces
|
||||
|
||||
def __call__(self, ob):
|
||||
if isinstance(ob, DescriptorAwareMetaClasses):
|
||||
classImplements(ob, *self.interfaces)
|
||||
return ob
|
||||
|
||||
spec = Implements(*self.interfaces)
|
||||
try:
|
||||
ob.__implemented__ = spec
|
||||
except AttributeError:
|
||||
raise TypeError("Can't declare implements", ob)
|
||||
return ob
|
||||
|
||||
class implementer_only:
|
||||
"""Declare the only interfaces implemented by instances of a class
|
||||
|
||||
This function is called as a class decorator.
|
||||
|
||||
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::
|
||||
|
||||
@implementer_only(I1)
|
||||
class C(object): pass
|
||||
|
||||
is equivalent to calling::
|
||||
|
||||
classImplementsOnly(I1)
|
||||
|
||||
after the class has been created.
|
||||
"""
|
||||
|
||||
def __init__(self, *interfaces):
|
||||
self.interfaces = interfaces
|
||||
|
||||
def __call__(self, ob):
|
||||
if isinstance(ob, (FunctionType, MethodType)):
|
||||
# XXX Does this decorator make sense for anything but classes?
|
||||
# I don't think so. There can be no inheritance of interfaces
|
||||
# on a method pr function....
|
||||
raise ValueError('The implementer_only decorator is not '
|
||||
'supported for methods or functions.')
|
||||
else:
|
||||
# Assume it's a class:
|
||||
classImplementsOnly(ob, *self.interfaces)
|
||||
return ob
|
||||
|
||||
def _implements(name, interfaces, classImplements):
|
||||
# This entire approach is invalid under Py3K. Don't even try to fix
|
||||
# the coverage for this block there. :(
|
||||
if PYTHON3: #pragma NO COVER
|
||||
raise TypeError('Class advice impossible in Python3')
|
||||
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:
|
||||
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.
|
||||
"""
|
||||
# This entire approach is invalid under Py3K. Don't even try to fix
|
||||
# the coverage for this block there. :(
|
||||
if PYTHON3: #pragma NO COVER
|
||||
raise TypeError(_ADVICE_ERROR % 'implementer')
|
||||
_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.
|
||||
"""
|
||||
# This entire approach is invalid under Py3K. Don't even try to fix
|
||||
# the coverage for this block there. :(
|
||||
if PYTHON3: #pragma NO COVER
|
||||
raise TypeError(_ADVICE_ERROR % 'implementer_only')
|
||||
_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
|
||||
"""
|
||||
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 a weak value dictionary.
|
||||
"""
|
||||
spec = InstanceDeclarations.get(interfaces)
|
||||
if spec is None:
|
||||
spec = ProvidesClass(*interfaces)
|
||||
InstanceDeclarations[interfaces] = spec
|
||||
|
||||
return spec
|
||||
|
||||
Provides.__safe_for_unpickling__ = True
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
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)
|
||||
# Note that we can't get here from Py3k tests: there is no normal
|
||||
# class which isn't descriptor aware.
|
||||
if not isinstance(object,
|
||||
DescriptorAwareMetaClasses): #pragma NO COVER Py3k
|
||||
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.
|
||||
"""
|
||||
directlyProvides(object, directlyProvidedBy(object), *interfaces)
|
||||
|
||||
def noLongerProvides(object, interface):
|
||||
""" Removes a directly provided interface from an object.
|
||||
"""
|
||||
directlyProvides(object, directlyProvidedBy(object) - interface)
|
||||
if interface.providedBy(object):
|
||||
raise ValueError("Can only remove directly provided interfaces.")
|
||||
|
||||
class ClassProvidesBaseFallback(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__')
|
||||
|
||||
ClassProvidesBasePy = ClassProvidesBaseFallback # BBB
|
||||
ClassProvidesBase = ClassProvidesBaseFallback
|
||||
|
||||
# Try to get C base:
|
||||
try:
|
||||
import _zope_interface_coptimizations
|
||||
except ImportError: #pragma NO COVERAGE
|
||||
pass
|
||||
else: #pragma NO COVERAGE
|
||||
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.
|
||||
"""
|
||||
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 directlyProvides for a class. For example::
|
||||
|
||||
classProvides(I1)
|
||||
|
||||
is equivalent to calling::
|
||||
|
||||
directlyProvides(theclass, I1)
|
||||
|
||||
after the class has been created.
|
||||
"""
|
||||
# This entire approach is invalid under Py3K. Don't even try to fix
|
||||
# the coverage for this block there. :(
|
||||
|
||||
if PYTHON3: #pragma NO COVER
|
||||
raise TypeError(_ADVICE_ERROR % 'provider')
|
||||
|
||||
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("classProvides 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):
|
||||
# This entire approach is invalid under Py3K. Don't even try to fix
|
||||
# the coverage for this block there. :(
|
||||
interfaces = cls.__dict__['__provides__']
|
||||
del cls.__provides__
|
||||
directlyProvides(cls, *interfaces)
|
||||
return cls
|
||||
|
||||
class provider:
|
||||
"""Class decorator version of classProvides"""
|
||||
|
||||
def __init__(self, *interfaces):
|
||||
self.interfaces = interfaces
|
||||
|
||||
def __call__(self, ob):
|
||||
directlyProvides(ob, *self.interfaces)
|
||||
return ob
|
||||
|
||||
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.")
|
||||
|
||||
locals["__provides__"] = Provides(ModuleType,
|
||||
*_normalizeargs(interfaces))
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Declaration querying support
|
||||
|
||||
# XXX: is this a fossil? Nobody calls it, no unit tests exercise it, no
|
||||
# doctests import it, and the package __init__ doesn't import it.
|
||||
def ObjectSpecification(direct, cls):
|
||||
"""Provide object specifications
|
||||
|
||||
These combine information for the object and for it's classes.
|
||||
"""
|
||||
return Provides(cls, direct) #pragma NO COVER fossil
|
||||
|
||||
def getObjectSpecificationFallback(ob):
|
||||
|
||||
provides = getattr(ob, '__provides__', None)
|
||||
if provides is not None:
|
||||
if isinstance(provides, SpecificationBase):
|
||||
return provides
|
||||
|
||||
try:
|
||||
cls = ob.__class__
|
||||
except AttributeError:
|
||||
# We can't get the class, so just consider provides
|
||||
return _empty
|
||||
|
||||
return implementedBy(cls)
|
||||
|
||||
getObjectSpecification = getObjectSpecificationFallback
|
||||
|
||||
def providedByFallback(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
|
||||
providedBy = providedByFallback
|
||||
|
||||
class ObjectSpecificationDescriptorFallback(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
|
||||
"""
|
||||
if inst is None:
|
||||
return getObjectSpecification(cls)
|
||||
|
||||
provides = getattr(inst, '__provides__', None)
|
||||
if provides is not None:
|
||||
return provides
|
||||
|
||||
return implementedBy(cls)
|
||||
|
||||
ObjectSpecificationDescriptor = ObjectSpecificationDescriptorFallback
|
||||
|
||||
##############################################################################
|
||||
|
||||
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: #pragma NO COVER
|
||||
pass
|
||||
else: #pragma NO COVER PyPy
|
||||
from _zope_interface_coptimizations import implementedBy
|
||||
from _zope_interface_coptimizations import providedBy
|
||||
from _zope_interface_coptimizations import getObjectSpecification
|
||||
from _zope_interface_coptimizations import ObjectSpecificationDescriptor
|
||||
|
||||
objectSpecificationDescriptor = ObjectSpecificationDescriptor()
|
||||
104
zope/interface/document.py
Normal file
104
zope/interface/document.py
Normal file
@@ -0,0 +1,104 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
""" Pretty-Print an Interface object as structured text (Yum)
|
||||
|
||||
This module provides a function, asStructuredText, for rendering an
|
||||
interface as structured text.
|
||||
"""
|
||||
import zope.interface
|
||||
|
||||
def asStructuredText(I, munge=0):
|
||||
""" Output structured text format. Note, this will whack any existing
|
||||
'structured' format of the text. """
|
||||
|
||||
r = [I.getName()]
|
||||
outp = r.append
|
||||
level = 1
|
||||
|
||||
if I.getDoc():
|
||||
outp(_justify_and_indent(_trim_doc_string(I.getDoc()), level))
|
||||
|
||||
bases = [base
|
||||
for base in I.__bases__
|
||||
if base is not zope.interface.Interface
|
||||
]
|
||||
if bases:
|
||||
outp(_justify_and_indent("This interface extends:", level, munge))
|
||||
level += 1
|
||||
for b in bases:
|
||||
item = "o %s" % b.getName()
|
||||
outp(_justify_and_indent(_trim_doc_string(item), level, munge))
|
||||
level -= 1
|
||||
|
||||
namesAndDescriptions = sorted(I.namesAndDescriptions())
|
||||
|
||||
outp(_justify_and_indent("Attributes:", level, munge))
|
||||
level += 1
|
||||
for name, desc in namesAndDescriptions:
|
||||
if not hasattr(desc, 'getSignatureString'): # ugh...
|
||||
item = "%s -- %s" % (desc.getName(),
|
||||
desc.getDoc() or 'no documentation')
|
||||
outp(_justify_and_indent(_trim_doc_string(item), level, munge))
|
||||
level -= 1
|
||||
|
||||
outp(_justify_and_indent("Methods:", level, munge))
|
||||
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))
|
||||
|
||||
return "\n\n".join(r) + "\n\n"
|
||||
|
||||
|
||||
def _trim_doc_string(text):
|
||||
""" Trims a doc string to make it format
|
||||
correctly with structured text. """
|
||||
|
||||
lines = text.replace('\r\n', '\n').split('\n')
|
||||
nlines = [lines.pop(0)]
|
||||
if lines:
|
||||
min_indent = min([len(line) - len(line.lstrip())
|
||||
for line in lines])
|
||||
for line in lines:
|
||||
nlines.append(line[min_indent:])
|
||||
|
||||
return '\n'.join(nlines)
|
||||
|
||||
|
||||
def _justify_and_indent(text, level, munge=0, width=72):
|
||||
""" indent and justify text, rejustify (munge) if specified """
|
||||
|
||||
indent = " " * level
|
||||
|
||||
if munge:
|
||||
lines = []
|
||||
line = indent
|
||||
text = text.split()
|
||||
|
||||
for word in text:
|
||||
line = ' '.join([line, word])
|
||||
if len(line) > width:
|
||||
lines.append(line)
|
||||
line = indent
|
||||
else:
|
||||
lines.append(line)
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
else:
|
||||
return indent + \
|
||||
text.strip().replace("\r\n", "\n") .replace("\n", "\n" + indent)
|
||||
67
zope/interface/exceptions.py
Normal file
67
zope/interface/exceptions.py
Normal file
@@ -0,0 +1,67 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Interface-specific exceptions
|
||||
"""
|
||||
|
||||
class Invalid(Exception):
|
||||
"""A 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.
|
||||
"""
|
||||
712
zope/interface/interface.py
Normal file
712
zope/interface/interface.py
Normal file
@@ -0,0 +1,712 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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 object implementation
|
||||
"""
|
||||
from __future__ import generators
|
||||
|
||||
import sys
|
||||
from types import MethodType
|
||||
from types import FunctionType
|
||||
import warnings
|
||||
import weakref
|
||||
|
||||
from zope.interface.exceptions import Invalid
|
||||
from zope.interface.ro import ro
|
||||
|
||||
|
||||
CO_VARARGS = 4
|
||||
CO_VARKEYWORDS = 8
|
||||
TAGGED_DATA = '__interface_tagged_values__'
|
||||
|
||||
_decorator_non_return = object()
|
||||
|
||||
def invariant(call):
|
||||
f_locals = sys._getframe(1).f_locals
|
||||
tags = f_locals.setdefault(TAGGED_DATA, {})
|
||||
invariants = tags.setdefault('invariants', [])
|
||||
invariants.append(call)
|
||||
return _decorator_non_return
|
||||
|
||||
|
||||
def taggedValue(key, value):
|
||||
"""Attaches a tagged value to an interface at definition time."""
|
||||
f_locals = sys._getframe(1).f_locals
|
||||
tagged_values = f_locals.setdefault(TAGGED_DATA, {})
|
||||
tagged_values[key] = value
|
||||
return _decorator_non_return
|
||||
|
||||
|
||||
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
|
||||
"""
|
||||
spec = providedBy(ob)
|
||||
return self in spec._implied
|
||||
|
||||
def implementedBy(self, cls):
|
||||
"""Test whether the specification is implemented by a class or factory.
|
||||
|
||||
Raise TypeError if argument is neither a class nor a callable.
|
||||
"""
|
||||
spec = implementedBy(cls)
|
||||
return self in spec._implied
|
||||
|
||||
def isOrExtends(self, interface):
|
||||
"""Is the interface the same as or extend the given interface
|
||||
"""
|
||||
return interface in self._implied
|
||||
|
||||
__call__ = isOrExtends
|
||||
|
||||
SpecificationBase = SpecificationBasePy
|
||||
try:
|
||||
from _zope_interface_coptimizations import SpecificationBase
|
||||
except ImportError: #pragma NO COVER
|
||||
pass
|
||||
|
||||
_marker = object()
|
||||
class InterfaceBasePy(object):
|
||||
"""Base class that wants to be replaced with a C base :)
|
||||
"""
|
||||
|
||||
def __call__(self, obj, alternate=_marker):
|
||||
"""Adapt an object to the interface
|
||||
"""
|
||||
conform = getattr(obj, '__conform__', None)
|
||||
if conform is not None:
|
||||
adapter = self._call_conform(conform)
|
||||
if adapter is not None:
|
||||
return adapter
|
||||
|
||||
adapter = self.__adapt__(obj)
|
||||
|
||||
if adapter is not None:
|
||||
return adapter
|
||||
elif alternate is not _marker:
|
||||
return alternate
|
||||
else:
|
||||
raise TypeError("Could not adapt", obj, self)
|
||||
|
||||
def __adapt__(self, obj):
|
||||
"""Adapt an object to the reciever
|
||||
"""
|
||||
if self.providedBy(obj):
|
||||
return obj
|
||||
|
||||
for hook in adapter_hooks:
|
||||
adapter = hook(self, obj)
|
||||
if adapter is not None:
|
||||
return adapter
|
||||
|
||||
|
||||
InterfaceBase = InterfaceBasePy
|
||||
try:
|
||||
from _zope_interface_coptimizations import InterfaceBase
|
||||
except ImportError: #pragma NO COVER
|
||||
pass
|
||||
|
||||
|
||||
adapter_hooks = []
|
||||
try:
|
||||
from _zope_interface_coptimizations import adapter_hooks
|
||||
except ImportError: #pragma NO COVER
|
||||
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 bases, their
|
||||
relations with other specifications are adjusted accordingly.
|
||||
"""
|
||||
|
||||
# Copy some base class methods for speed
|
||||
isOrExtends = SpecificationBase.isOrExtends
|
||||
providedBy = SpecificationBase.providedBy
|
||||
|
||||
def __init__(self, bases=()):
|
||||
self._implied = {}
|
||||
self.dependents = weakref.WeakKeyDictionary()
|
||||
self.__bases__ = tuple(bases)
|
||||
|
||||
def subscribe(self, dependent):
|
||||
self.dependents[dependent] = self.dependents.get(dependent, 0) + 1
|
||||
|
||||
def unsubscribe(self, dependent):
|
||||
n = self.dependents.get(dependent, 0) - 1
|
||||
if not n:
|
||||
del self.dependents[dependent]
|
||||
elif n > 0:
|
||||
self.dependents[dependent] = n
|
||||
else:
|
||||
raise KeyError(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(self)
|
||||
|
||||
__bases__ = property(
|
||||
|
||||
lambda self: self.__dict__.get('__bases__', ()),
|
||||
__setBases,
|
||||
)
|
||||
|
||||
def changed(self, originally_changed):
|
||||
"""We, or something we depend on, have changed
|
||||
"""
|
||||
try:
|
||||
del self._v_attrs
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
implied = self._implied
|
||||
implied.clear()
|
||||
|
||||
ancestors = ro(self)
|
||||
|
||||
try:
|
||||
if Interface not in ancestors:
|
||||
ancestors.append(Interface)
|
||||
except NameError:
|
||||
pass # defining Interface itself
|
||||
|
||||
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 tuple(self.dependents.keys()):
|
||||
dependent.changed(originally_changed)
|
||||
|
||||
|
||||
def interfaces(self):
|
||||
"""Return an iterator for the interfaces in the specification.
|
||||
"""
|
||||
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
|
||||
"""
|
||||
return ((interface in self._implied)
|
||||
and
|
||||
((not strict) or (self != interface))
|
||||
)
|
||||
|
||||
def weakref(self, callback=None):
|
||||
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, InterfaceBase, 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 attrs is None:
|
||||
attrs = {}
|
||||
|
||||
if __module__ is None:
|
||||
__module__ = attrs.get('__module__')
|
||||
if isinstance(__module__, str):
|
||||
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): #pragma NO COVERAGE
|
||||
pass
|
||||
|
||||
self.__module__ = __module__
|
||||
|
||||
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__)
|
||||
|
||||
tagged_data = attrs.pop(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 list(attrs.items()):
|
||||
if name in ('__locals__', '__qualname__'):
|
||||
# __locals__: Python 3 sometimes adds this.
|
||||
# __qualname__: PEP 3155 (Python 3.3+)
|
||||
del attrs[name]
|
||||
continue
|
||||
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)
|
||||
elif attr is _decorator_non_return:
|
||||
del attrs[name]
|
||||
else:
|
||||
raise InvalidInterface("Concrete attribute, " + name)
|
||||
|
||||
self.__attrs = attrs
|
||||
|
||||
self.__identifier__ = "%s.%s" % (self.__module__, self.__name__)
|
||||
|
||||
def interfaces(self):
|
||||
"""Return an iterator for the interfaces in the specification.
|
||||
"""
|
||||
yield self
|
||||
|
||||
def getBases(self):
|
||||
return self.__bases__
|
||||
|
||||
def isEqualOrExtendedBy(self, other):
|
||||
"""Same interface or extends?"""
|
||||
return self == other or other.extends(self)
|
||||
|
||||
def names(self, all=False):
|
||||
"""Return the attribute names defined by the interface."""
|
||||
if not all:
|
||||
return self.__attrs.keys()
|
||||
|
||||
r = self.__attrs.copy()
|
||||
|
||||
for base in self.__bases__:
|
||||
r.update(dict.fromkeys(base.names(all)))
|
||||
|
||||
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 base in self.__bases__[::-1]:
|
||||
r.update(dict(base.namesAndDescriptions(all)))
|
||||
|
||||
r.update(self.__attrs)
|
||||
|
||||
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__, klass)
|
||||
klass=klass[self.__name__]
|
||||
|
||||
self.__d(klass)
|
||||
|
||||
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 as 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
|
||||
if errors:
|
||||
raise Invalid(errors)
|
||||
|
||||
#XXX I believe this is a fossil: nobody calls it anywhere.
|
||||
#def _getInterface(self, ob, name):
|
||||
# """Retrieve a named interface."""
|
||||
# return None
|
||||
|
||||
def __d(self, klass):
|
||||
for k, v in self.__attrs.items():
|
||||
if isinstance(v, Method) and not (k in klass.__dict__):
|
||||
setattr(klass, k, v)
|
||||
|
||||
for b in self.__bases__:
|
||||
b.__d(klass)
|
||||
|
||||
def __repr__(self):
|
||||
try:
|
||||
return self._v_repr
|
||||
except AttributeError:
|
||||
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_conform(self, conform):
|
||||
try:
|
||||
return conform(self)
|
||||
except TypeError: #pragma NO COVER
|
||||
# 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
|
||||
|
||||
return None #pragma NO COVER
|
||||
|
||||
def __reduce__(self):
|
||||
return self.__name__
|
||||
|
||||
def __cmp(self, other):
|
||||
# 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 other is None:
|
||||
return -1
|
||||
|
||||
n1 = (getattr(self, '__name__', ''), getattr(self, '__module__', ''))
|
||||
n2 = (getattr(other, '__name__', ''), getattr(other, '__module__', ''))
|
||||
|
||||
# This spelling works under Python3, which doesn't have cmp().
|
||||
return (n1 > n2) - (n1 < n2)
|
||||
|
||||
def __hash__(self):
|
||||
d = self.__dict__
|
||||
if '__module__' not in d or '__name__' not in d: #pragma NO COVER
|
||||
warnings.warn('Hashing uninitialized InterfaceClass instance')
|
||||
return 1
|
||||
return hash((self.__name__, self.__module__))
|
||||
|
||||
def __eq__(self, other):
|
||||
c = self.__cmp(other)
|
||||
return c == 0
|
||||
|
||||
def __ne__(self, other):
|
||||
c = self.__cmp(other)
|
||||
return c != 0
|
||||
|
||||
def __lt__(self, other):
|
||||
c = self.__cmp(other)
|
||||
return c < 0
|
||||
|
||||
def __le__(self, other):
|
||||
c = self.__cmp(other)
|
||||
return c <= 0
|
||||
|
||||
def __gt__(self, other):
|
||||
c = self.__cmp(other)
|
||||
return c > 0
|
||||
|
||||
def __ge__(self, other):
|
||||
c = self.__cmp(other)
|
||||
return c >= 0
|
||||
|
||||
|
||||
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)
|
||||
|
||||
positional = required = ()
|
||||
_optional = varargs = kwargs = None
|
||||
def _get_optional(self):
|
||||
if self._optional is None:
|
||||
return {}
|
||||
return self._optional
|
||||
def _set_optional(self, opt):
|
||||
self._optional = opt
|
||||
def _del_optional(self):
|
||||
self._optional = None
|
||||
optional = property(_get_optional, _set_optional, _del_optional)
|
||||
|
||||
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.append(v)
|
||||
if v in self.optional.keys():
|
||||
sig[-1] += "=" + repr(self.optional[v])
|
||||
if self.varargs:
|
||||
sig.append("*" + self.varargs)
|
||||
if self.kwargs:
|
||||
sig.append("**" + self.kwargs)
|
||||
|
||||
return "(%s)" % ", ".join(sig)
|
||||
|
||||
def fromFunction(func, interface=None, imlevel=0, name=None):
|
||||
name = name or func.__name__
|
||||
method = Method(name, func.__doc__)
|
||||
defaults = getattr(func, '__defaults__', None) or ()
|
||||
code = 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.
|
||||
opt.update(dict(zip(names[nr:], defaults)))
|
||||
|
||||
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):
|
||||
if isinstance(meth, MethodType):
|
||||
func = meth.__func__
|
||||
else:
|
||||
func = meth
|
||||
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)
|
||||
|
||||
from zope.interface.interfaces import ISpecification
|
||||
classImplements(Specification, ISpecification)
|
||||
|
||||
# We import this here to deal with module dependencies.
|
||||
from zope.interface.declarations import implementedBy
|
||||
from zope.interface.declarations import providedBy
|
||||
from zope.interface.exceptions import InvalidInterface
|
||||
from zope.interface.exceptions import BrokenImplementation
|
||||
1288
zope/interface/interfaces.py
Normal file
1288
zope/interface/interfaces.py
Normal file
File diff suppressed because it is too large
Load Diff
530
zope/interface/registry.py
Normal file
530
zope/interface/registry.py
Normal file
@@ -0,0 +1,530 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2006 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Basic components support
|
||||
"""
|
||||
try:
|
||||
from zope.event import notify
|
||||
except ImportError: #pragma NO COVER
|
||||
def notify(*arg, **kw): pass
|
||||
|
||||
from zope.interface.interfaces import ISpecification
|
||||
from zope.interface.interfaces import ComponentLookupError
|
||||
from zope.interface.interfaces import IAdapterRegistration
|
||||
from zope.interface.interfaces import IComponents
|
||||
from zope.interface.interfaces import IHandlerRegistration
|
||||
from zope.interface.interfaces import ISubscriptionAdapterRegistration
|
||||
from zope.interface.interfaces import IUtilityRegistration
|
||||
from zope.interface.interfaces import Registered
|
||||
from zope.interface.interfaces import Unregistered
|
||||
|
||||
from zope.interface.interface import Interface
|
||||
from zope.interface.declarations import implementedBy
|
||||
from zope.interface.declarations import implementer
|
||||
from zope.interface.declarations import implementer_only
|
||||
from zope.interface.declarations import providedBy
|
||||
from zope.interface.adapter import AdapterRegistry
|
||||
from zope.interface._compat import _u
|
||||
from zope.interface._compat import CLASS_TYPES
|
||||
from zope.interface._compat import STRING_TYPES
|
||||
|
||||
|
||||
@implementer(IComponents)
|
||||
class Components(object):
|
||||
|
||||
def __init__(self, name='', bases=()):
|
||||
assert isinstance(name, STRING_TYPES)
|
||||
self.__name__ = name
|
||||
self._init_registries()
|
||||
self._init_registrations()
|
||||
self.__bases__ = tuple(bases)
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s %s>" % (self.__class__.__name__, self.__name__)
|
||||
|
||||
def _init_registries(self):
|
||||
self.adapters = AdapterRegistry()
|
||||
self.utilities = AdapterRegistry()
|
||||
|
||||
def _init_registrations(self):
|
||||
self._utility_registrations = {}
|
||||
self._adapter_registrations = {}
|
||||
self._subscription_registrations = []
|
||||
self._handler_registrations = []
|
||||
|
||||
def _getBases(self):
|
||||
# Subclasses might override
|
||||
return self.__dict__.get('__bases__', ())
|
||||
|
||||
def _setBases(self, bases):
|
||||
# Subclasses might override
|
||||
self.adapters.__bases__ = tuple([
|
||||
base.adapters for base in bases])
|
||||
self.utilities.__bases__ = tuple([
|
||||
base.utilities for base in bases])
|
||||
self.__dict__['__bases__'] = tuple(bases)
|
||||
|
||||
__bases__ = property(
|
||||
lambda self: self._getBases(),
|
||||
lambda self, bases: self._setBases(bases),
|
||||
)
|
||||
|
||||
def registerUtility(self, component=None, provided=None, name=_u(''),
|
||||
info=_u(''), event=True, factory=None):
|
||||
if factory:
|
||||
if component:
|
||||
raise TypeError("Can't specify factory and component.")
|
||||
component = factory()
|
||||
|
||||
if provided is None:
|
||||
provided = _getUtilityProvided(component)
|
||||
|
||||
reg = self._utility_registrations.get((provided, name))
|
||||
if reg is not None:
|
||||
if reg[:2] == (component, info):
|
||||
# already registered
|
||||
return
|
||||
self.unregisterUtility(reg[0], provided, name)
|
||||
|
||||
subscribed = False
|
||||
for ((p, _), data) in iter(self._utility_registrations.items()):
|
||||
if p == provided and data[0] == component:
|
||||
subscribed = True
|
||||
break
|
||||
|
||||
self._utility_registrations[(provided, name)] = component, info, factory
|
||||
self.utilities.register((), provided, name, component)
|
||||
|
||||
if not subscribed:
|
||||
self.utilities.subscribe((), provided, component)
|
||||
|
||||
if event:
|
||||
notify(Registered(
|
||||
UtilityRegistration(self, provided, name, component, info,
|
||||
factory)
|
||||
))
|
||||
|
||||
def unregisterUtility(self, component=None, provided=None, name=_u(''),
|
||||
factory=None):
|
||||
if factory:
|
||||
if component:
|
||||
raise TypeError("Can't specify factory and component.")
|
||||
component = factory()
|
||||
|
||||
if provided is None:
|
||||
if component is None:
|
||||
raise TypeError("Must specify one of component, factory and "
|
||||
"provided")
|
||||
provided = _getUtilityProvided(component)
|
||||
|
||||
old = self._utility_registrations.get((provided, name))
|
||||
if (old is None) or ((component is not None) and
|
||||
(component != old[0])):
|
||||
return False
|
||||
|
||||
if component is None:
|
||||
component = old[0]
|
||||
|
||||
# Note that component is now the old thing registered
|
||||
|
||||
del self._utility_registrations[(provided, name)]
|
||||
self.utilities.unregister((), provided, name)
|
||||
|
||||
subscribed = False
|
||||
for ((p, _), data) in iter(self._utility_registrations.items()):
|
||||
if p == provided and data[0] == component:
|
||||
subscribed = True
|
||||
break
|
||||
|
||||
if not subscribed:
|
||||
self.utilities.unsubscribe((), provided, component)
|
||||
|
||||
notify(Unregistered(
|
||||
UtilityRegistration(self, provided, name, component, *old[1:])
|
||||
))
|
||||
|
||||
return True
|
||||
|
||||
def registeredUtilities(self):
|
||||
for ((provided, name), data
|
||||
) in iter(self._utility_registrations.items()):
|
||||
yield UtilityRegistration(self, provided, name, *data)
|
||||
|
||||
def queryUtility(self, provided, name=_u(''), default=None):
|
||||
return self.utilities.lookup((), provided, name, default)
|
||||
|
||||
def getUtility(self, provided, name=_u('')):
|
||||
utility = self.utilities.lookup((), provided, name)
|
||||
if utility is None:
|
||||
raise ComponentLookupError(provided, name)
|
||||
return utility
|
||||
|
||||
def getUtilitiesFor(self, interface):
|
||||
for name, utility in self.utilities.lookupAll((), interface):
|
||||
yield name, utility
|
||||
|
||||
def getAllUtilitiesRegisteredFor(self, interface):
|
||||
return self.utilities.subscriptions((), interface)
|
||||
|
||||
def registerAdapter(self, factory, required=None, provided=None,
|
||||
name=_u(''), info=_u(''), event=True):
|
||||
if provided is None:
|
||||
provided = _getAdapterProvided(factory)
|
||||
required = _getAdapterRequired(factory, required)
|
||||
self._adapter_registrations[(required, provided, name)
|
||||
] = factory, info
|
||||
self.adapters.register(required, provided, name, factory)
|
||||
|
||||
if event:
|
||||
notify(Registered(
|
||||
AdapterRegistration(self, required, provided, name,
|
||||
factory, info)
|
||||
))
|
||||
|
||||
|
||||
def unregisterAdapter(self, factory=None,
|
||||
required=None, provided=None, name=_u(''),
|
||||
):
|
||||
if provided is None:
|
||||
if factory is None:
|
||||
raise TypeError("Must specify one of factory and provided")
|
||||
provided = _getAdapterProvided(factory)
|
||||
|
||||
if (required is None) and (factory is None):
|
||||
raise TypeError("Must specify one of factory and required")
|
||||
|
||||
required = _getAdapterRequired(factory, required)
|
||||
old = self._adapter_registrations.get((required, provided, name))
|
||||
if (old is None) or ((factory is not None) and
|
||||
(factory != old[0])):
|
||||
return False
|
||||
|
||||
del self._adapter_registrations[(required, provided, name)]
|
||||
self.adapters.unregister(required, provided, name)
|
||||
|
||||
notify(Unregistered(
|
||||
AdapterRegistration(self, required, provided, name,
|
||||
*old)
|
||||
))
|
||||
|
||||
return True
|
||||
|
||||
def registeredAdapters(self):
|
||||
for ((required, provided, name), (component, info)
|
||||
) in iter(self._adapter_registrations.items()):
|
||||
yield AdapterRegistration(self, required, provided, name,
|
||||
component, info)
|
||||
|
||||
def queryAdapter(self, object, interface, name=_u(''), default=None):
|
||||
return self.adapters.queryAdapter(object, interface, name, default)
|
||||
|
||||
def getAdapter(self, object, interface, name=_u('')):
|
||||
adapter = self.adapters.queryAdapter(object, interface, name)
|
||||
if adapter is None:
|
||||
raise ComponentLookupError(object, interface, name)
|
||||
return adapter
|
||||
|
||||
def queryMultiAdapter(self, objects, interface, name=_u(''),
|
||||
default=None):
|
||||
return self.adapters.queryMultiAdapter(
|
||||
objects, interface, name, default)
|
||||
|
||||
def getMultiAdapter(self, objects, interface, name=_u('')):
|
||||
adapter = self.adapters.queryMultiAdapter(objects, interface, name)
|
||||
if adapter is None:
|
||||
raise ComponentLookupError(objects, interface, name)
|
||||
return adapter
|
||||
|
||||
def getAdapters(self, objects, provided):
|
||||
for name, factory in self.adapters.lookupAll(
|
||||
list(map(providedBy, objects)),
|
||||
provided):
|
||||
adapter = factory(*objects)
|
||||
if adapter is not None:
|
||||
yield name, adapter
|
||||
|
||||
def registerSubscriptionAdapter(self,
|
||||
factory, required=None, provided=None,
|
||||
name=_u(''), info=_u(''),
|
||||
event=True):
|
||||
if name:
|
||||
raise TypeError("Named subscribers are not yet supported")
|
||||
if provided is None:
|
||||
provided = _getAdapterProvided(factory)
|
||||
required = _getAdapterRequired(factory, required)
|
||||
self._subscription_registrations.append(
|
||||
(required, provided, name, factory, info)
|
||||
)
|
||||
self.adapters.subscribe(required, provided, factory)
|
||||
|
||||
if event:
|
||||
notify(Registered(
|
||||
SubscriptionRegistration(self, required, provided, name,
|
||||
factory, info)
|
||||
))
|
||||
|
||||
def registeredSubscriptionAdapters(self):
|
||||
for data in self._subscription_registrations:
|
||||
yield SubscriptionRegistration(self, *data)
|
||||
|
||||
def unregisterSubscriptionAdapter(self, factory=None,
|
||||
required=None, provided=None, name=_u(''),
|
||||
):
|
||||
if name:
|
||||
raise TypeError("Named subscribers are not yet supported")
|
||||
if provided is None:
|
||||
if factory is None:
|
||||
raise TypeError("Must specify one of factory and provided")
|
||||
provided = _getAdapterProvided(factory)
|
||||
|
||||
if (required is None) and (factory is None):
|
||||
raise TypeError("Must specify one of factory and required")
|
||||
|
||||
required = _getAdapterRequired(factory, required)
|
||||
|
||||
if factory is None:
|
||||
new = [(r, p, n, f, i)
|
||||
for (r, p, n, f, i)
|
||||
in self._subscription_registrations
|
||||
if not (r == required and p == provided)
|
||||
]
|
||||
else:
|
||||
new = [(r, p, n, f, i)
|
||||
for (r, p, n, f, i)
|
||||
in self._subscription_registrations
|
||||
if not (r == required and p == provided and f == factory)
|
||||
]
|
||||
|
||||
if len(new) == len(self._subscription_registrations):
|
||||
return False
|
||||
|
||||
|
||||
self._subscription_registrations[:] = new
|
||||
self.adapters.unsubscribe(required, provided, factory)
|
||||
|
||||
notify(Unregistered(
|
||||
SubscriptionRegistration(self, required, provided, name,
|
||||
factory, '')
|
||||
))
|
||||
|
||||
return True
|
||||
|
||||
def subscribers(self, objects, provided):
|
||||
return self.adapters.subscribers(objects, provided)
|
||||
|
||||
def registerHandler(self,
|
||||
factory, required=None,
|
||||
name=_u(''), info=_u(''),
|
||||
event=True):
|
||||
if name:
|
||||
raise TypeError("Named handlers are not yet supported")
|
||||
required = _getAdapterRequired(factory, required)
|
||||
self._handler_registrations.append(
|
||||
(required, name, factory, info)
|
||||
)
|
||||
self.adapters.subscribe(required, None, factory)
|
||||
|
||||
if event:
|
||||
notify(Registered(
|
||||
HandlerRegistration(self, required, name, factory, info)
|
||||
))
|
||||
|
||||
def registeredHandlers(self):
|
||||
for data in self._handler_registrations:
|
||||
yield HandlerRegistration(self, *data)
|
||||
|
||||
def unregisterHandler(self, factory=None, required=None, name=_u('')):
|
||||
if name:
|
||||
raise TypeError("Named subscribers are not yet supported")
|
||||
|
||||
if (required is None) and (factory is None):
|
||||
raise TypeError("Must specify one of factory and required")
|
||||
|
||||
required = _getAdapterRequired(factory, required)
|
||||
|
||||
if factory is None:
|
||||
new = [(r, n, f, i)
|
||||
for (r, n, f, i)
|
||||
in self._handler_registrations
|
||||
if r != required
|
||||
]
|
||||
else:
|
||||
new = [(r, n, f, i)
|
||||
for (r, n, f, i)
|
||||
in self._handler_registrations
|
||||
if not (r == required and f == factory)
|
||||
]
|
||||
|
||||
if len(new) == len(self._handler_registrations):
|
||||
return False
|
||||
|
||||
self._handler_registrations[:] = new
|
||||
self.adapters.unsubscribe(required, None, factory)
|
||||
|
||||
notify(Unregistered(
|
||||
HandlerRegistration(self, required, name, factory, '')
|
||||
))
|
||||
|
||||
return True
|
||||
|
||||
def handle(self, *objects):
|
||||
self.adapters.subscribers(objects, None)
|
||||
|
||||
|
||||
def _getUtilityProvided(component):
|
||||
provided = list(providedBy(component))
|
||||
if len(provided) == 1:
|
||||
return provided[0]
|
||||
raise TypeError(
|
||||
"The utility doesn't provide a single interface "
|
||||
"and no provided interface was specified.")
|
||||
|
||||
def _getAdapterProvided(factory):
|
||||
provided = list(implementedBy(factory))
|
||||
if len(provided) == 1:
|
||||
return provided[0]
|
||||
raise TypeError(
|
||||
"The adapter factory doesn't implement a single interface "
|
||||
"and no provided interface was specified.")
|
||||
|
||||
def _getAdapterRequired(factory, required):
|
||||
if required is None:
|
||||
try:
|
||||
required = factory.__component_adapts__
|
||||
except AttributeError:
|
||||
raise TypeError(
|
||||
"The adapter factory doesn't have a __component_adapts__ "
|
||||
"attribute and no required specifications were specified"
|
||||
)
|
||||
elif ISpecification.providedBy(required):
|
||||
raise TypeError("the required argument should be a list of "
|
||||
"interfaces, not a single interface")
|
||||
|
||||
result = []
|
||||
for r in required:
|
||||
if r is None:
|
||||
r = Interface
|
||||
elif not ISpecification.providedBy(r):
|
||||
if isinstance(r, CLASS_TYPES):
|
||||
r = implementedBy(r)
|
||||
else:
|
||||
raise TypeError("Required specification must be a "
|
||||
"specification or class."
|
||||
)
|
||||
result.append(r)
|
||||
return tuple(result)
|
||||
|
||||
|
||||
@implementer(IUtilityRegistration)
|
||||
class UtilityRegistration(object):
|
||||
|
||||
def __init__(self, registry, provided, name, component, doc, factory=None):
|
||||
(self.registry, self.provided, self.name, self.component, self.info,
|
||||
self.factory
|
||||
) = registry, provided, name, component, doc, factory
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r, %s, %r, %s, %r, %r)' % (
|
||||
self.__class__.__name__,
|
||||
self.registry,
|
||||
getattr(self.provided, '__name__', None), self.name,
|
||||
getattr(self.component, '__name__', repr(self.component)),
|
||||
self.factory, self.info,
|
||||
)
|
||||
|
||||
def __hash__(self):
|
||||
return id(self)
|
||||
|
||||
def __eq__(self, other):
|
||||
return repr(self) == repr(other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return repr(self) != repr(other)
|
||||
|
||||
def __lt__(self, other):
|
||||
return repr(self) < repr(other)
|
||||
|
||||
def __le__(self, other):
|
||||
return repr(self) <= repr(other)
|
||||
|
||||
def __gt__(self, other):
|
||||
return repr(self) > repr(other)
|
||||
|
||||
def __ge__(self, other):
|
||||
return repr(self) >= repr(other)
|
||||
|
||||
@implementer(IAdapterRegistration)
|
||||
class AdapterRegistration(object):
|
||||
|
||||
def __init__(self, registry, required, provided, name, component, doc):
|
||||
(self.registry, self.required, self.provided, self.name,
|
||||
self.factory, self.info
|
||||
) = registry, required, provided, name, component, doc
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r, %s, %s, %r, %s, %r)' % (
|
||||
self.__class__.__name__,
|
||||
self.registry,
|
||||
'[' + ", ".join([r.__name__ for r in self.required]) + ']',
|
||||
getattr(self.provided, '__name__', None), self.name,
|
||||
getattr(self.factory, '__name__', repr(self.factory)), self.info,
|
||||
)
|
||||
|
||||
def __hash__(self):
|
||||
return id(self)
|
||||
|
||||
def __eq__(self, other):
|
||||
return repr(self) == repr(other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return repr(self) != repr(other)
|
||||
|
||||
def __lt__(self, other):
|
||||
return repr(self) < repr(other)
|
||||
|
||||
def __le__(self, other):
|
||||
return repr(self) <= repr(other)
|
||||
|
||||
def __gt__(self, other):
|
||||
return repr(self) > repr(other)
|
||||
|
||||
def __ge__(self, other):
|
||||
return repr(self) >= repr(other)
|
||||
|
||||
@implementer_only(ISubscriptionAdapterRegistration)
|
||||
class SubscriptionRegistration(AdapterRegistration):
|
||||
pass
|
||||
|
||||
|
||||
@implementer_only(IHandlerRegistration)
|
||||
class HandlerRegistration(AdapterRegistration):
|
||||
|
||||
def __init__(self, registry, required, name, handler, doc):
|
||||
(self.registry, self.required, self.name, self.handler, self.info
|
||||
) = registry, required, name, handler, doc
|
||||
|
||||
@property
|
||||
def factory(self):
|
||||
return self.handler
|
||||
|
||||
provided = None
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r, %s, %r, %s, %r)' % (
|
||||
self.__class__.__name__,
|
||||
self.registry,
|
||||
'[' + ", ".join([r.__name__ for r in self.required]) + ']',
|
||||
self.name,
|
||||
getattr(self.factory, '__name__', repr(self.factory)), self.info,
|
||||
)
|
||||
|
||||
69
zope/interface/ro.py
Normal file
69
zope/interface/ro.py
Normal file
@@ -0,0 +1,69 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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 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 = [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
|
||||
13
zope/interface/tests/__init__.py
Normal file
13
zope/interface/tests/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
import os
|
||||
import unittest
|
||||
|
||||
def additional_tests():
|
||||
suites = unittest.TestSuite()
|
||||
for file in os.listdir(os.path.dirname(__file__)):
|
||||
if file.endswith('.py') and file!='__init__.py':
|
||||
name = os.path.splitext(file)[0]
|
||||
module = __import__('.'.join((__name__, name)), globals(),
|
||||
locals(), [name])
|
||||
if hasattr(module, 'test_suite'):
|
||||
suites.addTests(module.test_suite())
|
||||
return suites
|
||||
42
zope/interface/tests/advisory_testing.py
Normal file
42
zope/interface/tests/advisory_testing.py
Normal file
@@ -0,0 +1,42 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
import sys
|
||||
|
||||
from zope.interface.advice import addClassAdvisor
|
||||
from zope.interface.advice import getFrameInfo
|
||||
|
||||
my_globals = globals()
|
||||
|
||||
def ping(log, value):
|
||||
|
||||
def pong(klass):
|
||||
log.append((value,klass))
|
||||
return [klass]
|
||||
|
||||
addClassAdvisor(pong)
|
||||
|
||||
try:
|
||||
from types import ClassType
|
||||
|
||||
class ClassicClass:
|
||||
__metaclass__ = ClassType
|
||||
classLevelFrameInfo = getFrameInfo(sys._getframe())
|
||||
except ImportError:
|
||||
ClassicClass = None
|
||||
|
||||
class NewStyleClass:
|
||||
__metaclass__ = type
|
||||
classLevelFrameInfo = getFrameInfo(sys._getframe())
|
||||
|
||||
moduleLevelFrameInfo = getFrameInfo(sys._getframe())
|
||||
23
zope/interface/tests/dummy.py
Normal file
23
zope/interface/tests/dummy.py
Normal file
@@ -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.
|
||||
#
|
||||
##############################################################################
|
||||
""" Dummy Module
|
||||
"""
|
||||
from zope.interface import moduleProvides
|
||||
from zope.interface.tests.idummy import IDummyModule
|
||||
|
||||
moduleProvides(IDummyModule)
|
||||
|
||||
def bar(baz):
|
||||
# Note: no 'self', because the module provides the interface directly.
|
||||
pass
|
||||
23
zope/interface/tests/idummy.py
Normal file
23
zope/interface/tests/idummy.py
Normal file
@@ -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.
|
||||
"""
|
||||
26
zope/interface/tests/ifoo.py
Normal file
26
zope/interface/tests/ifoo.py
Normal file
@@ -0,0 +1,26 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""IFoo test module
|
||||
"""
|
||||
from zope.interface import Interface
|
||||
|
||||
class IFoo(Interface):
|
||||
"""
|
||||
Dummy interface for unit tests.
|
||||
"""
|
||||
|
||||
def bar(baz):
|
||||
"""
|
||||
Just a note.
|
||||
"""
|
||||
26
zope/interface/tests/ifoo_other.py
Normal file
26
zope/interface/tests/ifoo_other.py
Normal file
@@ -0,0 +1,26 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""IFoo test module
|
||||
"""
|
||||
from zope.interface import Interface
|
||||
|
||||
class IFoo(Interface):
|
||||
"""
|
||||
Dummy interface for unit tests.
|
||||
"""
|
||||
|
||||
def bar(baz):
|
||||
"""
|
||||
Just a note.
|
||||
"""
|
||||
21
zope/interface/tests/m1.py
Normal file
21
zope/interface/tests/m1.py
Normal file
@@ -0,0 +1,21 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Test module that declares an interface
|
||||
"""
|
||||
from zope.interface import Interface, moduleProvides
|
||||
|
||||
class I1(Interface): pass
|
||||
class I2(Interface): pass
|
||||
|
||||
moduleProvides(I1, I2)
|
||||
15
zope/interface/tests/m2.py
Normal file
15
zope/interface/tests/m2.py
Normal file
@@ -0,0 +1,15 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Test module that doesn't declare an interface
|
||||
"""
|
||||
129
zope/interface/tests/odd.py
Normal file
129
zope/interface/tests/odd.py
Normal file
@@ -0,0 +1,129 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""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__ == (object,)
|
||||
True
|
||||
>>> 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
|
||||
>>> import sys
|
||||
>>> if sys.version[0] == '2': # This test only makes sense under Python 2.x
|
||||
... from types import ClassType
|
||||
... assert not isinstance(C, (type, ClassType))
|
||||
|
||||
>>> int(C.__class__.__class__ is C.__class__)
|
||||
1
|
||||
"""
|
||||
|
||||
# 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)
|
||||
1285
zope/interface/tests/test_adapter.py
Normal file
1285
zope/interface/tests/test_adapter.py
Normal file
File diff suppressed because it is too large
Load Diff
397
zope/interface/tests/test_advice.py
Normal file
397
zope/interface/tests/test_advice.py
Normal file
@@ -0,0 +1,397 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""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.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
|
||||
from zope.interface._compat import _skip_under_py2
|
||||
from zope.interface._compat import _skip_under_py3k
|
||||
|
||||
|
||||
class _SilencePy3Deprecations(unittest.TestCase):
|
||||
# silence deprecation warnings under py3
|
||||
|
||||
def failUnless(self, expr):
|
||||
# St00pid speling.
|
||||
return self.assertTrue(expr)
|
||||
|
||||
def failIf(self, expr):
|
||||
# St00pid speling.
|
||||
return self.assertFalse(expr)
|
||||
|
||||
|
||||
class FrameInfoTest(_SilencePy3Deprecations):
|
||||
|
||||
def test_w_module(self):
|
||||
from zope.interface.tests import advisory_testing
|
||||
(kind, module,
|
||||
f_locals, f_globals) = advisory_testing.moduleLevelFrameInfo
|
||||
self.assertEqual(kind, "module")
|
||||
for d in module.__dict__, f_locals, f_globals:
|
||||
self.failUnless(d is advisory_testing.my_globals)
|
||||
|
||||
@_skip_under_py3k
|
||||
def test_w_ClassicClass(self):
|
||||
from zope.interface.tests import advisory_testing
|
||||
if advisory_testing.ClassicClass is None:
|
||||
return
|
||||
(kind,
|
||||
module,
|
||||
f_locals,
|
||||
f_globals) = advisory_testing.ClassicClass.classLevelFrameInfo
|
||||
self.assertEqual(kind, "class")
|
||||
|
||||
self.failUnless(
|
||||
f_locals is advisory_testing.ClassicClass.__dict__) # ???
|
||||
for d in module.__dict__, f_globals:
|
||||
self.failUnless(d is advisory_testing.my_globals)
|
||||
|
||||
def test_w_NewStyleClass(self):
|
||||
from zope.interface.tests import advisory_testing
|
||||
(kind,
|
||||
module,
|
||||
f_locals,
|
||||
f_globals) = advisory_testing.NewStyleClass.classLevelFrameInfo
|
||||
self.assertEqual(kind, "class")
|
||||
|
||||
for d in module.__dict__, f_globals:
|
||||
self.failUnless(d is advisory_testing.my_globals)
|
||||
|
||||
def test_inside_function_call(self):
|
||||
from zope.interface.advice import getFrameInfo
|
||||
kind, module, f_locals, f_globals = getFrameInfo(sys._getframe())
|
||||
self.assertEqual(kind, "function call")
|
||||
self.failUnless(f_locals is locals()) # ???
|
||||
for d in module.__dict__, f_globals:
|
||||
self.failUnless(d is globals())
|
||||
|
||||
def test_inside_exec(self):
|
||||
from zope.interface.advice import getFrameInfo
|
||||
_globals = {'getFrameInfo': getFrameInfo}
|
||||
_locals = {}
|
||||
exec(_FUNKY_EXEC, _globals, _locals)
|
||||
self.assertEqual(_locals['kind'], "exec")
|
||||
self.failUnless(_locals['f_locals'] is _locals)
|
||||
self.failUnless(_locals['module'] is None)
|
||||
self.failUnless(_locals['f_globals'] is _globals)
|
||||
|
||||
|
||||
_FUNKY_EXEC = """\
|
||||
import sys
|
||||
kind, module, f_locals, f_globals = getFrameInfo(sys._getframe())
|
||||
"""
|
||||
|
||||
class AdviceTests(_SilencePy3Deprecations):
|
||||
|
||||
@_skip_under_py3k
|
||||
def test_order(self):
|
||||
from zope.interface.tests.advisory_testing import ping
|
||||
log = []
|
||||
class Foo(object):
|
||||
ping(log, 1)
|
||||
ping(log, 2)
|
||||
ping(log, 3)
|
||||
|
||||
# Strip the list nesting
|
||||
for i in 1, 2, 3:
|
||||
self.failUnless(isinstance(Foo, list))
|
||||
Foo, = Foo
|
||||
|
||||
self.assertEqual(log, [(1, Foo), (2, [Foo]), (3, [[Foo]])])
|
||||
|
||||
def TODOtest_outside(self):
|
||||
from zope.interface.tests.advisory_testing import ping
|
||||
# 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"
|
||||
)
|
||||
|
||||
@_skip_under_py3k
|
||||
def test_single_explicit_meta(self):
|
||||
from zope.interface.tests.advisory_testing import ping
|
||||
|
||||
class Metaclass(type):
|
||||
pass
|
||||
|
||||
class Concrete(Metaclass):
|
||||
__metaclass__ = Metaclass
|
||||
ping([],1)
|
||||
|
||||
Concrete, = Concrete
|
||||
self.failUnless(Concrete.__class__ is Metaclass)
|
||||
|
||||
|
||||
@_skip_under_py3k
|
||||
def test_mixed_metas(self):
|
||||
from zope.interface.tests.advisory_testing import ping
|
||||
|
||||
class Metaclass1(type):
|
||||
pass
|
||||
|
||||
class Metaclass2(type):
|
||||
pass
|
||||
|
||||
class Base1:
|
||||
__metaclass__ = Metaclass1
|
||||
|
||||
class Base2:
|
||||
__metaclass__ = Metaclass2
|
||||
|
||||
try:
|
||||
class Derived(Base1, Base2):
|
||||
ping([], 1)
|
||||
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError("Should have gotten incompatibility error")
|
||||
|
||||
class Metaclass3(Metaclass1, Metaclass2):
|
||||
pass
|
||||
|
||||
class Derived(Base1, Base2):
|
||||
__metaclass__ = Metaclass3
|
||||
ping([], 1)
|
||||
|
||||
self.failUnless(isinstance(Derived, list))
|
||||
Derived, = Derived
|
||||
self.failUnless(isinstance(Derived, Metaclass3))
|
||||
|
||||
@_skip_under_py3k
|
||||
def test_meta_no_bases(self):
|
||||
from zope.interface.tests.advisory_testing import ping
|
||||
try:
|
||||
from types import ClassType
|
||||
except ImportError:
|
||||
return
|
||||
class Thing:
|
||||
ping([], 1)
|
||||
klass, = Thing # unpack list created by pong
|
||||
self.assertEqual(type(klass), ClassType)
|
||||
|
||||
|
||||
class Test_isClassAdvisor(_SilencePy3Deprecations):
|
||||
|
||||
def _callFUT(self, *args, **kw):
|
||||
from zope.interface.advice import isClassAdvisor
|
||||
return isClassAdvisor(*args, **kw)
|
||||
|
||||
def test_w_non_function(self):
|
||||
self.assertEqual(self._callFUT(self), False)
|
||||
|
||||
def test_w_normal_function(self):
|
||||
def foo():
|
||||
pass
|
||||
self.assertEqual(self._callFUT(foo), False)
|
||||
|
||||
def test_w_advisor_function(self):
|
||||
def bar():
|
||||
pass
|
||||
bar.previousMetaclass = object()
|
||||
self.assertEqual(self._callFUT(bar), True)
|
||||
|
||||
|
||||
class Test_determineMetaclass(_SilencePy3Deprecations):
|
||||
|
||||
def _callFUT(self, *args, **kw):
|
||||
from zope.interface.advice import determineMetaclass
|
||||
return determineMetaclass(*args, **kw)
|
||||
|
||||
@_skip_under_py3k
|
||||
def test_empty(self):
|
||||
from types import ClassType
|
||||
self.assertEqual(self._callFUT(()), ClassType)
|
||||
|
||||
def test_empty_w_explicit_metatype(self):
|
||||
class Meta(type):
|
||||
pass
|
||||
self.assertEqual(self._callFUT((), Meta), Meta)
|
||||
|
||||
def test_single(self):
|
||||
class Meta(type):
|
||||
pass
|
||||
self.assertEqual(self._callFUT((Meta,)), type)
|
||||
|
||||
@_skip_under_py3k
|
||||
def test_meta_of_class(self):
|
||||
class Metameta(type):
|
||||
pass
|
||||
|
||||
class Meta(type):
|
||||
__metaclass__ = Metameta
|
||||
|
||||
self.assertEqual(self._callFUT((Meta, type)), Metameta)
|
||||
|
||||
@_skip_under_py2
|
||||
def test_meta_of_class_py3k(self):
|
||||
# Work around SyntaxError under Python2.
|
||||
EXEC = '\n'.join([
|
||||
'class Metameta(type):',
|
||||
' pass',
|
||||
'class Meta(type, metaclass=Metameta):',
|
||||
' pass',
|
||||
])
|
||||
globs = {}
|
||||
exec(EXEC, globs)
|
||||
Meta = globs['Meta']
|
||||
Metameta = globs['Metameta']
|
||||
|
||||
self.assertEqual(self._callFUT((Meta, type)), Metameta)
|
||||
|
||||
@_skip_under_py3k
|
||||
def test_multiple_in_hierarchy(self):
|
||||
class Meta_A(type):
|
||||
pass
|
||||
class Meta_B(Meta_A):
|
||||
pass
|
||||
class A(type):
|
||||
__metaclass__ = Meta_A
|
||||
class B(type):
|
||||
__metaclass__ = Meta_B
|
||||
self.assertEqual(self._callFUT((A, B,)), Meta_B)
|
||||
|
||||
@_skip_under_py2
|
||||
def test_multiple_in_hierarchy_py3k(self):
|
||||
# Work around SyntaxError under Python2.
|
||||
EXEC = '\n'.join([
|
||||
'class Meta_A(type):',
|
||||
' pass',
|
||||
'class Meta_B(Meta_A):',
|
||||
' pass',
|
||||
'class A(type, metaclass=Meta_A):',
|
||||
' pass',
|
||||
'class B(type, metaclass=Meta_B):',
|
||||
' pass',
|
||||
])
|
||||
globs = {}
|
||||
exec(EXEC, globs)
|
||||
Meta_A = globs['Meta_A']
|
||||
Meta_B = globs['Meta_B']
|
||||
A = globs['A']
|
||||
B = globs['B']
|
||||
self.assertEqual(self._callFUT((A, B)), Meta_B)
|
||||
|
||||
@_skip_under_py3k
|
||||
def test_multiple_not_in_hierarchy(self):
|
||||
class Meta_A(type):
|
||||
pass
|
||||
class Meta_B(type):
|
||||
pass
|
||||
class A(type):
|
||||
__metaclass__ = Meta_A
|
||||
class B(type):
|
||||
__metaclass__ = Meta_B
|
||||
self.assertRaises(TypeError, self._callFUT, (A, B,))
|
||||
|
||||
@_skip_under_py2
|
||||
def test_multiple_not_in_hierarchy_py3k(self):
|
||||
# Work around SyntaxError under Python2.
|
||||
EXEC = '\n'.join([
|
||||
'class Meta_A(type):',
|
||||
' pass',
|
||||
'class Meta_B(type):',
|
||||
' pass',
|
||||
'class A(type, metaclass=Meta_A):',
|
||||
' pass',
|
||||
'class B(type, metaclass=Meta_B):',
|
||||
' pass',
|
||||
])
|
||||
globs = {}
|
||||
exec(EXEC, globs)
|
||||
Meta_A = globs['Meta_A']
|
||||
Meta_B = globs['Meta_B']
|
||||
A = globs['A']
|
||||
B = globs['B']
|
||||
self.assertRaises(TypeError, self._callFUT, (A, B))
|
||||
|
||||
|
||||
class Test_minimalBases(_SilencePy3Deprecations):
|
||||
|
||||
def _callFUT(self, klasses):
|
||||
from zope.interface.advice import minimalBases
|
||||
return minimalBases(klasses)
|
||||
|
||||
def test_empty(self):
|
||||
self.assertEqual(self._callFUT([]), [])
|
||||
|
||||
@_skip_under_py3k
|
||||
def test_w_oldstyle_meta(self):
|
||||
class C:
|
||||
pass
|
||||
self.assertEqual(self._callFUT([type(C)]), [])
|
||||
|
||||
@_skip_under_py3k
|
||||
def test_w_oldstyle_class(self):
|
||||
class C:
|
||||
pass
|
||||
self.assertEqual(self._callFUT([C]), [C])
|
||||
|
||||
def test_w_newstyle_meta(self):
|
||||
self.assertEqual(self._callFUT([type]), [type])
|
||||
|
||||
def test_w_newstyle_class(self):
|
||||
class C(object):
|
||||
pass
|
||||
self.assertEqual(self._callFUT([C]), [C])
|
||||
|
||||
def test_simple_hierarchy_skips_implied(self):
|
||||
class A(object):
|
||||
pass
|
||||
class B(A):
|
||||
pass
|
||||
class C(B):
|
||||
pass
|
||||
class D(object):
|
||||
pass
|
||||
self.assertEqual(self._callFUT([A, B, C]), [C])
|
||||
self.assertEqual(self._callFUT([A, C]), [C])
|
||||
self.assertEqual(self._callFUT([B, C]), [C])
|
||||
self.assertEqual(self._callFUT([A, B]), [B])
|
||||
self.assertEqual(self._callFUT([D, B, D]), [B, D])
|
||||
|
||||
def test_repeats_kicked_to_end_of_queue(self):
|
||||
class A(object):
|
||||
pass
|
||||
class B(object):
|
||||
pass
|
||||
self.assertEqual(self._callFUT([A, B, A]), [B, A])
|
||||
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(FrameInfoTest),
|
||||
unittest.makeSuite(AdviceTests),
|
||||
unittest.makeSuite(Test_isClassAdvisor),
|
||||
unittest.makeSuite(Test_determineMetaclass),
|
||||
unittest.makeSuite(Test_minimalBases),
|
||||
))
|
||||
1576
zope/interface/tests/test_declarations.py
Normal file
1576
zope/interface/tests/test_declarations.py
Normal file
File diff suppressed because it is too large
Load Diff
286
zope/interface/tests/test_document.py
Normal file
286
zope/interface/tests/test_document.py
Normal file
@@ -0,0 +1,286 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Documentation tests.
|
||||
"""
|
||||
import unittest
|
||||
|
||||
|
||||
class Test_asStructuredText(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, iface):
|
||||
from zope.interface.document import asStructuredText
|
||||
return asStructuredText(iface)
|
||||
|
||||
def test_asStructuredText_no_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"INoDocstring",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
""
|
||||
])
|
||||
class INoDocstring(Interface):
|
||||
pass
|
||||
self.assertEqual(self._callFUT(INoDocstring), EXPECTED)
|
||||
|
||||
def test_asStructuredText_empty_with_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IEmpty",
|
||||
" This is an empty interface.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
""
|
||||
])
|
||||
class IEmpty(Interface):
|
||||
""" This is an empty interface.
|
||||
"""
|
||||
self.assertEqual(self._callFUT(IEmpty), EXPECTED)
|
||||
|
||||
def test_asStructuredText_empty_with_multiline_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n'.join([
|
||||
"IEmpty",
|
||||
"",
|
||||
" This is an empty interface.",
|
||||
" ",
|
||||
(" It can be used to annotate any class or object, "
|
||||
"because it promises"),
|
||||
" nothing.",
|
||||
"",
|
||||
" Attributes:",
|
||||
"",
|
||||
" Methods:",
|
||||
"",
|
||||
""
|
||||
])
|
||||
class IEmpty(Interface):
|
||||
""" This is an empty interface.
|
||||
|
||||
It can be used to annotate any class or object, because it promises
|
||||
nothing.
|
||||
"""
|
||||
self.assertEqual(self._callFUT(IEmpty), EXPECTED)
|
||||
|
||||
def test_asStructuredText_with_attribute_no_docstring(self):
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IHasAttribute",
|
||||
" This interface has an attribute.",
|
||||
" Attributes:",
|
||||
" an_attribute -- no documentation",
|
||||
" Methods:",
|
||||
""
|
||||
])
|
||||
class IHasAttribute(Interface):
|
||||
""" This interface has an attribute.
|
||||
"""
|
||||
an_attribute = Attribute('an_attribute')
|
||||
|
||||
self.assertEqual(self._callFUT(IHasAttribute), EXPECTED)
|
||||
|
||||
def test_asStructuredText_with_attribute_with_docstring(self):
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IHasAttribute",
|
||||
" This interface has an attribute.",
|
||||
" Attributes:",
|
||||
" an_attribute -- This attribute is documented.",
|
||||
" Methods:",
|
||||
""
|
||||
])
|
||||
class IHasAttribute(Interface):
|
||||
""" This interface has an attribute.
|
||||
"""
|
||||
an_attribute = Attribute('an_attribute',
|
||||
'This attribute is documented.')
|
||||
|
||||
self.assertEqual(self._callFUT(IHasAttribute), EXPECTED)
|
||||
|
||||
def test_asStructuredText_with_method_no_args_no_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IHasMethod",
|
||||
" This interface has a method.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
" aMethod() -- no documentation",
|
||||
""
|
||||
])
|
||||
class IHasMethod(Interface):
|
||||
""" This interface has a method.
|
||||
"""
|
||||
def aMethod():
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT(IHasMethod), EXPECTED)
|
||||
|
||||
def test_asStructuredText_with_method_positional_args_no_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IHasMethod",
|
||||
" This interface has a method.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
" aMethod(first, second) -- no documentation",
|
||||
""
|
||||
])
|
||||
class IHasMethod(Interface):
|
||||
""" This interface has a method.
|
||||
"""
|
||||
def aMethod(first, second):
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT(IHasMethod), EXPECTED)
|
||||
|
||||
def test_asStructuredText_with_method_starargs_no_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IHasMethod",
|
||||
" This interface has a method.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
" aMethod(first, second, *rest) -- no documentation",
|
||||
""
|
||||
])
|
||||
class IHasMethod(Interface):
|
||||
""" This interface has a method.
|
||||
"""
|
||||
def aMethod(first, second, *rest):
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT(IHasMethod), EXPECTED)
|
||||
|
||||
def test_asStructuredText_with_method_kwargs_no_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IHasMethod",
|
||||
" This interface has a method.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
" aMethod(first, second, **kw) -- no documentation",
|
||||
""
|
||||
])
|
||||
class IHasMethod(Interface):
|
||||
""" This interface has a method.
|
||||
"""
|
||||
def aMethod(first, second, **kw):
|
||||
pass
|
||||
|
||||
self.assertEqual(self._callFUT(IHasMethod), EXPECTED)
|
||||
|
||||
def test_asStructuredText_with_method_with_docstring(self):
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IHasMethod",
|
||||
" This interface has a method.",
|
||||
" Attributes:",
|
||||
" Methods:",
|
||||
" aMethod() -- This method is documented.",
|
||||
""
|
||||
])
|
||||
class IHasMethod(Interface):
|
||||
""" This interface has a method.
|
||||
"""
|
||||
def aMethod():
|
||||
"""This method is documented.
|
||||
"""
|
||||
|
||||
self.assertEqual(self._callFUT(IHasMethod), EXPECTED)
|
||||
|
||||
def test_asStructuredText_derived_ignores_base(self):
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
EXPECTED = '\n\n'.join([
|
||||
"IDerived",
|
||||
" IDerived doc",
|
||||
" This interface extends:",
|
||||
" o IBase",
|
||||
" Attributes:",
|
||||
" attr1 -- no documentation",
|
||||
" attr2 -- attr2 doc",
|
||||
" Methods:",
|
||||
" method3() -- method3 doc",
|
||||
" method4() -- no documentation",
|
||||
" method5() -- method5 doc",
|
||||
"",
|
||||
])
|
||||
|
||||
class IBase(Interface):
|
||||
def method1():
|
||||
pass
|
||||
def method2():
|
||||
pass
|
||||
|
||||
class IDerived(IBase):
|
||||
"IDerived doc"
|
||||
attr1 = Attribute('attr1')
|
||||
attr2 = Attribute('attr2', 'attr2 doc')
|
||||
|
||||
def method3():
|
||||
"method3 doc"
|
||||
def method4():
|
||||
pass
|
||||
def method5():
|
||||
"method5 doc"
|
||||
|
||||
self.assertEqual(self._callFUT(IDerived), EXPECTED)
|
||||
|
||||
class Test__justify_and_indent(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, text, level, **kw):
|
||||
from zope.interface.document import _justify_and_indent
|
||||
return _justify_and_indent(text, level, **kw)
|
||||
|
||||
def test_simple_level_0(self):
|
||||
LINES = ['Three blind mice', 'See how they run']
|
||||
text = '\n'.join(LINES)
|
||||
self.assertEqual(self._callFUT(text, 0), text)
|
||||
|
||||
def test_simple_level_1(self):
|
||||
LINES = ['Three blind mice', 'See how they run']
|
||||
text = '\n'.join(LINES)
|
||||
self.assertEqual(self._callFUT(text, 1),
|
||||
'\n'.join([' ' + line for line in LINES]))
|
||||
|
||||
def test_simple_level_2(self):
|
||||
LINES = ['Three blind mice', 'See how they run']
|
||||
text = '\n'.join(LINES)
|
||||
self.assertEqual(self._callFUT(text, 1),
|
||||
'\n'.join([' ' + line for line in LINES]))
|
||||
|
||||
def test_simple_w_CRLF(self):
|
||||
LINES = ['Three blind mice', 'See how they run']
|
||||
text = '\r\n'.join(LINES)
|
||||
self.assertEqual(self._callFUT(text, 1),
|
||||
'\n'.join([' ' + line for line in LINES]))
|
||||
|
||||
def test_with_munge(self):
|
||||
TEXT = ("This is a piece of text longer than 15 characters, \n"
|
||||
"and split across multiple lines.")
|
||||
EXPECTED = (" This is a piece\n"
|
||||
" of text longer\n"
|
||||
" than 15 characters,\n"
|
||||
" and split across\n"
|
||||
" multiple lines.\n"
|
||||
" ")
|
||||
self.assertEqual(self._callFUT(TEXT, 1, munge=1, width=15), EXPECTED)
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(Test_asStructuredText),
|
||||
unittest.makeSuite(Test__justify_and_indent),
|
||||
))
|
||||
41
zope/interface/tests/test_element.py
Normal file
41
zope/interface/tests/test_element.py
Normal file
@@ -0,0 +1,41 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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)
|
||||
|
||||
|
||||
def test_suite():
|
||||
suite = unittest.TestSuite()
|
||||
suite.addTest(unittest.makeSuite(TestElement))
|
||||
return suite
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(defaultTest='test_suite')
|
||||
75
zope/interface/tests/test_exceptions.py
Normal file
75
zope/interface/tests/test_exceptions.py
Normal file
@@ -0,0 +1,75 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2010 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.
|
||||
#
|
||||
##############################################################################
|
||||
""" zope.interface.exceptions unit tests
|
||||
"""
|
||||
import unittest
|
||||
|
||||
def _makeIface():
|
||||
from zope.interface import Interface
|
||||
class IDummy(Interface):
|
||||
pass
|
||||
return IDummy
|
||||
|
||||
class DoesNotImplementTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.interface.exceptions import DoesNotImplement
|
||||
return DoesNotImplement
|
||||
|
||||
def _makeOne(self, iface=None):
|
||||
if iface is None:
|
||||
iface = _makeIface()
|
||||
return self._getTargetClass()(iface)
|
||||
|
||||
def test___str__(self):
|
||||
dni = self._makeOne()
|
||||
# XXX The trailing newlines and blank spaces are a stupid artifact.
|
||||
self.assertEqual(str(dni),
|
||||
'An object does not implement interface <InterfaceClass '
|
||||
'zope.interface.tests.test_exceptions.IDummy>\n\n ')
|
||||
|
||||
class BrokenImplementationTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.interface.exceptions import BrokenImplementation
|
||||
return BrokenImplementation
|
||||
|
||||
def _makeOne(self, iface=None, name='missing'):
|
||||
if iface is None:
|
||||
iface = _makeIface()
|
||||
return self._getTargetClass()(iface, name)
|
||||
|
||||
def test___str__(self):
|
||||
dni = self._makeOne()
|
||||
# XXX The trailing newlines and blank spaces are a stupid artifact.
|
||||
self.assertEqual(str(dni),
|
||||
'An object has failed to implement interface <InterfaceClass '
|
||||
'zope.interface.tests.test_exceptions.IDummy>\n\n'
|
||||
' The missing attribute was not provided.\n ')
|
||||
|
||||
class BrokenMethodImplementationTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
return BrokenMethodImplementation
|
||||
|
||||
def _makeOne(self, method='aMethod', mess='I said so'):
|
||||
return self._getTargetClass()(method, mess)
|
||||
|
||||
def test___str__(self):
|
||||
dni = self._makeOne()
|
||||
self.assertEqual(str(dni),
|
||||
'The implementation of aMethod violates its contract\n'
|
||||
' because I said so.\n ')
|
||||
|
||||
2089
zope/interface/tests/test_interface.py
Normal file
2089
zope/interface/tests/test_interface.py
Normal file
File diff suppressed because it is too large
Load Diff
115
zope/interface/tests/test_interfaces.py
Normal file
115
zope/interface/tests/test_interfaces.py
Normal file
@@ -0,0 +1,115 @@
|
||||
import unittest
|
||||
|
||||
class _SilencePy3Deprecations(unittest.TestCase):
|
||||
# silence deprecation warnings under py3
|
||||
|
||||
def failUnless(self, expr):
|
||||
# St00pid speling.
|
||||
return self.assertTrue(expr)
|
||||
|
||||
def failIf(self, expr):
|
||||
# St00pid speling.
|
||||
return self.assertFalse(expr)
|
||||
|
||||
|
||||
class _ConformsToIObjectEvent(object):
|
||||
|
||||
def _makeOne(self, target=None):
|
||||
if target is None:
|
||||
target = object()
|
||||
return self._getTargetClass()(target)
|
||||
|
||||
def test_class_conforms_to_IObjectEvent(self):
|
||||
from zope.interface.interfaces import IObjectEvent
|
||||
from zope.interface.verify import verifyClass
|
||||
verifyClass(IObjectEvent, self._getTargetClass())
|
||||
|
||||
def test_instance_conforms_to_IObjectEvent(self):
|
||||
from zope.interface.interfaces import IObjectEvent
|
||||
from zope.interface.verify import verifyObject
|
||||
verifyObject(IObjectEvent, self._makeOne())
|
||||
|
||||
|
||||
class _ConformsToIRegistrationEvent(_ConformsToIObjectEvent):
|
||||
|
||||
def test_class_conforms_to_IRegistrationEvent(self):
|
||||
from zope.interface.interfaces import IRegistrationEvent
|
||||
from zope.interface.verify import verifyClass
|
||||
verifyClass(IRegistrationEvent, self._getTargetClass())
|
||||
|
||||
def test_instance_conforms_to_IRegistrationEvent(self):
|
||||
from zope.interface.interfaces import IRegistrationEvent
|
||||
from zope.interface.verify import verifyObject
|
||||
verifyObject(IRegistrationEvent, self._makeOne())
|
||||
|
||||
|
||||
class ObjectEventTests(_SilencePy3Deprecations, _ConformsToIObjectEvent):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.interface.interfaces import ObjectEvent
|
||||
return ObjectEvent
|
||||
|
||||
def test_ctor(self):
|
||||
target = object()
|
||||
event = self._makeOne(target)
|
||||
self.failUnless(event.object is target)
|
||||
|
||||
|
||||
class RegistrationEventTests(_SilencePy3Deprecations,
|
||||
_ConformsToIRegistrationEvent):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.interface.interfaces import RegistrationEvent
|
||||
return RegistrationEvent
|
||||
|
||||
def test___repr__(self):
|
||||
target = object()
|
||||
event = self._makeOne(target)
|
||||
r = repr(event)
|
||||
self.assertEqual(r.splitlines(),
|
||||
['RegistrationEvent event:', repr(target)])
|
||||
|
||||
|
||||
class RegisteredTests(_SilencePy3Deprecations,
|
||||
_ConformsToIRegistrationEvent):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.interface.interfaces import Registered
|
||||
return Registered
|
||||
|
||||
def test_class_conforms_to_IRegistered(self):
|
||||
from zope.interface.interfaces import IRegistered
|
||||
from zope.interface.verify import verifyClass
|
||||
verifyClass(IRegistered, self._getTargetClass())
|
||||
|
||||
def test_instance_conforms_to_IRegistered(self):
|
||||
from zope.interface.interfaces import IRegistered
|
||||
from zope.interface.verify import verifyObject
|
||||
verifyObject(IRegistered, self._makeOne())
|
||||
|
||||
|
||||
class UnregisteredTests(_SilencePy3Deprecations,
|
||||
_ConformsToIRegistrationEvent):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.interface.interfaces import Unregistered
|
||||
return Unregistered
|
||||
|
||||
def test_class_conforms_to_IUnregistered(self):
|
||||
from zope.interface.interfaces import IUnregistered
|
||||
from zope.interface.verify import verifyClass
|
||||
verifyClass(IUnregistered, self._getTargetClass())
|
||||
|
||||
def test_instance_conforms_to_IUnregistered(self):
|
||||
from zope.interface.interfaces import IUnregistered
|
||||
from zope.interface.verify import verifyObject
|
||||
verifyObject(IUnregistered, self._makeOne())
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(ObjectEventTests),
|
||||
unittest.makeSuite(RegistrationEventTests),
|
||||
unittest.makeSuite(RegisteredTests),
|
||||
unittest.makeSuite(UnregisteredTests),
|
||||
))
|
||||
227
zope/interface/tests/test_odd_declarations.py
Normal file
227
zope/interface/tests/test_odd_declarations.py
Normal file
@@ -0,0 +1,227 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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 interface declarations against ExtensionClass-like classes.
|
||||
|
||||
These tests are to make sure we do something sane in the presence of
|
||||
classic ExtensionClass classes and instances.
|
||||
"""
|
||||
import unittest
|
||||
|
||||
from zope.interface.tests import odd
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface import directlyProvides
|
||||
from zope.interface import providedBy
|
||||
from zope.interface import directlyProvidedBy
|
||||
from zope.interface import classImplements
|
||||
from zope.interface import classImplementsOnly
|
||||
from zope.interface import implementedBy
|
||||
from zope.interface._compat import _skip_under_py3k
|
||||
|
||||
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):
|
||||
pass
|
||||
classImplements(A, I1)
|
||||
|
||||
class C(A, B):
|
||||
pass
|
||||
classImplements(C, I31)
|
||||
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
def failUnless(self, expr): # silence deprecation warnings under py3
|
||||
return self.assertTrue(expr)
|
||||
|
||||
def failIf(self, expr): # silence deprecation warnings under py3
|
||||
return self.assertFalse(expr)
|
||||
|
||||
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.failUnless(I1 in providedBy(c))
|
||||
self.failIf(I3 in providedBy(c))
|
||||
self.failUnless(providedBy(c).extends(I3))
|
||||
self.failUnless(providedBy(c).extends(I31))
|
||||
self.failIf(providedBy(c).extends(I5))
|
||||
|
||||
class COnly(A, B):
|
||||
pass
|
||||
classImplementsOnly(COnly, I31)
|
||||
|
||||
class D(COnly):
|
||||
pass
|
||||
classImplements(D, 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.failUnless(providedBy(c).extends(I3))
|
||||
self.failIf(providedBy(c).extends(I1))
|
||||
self.failUnless(providedBy(c).extends(I31))
|
||||
self.failUnless(providedBy(c).extends(I5))
|
||||
|
||||
class COnly(A, B): __implemented__ = I31
|
||||
class D(COnly):
|
||||
pass
|
||||
classImplements(D, 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.failUnless(providedBy(c).extends(I3))
|
||||
self.failIf(providedBy(c).extends(I1))
|
||||
self.failUnless(providedBy(c).extends(I31))
|
||||
self.failUnless(providedBy(c).extends(I5))
|
||||
|
||||
def test_classImplements(self):
|
||||
|
||||
@implementer(I3)
|
||||
class A(Odd):
|
||||
pass
|
||||
|
||||
@implementer(I4)
|
||||
class B(Odd):
|
||||
pass
|
||||
|
||||
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):
|
||||
@implementer(I3)
|
||||
class A(Odd):
|
||||
pass
|
||||
|
||||
@implementer(I4)
|
||||
class B(Odd):
|
||||
pass
|
||||
|
||||
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):
|
||||
pass
|
||||
classImplements(A, IA1, IA2)
|
||||
|
||||
class B(Odd):
|
||||
pass
|
||||
classImplements(B, IB)
|
||||
|
||||
class C(A, B):
|
||||
pass
|
||||
classImplements(C, IC)
|
||||
|
||||
|
||||
ob = C()
|
||||
directlyProvides(ob, I1, I2)
|
||||
self.failUnless(I1 in providedBy(ob))
|
||||
self.failUnless(I2 in providedBy(ob))
|
||||
self.failUnless(IA1 in providedBy(ob))
|
||||
self.failUnless(IA2 in providedBy(ob))
|
||||
self.failUnless(IB in providedBy(ob))
|
||||
self.failUnless(IC in providedBy(ob))
|
||||
|
||||
directlyProvides(ob, directlyProvidedBy(ob)-I2)
|
||||
self.failUnless(I1 in providedBy(ob))
|
||||
self.failIf(I2 in providedBy(ob))
|
||||
self.failIf(I2 in providedBy(ob))
|
||||
directlyProvides(ob, directlyProvidedBy(ob), I2)
|
||||
self.failUnless(I2 in providedBy(ob))
|
||||
|
||||
@_skip_under_py3k
|
||||
def test_directlyProvides_fails_for_odd_class(self):
|
||||
self.assertRaises(TypeError, directlyProvides, C, I5)
|
||||
|
||||
# see above
|
||||
#def TODO_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):
|
||||
pass
|
||||
classImplements(C1, I2)
|
||||
|
||||
class C2(C1):
|
||||
pass
|
||||
classImplements(C2, I3)
|
||||
|
||||
self.assertEqual([i.getName() for i in implementedBy(C2)],
|
||||
['I3', 'I2'])
|
||||
|
||||
def test_suite():
|
||||
import doctest
|
||||
suite = unittest.TestSuite()
|
||||
suite.addTest(unittest.makeSuite(Test))
|
||||
suite.addTest(doctest.DocTestSuite(odd))
|
||||
return suite
|
||||
2454
zope/interface/tests/test_registry.py
Normal file
2454
zope/interface/tests/test_registry.py
Normal file
File diff suppressed because it is too large
Load Diff
55
zope/interface/tests/test_sorting.py
Normal file
55
zope/interface/tests/test_sorting.py
Normal file
@@ -0,0 +1,55 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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
|
||||
"""
|
||||
|
||||
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, 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])
|
||||
|
||||
def test_suite():
|
||||
return TestSuite((
|
||||
makeSuite(Test),
|
||||
))
|
||||
|
||||
if __name__=='__main__':
|
||||
main(defaultTest='test_suite')
|
||||
571
zope/interface/tests/test_verify.py
Normal file
571
zope/interface/tests/test_verify.py
Normal file
@@ -0,0 +1,571 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
""" zope.interface.verify unit tests
|
||||
"""
|
||||
import unittest
|
||||
|
||||
|
||||
class Test_verifyClass(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, iface, klass):
|
||||
from zope.interface.verify import verifyClass
|
||||
return verifyClass(iface, klass)
|
||||
|
||||
def test_class_doesnt_implement(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface.exceptions import DoesNotImplement
|
||||
|
||||
class ICurrent(Interface):
|
||||
pass
|
||||
|
||||
class Current(object):
|
||||
pass
|
||||
|
||||
self.assertRaises(DoesNotImplement, self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_class_doesnt_implement_but_classImplements_later(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import classImplements
|
||||
|
||||
class ICurrent(Interface):
|
||||
pass
|
||||
|
||||
class Current(object):
|
||||
pass
|
||||
|
||||
classImplements(Current, ICurrent)
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_class_doesnt_have_required_method_simple(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
def method(): pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
pass
|
||||
|
||||
self.assertRaises(BrokenImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_class_has_required_method_simple(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
def method(): pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self):
|
||||
pass
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_class_doesnt_have_required_method_derived(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenImplementation
|
||||
|
||||
class IBase(Interface):
|
||||
def method():
|
||||
pass
|
||||
|
||||
class IDerived(IBase):
|
||||
pass
|
||||
|
||||
@implementer(IDerived)
|
||||
class Current(object):
|
||||
pass
|
||||
|
||||
self.assertRaises(BrokenImplementation,
|
||||
self._callFUT, IDerived, Current)
|
||||
|
||||
def test_class_has_required_method_derived(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class IBase(Interface):
|
||||
def method():
|
||||
pass
|
||||
|
||||
class IDerived(IBase):
|
||||
pass
|
||||
|
||||
@implementer(IDerived)
|
||||
class Current(object):
|
||||
|
||||
def method(self):
|
||||
pass
|
||||
|
||||
self._callFUT(IDerived, Current)
|
||||
|
||||
def test_method_takes_wrong_arg_names_but_OK(self):
|
||||
# We no longer require names to match.
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self, b):
|
||||
pass
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_method_takes_not_enough_args(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self):
|
||||
pass
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_method_doesnt_take_required_starargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(*args):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self):
|
||||
pass
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_method_doesnt_take_required_only_kwargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(**kw):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self):
|
||||
pass
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_method_takes_extra_arg(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self, a, b):
|
||||
pass
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_method_takes_extra_arg_with_default(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self, a, b=None):
|
||||
pass
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_method_takes_only_positional_args(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self, *args):
|
||||
pass
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_method_takes_only_kwargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self, **kw):
|
||||
pass
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_method_takes_extra_starargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self, a, *args):
|
||||
pass
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_method_takes_extra_starargs_and_kwargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self, a, *args, **kw):
|
||||
pass
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_method_doesnt_take_required_positional_and_starargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a, *args):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self, a):
|
||||
pass
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_method_takes_required_positional_and_starargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a, *args):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self, a, *args):
|
||||
pass
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_method_takes_only_starargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a, *args):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self, *args):
|
||||
pass
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_method_takes_required_kwargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(**kwargs):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self, **kw):
|
||||
pass
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_method_takes_positional_plus_required_starargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(*args):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self, a, *args):
|
||||
pass
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
|
||||
def test_method_doesnt_take_required_kwargs(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(**kwargs):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
def method(self, a):
|
||||
pass
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
|
||||
def test_class_has_method_for_iface_attr(self):
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
attr = Attribute("The foo Attribute")
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
def attr(self):
|
||||
pass
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_class_has_nonmethod_for_method(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenMethodImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
def method():
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
method = 1
|
||||
|
||||
self.assertRaises(BrokenMethodImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_class_has_attribute_for_attribute(self):
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
attr = Attribute("The foo Attribute")
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
|
||||
attr = 1
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_class_misses_attribute_for_attribute(self):
|
||||
# This check *passes* for verifyClass
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class ICurrent(Interface):
|
||||
attr = Attribute("The foo Attribute")
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
pass
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
def test_w_callable_non_func_method(self):
|
||||
from zope.interface.interface import Method
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
class QuasiMethod(Method):
|
||||
def __call__(self, *args, **kw):
|
||||
pass
|
||||
|
||||
class QuasiCallable(object):
|
||||
def __call__(self, *args, **kw):
|
||||
pass
|
||||
|
||||
class ICurrent(Interface):
|
||||
attr = QuasiMethod('This is callable')
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
attr = QuasiCallable()
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
|
||||
def test_w_decorated_method(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
|
||||
def decorator(func):
|
||||
# this is, in fact, zope.proxy.non_overridable
|
||||
return property(lambda self: func.__get__(self))
|
||||
|
||||
class ICurrent(Interface):
|
||||
|
||||
def method(a):
|
||||
pass
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current(object):
|
||||
|
||||
@decorator
|
||||
def method(self, a):
|
||||
pass
|
||||
|
||||
self._callFUT(ICurrent, Current)
|
||||
|
||||
class Test_verifyObject(Test_verifyClass):
|
||||
|
||||
def _callFUT(self, iface, target):
|
||||
from zope.interface.verify import verifyObject
|
||||
if isinstance(target, (type, type(OldSkool))):
|
||||
target = target()
|
||||
return verifyObject(iface, target)
|
||||
|
||||
def test_class_misses_attribute_for_attribute(self):
|
||||
# This check *fails* for verifyObject
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import Interface
|
||||
from zope.interface import implementer
|
||||
from zope.interface.exceptions import BrokenImplementation
|
||||
|
||||
class ICurrent(Interface):
|
||||
attr = Attribute("The foo Attribute")
|
||||
|
||||
@implementer(ICurrent)
|
||||
class Current:
|
||||
pass
|
||||
|
||||
self.assertRaises(BrokenImplementation,
|
||||
self._callFUT, ICurrent, Current)
|
||||
|
||||
def test_module_hit(self):
|
||||
from zope.interface.tests.idummy import IDummyModule
|
||||
from zope.interface.tests import dummy
|
||||
|
||||
self._callFUT(IDummyModule, dummy)
|
||||
|
||||
def test_module_miss(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface.tests import dummy
|
||||
from zope.interface.exceptions import DoesNotImplement
|
||||
|
||||
# same name, different object
|
||||
class IDummyModule(Interface):
|
||||
pass
|
||||
|
||||
self.assertRaises(DoesNotImplement,
|
||||
self._callFUT, IDummyModule, dummy)
|
||||
|
||||
class OldSkool:
|
||||
pass
|
||||
|
||||
def test_suite():
|
||||
#import doctest
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(Test_verifyClass),
|
||||
unittest.makeSuite(Test_verifyObject),
|
||||
# This one needs to turn into just docs.
|
||||
#doctest.DocFileSuite('../verify.txt',
|
||||
# optionflags=doctest.NORMALIZE_WHITESPACE),
|
||||
))
|
||||
120
zope/interface/verify.py
Normal file
120
zope/interface/verify.py
Normal file
@@ -0,0 +1,120 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Verify interface implementations
|
||||
"""
|
||||
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
|
||||
import sys
|
||||
|
||||
# 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):
|
||||
try:
|
||||
attr = getattr(candidate, name)
|
||||
except AttributeError:
|
||||
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)
|
||||
|
||||
if not isinstance(desc, Method):
|
||||
# If it's not a method, there's nothing else we can test
|
||||
continue
|
||||
|
||||
if isinstance(attr, FunctionType):
|
||||
if sys.version[0] == '3' and isinstance(candidate, type):
|
||||
# This is an "unbound method" in Python 3.
|
||||
meth = fromFunction(attr, iface, name=name,
|
||||
imlevel=1) #pragma NO COVERAGE
|
||||
else:
|
||||
# Nope, just a normal function
|
||||
meth = fromFunction(attr, iface, name=name)
|
||||
elif (isinstance(attr, MethodTypes)
|
||||
and type(attr.__func__) is FunctionType):
|
||||
meth = fromMethod(attr, iface, name)
|
||||
elif isinstance(attr, property) and vtype == 'c':
|
||||
# We without an instance we cannot be sure it's not a
|
||||
# callable.
|
||||
continue
|
||||
else:
|
||||
if not callable(attr):
|
||||
raise BrokenMethodImplementation(name, "Not a method")
|
||||
# sigh, it's callable, but we don't know how to introspect it, so
|
||||
# we have to give it a pass.
|
||||
continue #pragma NO COVERAGE
|
||||
|
||||
# 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"
|
||||
77
zope/schema/__init__.py
Normal file
77
zope/schema/__init__.py
Normal file
@@ -0,0 +1,77 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Schema package constructor
|
||||
"""
|
||||
# Field APIs
|
||||
from zope.schema._field import ASCII
|
||||
from zope.schema._field import ASCIILine
|
||||
from zope.schema._field import Bool
|
||||
from zope.schema._field import Bytes
|
||||
from zope.schema._field import BytesLine
|
||||
from zope.schema._field import Choice
|
||||
from zope.schema._field import Container
|
||||
from zope.schema._field import Date
|
||||
from zope.schema._field import Datetime
|
||||
from zope.schema._field import Decimal
|
||||
from zope.schema._field import Dict
|
||||
from zope.schema._field import DottedName
|
||||
from zope.schema._field import Field
|
||||
from zope.schema._field import Float
|
||||
from zope.schema._field import FrozenSet
|
||||
from zope.schema._field import Id
|
||||
from zope.schema._field import Int
|
||||
from zope.schema._field import InterfaceField
|
||||
from zope.schema._field import Iterable
|
||||
from zope.schema._field import List
|
||||
from zope.schema._field import MinMaxLen
|
||||
from zope.schema._field import NativeString
|
||||
from zope.schema._field import NativeStringLine
|
||||
from zope.schema._field import Object
|
||||
from zope.schema._field import Orderable
|
||||
from zope.schema._field import Password
|
||||
from zope.schema._field import Set
|
||||
from zope.schema._field import SourceText
|
||||
from zope.schema._field import Text
|
||||
from zope.schema._field import TextLine
|
||||
from zope.schema._field import Time
|
||||
from zope.schema._field import Timedelta
|
||||
from zope.schema._field import Tuple
|
||||
from zope.schema._field import URI
|
||||
|
||||
# Schema APIs
|
||||
from zope.schema._schema import getFields
|
||||
from zope.schema._schema import getFieldsInOrder
|
||||
from zope.schema._schema import getFieldNames
|
||||
from zope.schema._schema import getFieldNamesInOrder
|
||||
from zope.schema._schema import getValidationErrors
|
||||
from zope.schema._schema import getSchemaValidationErrors
|
||||
|
||||
# Acessor API
|
||||
from zope.schema.accessors import accessors
|
||||
|
||||
# Error API
|
||||
from zope.schema.interfaces import ValidationError
|
||||
from zope.schema._bootstrapinterfaces import NO_VALUE
|
||||
|
||||
|
||||
# pep 8 friendlyness
|
||||
ASCII, ASCIILine, Bool, Bytes, BytesLine, Choice, Container, Date, Datetime
|
||||
Decimal, Dict, DottedName, Field, Float, FrozenSet, Id, Int, InterfaceField
|
||||
Iterable, List, MinMaxLen, NativeString, NativeStringLine, Object, Orderable
|
||||
Password, Set, SourceText, Text, TextLine, Time, Timedelta, Tuple, URI
|
||||
getFields, getFieldsInOrder, getFieldNames, getFieldNamesInOrder,
|
||||
getValidationErrors, getSchemaValidationErrors
|
||||
accessors
|
||||
ValidationError
|
||||
NO_VALUE
|
||||
441
zope/schema/_bootstrapfields.py
Normal file
441
zope/schema/_bootstrapfields.py
Normal file
@@ -0,0 +1,441 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Bootstrapping fields
|
||||
"""
|
||||
__docformat__ = 'restructuredtext'
|
||||
|
||||
from zope.interface import Attribute
|
||||
from zope.interface import providedBy
|
||||
from zope.interface import implementer
|
||||
|
||||
from zope.schema._bootstrapinterfaces import ConstraintNotSatisfied
|
||||
from zope.schema._bootstrapinterfaces import IContextAwareDefaultFactory
|
||||
from zope.schema._bootstrapinterfaces import IFromUnicode
|
||||
from zope.schema._bootstrapinterfaces import NotAContainer
|
||||
from zope.schema._bootstrapinterfaces import NotAnIterator
|
||||
from zope.schema._bootstrapinterfaces import RequiredMissing
|
||||
from zope.schema._bootstrapinterfaces import StopValidation
|
||||
from zope.schema._bootstrapinterfaces import TooBig
|
||||
from zope.schema._bootstrapinterfaces import TooLong
|
||||
from zope.schema._bootstrapinterfaces import TooShort
|
||||
from zope.schema._bootstrapinterfaces import TooSmall
|
||||
from zope.schema._bootstrapinterfaces import WrongType
|
||||
from zope.schema._compat import u
|
||||
from zope.schema._compat import text_type
|
||||
from zope.schema._compat import integer_types
|
||||
|
||||
from zope.schema._schema import getFields
|
||||
|
||||
|
||||
class ValidatedProperty(object):
|
||||
|
||||
def __init__(self, name, check=None):
|
||||
self._info = name, check
|
||||
|
||||
def __set__(self, inst, value):
|
||||
name, check = self._info
|
||||
if value != inst.missing_value:
|
||||
if check is not None:
|
||||
check(inst, value)
|
||||
else:
|
||||
inst.validate(value)
|
||||
inst.__dict__[name] = value
|
||||
|
||||
def __get__(self, inst, owner):
|
||||
name, check = self._info
|
||||
return inst.__dict__[name]
|
||||
|
||||
|
||||
class DefaultProperty(ValidatedProperty):
|
||||
|
||||
def __get__(self, inst, owner):
|
||||
name, check = self._info
|
||||
defaultFactory = inst.__dict__.get('defaultFactory')
|
||||
# If there is no default factory, simply return the default.
|
||||
if defaultFactory is None:
|
||||
return inst.__dict__[name]
|
||||
# Get the default value by calling the factory. Some factories might
|
||||
# require a context to produce a value.
|
||||
if IContextAwareDefaultFactory.providedBy(defaultFactory):
|
||||
value = defaultFactory(inst.context)
|
||||
else:
|
||||
value = defaultFactory()
|
||||
# Check that the created value is valid.
|
||||
if check is not None:
|
||||
check(inst, value)
|
||||
elif value != inst.missing_value:
|
||||
inst.validate(value)
|
||||
return value
|
||||
|
||||
|
||||
class Field(Attribute):
|
||||
|
||||
# Type restrictions, if any
|
||||
_type = None
|
||||
context = None
|
||||
|
||||
# If a field has no assigned value, it will be set to missing_value.
|
||||
missing_value = None
|
||||
|
||||
# This is the default value for the missing_value argument to the
|
||||
# Field constructor. A marker is helpful since we don't want to
|
||||
# overwrite missing_value if it is set differently on a Field
|
||||
# subclass and isn't specified via the constructor.
|
||||
__missing_value_marker = object()
|
||||
|
||||
# Note that the "order" field has a dual existance:
|
||||
# 1. The class variable Field.order is used as a source for the
|
||||
# monotonically increasing values used to provide...
|
||||
# 2. The instance variable self.order which provides a
|
||||
# monotonically increasing value that tracks the creation order
|
||||
# of Field (including Field subclass) instances.
|
||||
order = 0
|
||||
|
||||
default = DefaultProperty('default')
|
||||
|
||||
# These were declared as slots in zope.interface, we override them here to
|
||||
# get rid of the dedcriptors so they don't break .bind()
|
||||
__name__ = None
|
||||
interface = None
|
||||
_Element__tagged_values = None
|
||||
|
||||
def __init__(self, title=u(''), description=u(''), __name__='',
|
||||
required=True, readonly=False, constraint=None, default=None,
|
||||
defaultFactory=None, missing_value=__missing_value_marker):
|
||||
"""Pass in field values as keyword parameters.
|
||||
|
||||
|
||||
Generally, you want to pass either a title and description, or
|
||||
a doc string. If you pass no doc string, it will be computed
|
||||
from the title and description. If you pass a doc string that
|
||||
follows the Python coding style (title line separated from the
|
||||
body by a blank line), the title and description will be
|
||||
computed from the doc string. Unfortunately, the doc string
|
||||
must be passed as a positional argument.
|
||||
|
||||
Here are some examples:
|
||||
|
||||
>>> from zope.schema._compat import u
|
||||
>>> f = Field()
|
||||
>>> f.__doc__, f.title, f.description
|
||||
('', u'', u'')
|
||||
|
||||
>>> f = Field(title=u('sample'))
|
||||
>>> f.__doc__, f.title, f.description
|
||||
(u'sample', u'sample', u'')
|
||||
|
||||
>>> f = Field(title=u('sample'), description=u('blah blah\\nblah'))
|
||||
>>> f.__doc__, f.title, f.description
|
||||
(u'sample\\n\\nblah blah\\nblah', u'sample', u'blah blah\\nblah')
|
||||
"""
|
||||
__doc__ = ''
|
||||
if title:
|
||||
if description:
|
||||
__doc__ = "%s\n\n%s" % (title, description)
|
||||
else:
|
||||
__doc__ = title
|
||||
elif description:
|
||||
__doc__ = description
|
||||
|
||||
super(Field, self).__init__(__name__, __doc__)
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.required = required
|
||||
self.readonly = readonly
|
||||
if constraint is not None:
|
||||
self.constraint = constraint
|
||||
self.default = default
|
||||
self.defaultFactory = defaultFactory
|
||||
|
||||
# Keep track of the order of field definitions
|
||||
Field.order += 1
|
||||
self.order = Field.order
|
||||
|
||||
if missing_value is not self.__missing_value_marker:
|
||||
self.missing_value = missing_value
|
||||
|
||||
def constraint(self, value):
|
||||
return True
|
||||
|
||||
def bind(self, object):
|
||||
clone = self.__class__.__new__(self.__class__)
|
||||
clone.__dict__.update(self.__dict__)
|
||||
clone.context = object
|
||||
return clone
|
||||
|
||||
def validate(self, value):
|
||||
if value == self.missing_value:
|
||||
if self.required:
|
||||
raise RequiredMissing(self.__name__)
|
||||
else:
|
||||
try:
|
||||
self._validate(value)
|
||||
except StopValidation:
|
||||
pass
|
||||
|
||||
def __eq__(self, other):
|
||||
# should be the same type
|
||||
if type(self) != type(other):
|
||||
return False
|
||||
|
||||
# should have the same properties
|
||||
names = {} # used as set of property names, ignoring values
|
||||
for interface in providedBy(self):
|
||||
names.update(getFields(interface))
|
||||
|
||||
# order will be different always, don't compare it
|
||||
if 'order' in names:
|
||||
del names['order']
|
||||
for name in names:
|
||||
if getattr(self, name) != getattr(other, name):
|
||||
return False
|
||||
return True
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def _validate(self, value):
|
||||
if self._type is not None and not isinstance(value, self._type):
|
||||
raise WrongType(value, self._type, self.__name__)
|
||||
|
||||
if not self.constraint(value):
|
||||
raise ConstraintNotSatisfied(value, self.__name__)
|
||||
|
||||
def get(self, object):
|
||||
return getattr(object, self.__name__)
|
||||
|
||||
def query(self, object, default=None):
|
||||
return getattr(object, self.__name__, default)
|
||||
|
||||
def set(self, object, value):
|
||||
if self.readonly:
|
||||
raise TypeError("Can't set values on read-only fields "
|
||||
"(name=%s, class=%s.%s)"
|
||||
% (self.__name__,
|
||||
object.__class__.__module__,
|
||||
object.__class__.__name__))
|
||||
setattr(object, self.__name__, value)
|
||||
|
||||
|
||||
class Container(Field):
|
||||
|
||||
def _validate(self, value):
|
||||
super(Container, self)._validate(value)
|
||||
|
||||
if not hasattr(value, '__contains__'):
|
||||
try:
|
||||
iter(value)
|
||||
except TypeError:
|
||||
raise NotAContainer(value)
|
||||
|
||||
|
||||
# XXX This class violates the Liskov Substituability Principle: it
|
||||
# is derived from Container, but cannot be used everywhere an instance
|
||||
# of Container could be, because it's '_validate' is more restrictive.
|
||||
class Iterable(Container):
|
||||
|
||||
def _validate(self, value):
|
||||
super(Iterable, self)._validate(value)
|
||||
|
||||
# See if we can get an iterator for it
|
||||
try:
|
||||
iter(value)
|
||||
except TypeError:
|
||||
raise NotAnIterator(value)
|
||||
|
||||
|
||||
class Orderable(object):
|
||||
"""Values of ordered fields can be sorted.
|
||||
|
||||
They can be restricted to a range of values.
|
||||
|
||||
Orderable is a mixin used in combination with Field.
|
||||
"""
|
||||
|
||||
min = ValidatedProperty('min')
|
||||
max = ValidatedProperty('max')
|
||||
|
||||
def __init__(self, min=None, max=None, default=None, **kw):
|
||||
|
||||
# Set min and max to None so that we can validate if
|
||||
# one of the super methods invoke validation.
|
||||
self.min = None
|
||||
self.max = None
|
||||
|
||||
super(Orderable, self).__init__(**kw)
|
||||
|
||||
# Now really set min and max
|
||||
self.min = min
|
||||
self.max = max
|
||||
|
||||
# We've taken over setting default so it can be limited by min
|
||||
# and max.
|
||||
self.default = default
|
||||
|
||||
def _validate(self, value):
|
||||
super(Orderable, self)._validate(value)
|
||||
|
||||
if self.min is not None and value < self.min:
|
||||
raise TooSmall(value, self.min)
|
||||
|
||||
if self.max is not None and value > self.max:
|
||||
raise TooBig(value, self.max)
|
||||
|
||||
|
||||
class MinMaxLen(object):
|
||||
"""Expresses constraints on the length of a field.
|
||||
|
||||
MinMaxLen is a mixin used in combination with Field.
|
||||
"""
|
||||
min_length = 0
|
||||
max_length = None
|
||||
|
||||
def __init__(self, min_length=0, max_length=None, **kw):
|
||||
self.min_length = min_length
|
||||
self.max_length = max_length
|
||||
super(MinMaxLen, self).__init__(**kw)
|
||||
|
||||
def _validate(self, value):
|
||||
super(MinMaxLen, self)._validate(value)
|
||||
|
||||
if self.min_length is not None and len(value) < self.min_length:
|
||||
raise TooShort(value, self.min_length)
|
||||
|
||||
if self.max_length is not None and len(value) > self.max_length:
|
||||
raise TooLong(value, self.max_length)
|
||||
|
||||
|
||||
@implementer(IFromUnicode)
|
||||
class Text(MinMaxLen, Field):
|
||||
"""A field containing text used for human discourse."""
|
||||
_type = text_type
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(Text, self).__init__(*args, **kw)
|
||||
|
||||
def fromUnicode(self, str):
|
||||
"""
|
||||
>>> from zope.schema._compat import u
|
||||
>>> from zope.schema._compat import b
|
||||
>>> t = Text(constraint=lambda v: 'x' in v)
|
||||
>>> t.fromUnicode(b("foo x spam"))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
WrongType: ('foo x spam', <type 'unicode'>, '')
|
||||
>>> t.fromUnicode(u("foo x spam"))
|
||||
u'foo x spam'
|
||||
>>> t.fromUnicode(u("foo spam"))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ConstraintNotSatisfied: (u'foo spam', '')
|
||||
"""
|
||||
self.validate(str)
|
||||
return str
|
||||
|
||||
|
||||
class TextLine(Text):
|
||||
"""A text field with no newlines."""
|
||||
|
||||
def constraint(self, value):
|
||||
return '\n' not in value and '\r' not in value
|
||||
|
||||
|
||||
class Password(TextLine):
|
||||
"""A text field containing a text used as a password."""
|
||||
|
||||
UNCHANGED_PASSWORD = object()
|
||||
|
||||
def set(self, context, value):
|
||||
"""Update the password.
|
||||
|
||||
We use a special marker value that a widget can use
|
||||
to tell us that the password didn't change. This is
|
||||
needed to support edit forms that don't display the
|
||||
existing password and want to work together with
|
||||
encryption.
|
||||
|
||||
"""
|
||||
if value is self.UNCHANGED_PASSWORD:
|
||||
return
|
||||
super(Password, self).set(context, value)
|
||||
|
||||
def validate(self, value):
|
||||
try:
|
||||
existing = bool(self.get(self.context))
|
||||
except AttributeError:
|
||||
existing = False
|
||||
if value is self.UNCHANGED_PASSWORD and existing:
|
||||
# Allow the UNCHANGED_PASSWORD value, if a password is set already
|
||||
return
|
||||
return super(Password, self).validate(value)
|
||||
|
||||
|
||||
class Bool(Field):
|
||||
"""A field representing a Bool."""
|
||||
|
||||
_type = bool
|
||||
|
||||
def _validate(self, value):
|
||||
# Convert integers to bools to they don't get mis-flagged
|
||||
# by the type check later.
|
||||
if isinstance(value, int):
|
||||
value = bool(value)
|
||||
Field._validate(self, value)
|
||||
|
||||
def set(self, object, value):
|
||||
if isinstance(value, int):
|
||||
value = bool(value)
|
||||
Field.set(self, object, value)
|
||||
|
||||
def fromUnicode(self, str):
|
||||
"""
|
||||
>>> from zope.schema._compat import b
|
||||
>>> b = Bool()
|
||||
>>> IFromUnicode.providedBy(b)
|
||||
True
|
||||
>>> b.fromUnicode('True')
|
||||
True
|
||||
>>> b.fromUnicode('')
|
||||
False
|
||||
>>> b.fromUnicode('true')
|
||||
True
|
||||
>>> b.fromUnicode('false') or b.fromUnicode('False')
|
||||
False
|
||||
"""
|
||||
v = str == 'True' or str == 'true'
|
||||
self.validate(v)
|
||||
return v
|
||||
|
||||
|
||||
@implementer(IFromUnicode)
|
||||
class Int(Orderable, Field):
|
||||
"""A field representing an Integer."""
|
||||
_type = integer_types
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(Int, self).__init__(*args, **kw)
|
||||
|
||||
def fromUnicode(self, str):
|
||||
"""
|
||||
>>> f = Int()
|
||||
>>> f.fromUnicode("125")
|
||||
125
|
||||
>>> f.fromUnicode("125.6") #doctest: +IGNORE_EXCEPTION_DETAIL
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: invalid literal for int(): 125.6
|
||||
"""
|
||||
v = int(str)
|
||||
self.validate(v)
|
||||
return v
|
||||
120
zope/schema/_bootstrapinterfaces.py
Normal file
120
zope/schema/_bootstrapinterfaces.py
Normal file
@@ -0,0 +1,120 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Bootstrap schema interfaces and exceptions
|
||||
"""
|
||||
import zope.interface
|
||||
|
||||
from zope.schema._messageid import _
|
||||
|
||||
|
||||
class StopValidation(Exception):
|
||||
"""Raised if the validation is completed early.
|
||||
|
||||
Note that this exception should be always caught, since it is just
|
||||
a way for the validator to save time.
|
||||
"""
|
||||
|
||||
|
||||
class ValidationError(zope.interface.Invalid):
|
||||
"""Raised if the Validation process fails."""
|
||||
|
||||
def doc(self):
|
||||
return self.__class__.__doc__
|
||||
|
||||
def __cmp__(self, other):
|
||||
if not hasattr(other, 'args'):
|
||||
return -1
|
||||
return cmp(self.args, other.args)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not hasattr(other, 'args'):
|
||||
return False
|
||||
return self.args == other.args
|
||||
|
||||
__hash__ = zope.interface.Invalid.__hash__ # python3
|
||||
|
||||
def __repr__(self): # pragma: no cover
|
||||
return '%s(%s)' % (self.__class__.__name__,
|
||||
', '.join(repr(arg) for arg in self.args))
|
||||
|
||||
|
||||
class RequiredMissing(ValidationError):
|
||||
__doc__ = _("""Required input is missing.""")
|
||||
|
||||
|
||||
class WrongType(ValidationError):
|
||||
__doc__ = _("""Object is of wrong type.""")
|
||||
|
||||
|
||||
class TooBig(ValidationError):
|
||||
__doc__ = _("""Value is too big""")
|
||||
|
||||
|
||||
class TooSmall(ValidationError):
|
||||
__doc__ = _("""Value is too small""")
|
||||
|
||||
|
||||
class TooLong(ValidationError):
|
||||
__doc__ = _("""Value is too long""")
|
||||
|
||||
|
||||
class TooShort(ValidationError):
|
||||
__doc__ = _("""Value is too short""")
|
||||
|
||||
|
||||
class InvalidValue(ValidationError):
|
||||
__doc__ = _("""Invalid value""")
|
||||
|
||||
|
||||
class ConstraintNotSatisfied(ValidationError):
|
||||
__doc__ = _("""Constraint not satisfied""")
|
||||
|
||||
|
||||
class NotAContainer(ValidationError):
|
||||
__doc__ = _("""Not a container""")
|
||||
|
||||
|
||||
class NotAnIterator(ValidationError):
|
||||
__doc__ = _("""Not an iterator""")
|
||||
|
||||
|
||||
class IFromUnicode(zope.interface.Interface):
|
||||
"""Parse a unicode string to a value
|
||||
|
||||
We will often adapt fields to this interface to support views and
|
||||
other applications that need to conver raw data as unicode
|
||||
values.
|
||||
"""
|
||||
|
||||
def fromUnicode(str):
|
||||
"""Convert a unicode string to a value.
|
||||
"""
|
||||
|
||||
|
||||
class IContextAwareDefaultFactory(zope.interface.Interface):
|
||||
"""A default factory that requires a context.
|
||||
|
||||
The context is the field context. If the field is not bound, context may
|
||||
be ``None``.
|
||||
"""
|
||||
|
||||
def __call__(context):
|
||||
"""Returns a default value for the field."""
|
||||
|
||||
|
||||
class NO_VALUE(object):
|
||||
def __repr__(self):
|
||||
return '<NO_VALUE>'
|
||||
|
||||
NO_VALUE = NO_VALUE()
|
||||
58
zope/schema/_compat.py
Normal file
58
zope/schema/_compat.py
Normal file
@@ -0,0 +1,58 @@
|
||||
import sys
|
||||
|
||||
PY3 = sys.version_info[0] >= 3
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError: # pragma: no cover
|
||||
from ordereddict import OrderedDict
|
||||
|
||||
# pep 8 friendlyness
|
||||
OrderedDict
|
||||
|
||||
|
||||
if PY3: # pragma: no cover
|
||||
|
||||
def b(s):
|
||||
return s.encode("latin-1")
|
||||
|
||||
def u(s):
|
||||
return s
|
||||
|
||||
string_types = str,
|
||||
text_type = str
|
||||
binary_type = bytes
|
||||
integer_types = int,
|
||||
|
||||
def non_native_string(x):
|
||||
if isinstance(x, bytes):
|
||||
return x
|
||||
return bytes(x, 'unicode_escape')
|
||||
|
||||
def make_binary(x):
|
||||
if isinstance(x, bytes):
|
||||
return x
|
||||
return x.encode('ascii')
|
||||
|
||||
else: # pragma: no cover
|
||||
|
||||
def b(s):
|
||||
return s
|
||||
|
||||
def u(s):
|
||||
return unicode(s, "unicode_escape")
|
||||
|
||||
string_types = basestring,
|
||||
text_type = unicode
|
||||
binary_type = str
|
||||
integer_types = (int, long)
|
||||
|
||||
def non_native_string(x):
|
||||
if isinstance(x, unicode):
|
||||
return x
|
||||
return unicode(x, 'unicode_escape')
|
||||
|
||||
def make_binary(x):
|
||||
if isinstance(x, str):
|
||||
return x
|
||||
return x.encode('ascii')
|
||||
701
zope/schema/_field.py
Normal file
701
zope/schema/_field.py
Normal file
@@ -0,0 +1,701 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Schema Fields
|
||||
"""
|
||||
__docformat__ = 'restructuredtext'
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import date
|
||||
from datetime import timedelta
|
||||
from datetime import time
|
||||
import decimal
|
||||
import re
|
||||
import threading
|
||||
|
||||
from zope.event import notify
|
||||
from zope.interface import classImplements
|
||||
from zope.interface import implementer
|
||||
from zope.interface import Interface
|
||||
from zope.interface.interfaces import IInterface
|
||||
from zope.interface.interfaces import IMethod
|
||||
|
||||
from zope.schema.interfaces import IASCII
|
||||
from zope.schema.interfaces import IASCIILine
|
||||
from zope.schema.interfaces import IBaseVocabulary
|
||||
from zope.schema.interfaces import IBeforeObjectAssignedEvent
|
||||
from zope.schema.interfaces import IBool
|
||||
from zope.schema.interfaces import IBytes
|
||||
from zope.schema.interfaces import IBytesLine
|
||||
from zope.schema.interfaces import IChoice
|
||||
from zope.schema.interfaces import IContextSourceBinder
|
||||
from zope.schema.interfaces import IDate
|
||||
from zope.schema.interfaces import IDatetime
|
||||
from zope.schema.interfaces import IDecimal
|
||||
from zope.schema.interfaces import IDict
|
||||
from zope.schema.interfaces import IDottedName
|
||||
from zope.schema.interfaces import IField
|
||||
from zope.schema.interfaces import IFloat
|
||||
from zope.schema.interfaces import IFromUnicode
|
||||
from zope.schema.interfaces import IFrozenSet
|
||||
from zope.schema.interfaces import IId
|
||||
from zope.schema.interfaces import IInt
|
||||
from zope.schema.interfaces import IInterfaceField
|
||||
from zope.schema.interfaces import IList
|
||||
from zope.schema.interfaces import IMinMaxLen
|
||||
from zope.schema.interfaces import IObject
|
||||
from zope.schema.interfaces import IPassword
|
||||
from zope.schema.interfaces import ISet
|
||||
from zope.schema.interfaces import ISource
|
||||
from zope.schema.interfaces import ISourceText
|
||||
from zope.schema.interfaces import IText
|
||||
from zope.schema.interfaces import ITextLine
|
||||
from zope.schema.interfaces import ITime
|
||||
from zope.schema.interfaces import ITimedelta
|
||||
from zope.schema.interfaces import ITuple
|
||||
from zope.schema.interfaces import IURI
|
||||
|
||||
from zope.schema.interfaces import ValidationError
|
||||
from zope.schema.interfaces import InvalidValue
|
||||
from zope.schema.interfaces import WrongType
|
||||
from zope.schema.interfaces import WrongContainedType
|
||||
from zope.schema.interfaces import NotUnique
|
||||
from zope.schema.interfaces import SchemaNotProvided
|
||||
from zope.schema.interfaces import SchemaNotFullyImplemented
|
||||
from zope.schema.interfaces import InvalidURI
|
||||
from zope.schema.interfaces import InvalidId
|
||||
from zope.schema.interfaces import InvalidDottedName
|
||||
from zope.schema.interfaces import ConstraintNotSatisfied
|
||||
|
||||
from zope.schema._bootstrapfields import Field
|
||||
from zope.schema._bootstrapfields import Container # API import for __init__
|
||||
from zope.schema._bootstrapfields import Iterable
|
||||
from zope.schema._bootstrapfields import Orderable
|
||||
from zope.schema._bootstrapfields import Text
|
||||
from zope.schema._bootstrapfields import TextLine
|
||||
from zope.schema._bootstrapfields import Bool
|
||||
from zope.schema._bootstrapfields import Int
|
||||
from zope.schema._bootstrapfields import Password
|
||||
from zope.schema._bootstrapfields import MinMaxLen
|
||||
from zope.schema.fieldproperty import FieldProperty
|
||||
from zope.schema.vocabulary import getVocabularyRegistry
|
||||
from zope.schema.vocabulary import VocabularyRegistryError
|
||||
from zope.schema.vocabulary import SimpleVocabulary
|
||||
|
||||
from zope.schema._compat import b
|
||||
from zope.schema._compat import text_type
|
||||
from zope.schema._compat import string_types
|
||||
from zope.schema._compat import binary_type
|
||||
from zope.schema._compat import PY3
|
||||
from zope.schema._compat import make_binary
|
||||
|
||||
# pep 8 friendlyness
|
||||
Container
|
||||
|
||||
# Fix up bootstrap field types
|
||||
Field.title = FieldProperty(IField['title'])
|
||||
Field.description = FieldProperty(IField['description'])
|
||||
Field.required = FieldProperty(IField['required'])
|
||||
Field.readonly = FieldProperty(IField['readonly'])
|
||||
# Default is already taken care of
|
||||
classImplements(Field, IField)
|
||||
|
||||
MinMaxLen.min_length = FieldProperty(IMinMaxLen['min_length'])
|
||||
MinMaxLen.max_length = FieldProperty(IMinMaxLen['max_length'])
|
||||
|
||||
classImplements(Text, IText)
|
||||
classImplements(TextLine, ITextLine)
|
||||
classImplements(Password, IPassword)
|
||||
classImplements(Bool, IBool)
|
||||
classImplements(Bool, IFromUnicode)
|
||||
classImplements(Int, IInt)
|
||||
|
||||
|
||||
@implementer(ISourceText)
|
||||
class SourceText(Text):
|
||||
__doc__ = ISourceText.__doc__
|
||||
_type = text_type
|
||||
|
||||
|
||||
@implementer(IBytes, IFromUnicode)
|
||||
class Bytes(MinMaxLen, Field):
|
||||
__doc__ = IBytes.__doc__
|
||||
|
||||
_type = binary_type
|
||||
|
||||
def fromUnicode(self, uc):
|
||||
""" See IFromUnicode.
|
||||
"""
|
||||
v = make_binary(uc)
|
||||
self.validate(v)
|
||||
return v
|
||||
|
||||
# for things which are of the str type on both Python 2 and 3
|
||||
if PY3: # pragma: no cover
|
||||
NativeString = Text
|
||||
else: # pragma: no cover
|
||||
NativeString = Bytes
|
||||
|
||||
|
||||
@implementer(IASCII)
|
||||
class ASCII(NativeString):
|
||||
__doc__ = IASCII.__doc__
|
||||
|
||||
def _validate(self, value):
|
||||
super(ASCII, self)._validate(value)
|
||||
if not value:
|
||||
return
|
||||
if not max(map(ord, value)) < 128:
|
||||
raise InvalidValue
|
||||
|
||||
|
||||
@implementer(IBytesLine)
|
||||
class BytesLine(Bytes):
|
||||
"""A Text field with no newlines."""
|
||||
|
||||
def constraint(self, value):
|
||||
# TODO: we should probably use a more general definition of newlines
|
||||
return b('\n') not in value
|
||||
|
||||
# for things which are of the str type on both Python 2 and 3
|
||||
if PY3: # pragma: no cover
|
||||
NativeStringLine = TextLine
|
||||
else: # pragma: no cover
|
||||
NativeStringLine = BytesLine
|
||||
|
||||
|
||||
@implementer(IASCIILine)
|
||||
class ASCIILine(ASCII):
|
||||
__doc__ = IASCIILine.__doc__
|
||||
|
||||
def constraint(self, value):
|
||||
# TODO: we should probably use a more general definition of newlines
|
||||
return '\n' not in value
|
||||
|
||||
|
||||
@implementer(IFloat, IFromUnicode)
|
||||
class Float(Orderable, Field):
|
||||
__doc__ = IFloat.__doc__
|
||||
_type = float
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(Float, self).__init__(*args, **kw)
|
||||
|
||||
def fromUnicode(self, uc):
|
||||
""" See IFromUnicode.
|
||||
"""
|
||||
v = float(uc)
|
||||
self.validate(v)
|
||||
return v
|
||||
|
||||
|
||||
@implementer(IDecimal, IFromUnicode)
|
||||
class Decimal(Orderable, Field):
|
||||
__doc__ = IDecimal.__doc__
|
||||
_type = decimal.Decimal
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(Decimal, self).__init__(*args, **kw)
|
||||
|
||||
def fromUnicode(self, uc):
|
||||
""" See IFromUnicode.
|
||||
"""
|
||||
try:
|
||||
v = decimal.Decimal(uc)
|
||||
except decimal.InvalidOperation:
|
||||
raise ValueError('invalid literal for Decimal(): %s' % uc)
|
||||
self.validate(v)
|
||||
return v
|
||||
|
||||
|
||||
@implementer(IDatetime)
|
||||
class Datetime(Orderable, Field):
|
||||
__doc__ = IDatetime.__doc__
|
||||
_type = datetime
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(Datetime, self).__init__(*args, **kw)
|
||||
|
||||
|
||||
@implementer(IDate)
|
||||
class Date(Orderable, Field):
|
||||
__doc__ = IDate.__doc__
|
||||
_type = date
|
||||
|
||||
def _validate(self, value):
|
||||
super(Date, self)._validate(value)
|
||||
if isinstance(value, datetime):
|
||||
raise WrongType(value, self._type, self.__name__)
|
||||
|
||||
|
||||
@implementer(ITimedelta)
|
||||
class Timedelta(Orderable, Field):
|
||||
__doc__ = ITimedelta.__doc__
|
||||
_type = timedelta
|
||||
|
||||
|
||||
@implementer(ITime)
|
||||
class Time(Orderable, Field):
|
||||
__doc__ = ITime.__doc__
|
||||
_type = time
|
||||
|
||||
|
||||
@implementer(IChoice, IFromUnicode)
|
||||
class Choice(Field):
|
||||
"""Choice fields can have a value found in a constant or dynamic set of
|
||||
values given by the field definition.
|
||||
"""
|
||||
|
||||
def __init__(self, values=None, vocabulary=None, source=None, **kw):
|
||||
"""Initialize object."""
|
||||
if vocabulary is not None:
|
||||
if (not isinstance(vocabulary, string_types)
|
||||
and not IBaseVocabulary.providedBy(vocabulary)):
|
||||
raise ValueError('vocabulary must be a string or implement '
|
||||
'IBaseVocabulary')
|
||||
if source is not None:
|
||||
raise ValueError(
|
||||
"You cannot specify both source and vocabulary.")
|
||||
elif source is not None:
|
||||
vocabulary = source
|
||||
|
||||
if (values is None and vocabulary is None):
|
||||
raise ValueError(
|
||||
"You must specify either values or vocabulary."
|
||||
)
|
||||
if values is not None and vocabulary is not None:
|
||||
raise ValueError(
|
||||
"You cannot specify both values and vocabulary."
|
||||
)
|
||||
|
||||
self.vocabulary = None
|
||||
self.vocabularyName = None
|
||||
if values is not None:
|
||||
self.vocabulary = SimpleVocabulary.fromValues(values)
|
||||
elif isinstance(vocabulary, string_types):
|
||||
self.vocabularyName = vocabulary
|
||||
else:
|
||||
if (not ISource.providedBy(vocabulary)
|
||||
and not IContextSourceBinder.providedBy(vocabulary)):
|
||||
raise ValueError('Invalid vocabulary')
|
||||
self.vocabulary = vocabulary
|
||||
# Before a default value is checked, it is validated. However, a
|
||||
# named vocabulary is usually not complete when these fields are
|
||||
# initialized. Therefore signal the validation method to ignore
|
||||
# default value checks during initialization of a Choice tied to a
|
||||
# registered vocabulary.
|
||||
self._init_field = (bool(self.vocabularyName) or
|
||||
IContextSourceBinder.providedBy(self.vocabulary))
|
||||
super(Choice, self).__init__(**kw)
|
||||
self._init_field = False
|
||||
|
||||
source = property(lambda self: self.vocabulary)
|
||||
|
||||
def bind(self, object):
|
||||
"""See zope.schema._bootstrapinterfaces.IField."""
|
||||
clone = super(Choice, self).bind(object)
|
||||
# get registered vocabulary if needed:
|
||||
if IContextSourceBinder.providedBy(self.vocabulary):
|
||||
clone.vocabulary = self.vocabulary(object)
|
||||
elif clone.vocabulary is None and self.vocabularyName is not None:
|
||||
vr = getVocabularyRegistry()
|
||||
clone.vocabulary = vr.get(object, self.vocabularyName)
|
||||
|
||||
if not ISource.providedBy(clone.vocabulary):
|
||||
raise ValueError('Invalid clone vocabulary')
|
||||
|
||||
return clone
|
||||
|
||||
def fromUnicode(self, str):
|
||||
""" See IFromUnicode.
|
||||
"""
|
||||
self.validate(str)
|
||||
return str
|
||||
|
||||
def _validate(self, value):
|
||||
# Pass all validations during initialization
|
||||
if self._init_field:
|
||||
return
|
||||
super(Choice, self)._validate(value)
|
||||
vocabulary = self.vocabulary
|
||||
if vocabulary is None:
|
||||
vr = getVocabularyRegistry()
|
||||
try:
|
||||
vocabulary = vr.get(None, self.vocabularyName)
|
||||
except VocabularyRegistryError:
|
||||
raise ValueError("Can't validate value without vocabulary")
|
||||
if value not in vocabulary:
|
||||
raise ConstraintNotSatisfied(value, self.__name__)
|
||||
|
||||
|
||||
_isuri = r"[a-zA-z0-9+.-]+:" # scheme
|
||||
_isuri += r"\S*$" # non space (should be pickier)
|
||||
_isuri = re.compile(_isuri).match
|
||||
|
||||
|
||||
@implementer(IURI, IFromUnicode)
|
||||
class URI(NativeStringLine):
|
||||
"""URI schema field
|
||||
"""
|
||||
|
||||
def _validate(self, value):
|
||||
super(URI, self)._validate(value)
|
||||
if _isuri(value):
|
||||
return
|
||||
|
||||
raise InvalidURI(value)
|
||||
|
||||
def fromUnicode(self, value):
|
||||
""" See IFromUnicode.
|
||||
"""
|
||||
v = str(value.strip())
|
||||
self.validate(v)
|
||||
return v
|
||||
|
||||
|
||||
_isdotted = re.compile(
|
||||
r"([a-zA-Z][a-zA-Z0-9_]*)"
|
||||
r"([.][a-zA-Z][a-zA-Z0-9_]*)*"
|
||||
# use the whole line
|
||||
r"$").match
|
||||
|
||||
|
||||
@implementer(IDottedName)
|
||||
class DottedName(NativeStringLine):
|
||||
"""Dotted name field.
|
||||
|
||||
Values of DottedName fields must be Python-style dotted names.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
self.min_dots = int(kw.pop("min_dots", 0))
|
||||
if self.min_dots < 0:
|
||||
raise ValueError("min_dots cannot be less than zero")
|
||||
self.max_dots = kw.pop("max_dots", None)
|
||||
if self.max_dots is not None:
|
||||
self.max_dots = int(self.max_dots)
|
||||
if self.max_dots < self.min_dots:
|
||||
raise ValueError("max_dots cannot be less than min_dots")
|
||||
super(DottedName, self).__init__(*args, **kw)
|
||||
|
||||
def _validate(self, value):
|
||||
"""
|
||||
|
||||
"""
|
||||
super(DottedName, self)._validate(value)
|
||||
if not _isdotted(value):
|
||||
raise InvalidDottedName(value)
|
||||
dots = value.count(".")
|
||||
if dots < self.min_dots:
|
||||
raise InvalidDottedName(
|
||||
"too few dots; %d required" % self.min_dots, value
|
||||
)
|
||||
if self.max_dots is not None and dots > self.max_dots:
|
||||
raise InvalidDottedName("too many dots; no more than %d allowed" %
|
||||
self.max_dots, value)
|
||||
|
||||
def fromUnicode(self, value):
|
||||
v = value.strip()
|
||||
if not isinstance(v, self._type):
|
||||
v = v.encode('ascii')
|
||||
self.validate(v)
|
||||
return v
|
||||
|
||||
|
||||
@implementer(IId, IFromUnicode)
|
||||
class Id(NativeStringLine):
|
||||
"""Id field
|
||||
|
||||
Values of id fields must be either uris or dotted names.
|
||||
"""
|
||||
|
||||
def _validate(self, value):
|
||||
super(Id, self)._validate(value)
|
||||
if _isuri(value):
|
||||
return
|
||||
if _isdotted(value) and "." in value:
|
||||
return
|
||||
|
||||
raise InvalidId(value)
|
||||
|
||||
def fromUnicode(self, value):
|
||||
""" See IFromUnicode.
|
||||
"""
|
||||
v = value.strip()
|
||||
if not isinstance(v, self._type):
|
||||
v = v.encode('ascii')
|
||||
self.validate(v)
|
||||
return v
|
||||
|
||||
|
||||
@implementer(IInterfaceField)
|
||||
class InterfaceField(Field):
|
||||
__doc__ = IInterfaceField.__doc__
|
||||
|
||||
def _validate(self, value):
|
||||
super(InterfaceField, self)._validate(value)
|
||||
if not IInterface.providedBy(value):
|
||||
raise WrongType("An interface is required", value, self.__name__)
|
||||
|
||||
|
||||
def _validate_sequence(value_type, value, errors=None):
|
||||
"""Validates a sequence value.
|
||||
|
||||
Returns a list of validation errors generated during the validation. If
|
||||
no errors are generated, returns an empty list.
|
||||
|
||||
value_type is a field. value is the sequence being validated. errors is
|
||||
an optional list of errors that will be prepended to the return value.
|
||||
|
||||
To illustrate, we'll use a text value type. All values must be unicode.
|
||||
|
||||
>>> from zope.schema._compat import u
|
||||
>>> from zope.schema._compat import b
|
||||
>>> field = TextLine(required=True)
|
||||
|
||||
To validate a sequence of various values:
|
||||
|
||||
>>> errors = _validate_sequence(field, (b('foo'), u('bar'), 1))
|
||||
>>> errors # XXX assumes Python2 reprs
|
||||
[WrongType('foo', <type 'unicode'>, ''), WrongType(1, <type 'unicode'>, '')]
|
||||
|
||||
The only valid value in the sequence is the second item. The others
|
||||
generated errors.
|
||||
|
||||
We can use the optional errors argument to collect additional errors
|
||||
for a new sequence:
|
||||
|
||||
>>> errors = _validate_sequence(field, (2, u('baz')), errors)
|
||||
>>> errors # XXX assumes Python2 reprs
|
||||
[WrongType('foo', <type 'unicode'>, ''), WrongType(1, <type 'unicode'>, ''), WrongType(2, <type 'unicode'>, '')]
|
||||
|
||||
"""
|
||||
if errors is None:
|
||||
errors = []
|
||||
if value_type is None:
|
||||
return errors
|
||||
for item in value:
|
||||
try:
|
||||
value_type.validate(item)
|
||||
except ValidationError as error:
|
||||
errors.append(error)
|
||||
return errors
|
||||
|
||||
|
||||
def _validate_uniqueness(value):
|
||||
temp_values = []
|
||||
for item in value:
|
||||
if item in temp_values:
|
||||
raise NotUnique(item)
|
||||
|
||||
temp_values.append(item)
|
||||
|
||||
|
||||
class AbstractCollection(MinMaxLen, Iterable):
|
||||
value_type = None
|
||||
unique = False
|
||||
|
||||
def __init__(self, value_type=None, unique=False, **kw):
|
||||
super(AbstractCollection, self).__init__(**kw)
|
||||
# whine if value_type is not a field
|
||||
if value_type is not None and not IField.providedBy(value_type):
|
||||
raise ValueError("'value_type' must be field instance.")
|
||||
self.value_type = value_type
|
||||
self.unique = unique
|
||||
|
||||
def bind(self, object):
|
||||
"""See zope.schema._bootstrapinterfaces.IField."""
|
||||
clone = super(AbstractCollection, self).bind(object)
|
||||
# binding value_type is necessary for choices with named vocabularies,
|
||||
# and possibly also for other fields.
|
||||
if clone.value_type is not None:
|
||||
clone.value_type = clone.value_type.bind(object)
|
||||
return clone
|
||||
|
||||
def _validate(self, value):
|
||||
super(AbstractCollection, self)._validate(value)
|
||||
errors = _validate_sequence(self.value_type, value)
|
||||
if errors:
|
||||
raise WrongContainedType(errors, self.__name__)
|
||||
if self.unique:
|
||||
_validate_uniqueness(value)
|
||||
|
||||
|
||||
@implementer(ITuple)
|
||||
class Tuple(AbstractCollection):
|
||||
"""A field representing a Tuple."""
|
||||
_type = tuple
|
||||
|
||||
|
||||
@implementer(IList)
|
||||
class List(AbstractCollection):
|
||||
"""A field representing a List."""
|
||||
_type = list
|
||||
|
||||
|
||||
@implementer(ISet)
|
||||
class Set(AbstractCollection):
|
||||
"""A field representing a set."""
|
||||
_type = set
|
||||
|
||||
def __init__(self, **kw):
|
||||
if 'unique' in kw: # set members are always unique
|
||||
raise TypeError(
|
||||
"__init__() got an unexpected keyword argument 'unique'")
|
||||
super(Set, self).__init__(unique=True, **kw)
|
||||
|
||||
|
||||
@implementer(IFrozenSet)
|
||||
class FrozenSet(AbstractCollection):
|
||||
_type = frozenset
|
||||
|
||||
def __init__(self, **kw):
|
||||
if 'unique' in kw: # set members are always unique
|
||||
raise TypeError(
|
||||
"__init__() got an unexpected keyword argument 'unique'")
|
||||
super(FrozenSet, self).__init__(unique=True, **kw)
|
||||
|
||||
|
||||
VALIDATED_VALUES = threading.local()
|
||||
|
||||
|
||||
def _validate_fields(schema, value, errors=None):
|
||||
if errors is None:
|
||||
errors = []
|
||||
# Interface can be used as schema property for Object fields that plan to
|
||||
# hold values of any type.
|
||||
# Because Interface does not include any Attribute, it is obviously not
|
||||
# worth looping on its methods and filter them all out.
|
||||
if schema is Interface:
|
||||
return errors
|
||||
# if `value` is part of a cyclic graph, we need to break the cycle to avoid
|
||||
# infinite recursion. Collect validated objects in a thread local dict by
|
||||
# it's python represenation. A previous version was setting a volatile
|
||||
# attribute which didn't work with security proxy
|
||||
if id(value) in VALIDATED_VALUES.__dict__:
|
||||
return errors
|
||||
VALIDATED_VALUES.__dict__[id(value)] = True
|
||||
# (If we have gotten here, we know that `value` provides an interface
|
||||
# other than zope.interface.Interface;
|
||||
# iow, we can rely on the fact that it is an instance
|
||||
# that supports attribute assignment.)
|
||||
try:
|
||||
for name in schema.names(all=True):
|
||||
if not IMethod.providedBy(schema[name]):
|
||||
try:
|
||||
attribute = schema[name]
|
||||
if IChoice.providedBy(attribute):
|
||||
# Choice must be bound before validation otherwise
|
||||
# IContextSourceBinder is not iterable in validation
|
||||
bound = attribute.bind(value)
|
||||
bound.validate(getattr(value, name))
|
||||
elif IField.providedBy(attribute):
|
||||
# validate attributes that are fields
|
||||
attribute.validate(getattr(value, name))
|
||||
except ValidationError as error:
|
||||
errors.append(error)
|
||||
except AttributeError as error:
|
||||
# property for the given name is not implemented
|
||||
errors.append(SchemaNotFullyImplemented(error))
|
||||
finally:
|
||||
del VALIDATED_VALUES.__dict__[id(value)]
|
||||
return errors
|
||||
|
||||
|
||||
@implementer(IObject)
|
||||
class Object(Field):
|
||||
__doc__ = IObject.__doc__
|
||||
|
||||
def __init__(self, schema, **kw):
|
||||
if not IInterface.providedBy(schema):
|
||||
raise WrongType
|
||||
|
||||
self.schema = schema
|
||||
super(Object, self).__init__(**kw)
|
||||
|
||||
def _validate(self, value):
|
||||
super(Object, self)._validate(value)
|
||||
|
||||
# schema has to be provided by value
|
||||
if not self.schema.providedBy(value):
|
||||
raise SchemaNotProvided
|
||||
|
||||
# check the value against schema
|
||||
errors = _validate_fields(self.schema, value)
|
||||
if errors:
|
||||
raise WrongContainedType(errors, self.__name__)
|
||||
|
||||
def set(self, object, value):
|
||||
# Announce that we're going to assign the value to the object.
|
||||
# Motivation: Widgets typically like to take care of policy-specific
|
||||
# actions, like establishing location.
|
||||
event = BeforeObjectAssignedEvent(value, self.__name__, object)
|
||||
notify(event)
|
||||
# The event subscribers are allowed to replace the object, thus we need
|
||||
# to replace our previous value.
|
||||
value = event.object
|
||||
super(Object, self).set(object, value)
|
||||
|
||||
|
||||
@implementer(IBeforeObjectAssignedEvent)
|
||||
class BeforeObjectAssignedEvent(object):
|
||||
"""An object is going to be assigned to an attribute on another object."""
|
||||
|
||||
def __init__(self, object, name, context):
|
||||
self.object = object
|
||||
self.name = name
|
||||
self.context = context
|
||||
|
||||
|
||||
@implementer(IDict)
|
||||
class Dict(MinMaxLen, Iterable):
|
||||
"""A field representing a Dict."""
|
||||
_type = dict
|
||||
key_type = None
|
||||
value_type = None
|
||||
|
||||
def __init__(self, key_type=None, value_type=None, **kw):
|
||||
super(Dict, self).__init__(**kw)
|
||||
# whine if key_type or value_type is not a field
|
||||
if key_type is not None and not IField.providedBy(key_type):
|
||||
raise ValueError("'key_type' must be field instance.")
|
||||
if value_type is not None and not IField.providedBy(value_type):
|
||||
raise ValueError("'value_type' must be field instance.")
|
||||
self.key_type = key_type
|
||||
self.value_type = value_type
|
||||
|
||||
def _validate(self, value):
|
||||
super(Dict, self)._validate(value)
|
||||
errors = []
|
||||
try:
|
||||
if self.value_type:
|
||||
errors = _validate_sequence(self.value_type, value.values(),
|
||||
errors)
|
||||
errors = _validate_sequence(self.key_type, value, errors)
|
||||
|
||||
if errors:
|
||||
raise WrongContainedType(errors, self.__name__)
|
||||
|
||||
finally:
|
||||
errors = None
|
||||
|
||||
def bind(self, object):
|
||||
"""See zope.schema._bootstrapinterfaces.IField."""
|
||||
clone = super(Dict, self).bind(object)
|
||||
# binding value_type is necessary for choices with named vocabularies,
|
||||
# and possibly also for other fields.
|
||||
if clone.key_type is not None:
|
||||
clone.key_type = clone.key_type.bind(object)
|
||||
if clone.value_type is not None:
|
||||
clone.value_type = clone.value_type.bind(object)
|
||||
return clone
|
||||
20
zope/schema/_messageid.py
Normal file
20
zope/schema/_messageid.py
Normal file
@@ -0,0 +1,20 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2000 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.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
try:
|
||||
from zope.i18nmessageid import MessageFactory
|
||||
except ImportError: # pragma: no cover
|
||||
from zope.schema._compat import text_type as _
|
||||
else: # pragma: no cover
|
||||
_ = MessageFactory("zope")
|
||||
90
zope/schema/_schema.py
Normal file
90
zope/schema/_schema.py
Normal file
@@ -0,0 +1,90 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Schema convenience functions
|
||||
"""
|
||||
|
||||
import zope.interface.verify
|
||||
|
||||
|
||||
def getFieldNames(schema):
|
||||
"""Return a list of all the Field names in a schema.
|
||||
"""
|
||||
from zope.schema.interfaces import IField
|
||||
return [name for name in schema if IField.providedBy(schema[name])]
|
||||
|
||||
|
||||
def getFields(schema):
|
||||
"""Return a dictionary containing all the Fields in a schema.
|
||||
"""
|
||||
from zope.schema.interfaces import IField
|
||||
fields = {}
|
||||
for name in schema:
|
||||
attr = schema[name]
|
||||
if IField.providedBy(attr):
|
||||
fields[name] = attr
|
||||
return fields
|
||||
|
||||
|
||||
def getFieldsInOrder(schema, _field_key=lambda x: x[1].order):
|
||||
"""Return a list of (name, value) tuples in native schema order.
|
||||
"""
|
||||
return sorted(getFields(schema).items(), key=_field_key)
|
||||
|
||||
|
||||
def getFieldNamesInOrder(schema):
|
||||
"""Return a list of all the Field names in a schema in schema order.
|
||||
"""
|
||||
return [name for name, field in getFieldsInOrder(schema)]
|
||||
|
||||
|
||||
def getValidationErrors(schema, object):
|
||||
"""Return a list of all validation errors.
|
||||
"""
|
||||
errors = getSchemaValidationErrors(schema, object)
|
||||
if errors:
|
||||
return errors
|
||||
|
||||
# Only validate invariants if there were no previous errors. Previous
|
||||
# errors could be missing attributes which would most likely make an
|
||||
# invariant raise an AttributeError.
|
||||
invariant_errors = []
|
||||
try:
|
||||
schema.validateInvariants(object, invariant_errors)
|
||||
except zope.interface.exceptions.Invalid:
|
||||
# Just collect errors
|
||||
pass
|
||||
errors = [(None, e) for e in invariant_errors]
|
||||
return errors
|
||||
|
||||
|
||||
def getSchemaValidationErrors(schema, object):
|
||||
errors = []
|
||||
for name in schema.names(all=True):
|
||||
if zope.interface.interfaces.IMethod.providedBy(schema[name]):
|
||||
continue
|
||||
attribute = schema[name]
|
||||
if not zope.schema.interfaces.IField.providedBy(attribute):
|
||||
continue
|
||||
try:
|
||||
value = getattr(object, name)
|
||||
except AttributeError as error:
|
||||
# property for the given name is not implemented
|
||||
errors.append((
|
||||
name, zope.schema.interfaces.SchemaNotFullyImplemented(error)))
|
||||
else:
|
||||
try:
|
||||
attribute.bind(object).validate(value)
|
||||
except zope.schema.ValidationError as e:
|
||||
errors.append((name, e))
|
||||
return errors
|
||||
120
zope/schema/accessors.py
Normal file
120
zope/schema/accessors.py
Normal file
@@ -0,0 +1,120 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""
|
||||
Field accessors
|
||||
===============
|
||||
|
||||
Accessors are used to model methods used to access data defined by fields.
|
||||
Accessors are fields that work by decorating existing fields.
|
||||
|
||||
To define accessors in an interface, use the accessors function::
|
||||
|
||||
class IMyInterface(Interface):
|
||||
|
||||
getFoo, setFoo = accessors(Text(title=u'Foo', ...))
|
||||
|
||||
getBar = accessors(TextLine(title=u'Foo', readonly=True, ...)
|
||||
|
||||
|
||||
Normally a read accessor and a write accessor are defined. Only a
|
||||
read accessor is defined for read-only fields.
|
||||
|
||||
Read accessors function as access method specifications and as field
|
||||
specifications. Write accessors are solely method specifications.
|
||||
"""
|
||||
|
||||
from zope.interface import providedBy, implementedBy
|
||||
from zope.interface.interface import Method
|
||||
|
||||
|
||||
class FieldReadAccessor(Method):
|
||||
"""Field read accessor
|
||||
"""
|
||||
|
||||
# A read field accessor is a method and a field.
|
||||
# A read accessor is a decorator of a field, using the given
|
||||
# fields properties to provide meta data.
|
||||
|
||||
def __provides__(self):
|
||||
return providedBy(self.field) + implementedBy(FieldReadAccessor)
|
||||
__provides__ = property(__provides__)
|
||||
|
||||
def __init__(self, field):
|
||||
self.field = field
|
||||
Method.__init__(self, '')
|
||||
self.__doc__ = 'get %s' % field.__doc__
|
||||
|
||||
def getSignatureString(self):
|
||||
return '()'
|
||||
|
||||
def getSignatureInfo(self):
|
||||
return {'positional': (),
|
||||
'required': (),
|
||||
'optional': (),
|
||||
'varargs': None,
|
||||
'kwargs': None,
|
||||
}
|
||||
|
||||
def get(self, object):
|
||||
return getattr(object, self.__name__)()
|
||||
|
||||
def query(self, object, default=None):
|
||||
try:
|
||||
f = getattr(object, self.__name__)
|
||||
except AttributeError:
|
||||
return default
|
||||
else:
|
||||
return f()
|
||||
|
||||
def set(self, object, value):
|
||||
if self.readonly:
|
||||
raise TypeError("Can't set values on read-only fields")
|
||||
getattr(object, self.writer.__name__)(value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.field, name)
|
||||
|
||||
def bind(self, object):
|
||||
clone = self.__class__.__new__(self.__class__)
|
||||
clone.__dict__.update(self.__dict__)
|
||||
clone.field = self.field.bind(object)
|
||||
return clone
|
||||
|
||||
|
||||
class FieldWriteAccessor(Method):
|
||||
|
||||
def __init__(self, field):
|
||||
Method.__init__(self, '')
|
||||
self.field = field
|
||||
self.__doc__ = 'set %s' % field.__doc__
|
||||
|
||||
def getSignatureString(self):
|
||||
return '(newvalue)'
|
||||
|
||||
def getSignatureInfo(self):
|
||||
return {'positional': ('newvalue',),
|
||||
'required': ('newvalue',),
|
||||
'optional': (),
|
||||
'varargs': None,
|
||||
'kwargs': None,
|
||||
}
|
||||
|
||||
|
||||
def accessors(field):
|
||||
reader = FieldReadAccessor(field)
|
||||
yield reader
|
||||
if not field.readonly:
|
||||
writer = FieldWriteAccessor(field)
|
||||
reader.writer = writer
|
||||
yield writer
|
||||
150
zope/schema/fieldproperty.py
Normal file
150
zope/schema/fieldproperty.py
Normal file
@@ -0,0 +1,150 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Computed attributes based on schema fields
|
||||
"""
|
||||
|
||||
from copy import copy
|
||||
import sys
|
||||
import zope.schema
|
||||
from zope import interface
|
||||
from zope import event
|
||||
from zope.schema import interfaces
|
||||
from zope.schema._bootstrapinterfaces import NO_VALUE
|
||||
|
||||
_marker = object()
|
||||
|
||||
|
||||
@interface.implementer(interfaces.IFieldUpdatedEvent)
|
||||
class FieldUpdatedEvent(object):
|
||||
|
||||
def __init__(self, inst, field, old_value, new_value):
|
||||
self.inst = inst
|
||||
self.field = field
|
||||
self.old_value = old_value
|
||||
self.new_value = new_value
|
||||
|
||||
|
||||
class FieldProperty(object):
|
||||
"""Computed attributes based on schema fields
|
||||
|
||||
Field properties provide default values, data validation and error messages
|
||||
based on data found in field meta-data.
|
||||
|
||||
Note that FieldProperties cannot be used with slots. They can only
|
||||
be used for attributes stored in instance dictionaries.
|
||||
"""
|
||||
|
||||
def __init__(self, field, name=None):
|
||||
if name is None:
|
||||
name = field.__name__
|
||||
|
||||
self.__field = field
|
||||
self.__name = name
|
||||
|
||||
def __get__(self, inst, klass):
|
||||
if inst is None:
|
||||
return self
|
||||
|
||||
value = inst.__dict__.get(self.__name, _marker)
|
||||
if value is _marker:
|
||||
field = self.__field.bind(inst)
|
||||
value = getattr(field, 'default', _marker)
|
||||
if value is _marker:
|
||||
raise AttributeError(self.__name)
|
||||
|
||||
return value
|
||||
|
||||
def queryValue(self, inst, default):
|
||||
value = inst.__dict__.get(self.__name, default)
|
||||
if value is default:
|
||||
field = self.__field.bind(inst)
|
||||
value = getattr(field, 'default', default)
|
||||
return value
|
||||
|
||||
def __set__(self, inst, value):
|
||||
field = self.__field.bind(inst)
|
||||
field.validate(value)
|
||||
if field.readonly and self.__name in inst.__dict__:
|
||||
raise ValueError(self.__name, 'field is readonly')
|
||||
oldvalue = self.queryValue(inst, NO_VALUE)
|
||||
inst.__dict__[self.__name] = value
|
||||
event.notify(FieldUpdatedEvent(inst, field, oldvalue, value))
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.__field, name)
|
||||
|
||||
|
||||
def createFieldProperties(schema, omit=[]):
|
||||
"""Creates a FieldProperty fields in `schema` on the class it is called on.
|
||||
|
||||
schema ... interface those fields should be added to class
|
||||
omit ... list of field names to be omitted in creation
|
||||
|
||||
"""
|
||||
frame = sys._getframe(1)
|
||||
for name in zope.schema.getFieldNamesInOrder(schema):
|
||||
if name in omit:
|
||||
continue
|
||||
frame.f_locals[name] = FieldProperty(schema[name])
|
||||
|
||||
|
||||
class FieldPropertyStoredThroughField(object):
|
||||
|
||||
def __init__(self, field, name=None):
|
||||
if name is None:
|
||||
name = field.__name__
|
||||
|
||||
self.field = copy(field)
|
||||
self.field.__name__ = "__st_%s_st" % self.field.__name__
|
||||
self.__name = name
|
||||
|
||||
def setValue(self, inst, field, value):
|
||||
field.set(inst, value)
|
||||
|
||||
def getValue(self, inst, field):
|
||||
return field.query(inst, _marker)
|
||||
|
||||
def queryValue(self, inst, field, default):
|
||||
return field.query(inst, default)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.field, name)
|
||||
|
||||
def __get__(self, inst, klass):
|
||||
if inst is None:
|
||||
return self
|
||||
|
||||
field = self.field.bind(inst)
|
||||
value = self.getValue(inst, field)
|
||||
if value is _marker:
|
||||
value = getattr(field, 'default', _marker)
|
||||
if value is _marker:
|
||||
raise AttributeError(self.__name)
|
||||
|
||||
return value
|
||||
|
||||
def __set__(self, inst, value):
|
||||
field = self.field.bind(inst)
|
||||
field.validate(value)
|
||||
if field.readonly:
|
||||
if self.queryValue(inst, field, _marker) is _marker:
|
||||
field.readonly = False
|
||||
self.setValue(inst, field, value)
|
||||
field.readonly = True
|
||||
return
|
||||
else:
|
||||
raise ValueError(self.__name, 'field is readonly')
|
||||
oldvalue = self.queryValue(inst, field, NO_VALUE)
|
||||
self.setValue(inst, field, value)
|
||||
event.notify(FieldUpdatedEvent(inst, self.field, oldvalue, value))
|
||||
764
zope/schema/interfaces.py
Normal file
764
zope/schema/interfaces.py
Normal file
@@ -0,0 +1,764 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Schema interfaces and exceptions
|
||||
"""
|
||||
__docformat__ = "reStructuredText"
|
||||
|
||||
from zope.interface import Interface, Attribute
|
||||
from zope.interface.common.mapping import IEnumerableMapping
|
||||
|
||||
|
||||
# Import from _bootstrapinterfaces only because other packages will expect
|
||||
# to find these interfaces here.
|
||||
from zope.schema._bootstrapfields import Field
|
||||
from zope.schema._bootstrapfields import Text
|
||||
from zope.schema._bootstrapfields import TextLine
|
||||
from zope.schema._bootstrapfields import Bool
|
||||
from zope.schema._bootstrapfields import Int
|
||||
from zope.schema._bootstrapinterfaces import StopValidation
|
||||
from zope.schema._bootstrapinterfaces import ValidationError
|
||||
from zope.schema._bootstrapinterfaces import IFromUnicode
|
||||
from zope.schema._bootstrapinterfaces import RequiredMissing
|
||||
from zope.schema._bootstrapinterfaces import WrongType
|
||||
from zope.schema._bootstrapinterfaces import ConstraintNotSatisfied
|
||||
from zope.schema._bootstrapinterfaces import NotAContainer
|
||||
from zope.schema._bootstrapinterfaces import NotAnIterator
|
||||
from zope.schema._bootstrapinterfaces import TooSmall
|
||||
from zope.schema._bootstrapinterfaces import TooBig
|
||||
from zope.schema._bootstrapinterfaces import TooLong
|
||||
from zope.schema._bootstrapinterfaces import TooShort
|
||||
from zope.schema._bootstrapinterfaces import InvalidValue
|
||||
from zope.schema._bootstrapinterfaces import IContextAwareDefaultFactory
|
||||
|
||||
from zope.schema._compat import PY3
|
||||
from zope.schema._compat import u
|
||||
from zope.schema._messageid import _
|
||||
|
||||
|
||||
# pep 8 friendlyness
|
||||
StopValidation, ValidationError, IFromUnicode, RequiredMissing, WrongType
|
||||
ConstraintNotSatisfied, NotAContainer, NotAnIterator
|
||||
TooSmall, TooBig, TooLong, TooShort, InvalidValue, IContextAwareDefaultFactory
|
||||
|
||||
|
||||
class WrongContainedType(ValidationError):
|
||||
__doc__ = _("""Wrong contained type""")
|
||||
|
||||
|
||||
class NotUnique(ValidationError):
|
||||
__doc__ = _("""One or more entries of sequence are not unique.""")
|
||||
|
||||
|
||||
class SchemaNotFullyImplemented(ValidationError):
|
||||
__doc__ = _("""Schema not fully implemented""")
|
||||
|
||||
|
||||
class SchemaNotProvided(ValidationError):
|
||||
__doc__ = _("""Schema not provided""")
|
||||
|
||||
|
||||
class InvalidURI(ValidationError):
|
||||
__doc__ = _("""The specified URI is not valid.""")
|
||||
|
||||
|
||||
class InvalidId(ValidationError):
|
||||
__doc__ = _("""The specified id is not valid.""")
|
||||
|
||||
|
||||
class InvalidDottedName(ValidationError):
|
||||
__doc__ = _("""The specified dotted name is not valid.""")
|
||||
|
||||
|
||||
class Unbound(Exception):
|
||||
__doc__ = _("""The field is not bound.""")
|
||||
|
||||
|
||||
class IField(Interface):
|
||||
"""Basic Schema Field Interface.
|
||||
|
||||
Fields are used for Interface specifications. They at least provide
|
||||
a title, description and a default value. You can also
|
||||
specify if they are required and/or readonly.
|
||||
|
||||
The Field Interface is also used for validation and specifying
|
||||
constraints.
|
||||
|
||||
We want to make it possible for a IField to not only work
|
||||
on its value but also on the object this value is bound to.
|
||||
This enables a Field implementation to perform validation
|
||||
against an object which also marks a certain place.
|
||||
|
||||
Note that many fields need information about the object
|
||||
containing a field. For example, when validating a value to be
|
||||
set as an object attribute, it may be necessary for the field to
|
||||
introspect the object's state. This means that the field needs to
|
||||
have access to the object when performing validation::
|
||||
|
||||
bound = field.bind(object)
|
||||
bound.validate(value)
|
||||
|
||||
"""
|
||||
|
||||
def bind(object):
|
||||
"""Return a copy of this field which is bound to context.
|
||||
|
||||
The copy of the Field will have the 'context' attribute set
|
||||
to 'object'. This way a Field can implement more complex
|
||||
checks involving the object's location/environment.
|
||||
|
||||
Many fields don't need to be bound. Only fields that condition
|
||||
validation or properties on an object containing the field
|
||||
need to be bound.
|
||||
"""
|
||||
|
||||
title = TextLine(
|
||||
title=_("Title"),
|
||||
description=_("A short summary or label"),
|
||||
default=u(""),
|
||||
required=False,
|
||||
)
|
||||
|
||||
description = Text(
|
||||
title=_("Description"),
|
||||
description=_("A description of the field"),
|
||||
default=u(""),
|
||||
required=False,
|
||||
)
|
||||
|
||||
required = Bool(
|
||||
title=_("Required"),
|
||||
description=(_("Tells whether a field requires its value to exist.")),
|
||||
default=True)
|
||||
|
||||
readonly = Bool(
|
||||
title=_("Read Only"),
|
||||
description=_("If true, the field's value cannot be changed."),
|
||||
required=False,
|
||||
default=False)
|
||||
|
||||
default = Field(
|
||||
title=_("Default Value"),
|
||||
description=_("""The field default value may be None or a legal
|
||||
field value""")
|
||||
)
|
||||
|
||||
missing_value = Field(
|
||||
title=_("Missing Value"),
|
||||
description=_("""If input for this Field is missing, and that's ok,
|
||||
then this is the value to use""")
|
||||
)
|
||||
|
||||
order = Int(
|
||||
title=_("Field Order"),
|
||||
description=_("""
|
||||
The order attribute can be used to determine the order in
|
||||
which fields in a schema were defined. If one field is created
|
||||
after another (in the same thread), its order will be
|
||||
greater.
|
||||
|
||||
(Fields in separate threads could have the same order.)
|
||||
"""),
|
||||
required=True,
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
def constraint(value):
|
||||
"""Check a customized constraint on the value.
|
||||
|
||||
You can implement this method with your Field to
|
||||
require a certain constraint. This relaxes the need
|
||||
to inherit/subclass a Field you to add a simple constraint.
|
||||
Returns true if the given value is within the Field's constraint.
|
||||
"""
|
||||
|
||||
def validate(value):
|
||||
"""Validate that the given value is a valid field value.
|
||||
|
||||
Returns nothing but raises an error if the value is invalid.
|
||||
It checks everything specific to a Field and also checks
|
||||
with the additional constraint.
|
||||
"""
|
||||
|
||||
def get(object):
|
||||
"""Get the value of the field for the given object."""
|
||||
|
||||
def query(object, default=None):
|
||||
"""Query the value of the field for the given object.
|
||||
|
||||
Return the default if the value hasn't been set.
|
||||
"""
|
||||
|
||||
def set(object, value):
|
||||
"""Set the value of the field for the object
|
||||
|
||||
Raises a type error if the field is a read-only field.
|
||||
"""
|
||||
|
||||
|
||||
class IIterable(IField):
|
||||
"""Fields with a value that can be iterated over.
|
||||
|
||||
The value needs to support iteration; the implementation mechanism
|
||||
is not constrained. (Either `__iter__()` or `__getitem__()` may be
|
||||
used.)
|
||||
"""
|
||||
|
||||
|
||||
class IContainer(IField):
|
||||
"""Fields whose value allows an ``x in value`` check.
|
||||
|
||||
The value needs to support the `in` operator, but is not
|
||||
constrained in how it does so (whether it defines `__contains__()`
|
||||
or `__getitem__()` is immaterial).
|
||||
"""
|
||||
|
||||
|
||||
class IOrderable(IField):
|
||||
"""Field requiring its value to be orderable.
|
||||
|
||||
The set of value needs support a complete ordering; the
|
||||
implementation mechanism is not constrained. Either `__cmp__()` or
|
||||
'rich comparison' methods may be used.
|
||||
"""
|
||||
|
||||
|
||||
class ILen(IField):
|
||||
"""A Field requiring its value to have a length.
|
||||
|
||||
The value needs to have a conventional __len__ method.
|
||||
"""
|
||||
|
||||
|
||||
class IMinMax(IOrderable):
|
||||
"""Field requiring its value to be between min and max.
|
||||
|
||||
This implies that the value needs to support the IOrderable interface.
|
||||
"""
|
||||
|
||||
min = Field(
|
||||
title=_("Start of the range"),
|
||||
required=False,
|
||||
default=None
|
||||
)
|
||||
|
||||
max = Field(
|
||||
title=_("End of the range (including the value itself)"),
|
||||
required=False,
|
||||
default=None
|
||||
)
|
||||
|
||||
|
||||
class IMinMaxLen(ILen):
|
||||
"""Field requiring the length of its value to be within a range"""
|
||||
|
||||
min_length = Int(
|
||||
title=_("Minimum length"),
|
||||
description=_("""
|
||||
Value after whitespace processing cannot have less than
|
||||
`min_length` characters (if a string type) or elements (if
|
||||
another sequence type). If `min_length` is ``None``, there is
|
||||
no minimum.
|
||||
"""),
|
||||
required=False,
|
||||
min=0, # needs to be a positive number
|
||||
default=0)
|
||||
|
||||
max_length = Int(
|
||||
title=_("Maximum length"),
|
||||
description=_("""
|
||||
Value after whitespace processing cannot have greater
|
||||
or equal than `max_length` characters (if a string type) or
|
||||
elements (if another sequence type). If `max_length` is
|
||||
``None``, there is no maximum."""),
|
||||
required=False,
|
||||
min=0, # needs to be a positive number
|
||||
default=None)
|
||||
|
||||
|
||||
class IInterfaceField(IField):
|
||||
"""Fields with a value that is an interface (implementing
|
||||
zope.interface.Interface)."""
|
||||
|
||||
|
||||
class IBool(IField):
|
||||
"""Boolean Field."""
|
||||
|
||||
default = Bool(
|
||||
title=_("Default Value"),
|
||||
description=_("""The field default value may be None or a legal
|
||||
field value""")
|
||||
)
|
||||
|
||||
|
||||
class IBytes(IMinMaxLen, IIterable, IField):
|
||||
"""Field containing a byte string (like the python str).
|
||||
|
||||
The value might be constrained to be with length limits.
|
||||
"""
|
||||
|
||||
|
||||
class IText(IMinMaxLen, IIterable, IField):
|
||||
"""Field containing a unicode string."""
|
||||
|
||||
|
||||
# for things which are of the str type on both Python 2 and 3
|
||||
if PY3: # pragma: no cover
|
||||
INativeString = IText
|
||||
else: # pragma: no cover
|
||||
INativeString = IBytes
|
||||
|
||||
|
||||
class IASCII(INativeString):
|
||||
"""Field containing a 7-bit ASCII string. No characters > DEL
|
||||
(chr(127)) are allowed
|
||||
|
||||
The value might be constrained to be with length limits.
|
||||
"""
|
||||
|
||||
|
||||
class IBytesLine(IBytes):
|
||||
"""Field containing a byte string without newlines."""
|
||||
|
||||
|
||||
class IASCIILine(IASCII):
|
||||
"""Field containing a 7-bit ASCII string without newlines."""
|
||||
|
||||
|
||||
class ISourceText(IText):
|
||||
"""Field for source text of object."""
|
||||
|
||||
|
||||
class ITextLine(IText):
|
||||
"""Field containing a unicode string without newlines."""
|
||||
|
||||
|
||||
if PY3: # pragma: no cover
|
||||
INativeStringLine = ITextLine
|
||||
else: # pragma: no cover
|
||||
INativeStringLine = IBytesLine
|
||||
|
||||
|
||||
class IPassword(ITextLine):
|
||||
"Field containing a unicode string without newlines that is a password."
|
||||
|
||||
|
||||
class IInt(IMinMax, IField):
|
||||
"""Field containing an Integer Value."""
|
||||
|
||||
min = Int(
|
||||
title=_("Start of the range"),
|
||||
required=False,
|
||||
default=None
|
||||
)
|
||||
|
||||
max = Int(
|
||||
title=_("End of the range (excluding the value itself)"),
|
||||
required=False,
|
||||
default=None
|
||||
)
|
||||
|
||||
default = Int(
|
||||
title=_("Default Value"),
|
||||
description=_("""The field default value may be None or a legal
|
||||
field value""")
|
||||
)
|
||||
|
||||
|
||||
class IFloat(IMinMax, IField):
|
||||
"""Field containing a Float."""
|
||||
|
||||
|
||||
class IDecimal(IMinMax, IField):
|
||||
"""Field containing a Decimal."""
|
||||
|
||||
|
||||
class IDatetime(IMinMax, IField):
|
||||
"""Field containing a DateTime."""
|
||||
|
||||
|
||||
class IDate(IMinMax, IField):
|
||||
"""Field containing a date."""
|
||||
|
||||
|
||||
class ITimedelta(IMinMax, IField):
|
||||
"""Field containing a timedelta."""
|
||||
|
||||
|
||||
class ITime(IMinMax, IField):
|
||||
"""Field containing a time."""
|
||||
|
||||
|
||||
def _is_field(value):
|
||||
if not IField.providedBy(value):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _fields(values):
|
||||
for value in values:
|
||||
if not _is_field(value):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class IURI(INativeStringLine):
|
||||
"""A field containing an absolute URI
|
||||
"""
|
||||
|
||||
|
||||
class IId(INativeStringLine):
|
||||
"""A field containing a unique identifier
|
||||
|
||||
A unique identifier is either an absolute URI or a dotted name.
|
||||
If it's a dotted name, it should have a module/package name as a prefix.
|
||||
"""
|
||||
|
||||
|
||||
class IDottedName(INativeStringLine):
|
||||
"""Dotted name field.
|
||||
|
||||
Values of DottedName fields must be Python-style dotted names.
|
||||
"""
|
||||
|
||||
min_dots = Int(
|
||||
title=_("Minimum number of dots"),
|
||||
required=True,
|
||||
min=0,
|
||||
default=0
|
||||
)
|
||||
|
||||
max_dots = Int(
|
||||
title=_("Maximum number of dots (should not be less than min_dots)"),
|
||||
required=False,
|
||||
default=None
|
||||
)
|
||||
|
||||
|
||||
class IChoice(IField):
|
||||
"""Field whose value is contained in a predefined set
|
||||
|
||||
Only one, values or vocabulary, may be specified for a given choice.
|
||||
"""
|
||||
vocabulary = Field(
|
||||
title=_("Vocabulary or source providing values"),
|
||||
description=_("The ISource, IContextSourceBinder or IBaseVocabulary "
|
||||
"object that provides values for this field."),
|
||||
required=False,
|
||||
default=None
|
||||
)
|
||||
|
||||
vocabularyName = TextLine(
|
||||
title=_("Vocabulary name"),
|
||||
description=_("Vocabulary name to lookup in the vocabulary registry"),
|
||||
required=False,
|
||||
default=None
|
||||
)
|
||||
|
||||
# Collections:
|
||||
|
||||
# Abstract
|
||||
|
||||
|
||||
class ICollection(IMinMaxLen, IIterable, IContainer):
|
||||
"""Abstract interface containing a collection value.
|
||||
|
||||
The Value must be iterable and may have a min_length/max_length.
|
||||
"""
|
||||
|
||||
value_type = Field(
|
||||
title=_("Value Type"),
|
||||
description=_("Field value items must conform to the given type, "
|
||||
"expressed via a Field."))
|
||||
|
||||
unique = Bool(
|
||||
title=_('Unique Members'),
|
||||
description=_('Specifies whether the members of the collection '
|
||||
'must be unique.'),
|
||||
default=False)
|
||||
|
||||
|
||||
class ISequence(ICollection):
|
||||
"""Abstract interface specifying that the value is ordered"""
|
||||
|
||||
|
||||
class IUnorderedCollection(ICollection):
|
||||
"""Abstract interface specifying that the value cannot be ordered"""
|
||||
|
||||
|
||||
class IAbstractSet(IUnorderedCollection):
|
||||
"""An unordered collection of unique values."""
|
||||
|
||||
unique = Attribute("This ICollection interface attribute must be True")
|
||||
|
||||
|
||||
class IAbstractBag(IUnorderedCollection):
|
||||
"""An unordered collection of values, with no limitations on whether
|
||||
members are unique"""
|
||||
|
||||
unique = Attribute("This ICollection interface attribute must be False")
|
||||
|
||||
|
||||
# Concrete
|
||||
|
||||
|
||||
class ITuple(ISequence):
|
||||
"""Field containing a value that implements the API of a conventional
|
||||
Python tuple."""
|
||||
|
||||
|
||||
class IList(ISequence):
|
||||
"""Field containing a value that implements the API of a conventional
|
||||
Python list."""
|
||||
|
||||
|
||||
class ISet(IAbstractSet):
|
||||
"""Field containing a value that implements the API of a Python2.4+ set.
|
||||
"""
|
||||
|
||||
|
||||
class IFrozenSet(IAbstractSet):
|
||||
"""Field containing a value that implements the API of a conventional
|
||||
Python 2.4+ frozenset."""
|
||||
|
||||
# (end Collections)
|
||||
|
||||
|
||||
class IObject(IField):
|
||||
"""Field containing an Object value."""
|
||||
|
||||
schema = Attribute(
|
||||
"schema",
|
||||
_("The Interface that defines the Fields comprising the Object.")
|
||||
)
|
||||
|
||||
|
||||
class IBeforeObjectAssignedEvent(Interface):
|
||||
"""An object is going to be assigned to an attribute on another object.
|
||||
|
||||
Subscribers to this event can change the object on this event to change
|
||||
what object is going to be assigned. This is useful, e.g. for wrapping
|
||||
or replacing objects before they get assigned to conform to application
|
||||
policy.
|
||||
"""
|
||||
|
||||
object = Attribute("The object that is going to be assigned.")
|
||||
|
||||
name = Attribute("The name of the attribute under which the object "
|
||||
"will be assigned.")
|
||||
|
||||
context = Attribute("The context object where the object will be "
|
||||
"assigned to.")
|
||||
|
||||
|
||||
class IDict(IMinMaxLen, IIterable, IContainer):
|
||||
"""Field containing a conventional dict.
|
||||
|
||||
The key_type and value_type fields allow specification
|
||||
of restrictions for keys and values contained in the dict.
|
||||
"""
|
||||
|
||||
key_type = Attribute(
|
||||
"key_type",
|
||||
_("Field keys must conform to the given type, expressed via a Field.")
|
||||
)
|
||||
|
||||
value_type = Attribute(
|
||||
"value_type",
|
||||
_("Field values must conform to the given type, expressed "
|
||||
"via a Field.")
|
||||
)
|
||||
|
||||
|
||||
class ITerm(Interface):
|
||||
"""Object representing a single value in a vocabulary."""
|
||||
|
||||
value = Attribute(
|
||||
"value", "The value used to represent vocabulary term in a field.")
|
||||
|
||||
|
||||
class ITokenizedTerm(ITerm):
|
||||
"""Object representing a single value in a tokenized vocabulary.
|
||||
"""
|
||||
|
||||
# Should be a ``zope.schema.ASCIILine``, but `ASCIILine` is not a bootstrap
|
||||
# field.
|
||||
token = Attribute(
|
||||
"token",
|
||||
"""Token which can be used to represent the value on a stream.
|
||||
|
||||
The value of this attribute must be a non-empty 7-bit string.
|
||||
Control characters are not allowed.
|
||||
""")
|
||||
|
||||
|
||||
class ITitledTokenizedTerm(ITokenizedTerm):
|
||||
"""A tokenized term that includes a title."""
|
||||
|
||||
title = TextLine(title=_("Title"))
|
||||
|
||||
|
||||
class ISource(Interface):
|
||||
"""A set of values from which to choose
|
||||
|
||||
Sources represent sets of values. They are used to specify the
|
||||
source for choice fields.
|
||||
|
||||
Sources can be large (even infinite), in which case, they need to
|
||||
be queried to find out what their values are.
|
||||
"""
|
||||
|
||||
def __contains__(value):
|
||||
"""Return whether the value is available in this source
|
||||
"""
|
||||
|
||||
|
||||
class ISourceQueriables(Interface):
|
||||
"""A collection of objects for querying sources
|
||||
"""
|
||||
|
||||
def getQueriables():
|
||||
"""Return an iterable of objects that can be queried
|
||||
|
||||
The returned obects should be two-tuples with:
|
||||
|
||||
- A unicode id
|
||||
|
||||
The id must uniquely identify the queriable object within
|
||||
the set of queriable objects. Furthermore, in subsequent
|
||||
calls, the same id should be used for a given queriable
|
||||
object.
|
||||
|
||||
- A queriable object
|
||||
|
||||
This is an object for which there is a view provided for
|
||||
searching for items.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class IContextSourceBinder(Interface):
|
||||
def __call__(context):
|
||||
"""Return a context-bound instance that implements ISource.
|
||||
"""
|
||||
|
||||
|
||||
class IBaseVocabulary(ISource):
|
||||
"""Representation of a vocabulary.
|
||||
|
||||
At this most basic level, a vocabulary only need to support a test
|
||||
for containment. This can be implemented either by __contains__()
|
||||
or by sequence __getitem__() (the later only being useful for
|
||||
vocabularies which are intrinsically ordered).
|
||||
"""
|
||||
|
||||
def getTerm(value):
|
||||
"""Return the ITerm object for the term 'value'.
|
||||
|
||||
If 'value' is not a valid term, this method raises LookupError.
|
||||
"""
|
||||
|
||||
|
||||
class IIterableSource(ISource):
|
||||
"""Source which supports iteration over allowed values.
|
||||
|
||||
The objects iteration provides must be values from the source.
|
||||
"""
|
||||
|
||||
def __iter__():
|
||||
"""Return an iterator which provides the values from the source."""
|
||||
|
||||
def __len__():
|
||||
"""Return the number of valid values, or sys.maxint."""
|
||||
|
||||
|
||||
# BBB vocabularies are pending deprecation, hopefully in 3.3
|
||||
class IIterableVocabulary(Interface):
|
||||
"""Vocabulary which supports iteration over allowed values.
|
||||
|
||||
The objects iteration provides must conform to the ITerm
|
||||
interface.
|
||||
"""
|
||||
|
||||
def __iter__():
|
||||
"""Return an iterator which provides the terms from the vocabulary."""
|
||||
|
||||
def __len__():
|
||||
"""Return the number of valid terms, or sys.maxint."""
|
||||
|
||||
|
||||
class IVocabulary(IIterableVocabulary, IBaseVocabulary):
|
||||
"""Vocabulary which is iterable."""
|
||||
|
||||
|
||||
class IVocabularyTokenized(IVocabulary):
|
||||
"""Vocabulary that provides support for tokenized representation.
|
||||
|
||||
Terms returned from getTerm() and provided by iteration must
|
||||
conform to ITokenizedTerm.
|
||||
"""
|
||||
|
||||
def getTermByToken(token):
|
||||
"""Return an ITokenizedTerm for the passed-in token.
|
||||
|
||||
If `token` is not represented in the vocabulary, `LookupError`
|
||||
is raised.
|
||||
"""
|
||||
|
||||
|
||||
class ITreeVocabulary(IVocabularyTokenized, IEnumerableMapping):
|
||||
"""A tokenized vocabulary with a tree-like structure.
|
||||
|
||||
The tree is implemented as dictionary, with keys being ITokenizedTerm
|
||||
terms and the values being similar dictionaries. Leaf values are empty
|
||||
dictionaries.
|
||||
"""
|
||||
|
||||
|
||||
class IVocabularyRegistry(Interface):
|
||||
"""Registry that provides IBaseVocabulary objects for specific fields.
|
||||
"""
|
||||
|
||||
def get(object, name):
|
||||
"""Return the vocabulary named 'name' for the content object
|
||||
'object'.
|
||||
|
||||
When the vocabulary cannot be found, LookupError is raised.
|
||||
"""
|
||||
|
||||
|
||||
class IVocabularyFactory(Interface):
|
||||
"""Can create vocabularies."""
|
||||
|
||||
def __call__(context):
|
||||
"""The context provides a location that the vocabulary can make use of.
|
||||
"""
|
||||
|
||||
|
||||
class IFieldEvent(Interface):
|
||||
|
||||
field = Attribute("The field that has been changed")
|
||||
|
||||
object = Attribute("The object containing the field")
|
||||
|
||||
|
||||
class IFieldUpdatedEvent(IFieldEvent):
|
||||
"""
|
||||
A field has been modified
|
||||
|
||||
Subscribers will get the old and the new value together with the field
|
||||
"""
|
||||
|
||||
old_value = Attribute("The value of the field before modification")
|
||||
|
||||
new_value = Attribute("The value of the field after modification")
|
||||
49
zope/schema/tests/__init__.py
Normal file
49
zope/schema/tests/__init__.py
Normal file
@@ -0,0 +1,49 @@
|
||||
#
|
||||
# This file is necessary to make this directory a package.
|
||||
|
||||
import re
|
||||
|
||||
from zope.schema._compat import PY3
|
||||
from zope.testing import renormalizing
|
||||
|
||||
if PY3:
|
||||
py3_checker = renormalizing.RENormalizing([
|
||||
(re.compile(r"u'([^']*)'"),
|
||||
r"'\1'"),
|
||||
(re.compile(r"^b'([^']*)'"),
|
||||
r"'\1'"),
|
||||
(re.compile(r"([^'])b'([^']*)'"),
|
||||
r"\1'\2'"),
|
||||
(re.compile(r"<class 'bytes'>"),
|
||||
r"<type 'str'>"),
|
||||
(re.compile(r"<class 'str'>"),
|
||||
r"<type 'unicode'>"),
|
||||
(re.compile(r"zope.schema._bootstrapinterfaces.InvalidValue"),
|
||||
r"InvalidValue"),
|
||||
(re.compile(r"zope.schema.interfaces.InvalidId: '([^']*)'"),
|
||||
r"InvalidId: \1"),
|
||||
(re.compile(r"zope.schema.interfaces.InvalidId:"),
|
||||
r"InvalidId:"),
|
||||
(re.compile(r"zope.schema.interfaces.InvalidURI: '([^']*)'"),
|
||||
r"InvalidURI: \1"),
|
||||
(re.compile(r"zope.schema.interfaces.InvalidURI:"),
|
||||
r"InvalidURI:"),
|
||||
(re.compile(r"zope.schema.interfaces.InvalidDottedName: '([^']*)'"),
|
||||
r"InvalidDottedName: \1"),
|
||||
(re.compile(r"zope.schema.interfaces.InvalidDottedName:"),
|
||||
r"InvalidDottedName:"),
|
||||
(re.compile(
|
||||
r"zope.schema._bootstrapinterfaces.ConstraintNotSatisfied: '([^']*)'"
|
||||
),
|
||||
r"ConstraintNotSatisfied: \1"),
|
||||
(re.compile(
|
||||
r"zope.schema._bootstrapinterfaces.ConstraintNotSatisfied:"),
|
||||
r"ConstraintNotSatisfied:"),
|
||||
(re.compile(r"zope.schema._bootstrapinterfaces.WrongType:"),
|
||||
r"WrongType:"),
|
||||
])
|
||||
else:
|
||||
py3_checker = renormalizing.RENormalizing([
|
||||
(re.compile(r"([^'])b'([^']*)'"),
|
||||
r"\1'\2'"),
|
||||
])
|
||||
130
zope/schema/tests/states.py
Normal file
130
zope/schema/tests/states.py
Normal file
@@ -0,0 +1,130 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Sample vocabulary supporting state abbreviations.
|
||||
"""
|
||||
from zope.schema._compat import u
|
||||
from zope.interface import implementer
|
||||
from zope.schema import interfaces
|
||||
from zope.schema import Choice
|
||||
|
||||
# This table is based on information from the United States Postal Service:
|
||||
# http://www.usps.com/ncsc/lookups/abbreviations.html#states
|
||||
_states = {
|
||||
'AL': u('Alabama'),
|
||||
'AK': u('Alaska'),
|
||||
'AS': u('American Samoa'),
|
||||
'AZ': u('Arizona'),
|
||||
'AR': u('Arkansas'),
|
||||
'CA': u('California'),
|
||||
'CO': u('Colorado'),
|
||||
'CT': u('Connecticut'),
|
||||
'DE': u('Delaware'),
|
||||
'DC': u('District of Columbia'),
|
||||
'FM': u('Federated States of Micronesia'),
|
||||
'FL': u('Florida'),
|
||||
'GA': u('Georgia'),
|
||||
'GU': u('Guam'),
|
||||
'HI': u('Hawaii'),
|
||||
'ID': u('Idaho'),
|
||||
'IL': u('Illinois'),
|
||||
'IN': u('Indiana'),
|
||||
'IA': u('Iowa'),
|
||||
'KS': u('Kansas'),
|
||||
'KY': u('Kentucky'),
|
||||
'LA': u('Louisiana'),
|
||||
'ME': u('Maine'),
|
||||
'MH': u('Marshall Islands'),
|
||||
'MD': u('Maryland'),
|
||||
'MA': u('Massachusetts'),
|
||||
'MI': u('Michigan'),
|
||||
'MN': u('Minnesota'),
|
||||
'MS': u('Mississippi'),
|
||||
'MO': u('Missouri'),
|
||||
'MT': u('Montana'),
|
||||
'NE': u('Nebraska'),
|
||||
'NV': u('Nevada'),
|
||||
'NH': u('New Hampshire'),
|
||||
'NJ': u('New Jersey'),
|
||||
'NM': u('New Mexico'),
|
||||
'NY': u('New York'),
|
||||
'NC': u('North Carolina'),
|
||||
'ND': u('North Dakota'),
|
||||
'MP': u('Northern Mariana Islands'),
|
||||
'OH': u('Ohio'),
|
||||
'OK': u('Oklahoma'),
|
||||
'OR': u('Oregon'),
|
||||
'PW': u('Palau'),
|
||||
'PA': u('Pennsylvania'),
|
||||
'PR': u('Puerto Rico'),
|
||||
'RI': u('Rhode Island'),
|
||||
'SC': u('South Carolina'),
|
||||
'SD': u('South Dakota'),
|
||||
'TN': u('Tennessee'),
|
||||
'TX': u('Texas'),
|
||||
'UT': u('Utah'),
|
||||
'VT': u('Vermont'),
|
||||
'VI': u('Virgin Islands'),
|
||||
'VA': u('Virginia'),
|
||||
'WA': u('Washington'),
|
||||
'WV': u('West Virginia'),
|
||||
'WI': u('Wisconsin'),
|
||||
'WY': u('Wyoming'),
|
||||
}
|
||||
|
||||
|
||||
@implementer(interfaces.ITerm)
|
||||
class State(object):
|
||||
__slots__ = 'value', 'title'
|
||||
|
||||
def __init__(self, value, title):
|
||||
self.value = value
|
||||
self.title = title
|
||||
|
||||
for v, p in _states.items():
|
||||
_states[v] = State(v, p)
|
||||
|
||||
|
||||
class IStateVocabulary(interfaces.IVocabulary):
|
||||
"""Vocabularies that support the states database conform to this."""
|
||||
|
||||
|
||||
@implementer(IStateVocabulary)
|
||||
class StateVocabulary(object):
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, object=None):
|
||||
pass
|
||||
|
||||
def __contains__(self, value):
|
||||
return value in _states
|
||||
|
||||
def __iter__(self):
|
||||
return iter(_states.values())
|
||||
|
||||
def __len__(self):
|
||||
return len(_states)
|
||||
|
||||
def getTerm(self, value):
|
||||
return _states[value]
|
||||
|
||||
|
||||
class StateSelectionField(Choice):
|
||||
|
||||
vocabulary = StateVocabulary()
|
||||
|
||||
def __init__(self, **kw):
|
||||
super(StateSelectionField, self).__init__(
|
||||
vocabulary=StateSelectionField.vocabulary,
|
||||
**kw)
|
||||
self.vocabularyName = "states"
|
||||
822
zope/schema/tests/test__bootstrapfields.py
Normal file
822
zope/schema/tests/test__bootstrapfields.py
Normal file
@@ -0,0 +1,822 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2012 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.
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
|
||||
class ValidatedPropertyTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema._bootstrapfields import ValidatedProperty
|
||||
return ValidatedProperty
|
||||
|
||||
def _makeOne(self, *args, **kw):
|
||||
return self._getTargetClass()(*args, **kw)
|
||||
|
||||
def test___set___not_missing_w_check(self):
|
||||
_checked = []
|
||||
|
||||
def _check(inst, value):
|
||||
_checked.append((inst, value))
|
||||
|
||||
class Test(DummyInst):
|
||||
_prop = None
|
||||
prop = self._makeOne('_prop', _check)
|
||||
inst = Test()
|
||||
inst.prop = 'PROP'
|
||||
self.assertEqual(inst._prop, 'PROP')
|
||||
self.assertEqual(_checked, [(inst, 'PROP')])
|
||||
|
||||
def test___set___not_missing_wo_check(self):
|
||||
class Test(DummyInst):
|
||||
_prop = None
|
||||
prop = self._makeOne('_prop')
|
||||
inst = Test(ValueError)
|
||||
|
||||
def _provoke(inst):
|
||||
inst.prop = 'PROP'
|
||||
self.assertRaises(ValueError, _provoke, inst)
|
||||
self.assertEqual(inst._prop, None)
|
||||
|
||||
def test___set___w_missing_wo_check(self):
|
||||
class Test(DummyInst):
|
||||
_prop = None
|
||||
prop = self._makeOne('_prop')
|
||||
inst = Test(ValueError)
|
||||
inst.prop = DummyInst.missing_value
|
||||
self.assertEqual(inst._prop, DummyInst.missing_value)
|
||||
|
||||
def test___get__(self):
|
||||
class Test(DummyInst):
|
||||
_prop = None
|
||||
prop = self._makeOne('_prop')
|
||||
inst = Test()
|
||||
inst._prop = 'PROP'
|
||||
self.assertEqual(inst.prop, 'PROP')
|
||||
|
||||
|
||||
class DefaultPropertyTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema._bootstrapfields import DefaultProperty
|
||||
return DefaultProperty
|
||||
|
||||
def _makeOne(self, *args, **kw):
|
||||
return self._getTargetClass()(*args, **kw)
|
||||
|
||||
def test___get___wo_defaultFactory_miss(self):
|
||||
class Test(DummyInst):
|
||||
_prop = None
|
||||
prop = self._makeOne('_prop')
|
||||
inst = Test()
|
||||
inst.defaultFactory = None
|
||||
|
||||
def _provoke(inst):
|
||||
return inst.prop
|
||||
self.assertRaises(KeyError, _provoke, inst)
|
||||
|
||||
def test___get___wo_defaultFactory_hit(self):
|
||||
class Test(DummyInst):
|
||||
_prop = None
|
||||
prop = self._makeOne('_prop')
|
||||
inst = Test()
|
||||
inst.defaultFactory = None
|
||||
inst._prop = 'PROP'
|
||||
self.assertEqual(inst.prop, 'PROP')
|
||||
|
||||
def test__get___wo_defaultFactory_in_dict(self):
|
||||
class Test(DummyInst):
|
||||
_prop = None
|
||||
prop = self._makeOne('_prop')
|
||||
inst = Test()
|
||||
inst._prop = 'PROP'
|
||||
self.assertEqual(inst.prop, 'PROP')
|
||||
|
||||
def test___get___w_defaultFactory_not_ICAF_no_check(self):
|
||||
class Test(DummyInst):
|
||||
_prop = None
|
||||
prop = self._makeOne('_prop')
|
||||
inst = Test(ValueError)
|
||||
|
||||
def _factory():
|
||||
return 'PROP'
|
||||
inst.defaultFactory = _factory
|
||||
|
||||
def _provoke(inst):
|
||||
return inst.prop
|
||||
self.assertRaises(ValueError, _provoke, inst)
|
||||
|
||||
def test___get___w_defaultFactory_w_ICAF_w_check(self):
|
||||
from zope.interface import directlyProvides
|
||||
from zope.schema._bootstrapinterfaces \
|
||||
import IContextAwareDefaultFactory
|
||||
_checked = []
|
||||
|
||||
def _check(inst, value):
|
||||
_checked.append((inst, value))
|
||||
|
||||
class Test(DummyInst):
|
||||
_prop = None
|
||||
prop = self._makeOne('_prop', _check)
|
||||
inst = Test(ValueError)
|
||||
inst.context = object()
|
||||
_called_with = []
|
||||
|
||||
def _factory(context):
|
||||
_called_with.append(context)
|
||||
return 'PROP'
|
||||
directlyProvides(_factory, IContextAwareDefaultFactory)
|
||||
inst.defaultFactory = _factory
|
||||
self.assertEqual(inst.prop, 'PROP')
|
||||
self.assertEqual(_checked, [(inst, 'PROP')])
|
||||
self.assertEqual(_called_with, [inst.context])
|
||||
|
||||
|
||||
class FieldTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema._bootstrapfields import Field
|
||||
return Field
|
||||
|
||||
def _makeOne(self, *args, **kw):
|
||||
return self._getTargetClass()(*args, **kw)
|
||||
|
||||
def test_ctor_defaults(self):
|
||||
from zope.schema._compat import u
|
||||
field = self._makeOne()
|
||||
self.assertEqual(field.__name__, u(''))
|
||||
self.assertEqual(field.__doc__, u(''))
|
||||
self.assertEqual(field.title, u(''))
|
||||
self.assertEqual(field.description, u(''))
|
||||
self.assertEqual(field.required, True)
|
||||
self.assertEqual(field.readonly, False)
|
||||
self.assertEqual(field.constraint(object()), True)
|
||||
self.assertEqual(field.default, None)
|
||||
self.assertEqual(field.defaultFactory, None)
|
||||
self.assertEqual(field.missing_value, None)
|
||||
self.assertEqual(field.context, None)
|
||||
|
||||
def test_ctor_w_title_wo_description(self):
|
||||
from zope.schema._compat import u
|
||||
field = self._makeOne(u('TITLE'))
|
||||
self.assertEqual(field.__name__, u(''))
|
||||
self.assertEqual(field.__doc__, u('TITLE'))
|
||||
self.assertEqual(field.title, u('TITLE'))
|
||||
self.assertEqual(field.description, u(''))
|
||||
|
||||
def test_ctor_wo_title_w_description(self):
|
||||
from zope.schema._compat import u
|
||||
field = self._makeOne(description=u('DESC'))
|
||||
self.assertEqual(field.__name__, u(''))
|
||||
self.assertEqual(field.__doc__, u('DESC'))
|
||||
self.assertEqual(field.title, u(''))
|
||||
self.assertEqual(field.description, u('DESC'))
|
||||
|
||||
def test_ctor_w_both_title_and_description(self):
|
||||
from zope.schema._compat import u
|
||||
field = self._makeOne(u('TITLE'), u('DESC'), u('NAME'))
|
||||
self.assertEqual(field.__name__, u('NAME'))
|
||||
self.assertEqual(field.__doc__, u('TITLE\n\nDESC'))
|
||||
self.assertEqual(field.title, u('TITLE'))
|
||||
self.assertEqual(field.description, u('DESC'))
|
||||
|
||||
def test_ctor_order_madness(self):
|
||||
klass = self._getTargetClass()
|
||||
order_before = klass.order
|
||||
field = self._makeOne()
|
||||
order_after = klass.order
|
||||
self.assertEqual(order_after, order_before + 1)
|
||||
self.assertEqual(field.order, order_after)
|
||||
|
||||
def test_explicit_required_readonly_missingValue(self):
|
||||
obj = object()
|
||||
field = self._makeOne(required=False, readonly=True, missing_value=obj)
|
||||
self.assertEqual(field.required, False)
|
||||
self.assertEqual(field.readonly, True)
|
||||
self.assertEqual(field.missing_value, obj)
|
||||
|
||||
def test_explicit_constraint_default(self):
|
||||
_called_with = []
|
||||
obj = object()
|
||||
|
||||
def _constraint(value):
|
||||
_called_with.append(value)
|
||||
return value is obj
|
||||
field = self._makeOne(
|
||||
required=False, readonly=True, constraint=_constraint, default=obj
|
||||
)
|
||||
self.assertEqual(field.required, False)
|
||||
self.assertEqual(field.readonly, True)
|
||||
self.assertEqual(_called_with, [obj])
|
||||
self.assertEqual(field.constraint(self), False)
|
||||
self.assertEqual(_called_with, [obj, self])
|
||||
self.assertEqual(field.default, obj)
|
||||
|
||||
def test_explicit_defaultFactory(self):
|
||||
_called_with = []
|
||||
obj = object()
|
||||
|
||||
def _constraint(value):
|
||||
_called_with.append(value)
|
||||
return value is obj
|
||||
|
||||
def _factory():
|
||||
return obj
|
||||
field = self._makeOne(
|
||||
required=False,
|
||||
readonly=True,
|
||||
constraint=_constraint,
|
||||
defaultFactory=_factory,
|
||||
)
|
||||
self.assertEqual(field.required, False)
|
||||
self.assertEqual(field.readonly, True)
|
||||
self.assertEqual(field.constraint(self), False)
|
||||
self.assertEqual(_called_with, [self])
|
||||
self.assertEqual(field.default, obj)
|
||||
self.assertEqual(_called_with, [self, obj])
|
||||
self.assertEqual(field.defaultFactory, _factory)
|
||||
|
||||
def test_explicit_defaultFactory_returning_missing_value(self):
|
||||
def _factory():
|
||||
return None
|
||||
field = self._makeOne(required=True,
|
||||
defaultFactory=_factory)
|
||||
self.assertEqual(field.default, None)
|
||||
|
||||
def test_bind(self):
|
||||
obj = object()
|
||||
field = self._makeOne()
|
||||
bound = field.bind(obj)
|
||||
self.assertEqual(bound.context, obj)
|
||||
expected = dict(field.__dict__)
|
||||
found = dict(bound.__dict__)
|
||||
found.pop('context')
|
||||
self.assertEqual(found, expected)
|
||||
self.assertEqual(bound.__class__, field.__class__)
|
||||
|
||||
def test_validate_missing_not_required(self):
|
||||
missing = object()
|
||||
|
||||
def _fail(value):
|
||||
return False
|
||||
field = self._makeOne(
|
||||
required=False, missing_value=missing, constraint=_fail,
|
||||
)
|
||||
self.assertEqual(field.validate(missing), None) # doesn't raise
|
||||
|
||||
def test_validate_missing_and_required(self):
|
||||
from zope.schema._bootstrapinterfaces import RequiredMissing
|
||||
missing = object()
|
||||
|
||||
def _fail(value):
|
||||
return False
|
||||
field = self._makeOne(
|
||||
required=True, missing_value=missing, constraint=_fail,
|
||||
)
|
||||
self.assertRaises(RequiredMissing, field.validate, missing)
|
||||
|
||||
def test_validate_wrong_type(self):
|
||||
from zope.schema._bootstrapinterfaces import WrongType
|
||||
|
||||
def _fail(value):
|
||||
return False
|
||||
field = self._makeOne(required=True, constraint=_fail)
|
||||
field._type = str
|
||||
self.assertRaises(WrongType, field.validate, 1)
|
||||
|
||||
def test_validate_constraint_fails(self):
|
||||
from zope.schema._bootstrapinterfaces import ConstraintNotSatisfied
|
||||
|
||||
def _fail(value):
|
||||
return False
|
||||
field = self._makeOne(required=True, constraint=_fail)
|
||||
field._type = int
|
||||
self.assertRaises(ConstraintNotSatisfied, field.validate, 1)
|
||||
|
||||
def test_validate_constraint_raises_StopValidation(self):
|
||||
from zope.schema._bootstrapinterfaces import StopValidation
|
||||
|
||||
def _fail(value):
|
||||
raise StopValidation
|
||||
field = self._makeOne(required=True, constraint=_fail)
|
||||
field._type = int
|
||||
field.validate(1) # doesn't raise
|
||||
|
||||
def test___eq___different_type(self):
|
||||
left = self._makeOne()
|
||||
|
||||
class Derived(self._getTargetClass()):
|
||||
pass
|
||||
right = Derived()
|
||||
self.assertEqual(left == right, False)
|
||||
self.assertEqual(left != right, True)
|
||||
|
||||
def test___eq___same_type_different_attrs(self):
|
||||
left = self._makeOne(required=True)
|
||||
right = self._makeOne(required=False)
|
||||
self.assertEqual(left == right, False)
|
||||
self.assertEqual(left != right, True)
|
||||
|
||||
def test___eq___same_type_same_attrs(self):
|
||||
left = self._makeOne()
|
||||
right = self._makeOne()
|
||||
self.assertEqual(left == right, True)
|
||||
self.assertEqual(left != right, False)
|
||||
|
||||
def test_get_miss(self):
|
||||
field = self._makeOne(__name__='nonesuch')
|
||||
inst = DummyInst()
|
||||
self.assertRaises(AttributeError, field.get, inst)
|
||||
|
||||
def test_get_hit(self):
|
||||
field = self._makeOne(__name__='extant')
|
||||
inst = DummyInst()
|
||||
inst.extant = 'EXTANT'
|
||||
self.assertEqual(field.get(inst), 'EXTANT')
|
||||
|
||||
def test_query_miss_no_default(self):
|
||||
field = self._makeOne(__name__='nonesuch')
|
||||
inst = DummyInst()
|
||||
self.assertEqual(field.query(inst), None)
|
||||
|
||||
def test_query_miss_w_default(self):
|
||||
field = self._makeOne(__name__='nonesuch')
|
||||
inst = DummyInst()
|
||||
self.assertEqual(field.query(inst, 'DEFAULT'), 'DEFAULT')
|
||||
|
||||
def test_query_hit(self):
|
||||
field = self._makeOne(__name__='extant')
|
||||
inst = DummyInst()
|
||||
inst.extant = 'EXTANT'
|
||||
self.assertEqual(field.query(inst), 'EXTANT')
|
||||
|
||||
def test_set_readonly(self):
|
||||
field = self._makeOne(__name__='lirame', readonly=True)
|
||||
inst = DummyInst()
|
||||
self.assertRaises(TypeError, field.set, inst, 'VALUE')
|
||||
|
||||
def test_set_hit(self):
|
||||
field = self._makeOne(__name__='extant')
|
||||
inst = DummyInst()
|
||||
inst.extant = 'BEFORE'
|
||||
field.set(inst, 'AFTER')
|
||||
self.assertEqual(inst.extant, 'AFTER')
|
||||
|
||||
|
||||
class ContainerTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema._bootstrapfields import Container
|
||||
return Container
|
||||
|
||||
def _makeOne(self, *args, **kw):
|
||||
return self._getTargetClass()(*args, **kw)
|
||||
|
||||
def test_validate_not_required(self):
|
||||
field = self._makeOne(required=False)
|
||||
field.validate(None)
|
||||
|
||||
def test_validate_required(self):
|
||||
from zope.schema.interfaces import RequiredMissing
|
||||
field = self._makeOne()
|
||||
self.assertRaises(RequiredMissing, field.validate, None)
|
||||
|
||||
def test__validate_not_collection_not_iterable(self):
|
||||
from zope.schema._bootstrapinterfaces import NotAContainer
|
||||
cont = self._makeOne()
|
||||
self.assertRaises(NotAContainer, cont._validate, object())
|
||||
|
||||
def test__validate_collection_but_not_iterable(self):
|
||||
cont = self._makeOne()
|
||||
|
||||
class Dummy(object):
|
||||
def __contains__(self, item):
|
||||
return False
|
||||
cont._validate(Dummy()) # doesn't raise
|
||||
|
||||
def test__validate_not_collection_but_iterable(self):
|
||||
cont = self._makeOne()
|
||||
|
||||
class Dummy(object):
|
||||
def __iter__(self):
|
||||
return iter(())
|
||||
cont._validate(Dummy()) # doesn't raise
|
||||
|
||||
def test__validate_w_collections(self):
|
||||
cont = self._makeOne()
|
||||
cont._validate(()) # doesn't raise
|
||||
cont._validate([]) # doesn't raise
|
||||
cont._validate('') # doesn't raise
|
||||
cont._validate({}) # doesn't raise
|
||||
|
||||
|
||||
class IterableTests(ContainerTests):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema._bootstrapfields import Iterable
|
||||
return Iterable
|
||||
|
||||
def test__validate_collection_but_not_iterable(self):
|
||||
from zope.schema._bootstrapinterfaces import NotAnIterator
|
||||
itr = self._makeOne()
|
||||
|
||||
class Dummy(object):
|
||||
def __contains__(self, item):
|
||||
return False
|
||||
self.assertRaises(NotAnIterator, itr._validate, Dummy())
|
||||
|
||||
|
||||
class OrderableTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema._bootstrapfields import Orderable
|
||||
return Orderable
|
||||
|
||||
def _makeOne(self, *args, **kw):
|
||||
# Orderable is a mixin for a type derived from Field
|
||||
from zope.schema._bootstrapfields import Field
|
||||
|
||||
class Mixed(self._getTargetClass(), Field):
|
||||
pass
|
||||
return Mixed(*args, **kw)
|
||||
|
||||
def test_ctor_defaults(self):
|
||||
ordb = self._makeOne()
|
||||
self.assertEqual(ordb.min, None)
|
||||
self.assertEqual(ordb.max, None)
|
||||
self.assertEqual(ordb.default, None)
|
||||
|
||||
def test_ctor_default_too_small(self):
|
||||
# This test exercises _validate, too
|
||||
from zope.schema._bootstrapinterfaces import TooSmall
|
||||
self.assertRaises(TooSmall, self._makeOne, min=0, default=-1)
|
||||
|
||||
def test_ctor_default_too_large(self):
|
||||
# This test exercises _validate, too
|
||||
from zope.schema._bootstrapinterfaces import TooBig
|
||||
self.assertRaises(TooBig, self._makeOne, max=10, default=11)
|
||||
|
||||
|
||||
class MinMaxLenTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema._bootstrapfields import MinMaxLen
|
||||
return MinMaxLen
|
||||
|
||||
def _makeOne(self, *args, **kw):
|
||||
# MinMaxLen is a mixin for a type derived from Field
|
||||
from zope.schema._bootstrapfields import Field
|
||||
|
||||
class Mixed(self._getTargetClass(), Field):
|
||||
pass
|
||||
return Mixed(*args, **kw)
|
||||
|
||||
def test_ctor_defaults(self):
|
||||
mml = self._makeOne()
|
||||
self.assertEqual(mml.min_length, 0)
|
||||
self.assertEqual(mml.max_length, None)
|
||||
|
||||
def test_validate_too_short(self):
|
||||
from zope.schema._bootstrapinterfaces import TooShort
|
||||
mml = self._makeOne(min_length=1)
|
||||
self.assertRaises(TooShort, mml._validate, ())
|
||||
|
||||
def test_validate_too_long(self):
|
||||
from zope.schema._bootstrapinterfaces import TooLong
|
||||
mml = self._makeOne(max_length=2)
|
||||
self.assertRaises(TooLong, mml._validate, (0, 1, 2))
|
||||
|
||||
|
||||
class TextTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema._bootstrapfields import Text
|
||||
return Text
|
||||
|
||||
def _makeOne(self, *args, **kw):
|
||||
return self._getTargetClass()(*args, **kw)
|
||||
|
||||
def test_ctor_defaults(self):
|
||||
from zope.schema._compat import text_type
|
||||
txt = self._makeOne()
|
||||
self.assertEqual(txt._type, text_type)
|
||||
|
||||
def test_validate_wrong_types(self):
|
||||
from zope.schema.interfaces import WrongType
|
||||
from zope.schema._compat import b
|
||||
field = self._makeOne()
|
||||
self.assertRaises(WrongType, field.validate, b(''))
|
||||
self.assertRaises(WrongType, field.validate, 1)
|
||||
self.assertRaises(WrongType, field.validate, 1.0)
|
||||
self.assertRaises(WrongType, field.validate, ())
|
||||
self.assertRaises(WrongType, field.validate, [])
|
||||
self.assertRaises(WrongType, field.validate, {})
|
||||
self.assertRaises(WrongType, field.validate, set())
|
||||
self.assertRaises(WrongType, field.validate, frozenset())
|
||||
self.assertRaises(WrongType, field.validate, object())
|
||||
|
||||
def test_validate_w_invalid_default(self):
|
||||
from zope.schema._compat import b
|
||||
from zope.schema.interfaces import ValidationError
|
||||
self.assertRaises(ValidationError, self._makeOne, default=b(''))
|
||||
|
||||
def test_validate_not_required(self):
|
||||
from zope.schema._compat import u
|
||||
field = self._makeOne(required=False)
|
||||
field.validate(u(''))
|
||||
field.validate(u('abc'))
|
||||
field.validate(u('abc\ndef'))
|
||||
field.validate(None)
|
||||
|
||||
def test_validate_required(self):
|
||||
from zope.schema.interfaces import RequiredMissing
|
||||
from zope.schema._compat import u
|
||||
field = self._makeOne()
|
||||
field.validate(u(''))
|
||||
field.validate(u('abc'))
|
||||
field.validate(u('abc\ndef'))
|
||||
self.assertRaises(RequiredMissing, field.validate, None)
|
||||
|
||||
def test_fromUnicode_miss(self):
|
||||
from zope.schema._bootstrapinterfaces import WrongType
|
||||
from zope.schema._compat import b
|
||||
deadbeef = b('DEADBEEF')
|
||||
txt = self._makeOne()
|
||||
self.assertRaises(WrongType, txt.fromUnicode, deadbeef)
|
||||
|
||||
def test_fromUnicode_hit(self):
|
||||
from zope.schema._compat import u
|
||||
deadbeef = u('DEADBEEF')
|
||||
txt = self._makeOne()
|
||||
self.assertEqual(txt.fromUnicode(deadbeef), deadbeef)
|
||||
|
||||
|
||||
class TextLineTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema._field import TextLine
|
||||
return TextLine
|
||||
|
||||
def _makeOne(self, *args, **kw):
|
||||
return self._getTargetClass()(*args, **kw)
|
||||
|
||||
def test_class_conforms_to_ITextLine(self):
|
||||
from zope.interface.verify import verifyClass
|
||||
from zope.schema.interfaces import ITextLine
|
||||
verifyClass(ITextLine, self._getTargetClass())
|
||||
|
||||
def test_instance_conforms_to_ITextLine(self):
|
||||
from zope.interface.verify import verifyObject
|
||||
from zope.schema.interfaces import ITextLine
|
||||
verifyObject(ITextLine, self._makeOne())
|
||||
|
||||
def test_validate_wrong_types(self):
|
||||
from zope.schema.interfaces import WrongType
|
||||
from zope.schema._compat import b
|
||||
field = self._makeOne()
|
||||
self.assertRaises(WrongType, field.validate, b(''))
|
||||
self.assertRaises(WrongType, field.validate, 1)
|
||||
self.assertRaises(WrongType, field.validate, 1.0)
|
||||
self.assertRaises(WrongType, field.validate, ())
|
||||
self.assertRaises(WrongType, field.validate, [])
|
||||
self.assertRaises(WrongType, field.validate, {})
|
||||
self.assertRaises(WrongType, field.validate, set())
|
||||
self.assertRaises(WrongType, field.validate, frozenset())
|
||||
self.assertRaises(WrongType, field.validate, object())
|
||||
|
||||
def test_validate_not_required(self):
|
||||
from zope.schema._compat import u
|
||||
field = self._makeOne(required=False)
|
||||
field.validate(u(''))
|
||||
field.validate(u('abc'))
|
||||
field.validate(None)
|
||||
|
||||
def test_validate_required(self):
|
||||
from zope.schema.interfaces import RequiredMissing
|
||||
from zope.schema._compat import u
|
||||
field = self._makeOne()
|
||||
field.validate(u(''))
|
||||
field.validate(u('abc'))
|
||||
self.assertRaises(RequiredMissing, field.validate, None)
|
||||
|
||||
def test_constraint(self):
|
||||
from zope.schema._compat import u
|
||||
field = self._makeOne()
|
||||
self.assertEqual(field.constraint(u('')), True)
|
||||
self.assertEqual(field.constraint(u('abc')), True)
|
||||
self.assertEqual(field.constraint(u('abc\ndef')), False)
|
||||
|
||||
|
||||
class PasswordTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema._bootstrapfields import Password
|
||||
return Password
|
||||
|
||||
def _makeOne(self, *args, **kw):
|
||||
return self._getTargetClass()(*args, **kw)
|
||||
|
||||
def test_set_unchanged(self):
|
||||
klass = self._getTargetClass()
|
||||
pw = self._makeOne()
|
||||
inst = DummyInst()
|
||||
before = dict(inst.__dict__)
|
||||
pw.set(inst, klass.UNCHANGED_PASSWORD) # doesn't raise, doesn't write
|
||||
after = dict(inst.__dict__)
|
||||
self.assertEqual(after, before)
|
||||
|
||||
def test_set_normal(self):
|
||||
pw = self._makeOne(__name__='password')
|
||||
inst = DummyInst()
|
||||
pw.set(inst, 'PASSWORD')
|
||||
self.assertEqual(inst.password, 'PASSWORD')
|
||||
|
||||
def test_validate_not_required(self):
|
||||
from zope.schema._compat import u
|
||||
field = self._makeOne(required=False)
|
||||
field.validate(u(''))
|
||||
field.validate(u('abc'))
|
||||
field.validate(None)
|
||||
|
||||
def test_validate_required(self):
|
||||
from zope.schema.interfaces import RequiredMissing
|
||||
from zope.schema._compat import u
|
||||
field = self._makeOne()
|
||||
field.validate(u(''))
|
||||
field.validate(u('abc'))
|
||||
self.assertRaises(RequiredMissing, field.validate, None)
|
||||
|
||||
def test_validate_unchanged_not_already_set(self):
|
||||
from zope.schema._bootstrapinterfaces import WrongType
|
||||
klass = self._getTargetClass()
|
||||
inst = DummyInst()
|
||||
pw = self._makeOne(__name__='password').bind(inst)
|
||||
self.assertRaises(WrongType,
|
||||
pw.validate, klass.UNCHANGED_PASSWORD)
|
||||
|
||||
def test_validate_unchanged_already_set(self):
|
||||
klass = self._getTargetClass()
|
||||
inst = DummyInst()
|
||||
inst.password = 'foobar'
|
||||
pw = self._makeOne(__name__='password').bind(inst)
|
||||
pw.validate(klass.UNCHANGED_PASSWORD) # doesn't raise
|
||||
|
||||
def test_constraint(self):
|
||||
from zope.schema._compat import u
|
||||
field = self._makeOne()
|
||||
self.assertEqual(field.constraint(u('')), True)
|
||||
self.assertEqual(field.constraint(u('abc')), True)
|
||||
self.assertEqual(field.constraint(u('abc\ndef')), False)
|
||||
|
||||
|
||||
class BoolTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema._bootstrapfields import Bool
|
||||
return Bool
|
||||
|
||||
def _makeOne(self, *args, **kw):
|
||||
return self._getTargetClass()(*args, **kw)
|
||||
|
||||
def test_ctor_defaults(self):
|
||||
txt = self._makeOne()
|
||||
self.assertEqual(txt._type, bool)
|
||||
|
||||
def test__validate_w_int(self):
|
||||
boo = self._makeOne()
|
||||
boo._validate(0) # doesn't raise
|
||||
boo._validate(1) # doesn't raise
|
||||
|
||||
def test_set_w_int(self):
|
||||
boo = self._makeOne(__name__='boo')
|
||||
inst = DummyInst()
|
||||
boo.set(inst, 0)
|
||||
self.assertEqual(inst.boo, False)
|
||||
boo.set(inst, 1)
|
||||
self.assertEqual(inst.boo, True)
|
||||
|
||||
def test_fromUnicode_miss(self):
|
||||
from zope.schema._compat import u
|
||||
txt = self._makeOne()
|
||||
self.assertEqual(txt.fromUnicode(u('')), False)
|
||||
self.assertEqual(txt.fromUnicode(u('0')), False)
|
||||
self.assertEqual(txt.fromUnicode(u('1')), False)
|
||||
self.assertEqual(txt.fromUnicode(u('False')), False)
|
||||
self.assertEqual(txt.fromUnicode(u('false')), False)
|
||||
|
||||
def test_fromUnicode_hit(self):
|
||||
from zope.schema._compat import u
|
||||
txt = self._makeOne()
|
||||
self.assertEqual(txt.fromUnicode(u('True')), True)
|
||||
self.assertEqual(txt.fromUnicode(u('true')), True)
|
||||
|
||||
|
||||
class IntTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema._bootstrapfields import Int
|
||||
return Int
|
||||
|
||||
def _makeOne(self, *args, **kw):
|
||||
return self._getTargetClass()(*args, **kw)
|
||||
|
||||
def test_ctor_defaults(self):
|
||||
from zope.schema._compat import integer_types
|
||||
txt = self._makeOne()
|
||||
self.assertEqual(txt._type, integer_types)
|
||||
|
||||
def test_validate_not_required(self):
|
||||
field = self._makeOne(required=False)
|
||||
field.validate(None)
|
||||
field.validate(10)
|
||||
field.validate(0)
|
||||
field.validate(-1)
|
||||
|
||||
def test_validate_required(self):
|
||||
from zope.schema.interfaces import RequiredMissing
|
||||
field = self._makeOne()
|
||||
field.validate(10)
|
||||
field.validate(0)
|
||||
field.validate(-1)
|
||||
self.assertRaises(RequiredMissing, field.validate, None)
|
||||
|
||||
def test_validate_min(self):
|
||||
from zope.schema.interfaces import TooSmall
|
||||
field = self._makeOne(min=10)
|
||||
field.validate(10)
|
||||
field.validate(20)
|
||||
self.assertRaises(TooSmall, field.validate, 9)
|
||||
self.assertRaises(TooSmall, field.validate, -10)
|
||||
|
||||
def test_validate_max(self):
|
||||
from zope.schema.interfaces import TooBig
|
||||
field = self._makeOne(max=10)
|
||||
field.validate(5)
|
||||
field.validate(9)
|
||||
field.validate(10)
|
||||
self.assertRaises(TooBig, field.validate, 11)
|
||||
self.assertRaises(TooBig, field.validate, 20)
|
||||
|
||||
def test_validate_min_and_max(self):
|
||||
from zope.schema.interfaces import TooBig
|
||||
from zope.schema.interfaces import TooSmall
|
||||
field = self._makeOne(min=0, max=10)
|
||||
field.validate(0)
|
||||
field.validate(5)
|
||||
field.validate(10)
|
||||
self.assertRaises(TooSmall, field.validate, -10)
|
||||
self.assertRaises(TooSmall, field.validate, -1)
|
||||
self.assertRaises(TooBig, field.validate, 11)
|
||||
self.assertRaises(TooBig, field.validate, 20)
|
||||
|
||||
def test_fromUnicode_miss(self):
|
||||
from zope.schema._compat import u
|
||||
txt = self._makeOne()
|
||||
self.assertRaises(ValueError, txt.fromUnicode, u(''))
|
||||
self.assertRaises(ValueError, txt.fromUnicode, u('False'))
|
||||
self.assertRaises(ValueError, txt.fromUnicode, u('True'))
|
||||
|
||||
def test_fromUnicode_hit(self):
|
||||
from zope.schema._compat import u
|
||||
txt = self._makeOne()
|
||||
self.assertEqual(txt.fromUnicode(u('0')), 0)
|
||||
self.assertEqual(txt.fromUnicode(u('1')), 1)
|
||||
self.assertEqual(txt.fromUnicode(u('-1')), -1)
|
||||
|
||||
|
||||
class DummyInst(object):
|
||||
missing_value = object()
|
||||
|
||||
def __init__(self, exc=None):
|
||||
self._exc = exc
|
||||
|
||||
def validate(self, value):
|
||||
if self._exc is not None:
|
||||
raise self._exc()
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(ValidatedPropertyTests),
|
||||
unittest.makeSuite(DefaultPropertyTests),
|
||||
unittest.makeSuite(FieldTests),
|
||||
unittest.makeSuite(ContainerTests),
|
||||
unittest.makeSuite(IterableTests),
|
||||
unittest.makeSuite(OrderableTests),
|
||||
unittest.makeSuite(MinMaxLenTests),
|
||||
unittest.makeSuite(TextTests),
|
||||
unittest.makeSuite(TextLineTests),
|
||||
unittest.makeSuite(PasswordTests),
|
||||
unittest.makeSuite(BoolTests),
|
||||
unittest.makeSuite(IntTests),
|
||||
))
|
||||
68
zope/schema/tests/test__bootstrapinterfaces.py
Normal file
68
zope/schema/tests/test__bootstrapinterfaces.py
Normal file
@@ -0,0 +1,68 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2012 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.
|
||||
#
|
||||
##############################################################################
|
||||
import unittest
|
||||
|
||||
|
||||
def _skip_under_py3(testcase):
|
||||
from zope.schema._compat import PY3
|
||||
if not PY3:
|
||||
return testcase
|
||||
|
||||
|
||||
class ValidationErrorTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema._bootstrapinterfaces import ValidationError
|
||||
return ValidationError
|
||||
|
||||
def _makeOne(self, *args, **kw):
|
||||
return self._getTargetClass()(*args, **kw)
|
||||
|
||||
def test_doc(self):
|
||||
class Derived(self._getTargetClass()):
|
||||
"""DERIVED"""
|
||||
inst = Derived()
|
||||
self.assertEqual(inst.doc(), 'DERIVED')
|
||||
|
||||
@_skip_under_py3
|
||||
def test___cmp___no_args(self):
|
||||
# Py3k??
|
||||
ve = self._makeOne()
|
||||
self.assertEqual(cmp(ve, object()), -1)
|
||||
|
||||
@_skip_under_py3
|
||||
def test___cmp___hit(self):
|
||||
# Py3k??
|
||||
left = self._makeOne('abc')
|
||||
right = self._makeOne('def')
|
||||
self.assertEqual(cmp(left, right), -1)
|
||||
self.assertEqual(cmp(left, left), 0)
|
||||
self.assertEqual(cmp(right, left), 1)
|
||||
|
||||
def test___eq___no_args(self):
|
||||
ve = self._makeOne()
|
||||
self.assertEqual(ve == object(), False)
|
||||
|
||||
def test___eq___w_args(self):
|
||||
left = self._makeOne('abc')
|
||||
right = self._makeOne('def')
|
||||
self.assertEqual(left == right, False)
|
||||
self.assertEqual(left == left, True)
|
||||
self.assertEqual(right == right, True)
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(ValidationErrorTests),
|
||||
))
|
||||
2122
zope/schema/tests/test__field.py
Normal file
2122
zope/schema/tests/test__field.py
Normal file
File diff suppressed because it is too large
Load Diff
315
zope/schema/tests/test_accessors.py
Normal file
315
zope/schema/tests/test_accessors.py
Normal file
@@ -0,0 +1,315 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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 Interface accessor methods.
|
||||
"""
|
||||
import unittest
|
||||
|
||||
|
||||
class FieldReadAccessorTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema.accessors import FieldReadAccessor
|
||||
return FieldReadAccessor
|
||||
|
||||
def _makeOne(self, field=None):
|
||||
from zope.schema import Text
|
||||
if field is None:
|
||||
field = Text(__name__='testing')
|
||||
return self._getTargetClass()(field)
|
||||
|
||||
def test_ctor_not_created_inside_interface(self):
|
||||
from zope.schema import Text
|
||||
from zope.schema._compat import u
|
||||
field = Text(title=u('Hmm'))
|
||||
wrapped = self._makeOne(field)
|
||||
self.assertTrue(wrapped.field is field)
|
||||
self.assertEqual(wrapped.__name__, '') # __name__ set when in iface
|
||||
self.assertEqual(wrapped.__doc__, 'get Hmm')
|
||||
|
||||
def test_ctor_created_inside_interface(self):
|
||||
from zope.interface import Interface
|
||||
from zope.schema import Text
|
||||
from zope.schema._compat import u
|
||||
field = Text(title=u('Hmm'))
|
||||
|
||||
class IFoo(Interface):
|
||||
getter = self._makeOne(field)
|
||||
getter = IFoo['getter']
|
||||
self.assertEqual(getter.__name__, 'getter')
|
||||
self.assertEqual(getter.__doc__, 'get Hmm')
|
||||
|
||||
def test___provides___w_field_no_provides(self):
|
||||
from zope.interface import implementedBy
|
||||
from zope.interface import providedBy
|
||||
wrapped = self._makeOne(object())
|
||||
self.assertEqual(list(providedBy(wrapped)),
|
||||
list(implementedBy(self._getTargetClass())))
|
||||
|
||||
def test___provides___w_field_w_provides(self):
|
||||
from zope.interface import implementedBy
|
||||
from zope.interface import providedBy
|
||||
from zope.schema import Text
|
||||
field = Text()
|
||||
field_provides = list(providedBy(field))
|
||||
wrapped = self._makeOne(field)
|
||||
wrapped_provides = list(providedBy(wrapped))
|
||||
self.assertEqual(wrapped_provides[:len(field_provides)],
|
||||
list(providedBy(field)))
|
||||
for iface in list(implementedBy(self._getTargetClass())):
|
||||
self.assertTrue(iface in wrapped_provides)
|
||||
|
||||
def test_getSignatureString(self):
|
||||
wrapped = self._makeOne()
|
||||
self.assertEqual(wrapped.getSignatureString(), '()')
|
||||
|
||||
def test_getSignatureInfo(self):
|
||||
wrapped = self._makeOne()
|
||||
info = wrapped.getSignatureInfo()
|
||||
self.assertEqual(info['positional'], ())
|
||||
self.assertEqual(info['required'], ())
|
||||
self.assertEqual(info['optional'], ())
|
||||
self.assertEqual(info['varargs'], None)
|
||||
self.assertEqual(info['kwargs'], None)
|
||||
|
||||
def test_get_miss(self):
|
||||
from zope.interface import Interface
|
||||
|
||||
class IFoo(Interface):
|
||||
getter = self._makeOne()
|
||||
getter = IFoo['getter']
|
||||
|
||||
class Foo(object):
|
||||
pass
|
||||
self.assertRaises(AttributeError, getter.get, Foo())
|
||||
|
||||
def test_get_hit(self):
|
||||
from zope.interface import Interface
|
||||
|
||||
class IFoo(Interface):
|
||||
getter = self._makeOne()
|
||||
getter = IFoo['getter']
|
||||
|
||||
class Foo(object):
|
||||
def getter(self):
|
||||
return '123'
|
||||
self.assertEqual(getter.get(Foo()), '123')
|
||||
|
||||
def test_query_miss_implicit_default(self):
|
||||
from zope.interface import Interface
|
||||
|
||||
class IFoo(Interface):
|
||||
getter = self._makeOne()
|
||||
getter = IFoo['getter']
|
||||
|
||||
class Foo(object):
|
||||
pass
|
||||
self.assertEqual(getter.query(Foo()), None)
|
||||
|
||||
def test_query_miss_explicit_default(self):
|
||||
from zope.interface import Interface
|
||||
|
||||
class IFoo(Interface):
|
||||
getter = self._makeOne()
|
||||
getter = IFoo['getter']
|
||||
|
||||
class Foo(object):
|
||||
pass
|
||||
self.assertEqual(getter.query(Foo(), 234), 234)
|
||||
|
||||
def test_query_hit(self):
|
||||
from zope.interface import Interface
|
||||
|
||||
class IFoo(Interface):
|
||||
getter = self._makeOne()
|
||||
getter = IFoo['getter']
|
||||
|
||||
class Foo(object):
|
||||
def getter(self):
|
||||
return '123'
|
||||
|
||||
self.assertEqual(getter.query(Foo()), '123')
|
||||
|
||||
def test_set_readonly(self):
|
||||
from zope.interface import Interface
|
||||
from zope.schema import Text
|
||||
field = Text(readonly=True)
|
||||
|
||||
class IFoo(Interface):
|
||||
getter = self._makeOne(field)
|
||||
getter = IFoo['getter']
|
||||
|
||||
class Foo(object):
|
||||
def getter(self):
|
||||
return '123'
|
||||
self.assertRaises(TypeError, getter.set, Foo(), '456')
|
||||
|
||||
def test_set_no_writer(self):
|
||||
from zope.interface import Interface
|
||||
|
||||
class IFoo(Interface):
|
||||
getter = self._makeOne()
|
||||
getter = IFoo['getter']
|
||||
|
||||
class Foo(object):
|
||||
def getter(self):
|
||||
return '123'
|
||||
|
||||
self.assertRaises(AttributeError, getter.set, Foo(), '456')
|
||||
|
||||
def test_set_w_writer(self):
|
||||
from zope.interface import Interface
|
||||
|
||||
class IFoo(Interface):
|
||||
getter = self._makeOne()
|
||||
|
||||
getter = IFoo['getter']
|
||||
_called_with = []
|
||||
|
||||
class Writer(object):
|
||||
pass
|
||||
|
||||
writer = Writer()
|
||||
writer.__name__ = 'setMe'
|
||||
getter.writer = writer
|
||||
|
||||
class Foo(object):
|
||||
def setMe(self, value):
|
||||
_called_with.append(value)
|
||||
|
||||
getter.set(Foo(), '456')
|
||||
self.assertEqual(_called_with, ['456'])
|
||||
|
||||
def test_bind(self):
|
||||
from zope.interface import Interface
|
||||
|
||||
class IFoo(Interface):
|
||||
getter = self._makeOne()
|
||||
|
||||
getter = IFoo['getter']
|
||||
context = object()
|
||||
bound = getter.bind(context)
|
||||
self.assertEqual(bound.__name__, 'getter')
|
||||
self.assertTrue(isinstance(bound.field, getter.field.__class__))
|
||||
self.assertTrue(bound.field.context is context)
|
||||
|
||||
|
||||
class FieldWriteAccessorTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema.accessors import FieldWriteAccessor
|
||||
return FieldWriteAccessor
|
||||
|
||||
def _makeOne(self, field=None):
|
||||
from zope.schema import Text
|
||||
if field is None:
|
||||
field = Text(__name__='testing')
|
||||
return self._getTargetClass()(field)
|
||||
|
||||
def test_ctor_not_created_inside_interface(self):
|
||||
from zope.schema import Text
|
||||
from zope.schema._compat import u
|
||||
field = Text(title=u('Hmm'))
|
||||
wrapped = self._makeOne(field)
|
||||
self.assertTrue(wrapped.field is field)
|
||||
self.assertEqual(wrapped.__name__, '') # __name__ set when in iface
|
||||
self.assertEqual(wrapped.__doc__, 'set Hmm')
|
||||
|
||||
def test_ctor_created_inside_interface(self):
|
||||
from zope.interface import Interface
|
||||
from zope.schema import Text
|
||||
from zope.schema._compat import u
|
||||
field = Text(title=u('Hmm'))
|
||||
|
||||
class IFoo(Interface):
|
||||
setter = self._makeOne(field)
|
||||
|
||||
setter = IFoo['setter']
|
||||
self.assertEqual(setter.__name__, 'setter')
|
||||
self.assertEqual(setter.__doc__, 'set Hmm')
|
||||
|
||||
def test_getSignatureString(self):
|
||||
wrapped = self._makeOne()
|
||||
self.assertEqual(wrapped.getSignatureString(), '(newvalue)')
|
||||
|
||||
def test_getSignatureInfo(self):
|
||||
wrapped = self._makeOne()
|
||||
info = wrapped.getSignatureInfo()
|
||||
self.assertEqual(info['positional'], ('newvalue',))
|
||||
self.assertEqual(info['required'], ('newvalue',))
|
||||
self.assertEqual(info['optional'], ())
|
||||
self.assertEqual(info['varargs'], None)
|
||||
self.assertEqual(info['kwargs'], None)
|
||||
|
||||
|
||||
class Test_accessors(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, *args, **kw):
|
||||
from zope.schema.accessors import accessors
|
||||
return accessors(*args, **kw)
|
||||
|
||||
def test_w_only_read_accessor(self):
|
||||
from zope.interface import Interface
|
||||
from zope.schema import Text
|
||||
from zope.schema._compat import u
|
||||
field = Text(title=u('Hmm'), readonly=True)
|
||||
|
||||
class IFoo(Interface):
|
||||
getter, = self._callFUT(field)
|
||||
|
||||
getter = IFoo['getter']
|
||||
self.assertEqual(getter.__name__, 'getter')
|
||||
self.assertEqual(getter.__doc__, 'get Hmm')
|
||||
self.assertEqual(getter.getSignatureString(), '()')
|
||||
info = getter.getSignatureInfo()
|
||||
self.assertEqual(info['positional'], ())
|
||||
self.assertEqual(info['required'], ())
|
||||
self.assertEqual(info['optional'], ())
|
||||
self.assertEqual(info['varargs'], None)
|
||||
self.assertEqual(info['kwargs'], None)
|
||||
|
||||
def test_w_read_and_write_accessors(self):
|
||||
from zope.interface import Interface
|
||||
from zope.schema import Text
|
||||
from zope.schema._compat import u
|
||||
field = Text(title=u('Hmm'))
|
||||
|
||||
class IFoo(Interface):
|
||||
getter, setter = self._callFUT(field)
|
||||
|
||||
getter = IFoo['getter']
|
||||
self.assertEqual(getter.__name__, 'getter')
|
||||
self.assertEqual(getter.getSignatureString(), '()')
|
||||
info = getter.getSignatureInfo()
|
||||
self.assertEqual(info['positional'], ())
|
||||
self.assertEqual(info['required'], ())
|
||||
self.assertEqual(info['optional'], ())
|
||||
self.assertEqual(info['varargs'], None)
|
||||
self.assertEqual(info['kwargs'], None)
|
||||
setter = IFoo['setter']
|
||||
self.assertEqual(setter.__name__, 'setter')
|
||||
self.assertEqual(setter.getSignatureString(), '(newvalue)')
|
||||
info = setter.getSignatureInfo()
|
||||
self.assertEqual(info['positional'], ('newvalue',))
|
||||
self.assertEqual(info['required'], ('newvalue',))
|
||||
self.assertEqual(info['optional'], ())
|
||||
self.assertEqual(info['varargs'], None)
|
||||
self.assertEqual(info['kwargs'], None)
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(FieldReadAccessorTests),
|
||||
unittest.makeSuite(FieldWriteAccessorTests),
|
||||
unittest.makeSuite(Test_accessors),
|
||||
))
|
||||
41
zope/schema/tests/test_equality.py
Normal file
41
zope/schema/tests/test_equality.py
Normal file
@@ -0,0 +1,41 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Field equality tests
|
||||
"""
|
||||
import unittest
|
||||
|
||||
|
||||
class FieldEqualityTests(unittest.TestCase):
|
||||
|
||||
def test_equality(self):
|
||||
|
||||
from zope.schema._compat import u
|
||||
from zope.schema import Int
|
||||
from zope.schema import Text
|
||||
|
||||
# pep 8 friendlyness
|
||||
u, Int, Text
|
||||
|
||||
equality = [
|
||||
'Text(title=u("Foo"), description=u("Bar"))',
|
||||
'Int(title=u("Foo"), description=u("Bar"))',
|
||||
]
|
||||
for text in equality:
|
||||
self.assertEqual(eval(text), eval(text))
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(FieldEqualityTests),
|
||||
))
|
||||
669
zope/schema/tests/test_fieldproperty.py
Normal file
669
zope/schema/tests/test_fieldproperty.py
Normal file
@@ -0,0 +1,669 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Field Properties tests
|
||||
"""
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class _Base(unittest.TestCase):
|
||||
|
||||
def _makeOne(self, field=None, name=None):
|
||||
from zope.schema import Text
|
||||
if field is None:
|
||||
field = Text(__name__='testing')
|
||||
if name is None:
|
||||
return self._getTargetClass()(field)
|
||||
return self._getTargetClass()(field, name)
|
||||
|
||||
|
||||
class _Integration(object):
|
||||
|
||||
def _makeImplementer(self):
|
||||
schema = _getSchema()
|
||||
|
||||
class _Implementer(object):
|
||||
title = self._makeOne(schema['title'])
|
||||
weight = self._makeOne(schema['weight'])
|
||||
code = self._makeOne(schema['code'])
|
||||
date = self._makeOne(schema['date'])
|
||||
|
||||
return _Implementer()
|
||||
|
||||
def test_basic(self):
|
||||
from zope.schema._compat import b
|
||||
from zope.schema._compat import u
|
||||
from zope.schema.interfaces import ValidationError
|
||||
c = self._makeImplementer()
|
||||
self.assertEqual(c.title, u('say something'))
|
||||
self.assertEqual(c.weight, None)
|
||||
self.assertEqual(c.code, b('xxxxxx'))
|
||||
self.assertRaises(ValidationError, setattr, c, 'title', b('foo'))
|
||||
self.assertRaises(ValidationError, setattr, c, 'weight', b('foo'))
|
||||
self.assertRaises(ValidationError, setattr, c, 'weight', -1.0)
|
||||
self.assertRaises(ValidationError, setattr, c, 'weight', 2)
|
||||
self.assertRaises(ValidationError, setattr, c, 'code', -1)
|
||||
self.assertRaises(ValidationError, setattr, c, 'code', b('xxxx'))
|
||||
self.assertRaises(ValidationError, setattr, c, 'code', u('xxxxxx'))
|
||||
|
||||
c.title = u('c is good')
|
||||
c.weight = 10.0
|
||||
c.code = b('abcdef')
|
||||
|
||||
self.assertEqual(c.title, u('c is good'))
|
||||
self.assertEqual(c.weight, 10)
|
||||
self.assertEqual(c.code, b('abcdef'))
|
||||
|
||||
def test_readonly(self):
|
||||
c = self._makeImplementer()
|
||||
# The date should be only settable once
|
||||
c.date = 0.0
|
||||
# Setting the value a second time should fail.
|
||||
self.assertRaises(ValueError, setattr, c, 'date', 1.0)
|
||||
|
||||
|
||||
class FieldPropertyTests(_Base, _Integration):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema.fieldproperty import FieldProperty
|
||||
return FieldProperty
|
||||
|
||||
def test_ctor_defaults(self):
|
||||
from zope.schema import Text
|
||||
field = Text(__name__='testing')
|
||||
cname = self._getTargetClass().__name__
|
||||
prop = self._makeOne(field)
|
||||
self.assertTrue(getattr(prop, '_%s__field' % cname) is field)
|
||||
self.assertEqual(getattr(prop, '_%s__name' % cname), 'testing')
|
||||
self.assertEqual(prop.__name__, 'testing')
|
||||
self.assertEqual(prop.description, field.description)
|
||||
self.assertEqual(prop.default, field.default)
|
||||
self.assertEqual(prop.readonly, field.readonly)
|
||||
self.assertEqual(prop.required, field.required)
|
||||
|
||||
def test_ctor_explicit(self):
|
||||
from zope.schema import Text
|
||||
from zope.schema._compat import u
|
||||
field = Text(
|
||||
__name__='testing',
|
||||
description=u('DESCRIPTION'),
|
||||
default=u('DEFAULT'),
|
||||
readonly=True,
|
||||
required=True,
|
||||
)
|
||||
cname = self._getTargetClass().__name__
|
||||
prop = self._makeOne(field, name='override')
|
||||
self.assertTrue(getattr(prop, '_%s__field' % cname) is field)
|
||||
self.assertEqual(getattr(prop, '_%s__name' % cname), 'override')
|
||||
self.assertEqual(prop.description, field.description)
|
||||
self.assertEqual(prop.default, field.default)
|
||||
self.assertEqual(prop.readonly, field.readonly)
|
||||
self.assertEqual(prop.required, field.required)
|
||||
|
||||
def test_query_value_with_default(self):
|
||||
from zope.schema import Text
|
||||
from zope.schema._compat import u
|
||||
field = Text(
|
||||
__name__='testing',
|
||||
description=u('DESCRIPTION'),
|
||||
default=u('DEFAULT'),
|
||||
readonly=True,
|
||||
required=True,
|
||||
)
|
||||
|
||||
prop = self._makeOne(field=field)
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
foo = Foo()
|
||||
self.assertEqual(prop.queryValue(foo, 'test'), u('DEFAULT'))
|
||||
foo.testing = u('NO')
|
||||
self.assertEqual(prop.queryValue(foo, 'test'), u('NO'))
|
||||
|
||||
def test_query_value_without_default(self):
|
||||
from zope.schema import Text
|
||||
from zope.schema._compat import u
|
||||
field = Text(
|
||||
__name__='testing',
|
||||
description=u('DESCRIPTION'),
|
||||
readonly=True,
|
||||
required=True,
|
||||
)
|
||||
|
||||
prop = self._makeOne(field=field)
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
foo = Foo()
|
||||
# field initialize its default to None if it hasn't any default
|
||||
# it should be zope.schema.NO_VALUE as 'None' has another semantic
|
||||
self.assertEqual(prop.queryValue(foo, 'test'), None)
|
||||
|
||||
def test___get___from_class(self):
|
||||
prop = self._makeOne()
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
|
||||
self.assertTrue(Foo.testing is prop)
|
||||
|
||||
def test___get___from_instance_pseudo_field_wo_default(self):
|
||||
class _Faux(object):
|
||||
def bind(self, other):
|
||||
return self
|
||||
prop = self._makeOne(_Faux(), 'nonesuch')
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
|
||||
foo = Foo()
|
||||
self.assertRaises(AttributeError, getattr, foo, 'testing')
|
||||
|
||||
def test___get___from_instance_miss_uses_field_default(self):
|
||||
prop = self._makeOne()
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
|
||||
foo = Foo()
|
||||
self.assertEqual(foo.testing, None)
|
||||
|
||||
def test___get___from_instance_hit(self):
|
||||
prop = self._makeOne(name='other')
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
|
||||
foo = Foo()
|
||||
foo.other = '123'
|
||||
self.assertEqual(foo.testing, '123')
|
||||
|
||||
def test___get___from_instance_hit_after_bind(self):
|
||||
class _Faux(object):
|
||||
default = '456'
|
||||
|
||||
def bind(self, other):
|
||||
return self
|
||||
|
||||
prop = self._makeOne(_Faux(), 'testing')
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
|
||||
foo = Foo()
|
||||
self.assertEqual(foo.testing, '456')
|
||||
|
||||
def test___set___not_readonly(self):
|
||||
class _Faux(object):
|
||||
readonly = False
|
||||
default = '456'
|
||||
|
||||
def bind(self, other):
|
||||
return self
|
||||
|
||||
faux = _Faux()
|
||||
_validated = []
|
||||
faux.validate = _validated.append
|
||||
prop = self._makeOne(faux, 'testing')
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
|
||||
foo = Foo()
|
||||
foo.testing = '123'
|
||||
self.assertEqual(foo.__dict__['testing'], '123')
|
||||
|
||||
def test___set___w_readonly_not_already_set(self):
|
||||
class _Faux(object):
|
||||
readonly = True
|
||||
default = '456'
|
||||
|
||||
def bind(self, other):
|
||||
return self
|
||||
|
||||
faux = _Faux()
|
||||
_validated = []
|
||||
faux.validate = _validated.append
|
||||
prop = self._makeOne(faux, 'testing')
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
|
||||
foo = Foo()
|
||||
foo.testing = '123'
|
||||
self.assertEqual(foo.__dict__['testing'], '123')
|
||||
self.assertEqual(_validated, ['123'])
|
||||
|
||||
def test___set___w_readonly_and_already_set(self):
|
||||
class _Faux(object):
|
||||
readonly = True
|
||||
default = '456'
|
||||
|
||||
def bind(self, other):
|
||||
return self
|
||||
|
||||
faux = _Faux()
|
||||
_validated = []
|
||||
faux.validate = _validated.append
|
||||
prop = self._makeOne(faux, 'testing')
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
|
||||
foo = Foo()
|
||||
foo.__dict__['testing'] = '789'
|
||||
self.assertRaises(ValueError, setattr, foo, 'testing', '123')
|
||||
self.assertEqual(_validated, ['123'])
|
||||
|
||||
def test_field_event(self):
|
||||
from zope.schema import Text
|
||||
from zope.schema._compat import u
|
||||
from zope.event import subscribers
|
||||
from zope.schema.fieldproperty import FieldUpdatedEvent
|
||||
log = []
|
||||
subscribers.append(log.append)
|
||||
self.assertEqual(log, [])
|
||||
field = Text(
|
||||
__name__='testing',
|
||||
description=u('DESCRIPTION'),
|
||||
default=u('DEFAULT'),
|
||||
readonly=True,
|
||||
required=True,
|
||||
)
|
||||
self.assertEqual(len(log), 6)
|
||||
event = log[0]
|
||||
self.assertTrue(isinstance(event, FieldUpdatedEvent))
|
||||
self.assertEqual(event.inst, field)
|
||||
self.assertEqual(event.old_value, 0)
|
||||
self.assertEqual(event.new_value, 0)
|
||||
self.assertEqual(
|
||||
[ev.field.__name__ for ev in log],
|
||||
['min_length', 'max_length', 'title', 'description', 'required', 'readonly'])
|
||||
|
||||
def test_field_event_update(self):
|
||||
from zope.schema import Text
|
||||
from zope.schema._compat import u
|
||||
from zope.event import subscribers
|
||||
from zope.schema.fieldproperty import FieldUpdatedEvent
|
||||
field = Text(
|
||||
__name__='testing',
|
||||
description=u('DESCRIPTION'),
|
||||
default=u('DEFAULT'),
|
||||
required=True,
|
||||
)
|
||||
prop = self._makeOne(field=field)
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
foo = Foo()
|
||||
|
||||
log = []
|
||||
subscribers.append(log.append)
|
||||
foo.testing = u('Bar')
|
||||
foo.testing = u('Foo')
|
||||
self.assertEqual(len(log), 2)
|
||||
event = log[1]
|
||||
self.assertTrue(isinstance(event, FieldUpdatedEvent))
|
||||
self.assertEqual(event.inst, foo)
|
||||
self.assertEqual(event.field, field)
|
||||
self.assertEqual(event.old_value, u('Bar'))
|
||||
self.assertEqual(event.new_value, u('Foo'))
|
||||
|
||||
|
||||
class FieldPropertyStoredThroughFieldTests(_Base, _Integration):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema.fieldproperty import FieldPropertyStoredThroughField
|
||||
return FieldPropertyStoredThroughField
|
||||
|
||||
def test_ctor_defaults(self):
|
||||
from zope.schema import Text
|
||||
field = Text(__name__='testing')
|
||||
cname = self._getTargetClass().__name__
|
||||
prop = self._makeOne(field)
|
||||
self.assertTrue(isinstance(prop.field, field.__class__))
|
||||
self.assertFalse(prop.field is field)
|
||||
self.assertEqual(prop.field.__name__, '__st_testing_st')
|
||||
self.assertEqual(prop.__name__, '__st_testing_st')
|
||||
self.assertEqual(getattr(prop, '_%s__name' % cname), 'testing')
|
||||
self.assertEqual(prop.description, field.description)
|
||||
self.assertEqual(prop.default, field.default)
|
||||
self.assertEqual(prop.readonly, field.readonly)
|
||||
self.assertEqual(prop.required, field.required)
|
||||
|
||||
def test_ctor_explicit(self):
|
||||
from zope.schema import Text
|
||||
from zope.schema._compat import u
|
||||
field = Text(
|
||||
__name__='testing',
|
||||
description=u('DESCRIPTION'),
|
||||
default=u('DEFAULT'),
|
||||
readonly=True,
|
||||
required=True,
|
||||
)
|
||||
cname = self._getTargetClass().__name__
|
||||
prop = self._makeOne(field, name='override')
|
||||
self.assertTrue(isinstance(prop.field, field.__class__))
|
||||
self.assertFalse(prop.field is field)
|
||||
self.assertEqual(prop.field.__name__, '__st_testing_st')
|
||||
self.assertEqual(prop.__name__, '__st_testing_st')
|
||||
self.assertEqual(getattr(prop, '_%s__name' % cname), 'override')
|
||||
self.assertEqual(prop.description, field.description)
|
||||
self.assertEqual(prop.default, field.default)
|
||||
self.assertEqual(prop.readonly, field.readonly)
|
||||
self.assertEqual(prop.required, field.required)
|
||||
|
||||
def test_setValue(self):
|
||||
from zope.schema import Text
|
||||
|
||||
class Foo(object):
|
||||
pass
|
||||
|
||||
foo = Foo()
|
||||
prop = self._makeOne()
|
||||
field = Text(__name__='testing')
|
||||
prop.setValue(foo, field, '123')
|
||||
self.assertEqual(foo.testing, '123')
|
||||
|
||||
def test_getValue_miss(self):
|
||||
from zope.schema import Text
|
||||
from zope.schema.fieldproperty import _marker
|
||||
|
||||
class Foo(object):
|
||||
pass
|
||||
|
||||
foo = Foo()
|
||||
prop = self._makeOne()
|
||||
field = Text(__name__='testing')
|
||||
value = prop.getValue(foo, field)
|
||||
self.assertTrue(value is _marker)
|
||||
|
||||
def test_getValue_hit(self):
|
||||
from zope.schema import Text
|
||||
|
||||
class Foo(object):
|
||||
pass
|
||||
|
||||
foo = Foo()
|
||||
foo.testing = '123'
|
||||
prop = self._makeOne()
|
||||
field = Text(__name__='testing')
|
||||
value = prop.getValue(foo, field)
|
||||
self.assertEqual(value, '123')
|
||||
|
||||
def test_queryValue_miss(self):
|
||||
from zope.schema import Text
|
||||
|
||||
class Foo(object):
|
||||
pass
|
||||
|
||||
foo = Foo()
|
||||
prop = self._makeOne()
|
||||
field = Text(__name__='testing')
|
||||
default = object()
|
||||
value = prop.queryValue(foo, field, default)
|
||||
self.assertTrue(value is default)
|
||||
|
||||
def test_queryValue_hit(self):
|
||||
from zope.schema import Text
|
||||
|
||||
class Foo(object):
|
||||
pass
|
||||
|
||||
foo = Foo()
|
||||
foo.testing = '123'
|
||||
prop = self._makeOne()
|
||||
field = Text(__name__='testing')
|
||||
default = object()
|
||||
value = prop.queryValue(foo, field, default)
|
||||
self.assertEqual(value, '123')
|
||||
|
||||
def test___get___from_class(self):
|
||||
prop = self._makeOne()
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
|
||||
self.assertTrue(Foo.testing is prop)
|
||||
|
||||
def test___get___from_instance_pseudo_field_wo_default(self):
|
||||
class _Faux(object):
|
||||
__name__ = 'Faux'
|
||||
|
||||
def bind(self, other):
|
||||
return self
|
||||
|
||||
def query(self, inst, default):
|
||||
return default
|
||||
|
||||
prop = self._makeOne(_Faux(), 'nonesuch')
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
|
||||
foo = Foo()
|
||||
self.assertRaises(AttributeError, getattr, foo, 'testing')
|
||||
|
||||
def test___get___from_instance_miss_uses_field_default(self):
|
||||
prop = self._makeOne()
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
|
||||
foo = Foo()
|
||||
self.assertEqual(foo.testing, None)
|
||||
|
||||
def test___get___from_instance_hit(self):
|
||||
from zope.schema import Text
|
||||
field = Text(__name__='testing')
|
||||
prop = self._makeOne(field, name='other')
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
|
||||
foo = Foo()
|
||||
foo.__dict__['__st_testing_st'] = '456'
|
||||
foo.other = '123'
|
||||
self.assertEqual(foo.testing, '456')
|
||||
|
||||
def test___set___not_readonly(self):
|
||||
class _Faux(object):
|
||||
__name__ = 'Faux'
|
||||
readonly = False
|
||||
default = '456'
|
||||
|
||||
def query(self, inst, default):
|
||||
return default
|
||||
|
||||
def bind(self, other):
|
||||
return self
|
||||
|
||||
def set(self, inst, value):
|
||||
setattr(inst, 'faux', value)
|
||||
|
||||
faux = _Faux()
|
||||
_validated = []
|
||||
faux.validate = _validated.append
|
||||
prop = self._makeOne(faux, 'testing')
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
|
||||
foo = Foo()
|
||||
foo.testing = '123'
|
||||
self.assertEqual(foo.__dict__['faux'], '123')
|
||||
self.assertEqual(_validated, ['123'])
|
||||
|
||||
def test___set___w_readonly_not_already_set(self):
|
||||
class _Faux(object):
|
||||
__name__ = 'Faux'
|
||||
readonly = True
|
||||
default = '456'
|
||||
|
||||
def bind(self, other):
|
||||
return self
|
||||
|
||||
def query(self, inst, default):
|
||||
return default
|
||||
|
||||
def set(self, inst, value):
|
||||
if self.readonly:
|
||||
raise ValueError
|
||||
setattr(inst, 'faux', value)
|
||||
|
||||
faux = _Faux()
|
||||
_validated = []
|
||||
faux.validate = _validated.append
|
||||
prop = self._makeOne(faux, 'testing')
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
|
||||
foo = Foo()
|
||||
foo.testing = '123'
|
||||
self.assertEqual(foo.__dict__['faux'], '123')
|
||||
self.assertEqual(_validated, ['123'])
|
||||
|
||||
def test___set___w_readonly_and_already_set(self):
|
||||
class _Faux(object):
|
||||
__name__ = 'Faux'
|
||||
readonly = True
|
||||
default = '456'
|
||||
|
||||
def bind(self, other):
|
||||
return self
|
||||
|
||||
def query(self, inst, default):
|
||||
return '789'
|
||||
|
||||
faux = _Faux()
|
||||
_validated = []
|
||||
faux.validate = _validated.append
|
||||
prop = self._makeOne(faux, 'testing')
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
|
||||
foo = Foo()
|
||||
foo.__dict__['testing'] = '789'
|
||||
self.assertRaises(ValueError, setattr, foo, 'testing', '123')
|
||||
|
||||
def test_field_event_update(self):
|
||||
from zope.schema import Text
|
||||
from zope.schema._compat import u
|
||||
from zope.event import subscribers
|
||||
from zope.schema.fieldproperty import FieldUpdatedEvent
|
||||
field = Text(
|
||||
__name__='testing',
|
||||
description=u('DESCRIPTION'),
|
||||
default=u('DEFAULT'),
|
||||
required=True,
|
||||
)
|
||||
prop = self._makeOne(field=field)
|
||||
|
||||
class Foo(object):
|
||||
testing = prop
|
||||
foo = Foo()
|
||||
|
||||
log = []
|
||||
subscribers.append(log.append)
|
||||
foo.testing = u('Bar')
|
||||
foo.testing = u('Foo')
|
||||
self.assertEqual(len(log), 2)
|
||||
event = log[1]
|
||||
self.assertTrue(isinstance(event, FieldUpdatedEvent))
|
||||
self.assertEqual(event.inst, foo)
|
||||
self.assertEqual(event.field, field)
|
||||
self.assertEqual(event.old_value, u('Bar'))
|
||||
self.assertEqual(event.new_value, u('Foo'))
|
||||
|
||||
def test_field_event(self):
|
||||
# fieldproperties are everywhere including in field themselfs
|
||||
# so event are triggered
|
||||
from zope.schema import Text
|
||||
from zope.schema._compat import u
|
||||
from zope.event import subscribers
|
||||
from zope.schema.fieldproperty import FieldUpdatedEvent
|
||||
log = []
|
||||
subscribers.append(log.append)
|
||||
self.assertEqual(log, [])
|
||||
field = Text(
|
||||
__name__='testing',
|
||||
description=u('DESCRIPTION'),
|
||||
default=u('DEFAULT'),
|
||||
readonly=True,
|
||||
required=True,
|
||||
)
|
||||
self.assertEqual(len(log), 6)
|
||||
# these are fieldproperties in the field
|
||||
self.assertEqual(
|
||||
[ev.field.__name__ for ev in log],
|
||||
['min_length', 'max_length', 'title', 'description', 'required', 'readonly'])
|
||||
event = log[0]
|
||||
self.assertTrue(isinstance(event, FieldUpdatedEvent))
|
||||
self.assertEqual(event.inst, field)
|
||||
self.assertEqual(event.old_value, 0)
|
||||
self.assertEqual(event.new_value, 0)
|
||||
|
||||
|
||||
def _getSchema():
|
||||
from zope.interface import Interface
|
||||
from zope.schema import Bytes
|
||||
from zope.schema import Float
|
||||
from zope.schema import Text
|
||||
from zope.schema._compat import b
|
||||
from zope.schema._compat import u
|
||||
|
||||
class Schema(Interface):
|
||||
title = Text(description=u("Short summary"),
|
||||
default=u('say something'))
|
||||
weight = Float(min=0.0)
|
||||
code = Bytes(min_length=6, max_length=6, default=b('xxxxxx'))
|
||||
date = Float(title=u('Date'), readonly=True)
|
||||
|
||||
return Schema
|
||||
|
||||
|
||||
class CreateFieldPropertiesTests(unittest.TestCase):
|
||||
"""Testing ..fieldproperty.createFieldProperties."""
|
||||
|
||||
def test_creates_fieldproperties_on_class(self):
|
||||
from zope.schema.fieldproperty import createFieldProperties
|
||||
from zope.schema.fieldproperty import FieldProperty
|
||||
schema = _getSchema()
|
||||
|
||||
class Dummy(object):
|
||||
createFieldProperties(schema)
|
||||
|
||||
self.assertTrue(isinstance(Dummy.title, FieldProperty))
|
||||
self.assertTrue(isinstance(Dummy.date, FieldProperty))
|
||||
self.assertTrue(Dummy.date._FieldProperty__field is schema['date'])
|
||||
|
||||
def test_fields_in_omit_are_not_created_on_class(self):
|
||||
from zope.schema.fieldproperty import createFieldProperties
|
||||
|
||||
class Dummy(object):
|
||||
createFieldProperties(_getSchema(), omit=['date', 'code'])
|
||||
|
||||
self.assertFalse(hasattr(Dummy, 'date'))
|
||||
self.assertFalse(hasattr(Dummy, 'code'))
|
||||
self.assertTrue(hasattr(Dummy, 'title'))
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(FieldPropertyTests),
|
||||
unittest.makeSuite(FieldPropertyStoredThroughFieldTests),
|
||||
unittest.makeSuite(CreateFieldPropertiesTests),
|
||||
))
|
||||
99
zope/schema/tests/test_interfaces.py
Normal file
99
zope/schema/tests/test_interfaces.py
Normal file
@@ -0,0 +1,99 @@
|
||||
import unittest
|
||||
|
||||
|
||||
class Test__is_field(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, value):
|
||||
from zope.schema.interfaces import _is_field
|
||||
return _is_field(value)
|
||||
|
||||
def test_non_fields(self):
|
||||
from zope.schema._compat import b
|
||||
from zope.schema._compat import u
|
||||
self.assertEqual(self._callFUT(None), False)
|
||||
self.assertEqual(self._callFUT(0), False)
|
||||
self.assertEqual(self._callFUT(0.0), False)
|
||||
self.assertEqual(self._callFUT(True), False)
|
||||
self.assertEqual(self._callFUT(b('')), False)
|
||||
self.assertEqual(self._callFUT(u('')), False)
|
||||
self.assertEqual(self._callFUT(()), False)
|
||||
self.assertEqual(self._callFUT([]), False)
|
||||
self.assertEqual(self._callFUT({}), False)
|
||||
self.assertEqual(self._callFUT(set()), False)
|
||||
self.assertEqual(self._callFUT(frozenset()), False)
|
||||
self.assertEqual(self._callFUT(object()), False)
|
||||
|
||||
def test_w_normal_fields(self):
|
||||
from zope.schema import Text
|
||||
from zope.schema import Bytes
|
||||
from zope.schema import Int
|
||||
from zope.schema import Float
|
||||
from zope.schema import Decimal
|
||||
self.assertEqual(self._callFUT(Text()), True)
|
||||
self.assertEqual(self._callFUT(Bytes()), True)
|
||||
self.assertEqual(self._callFUT(Int()), True)
|
||||
self.assertEqual(self._callFUT(Float()), True)
|
||||
self.assertEqual(self._callFUT(Decimal()), True)
|
||||
|
||||
def test_w_explicitly_provided(self):
|
||||
from zope.interface import directlyProvides
|
||||
from zope.schema.interfaces import IField
|
||||
|
||||
class Foo(object):
|
||||
pass
|
||||
|
||||
foo = Foo()
|
||||
self.assertEqual(self._callFUT(foo), False)
|
||||
directlyProvides(foo, IField)
|
||||
self.assertEqual(self._callFUT(foo), True)
|
||||
|
||||
|
||||
class Test__fields(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, values):
|
||||
from zope.schema.interfaces import _fields
|
||||
return _fields(values)
|
||||
|
||||
def test_empty_containers(self):
|
||||
self.assertEqual(self._callFUT(()), True)
|
||||
self.assertEqual(self._callFUT([]), True)
|
||||
|
||||
def test_w_non_fields(self):
|
||||
self.assertEqual(self._callFUT([None]), False)
|
||||
self.assertEqual(self._callFUT(['']), False)
|
||||
self.assertEqual(self._callFUT([object()]), False)
|
||||
|
||||
def test_w_fields(self):
|
||||
from zope.schema import Text
|
||||
from zope.schema import Bytes
|
||||
from zope.schema import Int
|
||||
from zope.schema import Float
|
||||
from zope.schema import Decimal
|
||||
self.assertEqual(self._callFUT([Text()]), True)
|
||||
self.assertEqual(self._callFUT([Bytes()]), True)
|
||||
self.assertEqual(self._callFUT([Int()]), True)
|
||||
self.assertEqual(self._callFUT([Float()]), True)
|
||||
self.assertEqual(self._callFUT([Decimal()]), True)
|
||||
self.assertEqual(
|
||||
self._callFUT([Text(), Bytes(), Int(), Float(), Decimal()]),
|
||||
True
|
||||
)
|
||||
|
||||
def test_w_mixed(self):
|
||||
from zope.schema import Text
|
||||
from zope.schema import Bytes
|
||||
from zope.schema import Int
|
||||
from zope.schema import Float
|
||||
from zope.schema import Decimal
|
||||
self.assertEqual(self._callFUT([Text(), 0]), False)
|
||||
self.assertEqual(
|
||||
self._callFUT([Text(), Bytes(), Int(), Float(), Decimal(), 0]),
|
||||
False
|
||||
)
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(Test__is_field),
|
||||
unittest.makeSuite(Test__fields),
|
||||
))
|
||||
276
zope/schema/tests/test_schema.py
Normal file
276
zope/schema/tests/test_schema.py
Normal file
@@ -0,0 +1,276 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Schema field tests
|
||||
"""
|
||||
import unittest
|
||||
|
||||
|
||||
def _makeSchema():
|
||||
from zope.schema._compat import b
|
||||
from zope.schema._compat import u
|
||||
from zope.interface import Interface
|
||||
from zope.schema import Bytes
|
||||
|
||||
class ISchemaTest(Interface):
|
||||
title = Bytes(
|
||||
title=u("Title"),
|
||||
description=u("Title"),
|
||||
default=b(""),
|
||||
required=True)
|
||||
description = Bytes(
|
||||
title=u("Description"),
|
||||
description=u("Description"),
|
||||
default=b(""),
|
||||
required=True)
|
||||
spam = Bytes(
|
||||
title=u("Spam"),
|
||||
description=u("Spam"),
|
||||
default=b(""),
|
||||
required=True)
|
||||
return ISchemaTest
|
||||
|
||||
|
||||
def _makeDerivedSchema(base=None):
|
||||
from zope.schema._compat import b
|
||||
from zope.schema._compat import u
|
||||
from zope.schema import Bytes
|
||||
if base is None:
|
||||
base = _makeSchema()
|
||||
|
||||
class ISchemaTestSubclass(base):
|
||||
foo = Bytes(
|
||||
title=u('Foo'),
|
||||
description=u('Fooness'),
|
||||
default=b(""),
|
||||
required=False)
|
||||
return ISchemaTestSubclass
|
||||
|
||||
|
||||
class Test_getFields(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, schema):
|
||||
from zope.schema import getFields
|
||||
return getFields(schema)
|
||||
|
||||
def test_simple(self):
|
||||
fields = self._callFUT(_makeSchema())
|
||||
|
||||
self.assertTrue('title' in fields)
|
||||
self.assertTrue('description' in fields)
|
||||
self.assertTrue('spam' in fields)
|
||||
|
||||
# test whether getName() has the right value
|
||||
for key, value in fields.items():
|
||||
self.assertEqual(key, value.getName())
|
||||
|
||||
def test_derived(self):
|
||||
fields = self._callFUT(_makeDerivedSchema())
|
||||
|
||||
self.assertTrue('title' in fields)
|
||||
self.assertTrue('description' in fields)
|
||||
self.assertTrue('spam' in fields)
|
||||
self.assertTrue('foo' in fields)
|
||||
|
||||
# test whether getName() has the right value
|
||||
for key, value in fields.items():
|
||||
self.assertEqual(key, value.getName())
|
||||
|
||||
|
||||
class Test_getFieldsInOrder(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, schema):
|
||||
from zope.schema import getFieldsInOrder
|
||||
return getFieldsInOrder(schema)
|
||||
|
||||
def test_simple(self):
|
||||
fields = self._callFUT(_makeSchema())
|
||||
field_names = [name for name, field in fields]
|
||||
self.assertEqual(field_names, ['title', 'description', 'spam'])
|
||||
for key, value in fields:
|
||||
self.assertEqual(key, value.getName())
|
||||
|
||||
def test_derived(self):
|
||||
fields = self._callFUT(_makeDerivedSchema())
|
||||
field_names = [name for name, field in fields]
|
||||
self.assertEqual(field_names, ['title', 'description', 'spam', 'foo'])
|
||||
for key, value in fields:
|
||||
self.assertEqual(key, value.getName())
|
||||
|
||||
|
||||
class Test_getFieldNames(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, schema):
|
||||
from zope.schema import getFieldNames
|
||||
return getFieldNames(schema)
|
||||
|
||||
def test_simple(self):
|
||||
names = self._callFUT(_makeSchema())
|
||||
self.assertEqual(len(names), 3)
|
||||
self.assertTrue('title' in names)
|
||||
self.assertTrue('description' in names)
|
||||
self.assertTrue('spam' in names)
|
||||
|
||||
def test_derived(self):
|
||||
names = self._callFUT(_makeDerivedSchema())
|
||||
self.assertEqual(len(names), 4)
|
||||
self.assertTrue('title' in names)
|
||||
self.assertTrue('description' in names)
|
||||
self.assertTrue('spam' in names)
|
||||
self.assertTrue('foo' in names)
|
||||
|
||||
|
||||
class Test_getFieldNamesInOrder(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, schema):
|
||||
from zope.schema import getFieldNamesInOrder
|
||||
return getFieldNamesInOrder(schema)
|
||||
|
||||
def test_simple(self):
|
||||
names = self._callFUT(_makeSchema())
|
||||
self.assertEqual(names, ['title', 'description', 'spam'])
|
||||
|
||||
def test_derived(self):
|
||||
names = self._callFUT(_makeDerivedSchema())
|
||||
self.assertEqual(names, ['title', 'description', 'spam', 'foo'])
|
||||
|
||||
|
||||
class Test_getValidationErrors(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, schema, object):
|
||||
from zope.schema import getValidationErrors
|
||||
return getValidationErrors(schema, object)
|
||||
|
||||
def test_schema(self):
|
||||
from zope.interface import Interface
|
||||
|
||||
class IEmpty(Interface):
|
||||
pass
|
||||
|
||||
errors = self._callFUT(IEmpty, object())
|
||||
self.assertEqual(len(errors), 0)
|
||||
|
||||
def test_schema_with_field_errors(self):
|
||||
from zope.interface import Interface
|
||||
from zope.schema import Text
|
||||
from zope.schema.interfaces import SchemaNotFullyImplemented
|
||||
|
||||
class IWithRequired(Interface):
|
||||
must = Text(required=True)
|
||||
|
||||
errors = self._callFUT(IWithRequired, object())
|
||||
self.assertEqual(len(errors), 1)
|
||||
self.assertEqual(errors[0][0], 'must')
|
||||
self.assertEqual(errors[0][1].__class__, SchemaNotFullyImplemented)
|
||||
|
||||
def test_schema_with_invariant_errors(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import invariant
|
||||
from zope.interface.exceptions import Invalid
|
||||
|
||||
class IWithFailingInvariant(Interface):
|
||||
@invariant
|
||||
def _epic_fail(obj):
|
||||
raise Invalid('testing')
|
||||
|
||||
errors = self._callFUT(IWithFailingInvariant, object())
|
||||
self.assertEqual(len(errors), 1)
|
||||
self.assertEqual(errors[0][0], None)
|
||||
self.assertEqual(errors[0][1].__class__, Invalid)
|
||||
|
||||
def test_schema_with_invariant_ok(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import invariant
|
||||
|
||||
class IWithPassingInvariant(Interface):
|
||||
@invariant
|
||||
def _hall_pass(obj):
|
||||
pass
|
||||
|
||||
errors = self._callFUT(IWithPassingInvariant, object())
|
||||
self.assertEqual(len(errors), 0)
|
||||
|
||||
|
||||
class Test_getSchemaValidationErrors(unittest.TestCase):
|
||||
|
||||
def _callFUT(self, schema, object):
|
||||
from zope.schema import getSchemaValidationErrors
|
||||
return getSchemaValidationErrors(schema, object)
|
||||
|
||||
def test_schema_wo_fields(self):
|
||||
from zope.interface import Interface
|
||||
from zope.interface import Attribute
|
||||
|
||||
class INoFields(Interface):
|
||||
def method():
|
||||
pass
|
||||
attr = Attribute('ignoreme')
|
||||
|
||||
errors = self._callFUT(INoFields, object())
|
||||
self.assertEqual(len(errors), 0)
|
||||
|
||||
def test_schema_with_fields_ok(self):
|
||||
from zope.interface import Interface
|
||||
from zope.schema import Text
|
||||
from zope.schema._compat import u
|
||||
|
||||
class IWithFields(Interface):
|
||||
foo = Text()
|
||||
bar = Text()
|
||||
|
||||
class Obj(object):
|
||||
foo = u('Foo')
|
||||
bar = u('Bar')
|
||||
|
||||
errors = self._callFUT(IWithFields, Obj())
|
||||
self.assertEqual(len(errors), 0)
|
||||
|
||||
def test_schema_with_missing_field(self):
|
||||
from zope.interface import Interface
|
||||
from zope.schema import Text
|
||||
from zope.schema.interfaces import SchemaNotFullyImplemented
|
||||
|
||||
class IWithRequired(Interface):
|
||||
must = Text(required=True)
|
||||
|
||||
errors = self._callFUT(IWithRequired, object())
|
||||
self.assertEqual(len(errors), 1)
|
||||
self.assertEqual(errors[0][0], 'must')
|
||||
self.assertEqual(errors[0][1].__class__, SchemaNotFullyImplemented)
|
||||
|
||||
def test_schema_with_invalid_field(self):
|
||||
from zope.interface import Interface
|
||||
from zope.schema import Int
|
||||
from zope.schema.interfaces import TooSmall
|
||||
|
||||
class IWithMinium(Interface):
|
||||
value = Int(required=True, min=0)
|
||||
|
||||
class Obj(object):
|
||||
value = -1
|
||||
|
||||
errors = self._callFUT(IWithMinium, Obj())
|
||||
self.assertEqual(len(errors), 1)
|
||||
self.assertEqual(errors[0][0], 'value')
|
||||
self.assertEqual(errors[0][1].__class__, TooSmall)
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(Test_getFields),
|
||||
unittest.makeSuite(Test_getFieldsInOrder),
|
||||
unittest.makeSuite(Test_getFieldNames),
|
||||
unittest.makeSuite(Test_getFieldNamesInOrder),
|
||||
unittest.makeSuite(Test_getValidationErrors),
|
||||
unittest.makeSuite(Test_getSchemaValidationErrors),
|
||||
))
|
||||
106
zope/schema/tests/test_states.py
Normal file
106
zope/schema/tests/test_states.py
Normal file
@@ -0,0 +1,106 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Tests of the states example.
|
||||
"""
|
||||
import unittest
|
||||
|
||||
|
||||
class StateSelectionTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
from zope.schema.vocabulary import _clear
|
||||
from zope.schema.vocabulary import getVocabularyRegistry
|
||||
from zope.schema.tests.states import StateVocabulary
|
||||
_clear()
|
||||
vr = getVocabularyRegistry()
|
||||
vr.register("states", StateVocabulary)
|
||||
|
||||
def tearDown(self):
|
||||
from zope.schema.vocabulary import _clear
|
||||
_clear()
|
||||
|
||||
def _makeSchema(self):
|
||||
from zope.schema._compat import u
|
||||
from zope.interface import Interface
|
||||
from zope.schema import Choice
|
||||
from zope.schema.tests.states import StateVocabulary
|
||||
|
||||
class IBirthInfo(Interface):
|
||||
state1 = Choice(
|
||||
title=u('State of Birth'),
|
||||
description=u('The state in which you were born.'),
|
||||
vocabulary="states",
|
||||
default="AL",
|
||||
)
|
||||
state2 = Choice(
|
||||
title=u('State of Birth'),
|
||||
description=u('The state in which you were born.'),
|
||||
vocabulary="states",
|
||||
default="AL",
|
||||
)
|
||||
state3 = Choice(
|
||||
title=u('Favorite State'),
|
||||
description=u('The state you like the most.'),
|
||||
vocabulary=StateVocabulary(),
|
||||
)
|
||||
state4 = Choice(
|
||||
title=u("Name"),
|
||||
description=u("The name of your new state"),
|
||||
vocabulary="states",
|
||||
)
|
||||
return IBirthInfo
|
||||
|
||||
def test_default_presentation(self):
|
||||
from zope.interface.verify import verifyObject
|
||||
from zope.schema.interfaces import IVocabulary
|
||||
schema = self._makeSchema()
|
||||
field = schema.getDescriptionFor("state1")
|
||||
bound = field.bind(object())
|
||||
self.assertTrue(verifyObject(IVocabulary, bound.vocabulary))
|
||||
self.assertEqual(bound.vocabulary.getTerm("VA").title, "Virginia")
|
||||
|
||||
def test_contains(self):
|
||||
from zope.interface.verify import verifyObject
|
||||
from zope.schema.interfaces import IVocabulary
|
||||
from zope.schema.tests.states import StateVocabulary
|
||||
vocab = StateVocabulary()
|
||||
self.assertTrue(verifyObject(IVocabulary, vocab))
|
||||
count = 0
|
||||
L = list(vocab)
|
||||
for term in L:
|
||||
count += 1
|
||||
self.assertTrue(term.value in vocab)
|
||||
self.assertEqual(count, len(vocab))
|
||||
# make sure we get the same values the second time around:
|
||||
L = [term.value for term in L]
|
||||
L.sort()
|
||||
L2 = [term.value for term in vocab]
|
||||
L2.sort()
|
||||
self.assertEqual(L, L2)
|
||||
|
||||
def test_prebound_vocabulary(self):
|
||||
from zope.interface.verify import verifyObject
|
||||
from zope.schema.interfaces import IVocabulary
|
||||
schema = self._makeSchema()
|
||||
field = schema.getDescriptionFor("state3")
|
||||
bound = field.bind(None)
|
||||
self.assertTrue(bound.vocabularyName is None)
|
||||
self.assertTrue(verifyObject(IVocabulary, bound.vocabulary))
|
||||
self.assertTrue("AL" in bound.vocabulary)
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(StateSelectionTest),
|
||||
))
|
||||
642
zope/schema/tests/test_vocabulary.py
Normal file
642
zope/schema/tests/test_vocabulary.py
Normal file
@@ -0,0 +1,642 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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 of the Vocabulary and related support APIs.
|
||||
"""
|
||||
import unittest
|
||||
|
||||
|
||||
class SimpleTermTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema.vocabulary import SimpleTerm
|
||||
return SimpleTerm
|
||||
|
||||
def _makeOne(self, *args, **kw):
|
||||
return self._getTargetClass()(*args, **kw)
|
||||
|
||||
def test_class_conforms_to_ITokenizedTerm(self):
|
||||
from zope.interface.verify import verifyClass
|
||||
from zope.schema.interfaces import ITokenizedTerm
|
||||
verifyClass(ITokenizedTerm, self._getTargetClass())
|
||||
|
||||
def test_instance_conforms_to_ITokenizedTerm(self):
|
||||
from zope.interface.verify import verifyObject
|
||||
from zope.schema.interfaces import ITokenizedTerm
|
||||
verifyObject(ITokenizedTerm, self._makeOne('VALUE'))
|
||||
|
||||
def test_ctor_defaults(self):
|
||||
from zope.schema.interfaces import ITitledTokenizedTerm
|
||||
term = self._makeOne('VALUE')
|
||||
self.assertEqual(term.value, 'VALUE')
|
||||
self.assertEqual(term.token, 'VALUE')
|
||||
self.assertEqual(term.title, None)
|
||||
self.assertFalse(ITitledTokenizedTerm.providedBy(term))
|
||||
|
||||
def test_ctor_explicit(self):
|
||||
from zope.schema.interfaces import ITitledTokenizedTerm
|
||||
term = self._makeOne('TERM', 'TOKEN', 'TITLE')
|
||||
self.assertEqual(term.value, 'TERM')
|
||||
self.assertEqual(term.token, 'TOKEN')
|
||||
self.assertEqual(term.title, 'TITLE')
|
||||
self.assertTrue(ITitledTokenizedTerm.providedBy(term))
|
||||
|
||||
def test_bytes_value(self):
|
||||
from zope.schema.interfaces import ITitledTokenizedTerm
|
||||
term = self._makeOne(b'term')
|
||||
self.assertEqual(term.value, b'term')
|
||||
self.assertEqual(term.token, 'term')
|
||||
self.assertFalse(ITitledTokenizedTerm.providedBy(term))
|
||||
|
||||
|
||||
class SimpleVocabularyTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema.vocabulary import SimpleVocabulary
|
||||
return SimpleVocabulary
|
||||
|
||||
def _makeOne(self, *args, **kw):
|
||||
return self._getTargetClass()(*args, **kw)
|
||||
|
||||
def test_class_conforms_to_IVocabularyTokenized(self):
|
||||
from zope.interface.verify import verifyClass
|
||||
from zope.schema.interfaces import IVocabularyTokenized
|
||||
verifyClass(IVocabularyTokenized, self._getTargetClass())
|
||||
|
||||
def test_instance_conforms_to_IVocabularyTokenized(self):
|
||||
from zope.interface.verify import verifyObject
|
||||
from zope.schema.interfaces import IVocabularyTokenized
|
||||
verifyObject(IVocabularyTokenized, self._makeOne(()))
|
||||
|
||||
def test_ctor_additional_interfaces(self):
|
||||
from zope.interface import Interface
|
||||
from zope.schema.vocabulary import SimpleTerm
|
||||
|
||||
class IStupid(Interface):
|
||||
pass
|
||||
|
||||
VALUES = [1, 4, 2, 9]
|
||||
vocabulary = self._makeOne([SimpleTerm(x) for x in VALUES], IStupid)
|
||||
self.assertTrue(IStupid.providedBy(vocabulary))
|
||||
self.assertEqual(len(vocabulary), len(VALUES))
|
||||
for value, term in zip(VALUES, vocabulary):
|
||||
self.assertEqual(term.value, value)
|
||||
for value in VALUES:
|
||||
self.assertTrue(value in vocabulary)
|
||||
self.assertFalse('ABC' in vocabulary)
|
||||
for term in vocabulary:
|
||||
self.assertTrue(vocabulary.getTerm(term.value) is term)
|
||||
self.assertTrue(vocabulary.getTermByToken(term.token) is term)
|
||||
|
||||
def test_fromValues(self):
|
||||
from zope.interface import Interface
|
||||
from zope.schema.interfaces import ITokenizedTerm
|
||||
|
||||
class IStupid(Interface):
|
||||
pass
|
||||
|
||||
VALUES = [1, 4, 2, 9]
|
||||
vocabulary = self._getTargetClass().fromValues(VALUES)
|
||||
self.assertEqual(len(vocabulary), len(VALUES))
|
||||
for value, term in zip(VALUES, vocabulary):
|
||||
self.assertTrue(ITokenizedTerm.providedBy(term))
|
||||
self.assertEqual(term.value, value)
|
||||
for value in VALUES:
|
||||
self.assertTrue(value in vocabulary)
|
||||
|
||||
def test_fromItems(self):
|
||||
from zope.interface import Interface
|
||||
from zope.schema.interfaces import ITokenizedTerm
|
||||
|
||||
class IStupid(Interface):
|
||||
pass
|
||||
|
||||
ITEMS = [('one', 1), ('two', 2), ('three', 3), ('fore!', 4)]
|
||||
vocabulary = self._getTargetClass().fromItems(ITEMS)
|
||||
self.assertEqual(len(vocabulary), len(ITEMS))
|
||||
for item, term in zip(ITEMS, vocabulary):
|
||||
self.assertTrue(ITokenizedTerm.providedBy(term))
|
||||
self.assertEqual(term.token, item[0])
|
||||
self.assertEqual(term.value, item[1])
|
||||
for item in ITEMS:
|
||||
self.assertTrue(item[1] in vocabulary)
|
||||
|
||||
def test_createTerm(self):
|
||||
from zope.schema.vocabulary import SimpleTerm
|
||||
VALUES = [1, 4, 2, 9]
|
||||
for value in VALUES:
|
||||
term = self._getTargetClass().createTerm(value)
|
||||
self.assertTrue(isinstance(term, SimpleTerm))
|
||||
self.assertEqual(term.value, value)
|
||||
self.assertEqual(term.token, str(value))
|
||||
|
||||
def test_getTerm_miss(self):
|
||||
vocabulary = self._makeOne(())
|
||||
self.assertRaises(LookupError, vocabulary.getTerm, 'nonesuch')
|
||||
|
||||
def test_getTermByToken_miss(self):
|
||||
vocabulary = self._makeOne(())
|
||||
self.assertRaises(LookupError, vocabulary.getTermByToken, 'nonesuch')
|
||||
|
||||
def test_nonunique_tokens(self):
|
||||
klass = self._getTargetClass()
|
||||
self.assertRaises(ValueError, klass.fromValues, [2, '2'])
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
klass.fromItems,
|
||||
[(1, 'one'), ('1', 'another one')]
|
||||
)
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
klass.fromItems,
|
||||
[(0, 'one'), (1, 'one')]
|
||||
)
|
||||
|
||||
def test_nonunique_tokens_swallow(self):
|
||||
klass = self._getTargetClass()
|
||||
items = [(0, 'one'), (1, 'one')]
|
||||
terms = [klass.createTerm(value, token) for (token, value) in items]
|
||||
vocab = self._getTargetClass()(terms, swallow_duplicates=True)
|
||||
self.assertEqual(vocab.getTerm('one').token, '1')
|
||||
|
||||
def test_nonunique_token_message(self):
|
||||
try:
|
||||
self._getTargetClass().fromValues([2, '2'])
|
||||
except ValueError as e:
|
||||
self.assertEqual(str(e), "term tokens must be unique: '2'")
|
||||
|
||||
def test_nonunique_token_messages(self):
|
||||
try:
|
||||
self._getTargetClass().fromItems([(0, 'one'), (1, 'one')])
|
||||
except ValueError as e:
|
||||
self.assertEqual(str(e), "term values must be unique: 'one'")
|
||||
|
||||
def test_overriding_createTerm(self):
|
||||
class MyTerm(object):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
self.token = repr(value)
|
||||
self.nextvalue = value + 1
|
||||
|
||||
class MyVocabulary(self._getTargetClass()):
|
||||
def createTerm(cls, value):
|
||||
return MyTerm(value)
|
||||
createTerm = classmethod(createTerm)
|
||||
|
||||
vocab = MyVocabulary.fromValues([1, 2, 3])
|
||||
for term in vocab:
|
||||
self.assertEqual(term.value + 1, term.nextvalue)
|
||||
|
||||
|
||||
# Test _createTermTree via TreeVocabulary.fromDict
|
||||
|
||||
|
||||
class TreeVocabularyTests(unittest.TestCase):
|
||||
|
||||
def _getTargetClass(self):
|
||||
from zope.schema.vocabulary import TreeVocabulary
|
||||
return TreeVocabulary
|
||||
|
||||
def tree_vocab_2(self):
|
||||
region_tree = {
|
||||
('regions', 'Regions'): {
|
||||
('aut', 'Austria'): {
|
||||
('tyr', 'Tyrol'): {
|
||||
('auss', 'Ausserfern'): {},
|
||||
}
|
||||
},
|
||||
('ger', 'Germany'): {
|
||||
('bav', 'Bavaria'): {}
|
||||
},
|
||||
}
|
||||
}
|
||||
return self._getTargetClass().fromDict(region_tree)
|
||||
|
||||
def business_tree(self):
|
||||
return {
|
||||
('services', 'services', 'Services'): {
|
||||
('reservations', 'reservations', 'Reservations'): {
|
||||
('res_host', 'res_host', 'Res Host'): {},
|
||||
('res_gui', 'res_gui', 'Res GUI'): {},
|
||||
},
|
||||
('check_in', 'check_in', 'Check-in'): {
|
||||
('dcs_host', 'dcs_host', 'DCS Host'): {},
|
||||
},
|
||||
},
|
||||
('infrastructure', 'infrastructure', 'Infrastructure'): {
|
||||
('communication_network', 'communication_network',
|
||||
'Communication/Network'): {
|
||||
('messaging', 'messaging', 'Messaging'): {},
|
||||
},
|
||||
('data_transaction', 'data_transaction',
|
||||
'Data/Transaction'): {
|
||||
('database', 'database', 'Database'): {},
|
||||
},
|
||||
('security', 'security', 'Security'): {},
|
||||
},
|
||||
}
|
||||
|
||||
def tree_vocab_3(self):
|
||||
return self._getTargetClass().fromDict(self.business_tree())
|
||||
|
||||
def test_implementation(self):
|
||||
from zope.interface.verify import verifyObject
|
||||
from zope.interface.common.mapping import IEnumerableMapping
|
||||
from zope.schema.interfaces import ITreeVocabulary
|
||||
from zope.schema.interfaces import IVocabulary
|
||||
from zope.schema.interfaces import IVocabularyTokenized
|
||||
for v in [self.tree_vocab_2(), self.tree_vocab_3()]:
|
||||
self.assertTrue(verifyObject(IEnumerableMapping, v))
|
||||
self.assertTrue(verifyObject(IVocabulary, v))
|
||||
self.assertTrue(verifyObject(IVocabularyTokenized, v))
|
||||
self.assertTrue(verifyObject(ITreeVocabulary, v))
|
||||
|
||||
def test_additional_interfaces(self):
|
||||
from zope.interface import Interface
|
||||
|
||||
class IStupid(Interface):
|
||||
pass
|
||||
|
||||
v = self._getTargetClass().fromDict({('one', '1'): {}}, IStupid)
|
||||
self.assertTrue(IStupid.providedBy(v))
|
||||
|
||||
def test_ordering(self):
|
||||
#The TreeVocabulary makes use of an OrderedDict to store it's
|
||||
#internal tree representation.
|
||||
#
|
||||
#Check that they keys are indeed oredered.
|
||||
from zope.schema._compat import OrderedDict
|
||||
|
||||
d = {
|
||||
(1, 'new_york', 'New York'): {
|
||||
(2, 'ny_albany', 'Albany'): {},
|
||||
(3, 'ny_new_york', 'New York'): {},
|
||||
},
|
||||
(4, 'california', 'California'): {
|
||||
(5, 'ca_los_angeles', 'Los Angeles'): {},
|
||||
(6, 'ca_san_francisco', 'San Francisco'): {},
|
||||
},
|
||||
(7, 'texas', 'Texas'): {},
|
||||
(8, 'florida', 'Florida'): {},
|
||||
(9, 'utah', 'Utah'): {},
|
||||
}
|
||||
dict_ = OrderedDict(sorted(d.items(), key=lambda t: t[0]))
|
||||
vocab = self._getTargetClass().fromDict(dict_)
|
||||
# Test keys
|
||||
self.assertEqual(
|
||||
[k.token for k in vocab.keys()],
|
||||
['1', '4', '7', '8', '9']
|
||||
)
|
||||
# Test __iter__
|
||||
self.assertEqual(
|
||||
[k.token for k in vocab],
|
||||
['1', '4', '7', '8', '9']
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
[k.token for k in vocab[[k for k in vocab.keys()][0]].keys()],
|
||||
['2', '3']
|
||||
)
|
||||
self.assertEqual(
|
||||
[k.token for k in vocab[[k for k in vocab.keys()][1]].keys()],
|
||||
['5', '6']
|
||||
)
|
||||
|
||||
def test_indexes(self):
|
||||
# TreeVocabulary creates three indexes for quick lookups,
|
||||
# term_by_value, term_by_value and path_by_value.
|
||||
tv2 = self.tree_vocab_2()
|
||||
self.assertEqual(
|
||||
[k for k in sorted(tv2.term_by_value.keys())],
|
||||
['Ausserfern', 'Austria', 'Bavaria', 'Germany', 'Regions', 'Tyrol']
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
[k for k in sorted(tv2.term_by_token.keys())],
|
||||
['auss', 'aut', 'bav', 'ger', 'regions', 'tyr']
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
[k for k in sorted(tv2.path_by_value.keys())],
|
||||
['Ausserfern', 'Austria', 'Bavaria', 'Germany', 'Regions', 'Tyrol']
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
[k for k in sorted(tv2.path_by_value.values())],
|
||||
[
|
||||
['Regions'],
|
||||
['Regions', 'Austria'],
|
||||
['Regions', 'Austria', 'Tyrol'],
|
||||
['Regions', 'Austria', 'Tyrol', 'Ausserfern'],
|
||||
['Regions', 'Germany'],
|
||||
['Regions', 'Germany', 'Bavaria'],
|
||||
]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
[k for k in sorted(self.tree_vocab_3().term_by_value.keys())],
|
||||
[
|
||||
'check_in',
|
||||
'communication_network',
|
||||
'data_transaction',
|
||||
'database',
|
||||
'dcs_host',
|
||||
'infrastructure',
|
||||
'messaging',
|
||||
'res_gui',
|
||||
'res_host',
|
||||
'reservations',
|
||||
'security',
|
||||
'services',
|
||||
]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
[k for k in sorted(self.tree_vocab_3().term_by_token.keys())],
|
||||
[
|
||||
'check_in',
|
||||
'communication_network',
|
||||
'data_transaction',
|
||||
'database',
|
||||
'dcs_host',
|
||||
'infrastructure',
|
||||
'messaging',
|
||||
'res_gui',
|
||||
'res_host',
|
||||
'reservations',
|
||||
'security',
|
||||
'services',
|
||||
]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
[k for k in sorted(self.tree_vocab_3().path_by_value.values())],
|
||||
[
|
||||
['infrastructure'],
|
||||
['infrastructure', 'communication_network'],
|
||||
['infrastructure', 'communication_network', 'messaging'],
|
||||
['infrastructure', 'data_transaction'],
|
||||
['infrastructure', 'data_transaction', 'database'],
|
||||
['infrastructure', 'security'],
|
||||
['services'],
|
||||
['services', 'check_in'],
|
||||
['services', 'check_in', 'dcs_host'],
|
||||
['services', 'reservations'],
|
||||
['services', 'reservations', 'res_gui'],
|
||||
['services', 'reservations', 'res_host'],
|
||||
]
|
||||
)
|
||||
|
||||
def test_termpath(self):
|
||||
tv2 = self.tree_vocab_2()
|
||||
tv3 = self.tree_vocab_3()
|
||||
self.assertEqual(
|
||||
tv2.getTermPath('Bavaria'),
|
||||
['Regions', 'Germany', 'Bavaria']
|
||||
)
|
||||
self.assertEqual(
|
||||
tv2.getTermPath('Austria'),
|
||||
['Regions', 'Austria']
|
||||
)
|
||||
self.assertEqual(
|
||||
tv2.getTermPath('Ausserfern'),
|
||||
['Regions', 'Austria', 'Tyrol', 'Ausserfern']
|
||||
)
|
||||
self.assertEqual(
|
||||
tv2.getTermPath('Non-existent'),
|
||||
[]
|
||||
)
|
||||
self.assertEqual(
|
||||
tv3.getTermPath('database'),
|
||||
["infrastructure", "data_transaction", "database"]
|
||||
)
|
||||
|
||||
def test_len(self):
|
||||
# len returns the number of all nodes in the dict
|
||||
self.assertEqual(len(self.tree_vocab_2()), 1)
|
||||
self.assertEqual(len(self.tree_vocab_3()), 2)
|
||||
|
||||
def test_contains(self):
|
||||
tv2 = self.tree_vocab_2()
|
||||
self.assertTrue('Regions' in tv2 and
|
||||
'Austria' in tv2 and
|
||||
'Bavaria' in tv2)
|
||||
|
||||
self.assertTrue('bav' not in tv2)
|
||||
self.assertTrue('foo' not in tv2)
|
||||
self.assertTrue({} not in tv2) # not hashable
|
||||
|
||||
tv3 = self.tree_vocab_3()
|
||||
self.assertTrue('database' in tv3 and
|
||||
'security' in tv3 and
|
||||
'services' in tv3)
|
||||
|
||||
self.assertTrue('Services' not in tv3)
|
||||
self.assertTrue('Database' not in tv3)
|
||||
self.assertTrue({} not in tv3) # not hashable
|
||||
|
||||
def test_values_and_items(self):
|
||||
for v in (self.tree_vocab_2(), self.tree_vocab_3()):
|
||||
for term in v:
|
||||
self.assertEqual([i for i in v.values()],
|
||||
[i for i in v._terms.values()])
|
||||
self.assertEqual([i for i in v.items()],
|
||||
[i for i in v._terms.items()])
|
||||
|
||||
def test_get(self):
|
||||
for v in [self.tree_vocab_2(), self.tree_vocab_3()]:
|
||||
for key, value in v.items():
|
||||
self.assertEqual(v.get(key), value)
|
||||
self.assertEqual(v[key], value)
|
||||
|
||||
def test_get_term(self):
|
||||
for v in (self.tree_vocab_2(), self.tree_vocab_3()):
|
||||
for term in v:
|
||||
self.assertTrue(v.getTerm(term.value) is term)
|
||||
self.assertTrue(v.getTermByToken(term.token) is term)
|
||||
self.assertRaises(LookupError, v.getTerm, 'non-present-value')
|
||||
self.assertRaises(LookupError,
|
||||
v.getTermByToken, 'non-present-token')
|
||||
|
||||
def test_nonunique_values_and_tokens(self):
|
||||
# Since we do term and value lookups, all terms' values and tokens
|
||||
# must be unique. This rule applies recursively.
|
||||
self.assertRaises(
|
||||
ValueError, self._getTargetClass().fromDict,
|
||||
{
|
||||
('one', '1'): {},
|
||||
('two', '1'): {},
|
||||
})
|
||||
self.assertRaises(
|
||||
ValueError, self._getTargetClass().fromDict,
|
||||
{
|
||||
('one', '1'): {},
|
||||
('one', '2'): {},
|
||||
})
|
||||
# Even nested tokens must be unique.
|
||||
self.assertRaises(
|
||||
ValueError, self._getTargetClass().fromDict,
|
||||
{
|
||||
('new_york', 'New York'): {
|
||||
('albany', 'Albany'): {},
|
||||
('new_york', 'New York'): {},
|
||||
},
|
||||
})
|
||||
# The same applies to nested values.
|
||||
self.assertRaises(
|
||||
ValueError, self._getTargetClass().fromDict,
|
||||
{
|
||||
('1', 'new_york'): {
|
||||
('2', 'albany'): {},
|
||||
('3', 'new_york'): {},
|
||||
},
|
||||
})
|
||||
# The title attribute does however not have to be unique.
|
||||
self._getTargetClass().fromDict({
|
||||
('1', 'new_york', 'New York'): {
|
||||
('2', 'ny_albany', 'Albany'): {},
|
||||
('3', 'ny_new_york', 'New York'): {},
|
||||
},
|
||||
})
|
||||
self._getTargetClass().fromDict({
|
||||
('one', '1', 'One'): {},
|
||||
('two', '2', 'One'): {},
|
||||
})
|
||||
|
||||
def test_nonunique_value_message(self):
|
||||
try:
|
||||
self._getTargetClass().fromDict({
|
||||
('one', '1'): {},
|
||||
('two', '1'): {},
|
||||
})
|
||||
except ValueError as e:
|
||||
self.assertEqual(str(e), "Term values must be unique: '1'")
|
||||
|
||||
def test_nonunique_token_message(self):
|
||||
try:
|
||||
self._getTargetClass().fromDict({
|
||||
('one', '1'): {},
|
||||
('one', '2'): {},
|
||||
})
|
||||
except ValueError as e:
|
||||
self.assertEqual(str(e), "Term tokens must be unique: 'one'")
|
||||
|
||||
def test_recursive_methods(self):
|
||||
#Test the _createTermTree and _getPathToTreeNode methods
|
||||
from zope.schema.vocabulary import _createTermTree
|
||||
tree = _createTermTree({}, self.business_tree())
|
||||
vocab = self._getTargetClass().fromDict(self.business_tree())
|
||||
|
||||
term_path = vocab._getPathToTreeNode(tree, "infrastructure")
|
||||
vocab_path = vocab._getPathToTreeNode(vocab, "infrastructure")
|
||||
self.assertEqual(term_path, vocab_path)
|
||||
self.assertEqual(term_path, ["infrastructure"])
|
||||
|
||||
term_path = vocab._getPathToTreeNode(tree, "security")
|
||||
vocab_path = vocab._getPathToTreeNode(vocab, "security")
|
||||
self.assertEqual(term_path, vocab_path)
|
||||
self.assertEqual(term_path, ["infrastructure", "security"])
|
||||
|
||||
term_path = vocab._getPathToTreeNode(tree, "database")
|
||||
vocab_path = vocab._getPathToTreeNode(vocab, "database")
|
||||
self.assertEqual(term_path, vocab_path)
|
||||
self.assertEqual(term_path,
|
||||
["infrastructure", "data_transaction", "database"])
|
||||
|
||||
term_path = vocab._getPathToTreeNode(tree, "dcs_host")
|
||||
vocab_path = vocab._getPathToTreeNode(vocab, "dcs_host")
|
||||
self.assertEqual(term_path, vocab_path)
|
||||
self.assertEqual(term_path, ["services", "check_in", "dcs_host"])
|
||||
|
||||
term_path = vocab._getPathToTreeNode(tree, "dummy")
|
||||
vocab_path = vocab._getPathToTreeNode(vocab, "dummy")
|
||||
self.assertEqual(term_path, vocab_path)
|
||||
self.assertEqual(term_path, [])
|
||||
|
||||
|
||||
class RegistryTests(unittest.TestCase):
|
||||
#Tests of the simple vocabulary and presentation registries.
|
||||
|
||||
def setUp(self):
|
||||
from zope.schema.vocabulary import _clear
|
||||
_clear()
|
||||
|
||||
def tearDown(self):
|
||||
from zope.schema.vocabulary import _clear
|
||||
_clear()
|
||||
|
||||
def test_setVocabularyRegistry(self):
|
||||
from zope.schema.vocabulary import setVocabularyRegistry
|
||||
from zope.schema.vocabulary import getVocabularyRegistry
|
||||
r = _makeDummyRegistry()
|
||||
setVocabularyRegistry(r)
|
||||
self.assertTrue(getVocabularyRegistry() is r)
|
||||
|
||||
def test_getVocabularyRegistry(self):
|
||||
from zope.schema.interfaces import IVocabularyRegistry
|
||||
from zope.schema.vocabulary import getVocabularyRegistry
|
||||
r = getVocabularyRegistry()
|
||||
self.assertTrue(IVocabularyRegistry.providedBy(r))
|
||||
|
||||
# TODO: still need to test the default implementation
|
||||
|
||||
|
||||
def _makeSampleVocabulary():
|
||||
from zope.interface import implementer
|
||||
from zope.schema.interfaces import IVocabulary
|
||||
|
||||
class SampleTerm(object):
|
||||
pass
|
||||
|
||||
@implementer(IVocabulary)
|
||||
class SampleVocabulary(object):
|
||||
|
||||
def __iter__(self):
|
||||
return iter([self.getTerm(x) for x in range(0, 10)])
|
||||
|
||||
def __contains__(self, value):
|
||||
return 0 <= value < 10
|
||||
|
||||
def __len__(self):
|
||||
return 10
|
||||
|
||||
def getTerm(self, value):
|
||||
if value in self:
|
||||
t = SampleTerm()
|
||||
t.value = value
|
||||
t.double = 2 * value
|
||||
return t
|
||||
raise LookupError("no such value: %r" % value)
|
||||
|
||||
return SampleVocabulary()
|
||||
|
||||
|
||||
def _makeDummyRegistry():
|
||||
from zope.schema.vocabulary import VocabularyRegistry
|
||||
|
||||
class DummyRegistry(VocabularyRegistry):
|
||||
def get(self, object, name):
|
||||
v = _makeSampleVocabulary()
|
||||
v.object = object
|
||||
v.name = name
|
||||
return v
|
||||
return DummyRegistry()
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(SimpleTermTests),
|
||||
unittest.makeSuite(SimpleVocabularyTests),
|
||||
unittest.makeSuite(TreeVocabularyTests),
|
||||
unittest.makeSuite(RegistryTests),
|
||||
))
|
||||
409
zope/schema/vocabulary.py
Normal file
409
zope/schema/vocabulary.py
Normal file
@@ -0,0 +1,409 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
##############################################################################
|
||||
"""Vocabulary support for schema.
|
||||
"""
|
||||
|
||||
from zope.interface import directlyProvides
|
||||
from zope.interface import implementer
|
||||
from zope.schema.interfaces import ITitledTokenizedTerm
|
||||
from zope.schema.interfaces import ITokenizedTerm
|
||||
from zope.schema.interfaces import ITreeVocabulary
|
||||
from zope.schema.interfaces import IVocabularyRegistry
|
||||
from zope.schema.interfaces import IVocabularyTokenized
|
||||
from zope.schema._compat import OrderedDict
|
||||
|
||||
# simple vocabularies performing enumerated-like tasks
|
||||
_marker = object()
|
||||
|
||||
|
||||
@implementer(ITokenizedTerm)
|
||||
class SimpleTerm(object):
|
||||
"""Simple tokenized term used by SimpleVocabulary."""
|
||||
|
||||
def __init__(self, value, token=None, title=None):
|
||||
"""Create a term for value and token. If token is omitted,
|
||||
str(value) is used for the token. If title is provided,
|
||||
term implements ITitledTokenizedTerm.
|
||||
"""
|
||||
self.value = value
|
||||
if token is None:
|
||||
token = value
|
||||
# In Python 3 str(bytes) returns str(repr(bytes)), which is not what
|
||||
# we want here. On the other hand, we want to try to keep the token as
|
||||
# readable as possible.
|
||||
self.token = str(token) \
|
||||
if not isinstance(token, bytes) \
|
||||
else str(token.decode('ascii', 'ignore'))
|
||||
self.title = title
|
||||
if title is not None:
|
||||
directlyProvides(self, ITitledTokenizedTerm)
|
||||
|
||||
|
||||
@implementer(IVocabularyTokenized)
|
||||
class SimpleVocabulary(object):
|
||||
"""Vocabulary that works from a sequence of terms."""
|
||||
|
||||
def __init__(self, terms, *interfaces, **kwargs):
|
||||
"""Initialize the vocabulary given a list of terms.
|
||||
|
||||
The vocabulary keeps a reference to the list of terms passed
|
||||
in; it should never be modified while the vocabulary is used.
|
||||
|
||||
One or more interfaces may also be provided so that alternate
|
||||
widgets may be bound without subclassing.
|
||||
|
||||
By default, ValueErrors are thrown if duplicate values or tokens
|
||||
are passed in. If you want to swallow these exceptions, pass
|
||||
in swallow_duplicates=True. In this case, the values will
|
||||
override themselves.
|
||||
"""
|
||||
self.by_value = {}
|
||||
self.by_token = {}
|
||||
self._terms = terms
|
||||
for term in self._terms:
|
||||
swallow_dupes = kwargs.get('swallow_duplicates', False)
|
||||
if not swallow_dupes:
|
||||
if term.value in self.by_value:
|
||||
raise ValueError(
|
||||
'term values must be unique: %s' % repr(term.value))
|
||||
if term.token in self.by_token:
|
||||
raise ValueError(
|
||||
'term tokens must be unique: %s' % repr(term.token))
|
||||
self.by_value[term.value] = term
|
||||
self.by_token[term.token] = term
|
||||
if interfaces:
|
||||
directlyProvides(self, *interfaces)
|
||||
|
||||
@classmethod
|
||||
def fromItems(cls, items, *interfaces):
|
||||
"""Construct a vocabulary from a list of (token, value) pairs.
|
||||
|
||||
The order of the items is preserved as the order of the terms
|
||||
in the vocabulary. Terms are created by calling the class
|
||||
method createTerm() with the pair (value, token).
|
||||
|
||||
One or more interfaces may also be provided so that alternate
|
||||
widgets may be bound without subclassing.
|
||||
"""
|
||||
terms = [cls.createTerm(value, token) for (token, value) in items]
|
||||
return cls(terms, *interfaces)
|
||||
|
||||
@classmethod
|
||||
def fromValues(cls, values, *interfaces):
|
||||
"""Construct a vocabulary from a simple list.
|
||||
|
||||
Values of the list become both the tokens and values of the
|
||||
terms in the vocabulary. The order of the values is preserved
|
||||
as the order of the terms in the vocabulary. Tokens are
|
||||
created by calling the class method createTerm() with the
|
||||
value as the only parameter.
|
||||
|
||||
One or more interfaces may also be provided so that alternate
|
||||
widgets may be bound without subclassing.
|
||||
"""
|
||||
terms = [cls.createTerm(value) for value in values]
|
||||
return cls(terms, *interfaces)
|
||||
|
||||
@classmethod
|
||||
def createTerm(cls, *args):
|
||||
"""Create a single term from data.
|
||||
|
||||
Subclasses may override this with a class method that creates
|
||||
a term of the appropriate type from the arguments.
|
||||
"""
|
||||
return SimpleTerm(*args)
|
||||
|
||||
def __contains__(self, value):
|
||||
"""See zope.schema.interfaces.IBaseVocabulary"""
|
||||
try:
|
||||
return value in self.by_value
|
||||
except TypeError:
|
||||
# sometimes values are not hashable
|
||||
return False
|
||||
|
||||
def getTerm(self, value):
|
||||
"""See zope.schema.interfaces.IBaseVocabulary"""
|
||||
try:
|
||||
return self.by_value[value]
|
||||
except KeyError:
|
||||
raise LookupError(value)
|
||||
|
||||
def getTermByToken(self, token):
|
||||
"""See zope.schema.interfaces.IVocabularyTokenized"""
|
||||
try:
|
||||
return self.by_token[token]
|
||||
except KeyError:
|
||||
raise LookupError(token)
|
||||
|
||||
def __iter__(self):
|
||||
"""See zope.schema.interfaces.IIterableVocabulary"""
|
||||
return iter(self._terms)
|
||||
|
||||
def __len__(self):
|
||||
"""See zope.schema.interfaces.IIterableVocabulary"""
|
||||
return len(self.by_value)
|
||||
|
||||
|
||||
def _createTermTree(ttree, dict_):
|
||||
""" Helper method that creates a tree-like dict with ITokenizedTerm
|
||||
objects as keys from a similar tree with tuples as keys.
|
||||
|
||||
See fromDict for more details.
|
||||
"""
|
||||
for key in sorted(dict_.keys()):
|
||||
term = SimpleTerm(key[1], key[0], key[-1])
|
||||
ttree[term] = TreeVocabulary.terms_factory()
|
||||
_createTermTree(ttree[term], dict_[key])
|
||||
return ttree
|
||||
|
||||
|
||||
@implementer(ITreeVocabulary)
|
||||
class TreeVocabulary(object):
|
||||
""" Vocabulary that relies on a tree (i.e nested) structure.
|
||||
"""
|
||||
# The default implementation uses a dict to create the tree structure. This
|
||||
# can however be overridden in a subclass by any other IEnumerableMapping
|
||||
# compliant object type. Python 2.7's OrderedDict for example.
|
||||
terms_factory = OrderedDict
|
||||
|
||||
def __init__(self, terms, *interfaces):
|
||||
"""Initialize the vocabulary given a recursive dict (i.e a tree) with
|
||||
ITokenizedTerm objects for keys and self-similar dicts representing the
|
||||
branches for values.
|
||||
|
||||
Refer to the method fromDict for more details.
|
||||
|
||||
Concerning the ITokenizedTerm keys, the 'value' and 'token' attributes
|
||||
of each key (including nested ones) must be unique.
|
||||
|
||||
One or more interfaces may also be provided so that alternate
|
||||
widgets may be bound without subclassing.
|
||||
"""
|
||||
self._terms = self.terms_factory()
|
||||
self._terms.update(terms)
|
||||
|
||||
self.path_by_value = {}
|
||||
self.term_by_value = {}
|
||||
self.term_by_token = {}
|
||||
self._populateIndexes(terms)
|
||||
|
||||
if interfaces:
|
||||
directlyProvides(self, *interfaces)
|
||||
|
||||
def __contains__(self, value):
|
||||
""" See zope.schema.interfaces.IBaseVocabulary
|
||||
|
||||
D.__contains__(k) -> True if D has a key k, else False
|
||||
"""
|
||||
try:
|
||||
return value in self.term_by_value
|
||||
except TypeError:
|
||||
# sometimes values are not hashable
|
||||
return False
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""x.__getitem__(y) <==> x[y]
|
||||
"""
|
||||
return self._terms.__getitem__(key)
|
||||
|
||||
def __iter__(self):
|
||||
"""See zope.schema.interfaces.IIterableVocabulary
|
||||
|
||||
x.__iter__() <==> iter(x)
|
||||
"""
|
||||
return self._terms.__iter__()
|
||||
|
||||
def __len__(self):
|
||||
"""x.__len__() <==> len(x)
|
||||
"""
|
||||
return self._terms.__len__()
|
||||
|
||||
def get(self, key, default=None):
|
||||
"""Get a value for a key
|
||||
|
||||
The default is returned if there is no value for the key.
|
||||
"""
|
||||
return self._terms.get(key, default)
|
||||
|
||||
def keys(self):
|
||||
"""Return the keys of the mapping object.
|
||||
"""
|
||||
return self._terms.keys()
|
||||
|
||||
def values(self):
|
||||
"""Return the values of the mapping object.
|
||||
"""
|
||||
return self._terms.values()
|
||||
|
||||
def items(self):
|
||||
"""Return the items of the mapping object.
|
||||
"""
|
||||
return self._terms.items()
|
||||
|
||||
@classmethod
|
||||
def fromDict(cls, dict_, *interfaces):
|
||||
"""Constructs a vocabulary from a dictionary-like object (like dict or
|
||||
OrderedDict), that has tuples for keys.
|
||||
|
||||
The tuples should have either 2 or 3 values, i.e:
|
||||
(token, value, title) or (token, value)
|
||||
|
||||
For example, a dict with 2-valued tuples:
|
||||
|
||||
dict_ = {
|
||||
('exampleregions', 'Regions used in ATVocabExample'): {
|
||||
('aut', 'Austria'): {
|
||||
('tyr', 'Tyrol'): {
|
||||
('auss', 'Ausserfern'): {},
|
||||
}
|
||||
},
|
||||
('ger', 'Germany'): {
|
||||
('bav', 'Bavaria'):{}
|
||||
},
|
||||
}
|
||||
}
|
||||
One or more interfaces may also be provided so that alternate
|
||||
widgets may be bound without subclassing.
|
||||
"""
|
||||
return cls(_createTermTree(cls.terms_factory(), dict_), *interfaces)
|
||||
|
||||
def _populateIndexes(self, tree):
|
||||
""" The TreeVocabulary contains three helper indexes for quick lookups.
|
||||
They are: term_by_value, term_by_token and path_by_value
|
||||
|
||||
This method recurses through the tree and populates these indexes.
|
||||
|
||||
tree: The tree (a nested/recursive dictionary).
|
||||
"""
|
||||
for term in tree.keys():
|
||||
value = getattr(term, 'value')
|
||||
token = getattr(term, 'token')
|
||||
|
||||
if value in self.term_by_value:
|
||||
raise ValueError(
|
||||
"Term values must be unique: '%s'" % value)
|
||||
|
||||
if token in self.term_by_token:
|
||||
raise ValueError(
|
||||
"Term tokens must be unique: '%s'" % token)
|
||||
|
||||
self.term_by_value[value] = term
|
||||
self.term_by_token[token] = term
|
||||
|
||||
if value not in self.path_by_value:
|
||||
self.path_by_value[value] = self._getPathToTreeNode(self,
|
||||
value)
|
||||
self._populateIndexes(tree[term])
|
||||
|
||||
def getTerm(self, value):
|
||||
"""See zope.schema.interfaces.IBaseVocabulary"""
|
||||
try:
|
||||
return self.term_by_value[value]
|
||||
except KeyError:
|
||||
raise LookupError(value)
|
||||
|
||||
def getTermByToken(self, token):
|
||||
"""See zope.schema.interfaces.IVocabularyTokenized"""
|
||||
try:
|
||||
return self.term_by_token[token]
|
||||
except KeyError:
|
||||
raise LookupError(token)
|
||||
|
||||
def _getPathToTreeNode(self, tree, node):
|
||||
"""Helper method that computes the path in the tree from the root
|
||||
to the given node.
|
||||
|
||||
The tree must be a recursive IEnumerableMapping object.
|
||||
"""
|
||||
path = []
|
||||
for parent, child in tree.items():
|
||||
if node == parent.value:
|
||||
return [node]
|
||||
path = self._getPathToTreeNode(child, node)
|
||||
if path:
|
||||
path.insert(0, parent.value)
|
||||
break
|
||||
return path
|
||||
|
||||
def getTermPath(self, value):
|
||||
"""Returns a list of strings representing the path from the root node
|
||||
to the node with the given value in the tree.
|
||||
|
||||
Returns an empty string if no node has that value.
|
||||
"""
|
||||
return self.path_by_value.get(value, [])
|
||||
|
||||
|
||||
# registry code
|
||||
class VocabularyRegistryError(LookupError):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
Exception.__init__(self, str(self))
|
||||
|
||||
def __str__(self):
|
||||
return "unknown vocabulary: %r" % self.name
|
||||
|
||||
|
||||
@implementer(IVocabularyRegistry)
|
||||
class VocabularyRegistry(object):
|
||||
__slots__ = '_map',
|
||||
|
||||
def __init__(self):
|
||||
self._map = {}
|
||||
|
||||
def get(self, object, name):
|
||||
"""See zope.schema.interfaces.IVocabularyRegistry"""
|
||||
try:
|
||||
vtype = self._map[name]
|
||||
except KeyError:
|
||||
raise VocabularyRegistryError(name)
|
||||
return vtype(object)
|
||||
|
||||
def register(self, name, factory):
|
||||
self._map[name] = factory
|
||||
|
||||
_vocabularies = None
|
||||
|
||||
|
||||
def getVocabularyRegistry():
|
||||
"""Return the vocabulary registry.
|
||||
|
||||
If the registry has not been created yet, an instance of
|
||||
VocabularyRegistry will be installed and used.
|
||||
"""
|
||||
if _vocabularies is None:
|
||||
setVocabularyRegistry(VocabularyRegistry())
|
||||
return _vocabularies
|
||||
|
||||
|
||||
def setVocabularyRegistry(registry):
|
||||
"""Set the vocabulary registry."""
|
||||
global _vocabularies
|
||||
_vocabularies = registry
|
||||
|
||||
|
||||
def _clear():
|
||||
"""Remove the registries (for use by tests)."""
|
||||
global _vocabularies
|
||||
_vocabularies = None
|
||||
|
||||
|
||||
try:
|
||||
from zope.testing.cleanup import addCleanUp
|
||||
except ImportError: # pragma: no cover
|
||||
# don't have that part of Zope
|
||||
pass
|
||||
else: # pragma: no cover
|
||||
addCleanUp(_clear)
|
||||
del addCleanUp
|
||||
Reference in New Issue
Block a user