2
# Copyright (c) 2008 Canonical
4
# Written by Marc Tardif <marc@interunion.ca>
6
# This file is part of Checkbox.
8
# Checkbox is free software: you can redistribute it and/or modify
9
# it under the terms of the GNU General Public License as published by
10
# the Free Software Foundation, either version 3 of the License, or
11
# (at your option) any later version.
13
# Checkbox is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
# GNU General Public License for more details.
18
# You should have received a copy of the GNU General Public License
19
# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
21
class InvalidError(Exception):
22
"""Raised when invalid input is received."""
26
class Constant(object):
27
"""Something that must be equal to a constant value."""
28
def __init__(self, value):
31
def coerce(self, value):
32
if value != self.value:
33
raise InvalidError("%r != %r" % (value, self.value))
38
"""Something which must apply to any of a number of different schemas."""
39
def __init__(self, *schemas):
41
@param schemas: Any number of other schema objects.
43
self.schemas = schemas
45
def coerce(self, value):
47
The result of the first schema which doesn't raise
48
L{InvalidError} from its C{coerce} method will be returned.
50
for schema in self.schemas:
52
return schema.coerce(value)
55
raise InvalidError("%r did not match any schema in %s"
56
% (value, self.schemas))
59
"""Something that must be a C{bool}."""
60
def coerce(self, value):
61
if not isinstance(value, bool):
62
raise InvalidError("%r is not a bool" % (value,))
67
"""Something that must be an C{int} or C{long}."""
68
def coerce(self, value):
69
if not isinstance(value, (int, long)):
70
raise InvalidError("%r isn't an int or long" % (value,))
75
"""Something that must be an C{int}, C{long}, or C{float}."""
76
def coerce(self, value):
77
if not isinstance(value, (int, long, float)):
78
raise InvalidError("%r isn't a float" % (value,))
83
"""Something that must be a C{str}."""
84
def coerce(self, value):
85
if not isinstance(value, str):
86
raise InvalidError("%r isn't a str" % (value,))
90
class Unicode(object):
91
"""Something that must be a C{unicode}."""
92
def coerce(self, value):
93
if not isinstance(value, unicode):
94
raise InvalidError("%r isn't a unicode" % (value,))
98
class UnicodeOrString(object):
99
"""Something that must be a C{unicode} or {str}.
101
If the value is a C{str}, it will automatically be decoded.
104
def __init__(self, encoding):
106
@param encoding: The encoding to automatically decode C{str}s with.
108
self.encoding = encoding
110
def coerce(self, value):
111
if isinstance(value, str):
113
value = value.decode(self.encoding)
114
except UnicodeDecodeError, e:
115
raise InvalidError("%r can't be decoded: %s" % (value, str(e)))
116
if not isinstance(value, unicode):
117
raise InvalidError("%r isn't a unicode" % (value,))
122
"""Something which must be a C{list}."""
123
def __init__(self, schema):
125
@param schema: The schema that all values of the list must match.
129
def coerce(self, value):
130
if not isinstance(value, list):
131
raise InvalidError("%r is not a list" % (value,))
132
new_list = list(value)
133
for i, subvalue in enumerate(value):
135
new_list[i] = self.schema.coerce(subvalue)
136
except InvalidError, e:
138
"%r could not coerce with %s: %s"
139
% (subvalue, self.schema, e))
144
"""Something which must be a fixed-length tuple."""
145
def __init__(self, *schema):
147
@param schema: A sequence of schemas, which will be applied to
148
each value in the tuple respectively.
152
def coerce(self, value):
153
if not isinstance(value, tuple):
154
raise InvalidError("%r is not a tuple" % (value,))
155
if len(value) != len(self.schema):
156
raise InvalidError("Need %s items, got %s in %r"
157
% (len(self.schema), len(value), value))
159
for schema, value in zip(self.schema, value):
160
new_value.append(schema.coerce(value))
161
return tuple(new_value)
164
class KeyDict(object):
165
"""Something which must be a C{dict} with defined keys.
167
The keys must be constant and the values must match a per-key schema.
169
def __init__(self, schema, optional=None):
171
@param schema: A dict mapping keys to schemas that the values
172
of those keys must match.
176
self.optional = set(optional)
179
def coerce(self, value):
181
if not isinstance(value, dict):
182
raise InvalidError("%r is not a dict." % (value,))
183
for k, v in value.iteritems():
184
if k not in self.schema:
185
raise InvalidError("%r is not a valid key as per %r"
188
new_dict[k] = self.schema[k].coerce(v)
189
except InvalidError, e:
191
"Value of %r key of dict %r could not coerce with %s: %s"
192
% (k, value, self.schema[k], e))
193
new_keys = set(new_dict.keys())
194
required_keys = set(self.schema.keys()) - self.optional
195
missing = required_keys - new_keys
197
raise InvalidError("Missing keys %s" % (missing,))
202
"""Something which must be a C{dict} with arbitrary keys."""
204
def __init__(self, key_schema, value_schema):
206
@param key_schema: The schema that keys must match.
207
@param value_schema: The schema that values must match.
209
self.key_schema = key_schema
210
self.value_schema = value_schema
212
def coerce(self, value):
213
if not isinstance(value, dict):
214
raise InvalidError("%r is not a dict." % (value,))
216
for k, v in value.items():
217
new_dict[self.key_schema.coerce(k)] = self.value_schema.coerce(v)