#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Interfaces describing the events and fields this package uses.
Also utility functions.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import warnings
import traceback
from zope.deprecation import deprecated
from zope.schema import Text
from zope.schema import TextLine
from zope.interface import Interface
from zope.interface import Attribute
from zope.interface import providedBy
from zope.interface import implementer
from zope.schema import interfaces as sch_interfaces
try:
from zope.schema._bootstrapfields import BeforeObjectAssignedEvent
except ImportError: # pragma: no cover
# BWC for older zope.schema.
from zope.schema._field import BeforeObjectAssignedEvent
__docformat__ = "restructuredtext en"
# pylint:disable=inherit-non-class,no-self-argument
[docs]class IBeforeSchemaFieldAssignedEvent(Interface):
"""
An event sent when certain schema fields will be assigning an
object to a property of another object.
The interface
:class:`zope.schema.interfaces.IBeforeObjectAssignedEvent` is a
sub-interface of this one once this module is imported.
"""
object = Attribute(u"The object that is going to be assigned. Subscribers may modify this")
name = Attribute(u"The name of the attribute under which the object will be assigned.")
context = Attribute(u"The context object where the object will be assigned to.")
# Make this a base of the zope interface so our handlers
# are compatible. This is dangerous if any lookups or registrations have already been done,
# as zope.interface maintains a cache of these things.
sch_interfaces.IBeforeObjectAssignedEvent.__bases__ = (IBeforeSchemaFieldAssignedEvent,)
[docs]@implementer(IBeforeSchemaFieldAssignedEvent)
class BeforeSchemaFieldAssignedEvent(object):
def __init__(self, obj, name, context):
self.object = obj
self.name = name
self.context = context
[docs]class IBeforeTextAssignedEvent(IBeforeSchemaFieldAssignedEvent):
"""
Event for assigning text.
"""
object = Text(title=u"The text being assigned.")
[docs]class IBeforeTextLineAssignedEvent(IBeforeTextAssignedEvent): # ITextLine extends IText
"""
Event for assigning text lines.
"""
object = TextLine(title=u"The text being assigned.")
[docs]class IBeforeContainerAssignedEvent(IBeforeSchemaFieldAssignedEvent):
"""
Event for assigning containers (__contains__).
"""
[docs]class IBeforeIterableAssignedEvent(IBeforeContainerAssignedEvent):
"""
Event for assigning iterables.
"""
[docs]class IBeforeCollectionAssignedEvent(IBeforeIterableAssignedEvent):
"""
Event for assigning collections.
"""
object = Attribute(u"The collection being assigned. May or may not be mutable.")
[docs]class IBeforeSetAssignedEvent(IBeforeCollectionAssignedEvent):
"""
Event for assigning sets.
"""
[docs]class IBeforeSequenceAssignedEvent(IBeforeCollectionAssignedEvent):
"""
Event for assigning sequences.
"""
object = Attribute(u"The sequence being assigned. May or may not be mutable.")
[docs]class IBeforeDictAssignedEvent(IBeforeIterableAssignedEvent):
"""
Event for assigning dicts.
"""
# The hierarchy is IContainer > IIterable > ICollection > ISequence > [ITuple, IList]
# Also: IContainer > IIterable > IDict
# Also: IContainer > IIterable > ISet
[docs]@implementer(IBeforeTextAssignedEvent)
class BeforeTextAssignedEvent(BeforeSchemaFieldAssignedEvent):
pass
[docs]@implementer(IBeforeTextLineAssignedEvent)
class BeforeTextLineAssignedEvent(BeforeTextAssignedEvent):
pass
[docs]@implementer(IBeforeCollectionAssignedEvent)
class BeforeCollectionAssignedEvent(BeforeSchemaFieldAssignedEvent):
object = None
[docs]@implementer(IBeforeSequenceAssignedEvent)
class BeforeSequenceAssignedEvent(BeforeCollectionAssignedEvent):
pass
[docs]@implementer(IBeforeSetAssignedEvent)
class BeforeSetAssignedEvent(BeforeCollectionAssignedEvent):
pass
[docs]@implementer(IBeforeDictAssignedEvent)
class BeforeDictAssignedEvent(BeforeSchemaFieldAssignedEvent):
pass
BeforeObjectAssignedEvent = BeforeObjectAssignedEvent
# Set up the alias for InvalidValue. We need to use an alias so that
# try/except and subclass works as expected. The unfortunate side
# effect is that to preserve the constructor we need to swizzle the
# __init__ of sch_interfaces.InvalidValue (the swizzling is harmless;
# in zope.schema 4.7, InvalidValue inherits the Exception constructor,
# which forbids any kwargs but allows arbitrary *args; we keep that
# behaviour). This is deprecated and will be removed in the future.
InvalidValue = sch_interfaces.InvalidValue
def _InvalidValue__init__(self, *args, **kwargs):
if 'field' in kwargs or 'value' in kwargs:
warnings.warn(
"Use zope.schema.interfaces.InvalidValue.with_field_and_value.",
DeprecationWarning)
field = kwargs.pop('field', None)
value = kwargs.pop('value', None)
if kwargs:
raise TypeError("Too many kwargs for function InvalidValue")
# like normal
sch_interfaces.ValidationError.__init__(self, *args)
if field is not None or value is not None:
self.with_field_and_value(field, value)
sch_interfaces.InvalidValue.__init__ = _InvalidValue__init__
del _InvalidValue__init__
deprecated('InvalidValue',
"This is a synonym for zope.schema.interfaces.InvalidValue. "
"Use its .with_field_and_value() method to set the field and value. "
"Do not try to pass them as keywords to the constructor."
)
assert hasattr(sch_interfaces.InvalidValue, 'value')
assert hasattr(sch_interfaces.ValidationError, 'field')
[docs]class IFromObject(Interface):
"""
Something that can convert one type of object to another,
following validation rules (see :class:`zope.schema.interfaces.IFromUnicode`)
"""
def fromObject(obj):
"""
Attempt to convert the object to the required type following
the rules of this object. Raises a TypeError or
:class:`zope.schema.interfaces.ValidationError` if this isn't
possible.
"""
[docs]class IVariant(sch_interfaces.IField, IFromObject):
"""
Similar to :class:`zope.schema.interfaces.IObject`, but
representing one of several different types.
If :meth:`fromObject` or :meth:`validate` fails, it should raise a
:class:`VariantValidationError`.
"""
[docs]class VariantValidationError(sch_interfaces.ValidationError):
"""
An error raised when a value is not suitable for any of the fields
of the variant.
The `errors` attribute is an ordered sequence of validation errors,
with one raised by each field of the variant in turn.
.. versionadded:: 1.8.0
"""
#: A sequence of validation errors
errors = ()
def __init__(self, field, value, errors):
super(VariantValidationError, self).__init__()
self.with_field_and_value(field, value)
self.errors = errors
def _ex_details(self):
lines = []
for e in self.errors:
info = traceback.format_exception_only(type(e), e)
# Trim trailing newline
info[-1] = info[-1].rstrip()
lines.append('\n'.join(info))
return lines
def _with_details(self, opening, detail_formatter):
lines = [' ' + e for e in self._ex_details()]
lines.append(' Field: ' + detail_formatter(self.field))
lines.append(' Value: ' + detail_formatter(self.value))
lines.append(opening)
lines.reverse()
return '\n'.join(lines)
def __str__(self):
s = super(VariantValidationError, self).__str__()
return self._with_details(s, str)
def __repr__(self):
s = super(VariantValidationError, self).__repr__()
return self._with_details(s, repr)
[docs]class IListOrTuple(sch_interfaces.IList):
pass
[docs]def find_most_derived_interface(ext_self, iface_upper_bound, possibilities=None):
"""
Search for the most derived version of the interface `iface_upper_bound`
implemented by `ext_self` and return that. Always returns at least `iface_upper_bound`
:keyword possibilities: An iterable of schemas to consider. If not given,
all the interfaces provided by ``ext_self`` will be considered.
"""
if possibilities is None:
possibilities = providedBy(ext_self)
_iface = iface_upper_bound
for iface in possibilities:
if iface.isOrExtends(_iface):
_iface = iface
return _iface
try:
from dm.zope.schema.interfaces import ISchemaConfigured as _ISchemaConfigured
except ImportError:
_ISchemaConfigured = Interface