Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/zope/interface/exceptions.py : 49%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1##############################################################################
2#
3# Copyright (c) 2002 Zope Foundation and Contributors.
4# All Rights Reserved.
5#
6# This software is subject to the provisions of the Zope Public License,
7# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11# FOR A PARTICULAR PURPOSE.
12#
13##############################################################################
14"""Interface-specific exceptions
15"""
17__all__ = [
18 # Invalid tree
19 'Invalid',
20 'DoesNotImplement',
21 'BrokenImplementation',
22 'BrokenMethodImplementation',
23 'MultipleInvalid',
24 # Other
25 'BadImplements',
26 'InvalidInterface',
27]
29class Invalid(Exception):
30 """A specification is violated
31 """
34class _TargetInvalid(Invalid):
35 # Internal use. Subclass this when you're describing
36 # a particular target object that's invalid according
37 # to a specific interface.
38 #
39 # For backwards compatibility, the *target* and *interface* are
40 # optional, and the signatures are inconsistent in their ordering.
41 #
42 # We deal with the inconsistency in ordering by defining the index
43 # of the two values in ``self.args``. *target* uses a marker object to
44 # distinguish "not given" from "given, but None", because the latter
45 # can be a value that gets passed to validation. For this reason, it must
46 # always be the last argument (we detect absense by the ``IndexError``).
48 _IX_INTERFACE = 0
49 _IX_TARGET = 1
50 # The exception to catch when indexing self.args indicating that
51 # an argument was not given. If all arguments are expected,
52 # a subclass should set this to ().
53 _NOT_GIVEN_CATCH = IndexError
54 _NOT_GIVEN = '<Not Given>'
56 def _get_arg_or_default(self, ix, default=None):
57 try:
58 return self.args[ix] # pylint:disable=unsubscriptable-object
59 except self._NOT_GIVEN_CATCH:
60 return default
62 @property
63 def interface(self):
64 return self._get_arg_or_default(self._IX_INTERFACE)
66 @property
67 def target(self):
68 return self._get_arg_or_default(self._IX_TARGET, self._NOT_GIVEN)
70 ###
71 # str
72 #
73 # The ``__str__`` of self is implemented by concatenating (%s), in order,
74 # these properties (none of which should have leading or trailing
75 # whitespace):
76 #
77 # - self._str_subject
78 # Begin the message, including a description of the target.
79 # - self._str_description
80 # Provide a general description of the type of error, including
81 # the interface name if possible and relevant.
82 # - self._str_conjunction
83 # Join the description to the details. Defaults to ": ".
84 # - self._str_details
85 # Provide details about how this particular instance of the error.
86 # - self._str_trailer
87 # End the message. Usually just a period.
88 ###
90 @property
91 def _str_subject(self):
92 target = self.target
93 if target is self._NOT_GIVEN:
94 return "An object"
95 return "The object %r" % (target,)
97 @property
98 def _str_description(self):
99 return "has failed to implement interface %s" % (
100 self.interface or '<Unknown>'
101 )
103 _str_conjunction = ": "
104 _str_details = "<unknown>"
105 _str_trailer = '.'
107 def __str__(self):
108 return "%s %s%s%s%s" % (
109 self._str_subject,
110 self._str_description,
111 self._str_conjunction,
112 self._str_details,
113 self._str_trailer
114 )
117class DoesNotImplement(_TargetInvalid):
118 """
119 DoesNotImplement(interface[, target])
121 The *target* (optional) does not implement the *interface*.
123 .. versionchanged:: 5.0.0
124 Add the *target* argument and attribute, and change the resulting
125 string value of this object accordingly.
126 """
128 _str_details = "Does not declaratively implement the interface"
131class BrokenImplementation(_TargetInvalid):
132 """
133 BrokenImplementation(interface, name[, target])
135 The *target* (optional) is missing the attribute *name*.
137 .. versionchanged:: 5.0.0
138 Add the *target* argument and attribute, and change the resulting
139 string value of this object accordingly.
141 The *name* can either be a simple string or a ``Attribute`` object.
142 """
144 _IX_NAME = _TargetInvalid._IX_INTERFACE + 1
145 _IX_TARGET = _IX_NAME + 1
147 @property
148 def name(self):
149 return self.args[1] # pylint:disable=unsubscriptable-object
151 @property
152 def _str_details(self):
153 return "The %s attribute was not provided" % (
154 repr(self.name) if isinstance(self.name, str) else self.name
155 )
158class BrokenMethodImplementation(_TargetInvalid):
159 """
160 BrokenMethodImplementation(method, message[, implementation, interface, target])
162 The *target* (optional) has a *method* in *implementation* that violates
163 its contract in a way described by *mess*.
165 .. versionchanged:: 5.0.0
166 Add the *interface* and *target* argument and attribute,
167 and change the resulting string value of this object accordingly.
169 The *method* can either be a simple string or a ``Method`` object.
171 .. versionchanged:: 5.0.0
172 If *implementation* is given, then the *message* will have the
173 string "implementation" replaced with an short but informative
174 representation of *implementation*.
176 """
178 _IX_IMPL = 2
179 _IX_INTERFACE = _IX_IMPL + 1
180 _IX_TARGET = _IX_INTERFACE + 1
182 @property
183 def method(self):
184 return self.args[0] # pylint:disable=unsubscriptable-object
186 @property
187 def mess(self):
188 return self.args[1] # pylint:disable=unsubscriptable-object
190 @staticmethod
191 def __implementation_str(impl):
192 # It could be a callable or some arbitrary object, we don't
193 # know yet.
194 import inspect # Inspect is a heavy-weight dependency, lots of imports
195 try:
196 sig = inspect.signature
197 formatsig = str
198 except AttributeError:
199 sig = inspect.getargspec
200 f = inspect.formatargspec
201 formatsig = lambda sig: f(*sig) # pylint:disable=deprecated-method
203 try:
204 sig = sig(impl)
205 except (ValueError, TypeError):
206 # Unable to introspect. Darn.
207 # This could be a non-callable, or a particular builtin,
208 # or a bound method that doesn't even accept 'self', e.g.,
209 # ``Class.method = lambda: None; Class().method``
210 return repr(impl)
212 try:
213 name = impl.__qualname__
214 except AttributeError:
215 name = impl.__name__
217 return name + formatsig(sig)
219 @property
220 def _str_details(self):
221 impl = self._get_arg_or_default(self._IX_IMPL, self._NOT_GIVEN)
222 message = self.mess
223 if impl is not self._NOT_GIVEN and 'implementation' in message:
224 message = message.replace("implementation", '%r')
225 message = message % (self.__implementation_str(impl),)
227 return 'The contract of %s is violated because %s' % (
228 repr(self.method) if isinstance(self.method, str) else self.method,
229 message,
230 )
233class MultipleInvalid(_TargetInvalid):
234 """
235 The *target* has failed to implement the *interface* in
236 multiple ways.
238 The failures are described by *exceptions*, a collection of
239 other `Invalid` instances.
241 .. versionadded:: 5.0
242 """
244 _NOT_GIVEN_CATCH = ()
246 def __init__(self, interface, target, exceptions):
247 super(MultipleInvalid, self).__init__(interface, target, tuple(exceptions))
249 @property
250 def exceptions(self):
251 return self.args[2] # pylint:disable=unsubscriptable-object
253 @property
254 def _str_details(self):
255 # It would be nice to use tabs here, but that
256 # is hard to represent in doctests.
257 return '\n ' + '\n '.join(
258 x._str_details.strip() if isinstance(x, _TargetInvalid) else str(x)
259 for x in self.exceptions
260 )
262 _str_conjunction = ':' # We don't want a trailing space, messes up doctests
263 _str_trailer = ''
266class InvalidInterface(Exception):
267 """The interface has invalid contents
268 """
270class BadImplements(TypeError):
271 """An implementation assertion is invalid
273 because it doesn't contain an interface or a sequence of valid
274 implementation assertions.
275 """