1
#Copyright (c) 2006 Simon Wittber
3
#Permission is hereby granted, free of charge, to any person
4
#obtaining a copy of this software and associated documentation files
5
#(the "Software"), to deal in the Software without restriction,
6
#including without limitation the rights to use, copy, modify, merge,
7
#publish, distribute, sublicense, and/or sell copies of the Software,
8
#and to permit persons to whom the Software is furnished to do so,
9
#subject to the following conditions:
11
#The above copyright notice and this permission notice shall be
12
#included in all copies or substantial portions of the Software.
14
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
#EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
#MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
#NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
18
#BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
19
#ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
#CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
Gherkin provides safe serialization for simple python types.
29
from types import IntType,TupleType,StringType,FloatType,LongType,ListType,DictType,NoneType,BooleanType,UnicodeType
31
from struct import pack, unpack
32
from cStringIO import StringIO
36
UNICODE_CODEC = 'utf-8'
40
def check_memo(*args):
44
return cache.setdefault(args, func(*args))
47
class Gherkin(object):
67
self.int_size = SIZEOF_INT
68
self.float_size = SIZEOF_FLOAT
71
DictType:self.enc_dict_type,
72
ListType:self.enc_list_type,
73
TupleType:self.enc_list_type,
74
IntType:memoize(self.enc_int_type),
75
FloatType:memoize(self.enc_float_type),
76
LongType:memoize(self.enc_long_type),
77
UnicodeType:memoize(self.enc_unicode_type),
78
StringType:memoize(self.enc_string_type),
79
NoneType:self.enc_none_type,
80
BooleanType:memoize(self.enc_bool_type)
84
self.protocol[TupleType]:self.dec_tuple_type,
85
self.protocol[ListType]:self.dec_list_type,
86
self.protocol[DictType]:self.dec_dict_type,
87
self.protocol[LongType]:self.dec_long_type,
88
self.protocol[StringType]:self.dec_string_type,
89
self.protocol[FloatType]:self.dec_float_type,
90
self.protocol[IntType]:self.dec_int_type,
91
self.protocol[NoneType]:self.dec_none_type,
92
self.protocol[BooleanType]:self.dec_bool_type,
93
self.protocol[UnicodeType]:self.dec_unicode_type,
94
self.protocol['HomogenousList']:self.dec_homogenous_list_type,
95
self.protocol['HomogenousTuple']:self.dec_homogenous_tuple_type
99
def enc_dict_type(self, obj):
100
data = "".join([self.encoder[type(i)](i) for i in obj.items()])
101
return "%s%s%s" % (self.protocol[DictType], pack("!L", len(data)), data)
103
def enc_list_type(self, obj):
104
if len(set([type(i) for i in obj])) == 1:
105
return self.enc_homogenous_list_type(obj)
106
data = "".join([self.encoder[type(i)](i) for i in obj])
107
return "%s%s%s" % (self.protocol[type(obj)], pack("!L", len(data)), data)
109
def enc_homogenous_list_type(self, obj):
110
data = "".join([self.encoder[type(i)](i)[1:] for i in obj])
111
if type(obj) == type([]):
112
prefix = self.protocol['HomogenousList']
114
prefix = self.protocol['HomogenousTuple']
116
return "%s%s%s%s" % (prefix, self.protocol[type(obj[0])], pack("!L", len(data)), data)
118
def enc_int_type(self, obj):
119
return "%s%s" % (self.protocol[IntType], pack("!i", obj))
121
def enc_float_type(self, obj):
122
return "%s%s" % (self.protocol[FloatType], pack("!d", obj))
124
def enc_long_type(self, obj):
132
return "%s%s%s%s" % (self.protocol[LongType], pre, pack("!L", len(obj)), obj)
134
def enc_unicode_type(self, obj):
135
obj = obj.encode(UNICODE_CODEC)
136
return "%s%s%s" % (self.protocol[UnicodeType], pack("!L", len(obj)), obj)
138
def enc_string_type(self, obj):
139
return "%s%s%s" % (self.protocol[StringType], pack("!L", len(obj)), obj)
141
def enc_none_type(self, obj):
142
return self.protocol[NoneType]
144
def enc_bool_type(self, obj):
145
return self.protocol[BooleanType] + str(int(obj))
147
def dumps(self, obj):
149
Return the string that would be written to a file by dump(value, file). The value must be a supported type. Raise a ValueError exception if value has (or contains an object that has) an unsupported type.
151
options = "".join((hex(self.version)[2:],hex(SIZEOF_INT)[2:],hex(SIZEOF_FLOAT)[2:]))
152
assert len(options) == 3
154
data = self.encoder[type(obj)](obj)
156
raise ValueError, "Type not supported. (%s)" % e
157
header = "".join((self.header, options))
158
assert len(header) == 6
159
return "".join((header, data))
161
def dump(self, obj, file):
163
Write the value on the open file. The value must be a supported type. The file must be an open file object such as sys.stdout or returned by open() or posix.popen(). It must be opened in binary mode ('wb' or 'w+b').
164
If the value has (or contains an object that has) an unsupported type, a ValueError exception is raised
166
return file.write(self.dumps(obj))
168
def build_sequence(self, data, cast=list):
169
size = unpack('!L', data.read(SIZEOF_INT))[0]
171
data_tell = data.tell
172
items_append = items.append
173
self_decoder = self.decoder
174
data_read = data.read
175
start_position = data.tell()
176
while (data_tell() - start_position) < size:
178
value = self_decoder[T](data)
182
def build_homogenous_sequence(self, data, cast=list):
184
size = unpack('!L', data.read(SIZEOF_INT))[0]
186
data_tell = data.tell
187
items_append = items.append
188
self_decoder = self.decoder
189
data_read = data.read
190
start_position = data.tell()
191
while (data_tell() - start_position) < size:
192
value = self_decoder[T](data)
196
def dec_tuple_type(self, data):
197
return self.build_sequence(data, cast=tuple)
199
def dec_list_type(self, data):
200
return self.build_sequence(data, cast=list)
202
def dec_homogenous_list_type(self, data):
203
return self.build_homogenous_sequence(data, cast=list)
205
def dec_homogenous_tuple_type(self, data):
206
return self.build_homogenous_sequence(data, cast=tuple)
208
def dec_dict_type(self, data):
209
return self.build_sequence(data, cast=dict)
211
def dec_long_type(self, data):
213
size = unpack('!L', data.read(self.int_size))[0]
214
value = long(data.read(size),16)
215
if pre == "-": value = -value
218
def dec_string_type(self, data):
219
size = unpack('!L', data.read(self.int_size))[0]
220
value = str(data.read(size))
223
def dec_float_type(self, data):
224
value = unpack('!d', data.read(self.float_size))[0]
227
def dec_int_type(self, data):
228
value = unpack('!i', data.read(self.int_size))[0]
231
def dec_none_type(self, data):
234
def dec_bool_type(self, data):
235
value = int(data.read(1))
238
def dec_unicode_type(self, data):
239
size = unpack('!L', data.read(self.int_size))[0]
240
value = data.read(size).decode(UNICODE_CODEC)
243
def loads(self, data):
245
Convert the string to a value. If no valid value is found, raise EOFError, ValueError or TypeError. Extra characters in the string are ignored.
248
buffer = StringIO(data)
249
header = buffer.read(len(self.header))
250
assert header == self.header
251
assert self.version <= int(buffer.read(1), 10)
252
self.int_size = int(buffer.read(1), 10)
253
self.float_size = int(buffer.read(1), 10)
255
value = self.decoder[buffer.read(1)](buffer)
257
raise ValueError, "Type prefix not supported. (%s)" % (e)
260
def load(self, file):
262
Read one value from the open file and return it. If no valid value is read, raise EOFError, ValueError or TypeError. The file must be an open file object opened in binary mode ('rb' or 'r+b').
264
return self.loads(file.read())
268
dumps = __gherk.dumps
269
loads = __gherk.loads