-
Notifications
You must be signed in to change notification settings - Fork 0
/
test_marshal.txt
155 lines (100 loc) · 3.63 KB
/
test_marshal.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
Marshal Marshaller
******************
>>> from cromlech.marshallers import MarshalMarshaller
>>> marshaller = MarshalMarshaller()
The `msgpack` marshaller handles binary data :
>>> marshaller.binary
True
Straightforward dump/load
=========================
Respecting the conventions set by the `marshal` and `json` native
packages, we use the `dumps` and `loads` to handle bytes.
>>> data = marshaller.dumps({'test': 'this is a test'})
>>> data
b'\xfb\xda\x04test\xfa\x0ethis is a test0'
>>> marshaller.loads(data)
{'test': 'this is a test'}
Non-basic types
---------------
Marshal can handle only the most basic python types.
>>> from datetime import datetime
>>> mydatetime = datetime(2017, 12, 19, 11, 30, 25)
>>> import pytest
>>> with pytest.raises(ValueError) as exc:
... data = marshaller.dumps(mydatetime)
>>> exc.value
ValueError('unmarshallable object')
Decorator
---------
We can use the marshaller as a decorator
>>> @marshaller.wraps
... def my_renderer():
... return {"three": 12.145}
>>> my_renderer
<function my_renderer at ...>
>>> my_renderer()
b'\xfb\xda\x05three\xe7\n\xd7\xa3p=J(@0'
>>> marshaller.loads(my_renderer())
{'three': 12.145}
Streams and files
=================
Each marshaller has 4 methods devoted to handling file or stream.
`dump` and `load` work with streams. `dump_to` and `load_from` work
with filesystem paths.
Files
-----
>>> import os
>>> path = str(getfixture('tmpdir'))
>>> filepath = os.path.join(path, 'data.mhr')
>>> struct = [1, "two", 3.00001]
>>> marshaller.dump_to(struct, filepath)
The `dump_to` method is in charge of writing the data in the file.
We can load the content of this file using the `load_from` method :
>>> marshaller.load_from(filepath)
[1, 'two', 3.00001]
We can check manually the content of the file :
>>> with open(filepath, 'rb') as fd:
... fd.read()
b'\xdb\x03\x00\x00\x00\xe9\x01\x00\x00\x00Z\x03twog9b->\x05\x00\x08@'
Streams
--------
The `dump` method is in charge writing the data in the stream.
We can interpret the content of this stream using the `load` method.
We use a binary stream since our marshaller handles binary data.
>>> from io import BytesIO
>>> stream = BytesIO()
>>> marshaller.dump(struct, stream)
>>> stream.getvalue()
b'\xdb\x03\x00\x00\x00\xe9\x01\x00\x00\x00Z\x03twog9b->\x05\x00\x08@'
We now return to the offset 0 of the file and we can load it:
>>> assert stream.seek(0) == 0
>>> marshaller.load(stream)
[1, 'two', 3.00001]
Concurrency and timeout
-----------------------
Concurrency is handled via a file lock. We use the cross-os `portalocker`
package.
For our test, let's simulate a lock in place :
>>> import portalocker
>>> lock = portalocker.Lock(filepath)
>>> assert lock.acquire()
When we try to write to this file, we get an `OSError` raised by our
marshaller in response of the lock timeout :
>>> import pytest
>>> with pytest.raises(IOError) as timeout:
... marshaller.dump_to(struct, filepath, timeout=0)
>>> timeout.value
OSError('Resource is busy and could not be freed.')
Once we release the problematic timeout, we can write :
>>> lock.release()
>>> marshaller.dump_to(struct, filepath, timeout=0)
The same happens with the read :
>>> assert lock.acquire()
>>> with pytest.raises(IOError) as timeout:
... marshaller.load_from(filepath, timeout=0)
>>> timeout.value
OSError('Resource is busy and could not be freed.')
>>> lock.release()
>>> assert marshaller.load_from(filepath, timeout=0) == struct
Please note that the read/write are tied by the same lock. You can't read
and write at the same time.