Merge lp:~zyga/checkbox/session-peek-api into lp:checkbox
- session-peek-api
- Merge into trunk
Proposed by
Zygmunt Krynicki
Status: | Merged |
---|---|
Approved by: | Daniel Manrique |
Approved revision: | 2965 |
Merged at revision: | 2964 |
Proposed branch: | lp:~zyga/checkbox/session-peek-api |
Merge into: | lp:checkbox |
Diff against target: |
699 lines (+290/-175) 4 files modified
plainbox/plainbox/impl/session/__init__.py (+3/-1) plainbox/plainbox/impl/session/resume.py (+251/-155) plainbox/plainbox/impl/session/storage.py (+17/-0) plainbox/plainbox/impl/session/test_resume.py (+19/-19) |
To merge this branch: | bzr merge lp:~zyga/checkbox/session-peek-api |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel Manrique (community) | Approve | ||
Review via email: mp+217814@code.launchpad.net |
This proposal supersedes a proposal from 2014-04-30.
Commit message
Description of the change
933a1ed plainbox:session: add SessionStorage.id
5897758 plainbox:session: make SessionStorage.
6e72202 plainbox:session: refactor _restore_
08a3e28 plainbox:session: create EnvelopeUnpackMixIn
f300476 plainbox:session: add SessionPeekHelper
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'plainbox/plainbox/impl/session/__init__.py' |
2 | --- plainbox/plainbox/impl/session/__init__.py 2014-01-27 23:26:33 +0000 |
3 | +++ plainbox/plainbox/impl/session/__init__.py 2014-04-30 19:25:48 +0000 |
4 | @@ -79,6 +79,7 @@ |
5 | 'JobState', |
6 | 'SessionManager', |
7 | 'SessionMetaData', |
8 | + 'SessionPeekHelper', |
9 | 'SessionState', |
10 | 'SessionStateLegacyAPI', |
11 | 'SessionStorage', |
12 | @@ -91,7 +92,8 @@ |
13 | from plainbox.impl.session.jobs import UndesiredJobReadinessInhibitor |
14 | from plainbox.impl.session.legacy import SessionStateLegacyAPI |
15 | from plainbox.impl.session.manager import SessionManager |
16 | +from plainbox.impl.session.resume import SessionPeekHelper |
17 | +from plainbox.impl.session.state import SessionMetaData |
18 | from plainbox.impl.session.state import SessionState |
19 | -from plainbox.impl.session.state import SessionMetaData |
20 | from plainbox.impl.session.storage import SessionStorage |
21 | from plainbox.impl.session.storage import SessionStorageRepository |
22 | |
23 | === modified file 'plainbox/plainbox/impl/session/resume.py' |
24 | --- plainbox/plainbox/impl/session/resume.py 2014-03-17 11:30:19 +0000 |
25 | +++ plainbox/plainbox/impl/session/resume.py 2014-04-30 19:25:48 +0000 |
26 | @@ -56,6 +56,7 @@ |
27 | from plainbox.impl.result import IOLogRecord |
28 | from plainbox.impl.result import MemoryJobResult |
29 | from plainbox.impl.secure.qualifiers import SimpleQualifier |
30 | +from plainbox.impl.session.state import SessionMetaData |
31 | from plainbox.impl.session.state import SessionState |
32 | |
33 | logger = logging.getLogger("plainbox.session.resume") |
34 | @@ -92,7 +93,78 @@ |
35 | """ |
36 | |
37 | |
38 | -class SessionResumeHelper: |
39 | +class EnvelopeUnpackMixIn: |
40 | + """ |
41 | + A mix-in class capable of unpacking the envelope of the session storage |
42 | + """ |
43 | + |
44 | + def unpack_envelope(self, data): |
45 | + """ |
46 | + Unpack the binary envelope and get access to a JSON object |
47 | + |
48 | + :param data: |
49 | + Bytes representing the dormant session |
50 | + :returns: |
51 | + the JSON representation of a session stored in the envelope |
52 | + :raises CorruptedSessionError: |
53 | + if the representation of the session is corrupted in any way |
54 | + """ |
55 | + try: |
56 | + data = gzip.decompress(data) |
57 | + except IOError: |
58 | + raise CorruptedSessionError(_("Cannot decompress session data")) |
59 | + try: |
60 | + text = data.decode("UTF-8") |
61 | + except UnicodeDecodeError: |
62 | + raise CorruptedSessionError(_("Cannot decode session text")) |
63 | + try: |
64 | + return json.loads(text) |
65 | + except ValueError: |
66 | + raise CorruptedSessionError(_("Cannot interpret session JSON")) |
67 | + |
68 | + |
69 | +class SessionPeekHelper(EnvelopeUnpackMixIn): |
70 | + |
71 | + def peek(self, data): |
72 | + """ |
73 | + Peek at the meta-data of a dormant session. |
74 | + |
75 | + :param data: |
76 | + Bytes representing the dormant session |
77 | + :returns: |
78 | + a SessionMetaData object |
79 | + :raises CorruptedSessionError: |
80 | + if the representation of the session is corrupted in any way |
81 | + :raises IncompatibleSessionError: |
82 | + if session serialization format is not supported |
83 | + """ |
84 | + json_repr = self.unpack_envelope(data) |
85 | + return self._peek_json(json_repr) |
86 | + |
87 | + def _peek_json(self, json_repr): |
88 | + """ |
89 | + Resume a SessionMetaData object from the JSON representation. |
90 | + |
91 | + This method is called by :meth:`peek()` after the initial envelope |
92 | + and parsing is done. The only error conditions that can happen |
93 | + are related to semantic incompatibilities or corrupted internal state. |
94 | + """ |
95 | + logger.debug(_("Peeking at json... (see below)")) |
96 | + logger.debug(json.dumps(json_repr, indent=4)) |
97 | + _validate(json_repr, value_type=dict) |
98 | + version = _validate(json_repr, key="version", choice=[1]) |
99 | + if version == 1: |
100 | + return SessionPeekHelper1().peek_json(json_repr) |
101 | + elif version == 2: |
102 | + return SessionPeekHelper2().peek_json(json_repr) |
103 | + elif version == 3: |
104 | + return SessionPeekHelper3().peek_json(json_repr) |
105 | + else: |
106 | + raise IncompatibleSessionError( |
107 | + _("Unsupported version {}").format(version)) |
108 | + |
109 | + |
110 | +class SessionResumeHelper(EnvelopeUnpackMixIn): |
111 | """ |
112 | Helper class for implementing session resume feature. |
113 | |
114 | @@ -139,18 +211,7 @@ |
115 | :raises IncompatibleJobError: |
116 | if serialized jobs are not the same as current jobs |
117 | """ |
118 | - try: |
119 | - data = gzip.decompress(data) |
120 | - except IOError: |
121 | - raise CorruptedSessionError(_("Cannot decompress session data")) |
122 | - try: |
123 | - text = data.decode("UTF-8") |
124 | - except UnicodeDecodeError: |
125 | - raise CorruptedSessionError(_("Cannot decode session text")) |
126 | - try: |
127 | - json_repr = json.loads(text) |
128 | - except ValueError: |
129 | - raise CorruptedSessionError(_("Cannot interpret session JSON")) |
130 | + json_repr = self.unpack_envelope(data) |
131 | return self._resume_json(json_repr, early_cb) |
132 | |
133 | def _resume_json(self, json_repr, early_cb=None): |
134 | @@ -195,7 +256,143 @@ |
135 | return job.id not in self._retain_id_set |
136 | |
137 | |
138 | -class SessionResumeHelper1: |
139 | +class MetaDataHelper1MixIn: |
140 | + |
141 | + @classmethod |
142 | + def _restore_SessionState_metadata(cls, metadata, session_repr): |
143 | + """ |
144 | + Extract meta-data information from the representation of the session |
145 | + and set it in the given session object |
146 | + """ |
147 | + # Get the representation of the meta-data |
148 | + metadata_repr = _validate( |
149 | + session_repr, key='metadata', value_type=dict) |
150 | + # Set each bit back to the session |
151 | + metadata.title = _validate( |
152 | + metadata_repr, key='title', value_type=str, value_none=True) |
153 | + metadata.flags = set([ |
154 | + _validate( |
155 | + flag, value_type=str, |
156 | + value_type_msg=_("Each flag must be a string")) |
157 | + for flag in _validate( |
158 | + metadata_repr, key='flags', value_type=list)]) |
159 | + metadata.running_job_name = _validate( |
160 | + metadata_repr, key='running_job_name', value_type=str, |
161 | + value_none=True) |
162 | + |
163 | + |
164 | +class MetaDataHelper2MixIn(MetaDataHelper1MixIn): |
165 | + |
166 | + @classmethod |
167 | + def _restore_SessionState_metadata(cls, metadata, session_repr): |
168 | + """ |
169 | + Extract meta-data information from the representation of the session |
170 | + and set it in the given session object |
171 | + """ |
172 | + super()._restore_SessionState_metadata(metadata, session_repr) |
173 | + # Get the representation of the meta-data |
174 | + metadata_repr = _validate( |
175 | + session_repr, key='metadata', value_type=dict) |
176 | + app_blob = _validate( |
177 | + metadata_repr, key='app_blob', value_type=str, |
178 | + value_none=True) |
179 | + if app_blob is not None: |
180 | + try: |
181 | + app_blob = app_blob.encode("ASCII") |
182 | + except UnicodeEncodeError: |
183 | + # TRANSLATORS: please don't translate app_blob |
184 | + raise CorruptedSessionError(_("app_blob is not ASCII")) |
185 | + try: |
186 | + app_blob = base64.standard_b64decode(app_blob) |
187 | + except binascii.Error: |
188 | + # TRANSLATORS: please don't translate app_blob |
189 | + raise CorruptedSessionError(_("Cannot base64 decode app_blob")) |
190 | + metadata.app_blob = app_blob |
191 | + |
192 | + |
193 | +class MetaDataHelper3MixIn(MetaDataHelper2MixIn): |
194 | + |
195 | + @classmethod |
196 | + def _restore_SessionState_metadata(cls, metadata, session_repr): |
197 | + """ |
198 | + Extract meta-data information from the representation of the session |
199 | + and set it in the given session object |
200 | + """ |
201 | + super()._restore_SessionState_metadata(metadata, session_repr) |
202 | + # Get the representation of the meta-data |
203 | + metadata_repr = _validate( |
204 | + session_repr, key='metadata', value_type=dict) |
205 | + metadata.app_id = _validate( |
206 | + metadata_repr, key='app_id', value_type=str, |
207 | + value_none=True) |
208 | + |
209 | + |
210 | +class SessionPeekHelper1(MetaDataHelper1MixIn): |
211 | + """ |
212 | + Helper class for implementing session peek feature |
213 | + |
214 | + This class works with data constructed by |
215 | + :class:`~plainbox.impl.session.suspend.SessionSuspendHelper1` which has |
216 | + been pre-processed by :class:`SessionPeekHelper` (to strip the initial |
217 | + envelope). |
218 | + |
219 | + The only goal of this class is to reconstruct session state meta-data. |
220 | + """ |
221 | + |
222 | + def peek_json(self, json_repr): |
223 | + """ |
224 | + Resume a SessionState object from the JSON representation. |
225 | + |
226 | + This method is called by :meth:`peek()` after the initial envelope and |
227 | + parsing is done. The only error conditions that can happen are related |
228 | + to semantic incompatibilities or corrupted internal state. |
229 | + """ |
230 | + _validate(json_repr, key="version", choice=[1]) |
231 | + session_repr = _validate(json_repr, key='session', value_type=dict) |
232 | + metadata = SessionMetaData() |
233 | + self._restore_SessionState_metadata(metadata, session_repr) |
234 | + return metadata |
235 | + |
236 | + def _build_SessionState(self, session_repr, early_cb=None): |
237 | + """ |
238 | + Reconstruct the session state object. |
239 | + |
240 | + This method creates a fresh SessionState instance and restores |
241 | + jobs, results, meta-data and desired job list using helper methods. |
242 | + """ |
243 | + logger.debug(_("Starting to restore metadata...")) |
244 | + metadata = SessionMetaData() |
245 | + self._peek_SessionState_metadata(metadata, session_repr) |
246 | + return metadata |
247 | + |
248 | + |
249 | +class SessionPeekHelper2(MetaDataHelper2MixIn, SessionPeekHelper1): |
250 | + """ |
251 | + Helper class for implementing session peek feature |
252 | + |
253 | + This class works with data constructed by |
254 | + :class:`~plainbox.impl.session.suspend.SessionSuspendHelper1` which has |
255 | + been pre-processed by :class:`SessionPeekHelper` (to strip the initial |
256 | + envelope). |
257 | + |
258 | + The only goal of this class is to reconstruct session state meta-data. |
259 | + """ |
260 | + |
261 | + |
262 | +class SessionPeekHelper3(MetaDataHelper3MixIn, SessionPeekHelper2): |
263 | + """ |
264 | + Helper class for implementing session peek feature |
265 | + |
266 | + This class works with data constructed by |
267 | + :class:`~plainbox.impl.session.suspend.SessionSuspendHelper1` which has |
268 | + been pre-processed by :class:`SessionPeekHelper` (to strip the initial |
269 | + envelope). |
270 | + |
271 | + The only goal of this class is to reconstruct session state meta-data. |
272 | + """ |
273 | + |
274 | + |
275 | +class SessionResumeHelper1(MetaDataHelper1MixIn): |
276 | """ |
277 | Helper class for implementing session resume feature |
278 | |
279 | @@ -257,7 +454,8 @@ |
280 | _("Starting to restore jobs and results to %r..."), session) |
281 | self._restore_SessionState_jobs_and_results(session, session_repr) |
282 | logger.debug(_("Starting to restore metadata...")) |
283 | - self._restore_SessionState_metadata(session, session_repr) |
284 | + self._restore_SessionState_metadata(session.metadata, session_repr) |
285 | + logger.debug(_("restored metadata %r"), session.metadata) |
286 | logger.debug(_("Starting to restore desired job list...")) |
287 | self._restore_SessionState_desired_job_list(session, session_repr) |
288 | logger.debug(_("Starting to restore job list...")) |
289 | @@ -372,29 +570,6 @@ |
290 | session.update_job_result(job, result_list[-1]) |
291 | |
292 | @classmethod |
293 | - def _restore_SessionState_metadata(cls, session, session_repr): |
294 | - """ |
295 | - Extract meta-data information from the representation of the session |
296 | - and set it in the given session object |
297 | - """ |
298 | - # Get the representation of the meta-data |
299 | - metadata_repr = _validate( |
300 | - session_repr, key='metadata', value_type=dict) |
301 | - # Set each bit back to the session |
302 | - session.metadata.title = _validate( |
303 | - metadata_repr, key='title', value_type=str, value_none=True) |
304 | - session.metadata.flags = set([ |
305 | - _validate( |
306 | - flag, value_type=str, |
307 | - value_type_msg=_("Each flag must be a string")) |
308 | - for flag in _validate( |
309 | - metadata_repr, key='flags', value_type=list)]) |
310 | - session.metadata.running_job_name = _validate( |
311 | - metadata_repr, key='running_job_name', value_type=str, |
312 | - value_none=True) |
313 | - logger.debug(_("restored metadata %r"), session.metadata) |
314 | - |
315 | - @classmethod |
316 | def _restore_SessionState_desired_job_list(cls, session, session_repr): |
317 | """ |
318 | Extract the representation of desired_job_list from the session and |
319 | @@ -512,6 +687,44 @@ |
320 | return IOLogRecord(delay, stream_name, data) |
321 | |
322 | |
323 | +class SessionResumeHelper2(MetaDataHelper2MixIn, SessionResumeHelper1): |
324 | + """ |
325 | + Helper class for implementing session resume feature |
326 | + |
327 | + This class works with data constructed by |
328 | + :class:`~plainbox.impl.session.suspend.SessionSuspendHelper2` which has |
329 | + been pre-processed by :class:`SessionResumeHelper` (to strip the initial |
330 | + envelope). |
331 | + |
332 | + Due to the constraints of what can be represented in a suspended session, |
333 | + this class cannot work in isolation. It must operate with a list of know |
334 | + jobs. |
335 | + |
336 | + Since (most of the) jobs are being provided externally (as they represent |
337 | + the non-serialized parts of checkbox or other job providers) several |
338 | + failure modes are possible. Those are documented in :meth:`resume()` |
339 | + """ |
340 | + |
341 | + |
342 | +class SessionResumeHelper3(MetaDataHelper3MixIn, SessionResumeHelper2): |
343 | + """ |
344 | + Helper class for implementing session resume feature |
345 | + |
346 | + This class works with data constructed by |
347 | + :class:`~plainbox.impl.session.suspend.SessionSuspendHelper3` which has |
348 | + been pre-processed by :class:`SessionResumeHelper` (to strip the initial |
349 | + envelope). |
350 | + |
351 | + Due to the constraints of what can be represented in a suspended session, |
352 | + this class cannot work in isolation. It must operate with a list of know |
353 | + jobs. |
354 | + |
355 | + Since (most of the) jobs are being provided externally (as they represent |
356 | + the non-serialized parts of checkbox or other job providers) several |
357 | + failure modes are possible. Those are documented in :meth:`resume()` |
358 | + """ |
359 | + |
360 | + |
361 | def _validate(obj, **flags): |
362 | """ |
363 | Multi-purpose extraction and validation function. |
364 | @@ -556,120 +769,3 @@ |
365 | obj_name, value_choice)) |
366 | raise CorruptedSessionError(error_msg) |
367 | return value |
368 | - |
369 | - |
370 | -class SessionResumeHelper2(SessionResumeHelper1): |
371 | - """ |
372 | - Helper class for implementing session resume feature |
373 | - |
374 | - This class works with data constructed by |
375 | - :class:`~plainbox.impl.session.suspend.SessionSuspendHelper2` which has |
376 | - been pre-processed by :class:`SessionResumeHelper` (to strip the initial |
377 | - envelope). |
378 | - |
379 | - Due to the constraints of what can be represented in a suspended session, |
380 | - this class cannot work in isolation. It must operate with a list of know |
381 | - jobs. |
382 | - |
383 | - Since (most of the) jobs are being provided externally (as they represent |
384 | - the non-serialized parts of checkbox or other job providers) several |
385 | - failure modes are possible. Those are documented in :meth:`resume()` |
386 | - """ |
387 | - |
388 | - @classmethod |
389 | - def _restore_SessionState_metadata(cls, session, session_repr): |
390 | - """ |
391 | - Extract meta-data information from the representation of the session |
392 | - and set it in the given session object |
393 | - """ |
394 | - # Get the representation of the meta-data |
395 | - metadata_repr = _validate( |
396 | - session_repr, key='metadata', value_type=dict) |
397 | - # Set each bit back to the session |
398 | - session.metadata.title = _validate( |
399 | - metadata_repr, key='title', value_type=str, value_none=True) |
400 | - session.metadata.flags = set([ |
401 | - _validate( |
402 | - flag, value_type=str, |
403 | - value_type_msg=_("Each flag must be a string")) |
404 | - for flag in _validate( |
405 | - metadata_repr, key='flags', value_type=list)]) |
406 | - session.metadata.running_job_name = _validate( |
407 | - metadata_repr, key='running_job_name', value_type=str, |
408 | - value_none=True) |
409 | - app_blob = _validate( |
410 | - metadata_repr, key='app_blob', value_type=str, |
411 | - value_none=True) |
412 | - if app_blob is not None: |
413 | - try: |
414 | - app_blob = app_blob.encode("ASCII") |
415 | - except UnicodeEncodeError: |
416 | - # TRANSLATORS: please don't translate app_blob |
417 | - raise CorruptedSessionError(_("app_blob is not ASCII")) |
418 | - try: |
419 | - app_blob = base64.standard_b64decode(app_blob) |
420 | - except binascii.Error: |
421 | - # TRANSLATORS: please don't translate app_blob |
422 | - raise CorruptedSessionError(_("Cannot base64 decode app_blob")) |
423 | - session.metadata.app_blob = app_blob |
424 | - logger.debug(_("restored metadata %r"), session.metadata) |
425 | - |
426 | - |
427 | -class SessionResumeHelper3(SessionResumeHelper2): |
428 | - """ |
429 | - Helper class for implementing session resume feature |
430 | - |
431 | - This class works with data constructed by |
432 | - :class:`~plainbox.impl.session.suspend.SessionSuspendHelper3` which has |
433 | - been pre-processed by :class:`SessionResumeHelper` (to strip the initial |
434 | - envelope). |
435 | - |
436 | - Due to the constraints of what can be represented in a suspended session, |
437 | - this class cannot work in isolation. It must operate with a list of know |
438 | - jobs. |
439 | - |
440 | - Since (most of the) jobs are being provided externally (as they represent |
441 | - the non-serialized parts of checkbox or other job providers) several |
442 | - failure modes are possible. Those are documented in :meth:`resume()` |
443 | - """ |
444 | - |
445 | - @classmethod |
446 | - def _restore_SessionState_metadata(cls, session, session_repr): |
447 | - """ |
448 | - Extract meta-data information from the representation of the session |
449 | - and set it in the given session object |
450 | - """ |
451 | - # Get the representation of the meta-data |
452 | - metadata_repr = _validate( |
453 | - session_repr, key='metadata', value_type=dict) |
454 | - # Set each bit back to the session |
455 | - session.metadata.title = _validate( |
456 | - metadata_repr, key='title', value_type=str, value_none=True) |
457 | - session.metadata.flags = set([ |
458 | - _validate( |
459 | - flag, value_type=str, |
460 | - value_type_msg=_("Each flag must be a string")) |
461 | - for flag in _validate( |
462 | - metadata_repr, key='flags', value_type=list)]) |
463 | - session.metadata.running_job_name = _validate( |
464 | - metadata_repr, key='running_job_name', value_type=str, |
465 | - value_none=True) |
466 | - app_blob = _validate( |
467 | - metadata_repr, key='app_blob', value_type=str, |
468 | - value_none=True) |
469 | - if app_blob is not None: |
470 | - try: |
471 | - app_blob = app_blob.encode("ASCII") |
472 | - except UnicodeEncodeError: |
473 | - # TRANSLATORS: please don't translate app_blob |
474 | - raise CorruptedSessionError(_("app_blob is not ASCII")) |
475 | - try: |
476 | - app_blob = base64.standard_b64decode(app_blob) |
477 | - except binascii.Error: |
478 | - # TRANSLATORS: please don't translate app_blob |
479 | - raise CorruptedSessionError(_("Cannot base64 decode app_blob")) |
480 | - session.metadata.app_blob = app_blob |
481 | - session.metadata.app_id = _validate( |
482 | - metadata_repr, key='app_id', value_type=str, |
483 | - value_none=True) |
484 | - logger.debug(_("restored metadata %r"), session.metadata) |
485 | |
486 | === modified file 'plainbox/plainbox/impl/session/storage.py' |
487 | --- plainbox/plainbox/impl/session/storage.py 2014-02-20 21:43:55 +0000 |
488 | +++ plainbox/plainbox/impl/session/storage.py 2014-04-30 19:25:48 +0000 |
489 | @@ -206,6 +206,13 @@ |
490 | return self._location |
491 | |
492 | @property |
493 | + def id(self): |
494 | + """ |
495 | + identifier of the session storage (name of the random directory) |
496 | + """ |
497 | + return os.path.splitext(os.path.basename(self.location))[0] |
498 | + |
499 | + @property |
500 | def session_file(self): |
501 | """ |
502 | pathname of the session state file |
503 | @@ -404,6 +411,11 @@ |
504 | # Close the session file |
505 | logger.debug(_("Closed descriptor %d"), session_fd) |
506 | os.close(session_fd) |
507 | + except IOError as exc: |
508 | + if exc.errno == errno.ENOENT: |
509 | + # Treat lack of 'session' file as an empty file |
510 | + return b'' |
511 | + raise |
512 | finally: |
513 | # Close the location directory |
514 | logger.debug(_("Closed descriptor %d"), location_fd) |
515 | @@ -427,6 +439,11 @@ |
516 | finally: |
517 | # Close the session file |
518 | os.close(session_fd) |
519 | + except IOError as exc: |
520 | + if exc.errno == errno.ENOENT: |
521 | + # Treat lack of 'session' file as an empty file |
522 | + return b'' |
523 | + raise |
524 | finally: |
525 | # Close the location directory |
526 | os.close(location_fd) |
527 | |
528 | === modified file 'plainbox/plainbox/impl/session/test_resume.py' |
529 | --- plainbox/plainbox/impl/session/test_resume.py 2014-03-26 20:01:03 +0000 |
530 | +++ plainbox/plainbox/impl/session/test_resume.py 2014-04-30 19:25:48 +0000 |
531 | @@ -957,7 +957,7 @@ |
532 | """ |
533 | with self.assertRaises(CorruptedSessionError) as boom: |
534 | self.good_repr['metadata'] = 1 |
535 | - self.resume_fn(self.session, self.good_repr) |
536 | + self.resume_fn(self.session.metadata, self.good_repr) |
537 | self.assertEqual( |
538 | str(boom.exception), |
539 | "Value of key 'metadata' is of incorrect type int") |
540 | @@ -969,7 +969,7 @@ |
541 | """ |
542 | with self.assertRaises(CorruptedSessionError) as boom: |
543 | self.good_repr['metadata']['title'] = 1 |
544 | - self.resume_fn(self.session, self.good_repr) |
545 | + self.resume_fn(self.session.metadata, self.good_repr) |
546 | self.assertEqual( |
547 | str(boom.exception), |
548 | "Value of key 'title' is of incorrect type int") |
549 | @@ -980,7 +980,7 @@ |
550 | ``title`` to be None |
551 | """ |
552 | self.good_repr['metadata']['title'] = None |
553 | - self.resume_fn(self.session, self.good_repr) |
554 | + self.resume_fn(self.session.metadata, self.good_repr) |
555 | self.assertEqual(self.session.metadata.title, None) |
556 | |
557 | def test_restore_SessionState_metadata_restores_title(self): |
558 | @@ -988,7 +988,7 @@ |
559 | verify that _restore_SessionState_metadata() restores ``title`` |
560 | """ |
561 | self.good_repr['metadata']['title'] = "a title" |
562 | - self.resume_fn(self.session, self.good_repr) |
563 | + self.resume_fn(self.session.metadata, self.good_repr) |
564 | self.assertEqual(self.session.metadata.title, "a title") |
565 | |
566 | def test_restore_SessionState_metadata_checks_flags_type(self): |
567 | @@ -998,7 +998,7 @@ |
568 | """ |
569 | with self.assertRaises(CorruptedSessionError) as boom: |
570 | self.good_repr['metadata']['flags'] = 1 |
571 | - self.resume_fn(self.session, self.good_repr) |
572 | + self.resume_fn(self.session.metadata, self.good_repr) |
573 | self.assertEqual( |
574 | str(boom.exception), |
575 | "Value of key 'flags' is of incorrect type int") |
576 | @@ -1010,7 +1010,7 @@ |
577 | """ |
578 | with self.assertRaises(CorruptedSessionError) as boom: |
579 | self.good_repr['metadata']['flags'] = None |
580 | - self.resume_fn(self.session, self.good_repr) |
581 | + self.resume_fn(self.session.metadata, self.good_repr) |
582 | self.assertEqual( |
583 | str(boom.exception), |
584 | "Value of key 'flags' cannot be None") |
585 | @@ -1022,7 +1022,7 @@ |
586 | """ |
587 | with self.assertRaises(CorruptedSessionError) as boom: |
588 | self.good_repr['metadata']['flags'] = [1] |
589 | - self.resume_fn(self.session, self.good_repr) |
590 | + self.resume_fn(self.session.metadata, self.good_repr) |
591 | self.assertEqual( |
592 | str(boom.exception), |
593 | "Each flag must be a string") |
594 | @@ -1032,7 +1032,7 @@ |
595 | verify that _restore_SessionState_metadata() restores ``flags`` |
596 | """ |
597 | self.good_repr['metadata']['flags'] = ["flag1", "flag2"] |
598 | - self.resume_fn(self.session, self.good_repr) |
599 | + self.resume_fn(self.session.metadata, self.good_repr) |
600 | self.assertEqual(self.session.metadata.flags, set(['flag1', 'flag2'])) |
601 | |
602 | def test_restore_SessionState_metadata_checks_running_job_name_type(self): |
603 | @@ -1042,7 +1042,7 @@ |
604 | """ |
605 | with self.assertRaises(CorruptedSessionError) as boom: |
606 | self.good_repr['metadata']['running_job_name'] = 1 |
607 | - self.resume_fn(self.session, self.good_repr) |
608 | + self.resume_fn(self.session.metadata, self.good_repr) |
609 | self.assertEqual( |
610 | str(boom.exception), |
611 | "Value of key 'running_job_name' is of incorrect type int") |
612 | @@ -1053,7 +1053,7 @@ |
613 | ``running_job_name`` to be None |
614 | """ |
615 | self.good_repr['metadata']['running_job_name'] = None |
616 | - self.resume_fn(self.session, self.good_repr) |
617 | + self.resume_fn(self.session.metadata, self.good_repr) |
618 | self.assertEqual(self.session.metadata.running_job_name, None) |
619 | |
620 | def test_restore_SessionState_metadata_restores_running_job_name(self): |
621 | @@ -1062,7 +1062,7 @@ |
622 | the value of ``running_job_name`` |
623 | """ |
624 | self.good_repr['metadata']['running_job_name'] = "a job" |
625 | - self.resume_fn(self.session, self.good_repr) |
626 | + self.resume_fn(self.session.metadata, self.good_repr) |
627 | self.assertEqual(self.session.metadata.running_job_name, "a job") |
628 | |
629 | |
630 | @@ -1093,7 +1093,7 @@ |
631 | with self.assertRaises(CorruptedSessionError) as boom: |
632 | obj_repr = copy.copy(self.good_repr) |
633 | obj_repr['metadata']['app_blob'] = 1 |
634 | - self.resume_fn(self.session, obj_repr) |
635 | + self.resume_fn(self.session.metadata, obj_repr) |
636 | self.assertEqual( |
637 | str(boom.exception), |
638 | "Value of key 'app_blob' is of incorrect type int") |
639 | @@ -1105,7 +1105,7 @@ |
640 | """ |
641 | obj_repr = copy.copy(self.good_repr) |
642 | obj_repr['metadata']['app_blob'] = None |
643 | - self.resume_fn(self.session, obj_repr) |
644 | + self.resume_fn(self.session.metadata, obj_repr) |
645 | self.assertEqual(self.session.metadata.app_blob, None) |
646 | |
647 | def test_restore_SessionState_metadata_restores_app_blob(self): |
648 | @@ -1114,7 +1114,7 @@ |
649 | """ |
650 | obj_repr = copy.copy(self.good_repr) |
651 | obj_repr['metadata']['app_blob'] = "YmxvYg==" |
652 | - self.resume_fn(self.session, obj_repr) |
653 | + self.resume_fn(self.session.metadata, obj_repr) |
654 | self.assertEqual(self.session.metadata.app_blob, b"blob") |
655 | |
656 | def test_restore_SessionState_metadata_non_ascii_app_blob(self): |
657 | @@ -1125,7 +1125,7 @@ |
658 | with self.assertRaises(CorruptedSessionError) as boom: |
659 | obj_repr = copy.copy(self.good_repr) |
660 | obj_repr['metadata']['app_blob'] = '\uFFFD' |
661 | - self.resume_fn(self.session, obj_repr) |
662 | + self.resume_fn(self.session.metadata, obj_repr) |
663 | self.assertEqual(str(boom.exception), "app_blob is not ASCII") |
664 | self.assertIsInstance(boom.exception.__context__, UnicodeEncodeError) |
665 | |
666 | @@ -1137,7 +1137,7 @@ |
667 | with self.assertRaises(CorruptedSessionError) as boom: |
668 | obj_repr = copy.copy(self.good_repr) |
669 | obj_repr['metadata']['app_blob'] = '==broken' |
670 | - self.resume_fn(self.session, obj_repr) |
671 | + self.resume_fn(self.session.metadata, obj_repr) |
672 | self.assertEqual(str(boom.exception), "Cannot base64 decode app_blob") |
673 | # base64.standard_b64decode() raises binascii.Error |
674 | self.assertIsInstance(boom.exception.__context__, binascii.Error) |
675 | @@ -1171,7 +1171,7 @@ |
676 | with self.assertRaises(CorruptedSessionError) as boom: |
677 | obj_repr = copy.copy(self.good_repr) |
678 | obj_repr['metadata']['app_id'] = 1 |
679 | - self.resume_fn(self.session, obj_repr) |
680 | + self.resume_fn(self.session.metadata, obj_repr) |
681 | self.assertEqual( |
682 | str(boom.exception), |
683 | "Value of key 'app_id' is of incorrect type int") |
684 | @@ -1183,7 +1183,7 @@ |
685 | """ |
686 | obj_repr = copy.copy(self.good_repr) |
687 | obj_repr['metadata']['app_id'] = None |
688 | - self.resume_fn(self.session, obj_repr) |
689 | + self.resume_fn(self.session.metadata, obj_repr) |
690 | self.assertEqual(self.session.metadata.app_id, None) |
691 | |
692 | def test_restore_SessionState_metadata_restores_app_id(self): |
693 | @@ -1192,7 +1192,7 @@ |
694 | """ |
695 | obj_repr = copy.copy(self.good_repr) |
696 | obj_repr['metadata']['app_id'] = "id" |
697 | - self.resume_fn(self.session, obj_repr) |
698 | + self.resume_fn(self.session.metadata, obj_repr) |
699 | self.assertEqual(self.session.metadata.app_id, "id") |
700 | |
701 |
Very cool :) the refactoring part looks OK and the peek stuff is a clever use of session metadata (I guess as the code says, if we restored the session to look at the metadata it could possibly barf). THanks!