Merge lp:~zyga/checkbox/session-peek-api into lp:checkbox

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
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.

Description of the change

933a1ed plainbox:session: add SessionStorage.id
5897758 plainbox:session: make SessionStorage.load_checkpoint() handle missing files
6e72202 plainbox:session: refactor _restore_SessionState_metadata()
08a3e28 plainbox:session: create EnvelopeUnpackMixIn
f300476 plainbox:session: add SessionPeekHelper

To post a comment you must log in.
Revision history for this message
Daniel Manrique (roadmr) wrote :

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!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'plainbox/plainbox/impl/session/__init__.py'
--- plainbox/plainbox/impl/session/__init__.py 2014-01-27 23:26:33 +0000
+++ plainbox/plainbox/impl/session/__init__.py 2014-04-30 19:25:48 +0000
@@ -79,6 +79,7 @@
79 'JobState',79 'JobState',
80 'SessionManager',80 'SessionManager',
81 'SessionMetaData',81 'SessionMetaData',
82 'SessionPeekHelper',
82 'SessionState',83 'SessionState',
83 'SessionStateLegacyAPI',84 'SessionStateLegacyAPI',
84 'SessionStorage',85 'SessionStorage',
@@ -91,7 +92,8 @@
91from plainbox.impl.session.jobs import UndesiredJobReadinessInhibitor92from plainbox.impl.session.jobs import UndesiredJobReadinessInhibitor
92from plainbox.impl.session.legacy import SessionStateLegacyAPI93from plainbox.impl.session.legacy import SessionStateLegacyAPI
93from plainbox.impl.session.manager import SessionManager94from plainbox.impl.session.manager import SessionManager
95from plainbox.impl.session.resume import SessionPeekHelper
96from plainbox.impl.session.state import SessionMetaData
94from plainbox.impl.session.state import SessionState97from plainbox.impl.session.state import SessionState
95from plainbox.impl.session.state import SessionMetaData
96from plainbox.impl.session.storage import SessionStorage98from plainbox.impl.session.storage import SessionStorage
97from plainbox.impl.session.storage import SessionStorageRepository99from plainbox.impl.session.storage import SessionStorageRepository
98100
=== modified file 'plainbox/plainbox/impl/session/resume.py'
--- plainbox/plainbox/impl/session/resume.py 2014-03-17 11:30:19 +0000
+++ plainbox/plainbox/impl/session/resume.py 2014-04-30 19:25:48 +0000
@@ -56,6 +56,7 @@
56from plainbox.impl.result import IOLogRecord56from plainbox.impl.result import IOLogRecord
57from plainbox.impl.result import MemoryJobResult57from plainbox.impl.result import MemoryJobResult
58from plainbox.impl.secure.qualifiers import SimpleQualifier58from plainbox.impl.secure.qualifiers import SimpleQualifier
59from plainbox.impl.session.state import SessionMetaData
59from plainbox.impl.session.state import SessionState60from plainbox.impl.session.state import SessionState
6061
61logger = logging.getLogger("plainbox.session.resume")62logger = logging.getLogger("plainbox.session.resume")
@@ -92,7 +93,78 @@
92 """93 """
9394
9495
95class SessionResumeHelper:96class EnvelopeUnpackMixIn:
97 """
98 A mix-in class capable of unpacking the envelope of the session storage
99 """
100
101 def unpack_envelope(self, data):
102 """
103 Unpack the binary envelope and get access to a JSON object
104
105 :param data:
106 Bytes representing the dormant session
107 :returns:
108 the JSON representation of a session stored in the envelope
109 :raises CorruptedSessionError:
110 if the representation of the session is corrupted in any way
111 """
112 try:
113 data = gzip.decompress(data)
114 except IOError:
115 raise CorruptedSessionError(_("Cannot decompress session data"))
116 try:
117 text = data.decode("UTF-8")
118 except UnicodeDecodeError:
119 raise CorruptedSessionError(_("Cannot decode session text"))
120 try:
121 return json.loads(text)
122 except ValueError:
123 raise CorruptedSessionError(_("Cannot interpret session JSON"))
124
125
126class SessionPeekHelper(EnvelopeUnpackMixIn):
127
128 def peek(self, data):
129 """
130 Peek at the meta-data of a dormant session.
131
132 :param data:
133 Bytes representing the dormant session
134 :returns:
135 a SessionMetaData object
136 :raises CorruptedSessionError:
137 if the representation of the session is corrupted in any way
138 :raises IncompatibleSessionError:
139 if session serialization format is not supported
140 """
141 json_repr = self.unpack_envelope(data)
142 return self._peek_json(json_repr)
143
144 def _peek_json(self, json_repr):
145 """
146 Resume a SessionMetaData object from the JSON representation.
147
148 This method is called by :meth:`peek()` after the initial envelope
149 and parsing is done. The only error conditions that can happen
150 are related to semantic incompatibilities or corrupted internal state.
151 """
152 logger.debug(_("Peeking at json... (see below)"))
153 logger.debug(json.dumps(json_repr, indent=4))
154 _validate(json_repr, value_type=dict)
155 version = _validate(json_repr, key="version", choice=[1])
156 if version == 1:
157 return SessionPeekHelper1().peek_json(json_repr)
158 elif version == 2:
159 return SessionPeekHelper2().peek_json(json_repr)
160 elif version == 3:
161 return SessionPeekHelper3().peek_json(json_repr)
162 else:
163 raise IncompatibleSessionError(
164 _("Unsupported version {}").format(version))
165
166
167class SessionResumeHelper(EnvelopeUnpackMixIn):
96 """168 """
97 Helper class for implementing session resume feature.169 Helper class for implementing session resume feature.
98170
@@ -139,18 +211,7 @@
139 :raises IncompatibleJobError:211 :raises IncompatibleJobError:
140 if serialized jobs are not the same as current jobs212 if serialized jobs are not the same as current jobs
141 """213 """
142 try:214 json_repr = self.unpack_envelope(data)
143 data = gzip.decompress(data)
144 except IOError:
145 raise CorruptedSessionError(_("Cannot decompress session data"))
146 try:
147 text = data.decode("UTF-8")
148 except UnicodeDecodeError:
149 raise CorruptedSessionError(_("Cannot decode session text"))
150 try:
151 json_repr = json.loads(text)
152 except ValueError:
153 raise CorruptedSessionError(_("Cannot interpret session JSON"))
154 return self._resume_json(json_repr, early_cb)215 return self._resume_json(json_repr, early_cb)
155216
156 def _resume_json(self, json_repr, early_cb=None):217 def _resume_json(self, json_repr, early_cb=None):
@@ -195,7 +256,143 @@
195 return job.id not in self._retain_id_set256 return job.id not in self._retain_id_set
196257
197258
198class SessionResumeHelper1:259class MetaDataHelper1MixIn:
260
261 @classmethod
262 def _restore_SessionState_metadata(cls, metadata, session_repr):
263 """
264 Extract meta-data information from the representation of the session
265 and set it in the given session object
266 """
267 # Get the representation of the meta-data
268 metadata_repr = _validate(
269 session_repr, key='metadata', value_type=dict)
270 # Set each bit back to the session
271 metadata.title = _validate(
272 metadata_repr, key='title', value_type=str, value_none=True)
273 metadata.flags = set([
274 _validate(
275 flag, value_type=str,
276 value_type_msg=_("Each flag must be a string"))
277 for flag in _validate(
278 metadata_repr, key='flags', value_type=list)])
279 metadata.running_job_name = _validate(
280 metadata_repr, key='running_job_name', value_type=str,
281 value_none=True)
282
283
284class MetaDataHelper2MixIn(MetaDataHelper1MixIn):
285
286 @classmethod
287 def _restore_SessionState_metadata(cls, metadata, session_repr):
288 """
289 Extract meta-data information from the representation of the session
290 and set it in the given session object
291 """
292 super()._restore_SessionState_metadata(metadata, session_repr)
293 # Get the representation of the meta-data
294 metadata_repr = _validate(
295 session_repr, key='metadata', value_type=dict)
296 app_blob = _validate(
297 metadata_repr, key='app_blob', value_type=str,
298 value_none=True)
299 if app_blob is not None:
300 try:
301 app_blob = app_blob.encode("ASCII")
302 except UnicodeEncodeError:
303 # TRANSLATORS: please don't translate app_blob
304 raise CorruptedSessionError(_("app_blob is not ASCII"))
305 try:
306 app_blob = base64.standard_b64decode(app_blob)
307 except binascii.Error:
308 # TRANSLATORS: please don't translate app_blob
309 raise CorruptedSessionError(_("Cannot base64 decode app_blob"))
310 metadata.app_blob = app_blob
311
312
313class MetaDataHelper3MixIn(MetaDataHelper2MixIn):
314
315 @classmethod
316 def _restore_SessionState_metadata(cls, metadata, session_repr):
317 """
318 Extract meta-data information from the representation of the session
319 and set it in the given session object
320 """
321 super()._restore_SessionState_metadata(metadata, session_repr)
322 # Get the representation of the meta-data
323 metadata_repr = _validate(
324 session_repr, key='metadata', value_type=dict)
325 metadata.app_id = _validate(
326 metadata_repr, key='app_id', value_type=str,
327 value_none=True)
328
329
330class SessionPeekHelper1(MetaDataHelper1MixIn):
331 """
332 Helper class for implementing session peek feature
333
334 This class works with data constructed by
335 :class:`~plainbox.impl.session.suspend.SessionSuspendHelper1` which has
336 been pre-processed by :class:`SessionPeekHelper` (to strip the initial
337 envelope).
338
339 The only goal of this class is to reconstruct session state meta-data.
340 """
341
342 def peek_json(self, json_repr):
343 """
344 Resume a SessionState object from the JSON representation.
345
346 This method is called by :meth:`peek()` after the initial envelope and
347 parsing is done. The only error conditions that can happen are related
348 to semantic incompatibilities or corrupted internal state.
349 """
350 _validate(json_repr, key="version", choice=[1])
351 session_repr = _validate(json_repr, key='session', value_type=dict)
352 metadata = SessionMetaData()
353 self._restore_SessionState_metadata(metadata, session_repr)
354 return metadata
355
356 def _build_SessionState(self, session_repr, early_cb=None):
357 """
358 Reconstruct the session state object.
359
360 This method creates a fresh SessionState instance and restores
361 jobs, results, meta-data and desired job list using helper methods.
362 """
363 logger.debug(_("Starting to restore metadata..."))
364 metadata = SessionMetaData()
365 self._peek_SessionState_metadata(metadata, session_repr)
366 return metadata
367
368
369class SessionPeekHelper2(MetaDataHelper2MixIn, SessionPeekHelper1):
370 """
371 Helper class for implementing session peek feature
372
373 This class works with data constructed by
374 :class:`~plainbox.impl.session.suspend.SessionSuspendHelper1` which has
375 been pre-processed by :class:`SessionPeekHelper` (to strip the initial
376 envelope).
377
378 The only goal of this class is to reconstruct session state meta-data.
379 """
380
381
382class SessionPeekHelper3(MetaDataHelper3MixIn, SessionPeekHelper2):
383 """
384 Helper class for implementing session peek feature
385
386 This class works with data constructed by
387 :class:`~plainbox.impl.session.suspend.SessionSuspendHelper1` which has
388 been pre-processed by :class:`SessionPeekHelper` (to strip the initial
389 envelope).
390
391 The only goal of this class is to reconstruct session state meta-data.
392 """
393
394
395class SessionResumeHelper1(MetaDataHelper1MixIn):
199 """396 """
200 Helper class for implementing session resume feature397 Helper class for implementing session resume feature
201398
@@ -257,7 +454,8 @@
257 _("Starting to restore jobs and results to %r..."), session)454 _("Starting to restore jobs and results to %r..."), session)
258 self._restore_SessionState_jobs_and_results(session, session_repr)455 self._restore_SessionState_jobs_and_results(session, session_repr)
259 logger.debug(_("Starting to restore metadata..."))456 logger.debug(_("Starting to restore metadata..."))
260 self._restore_SessionState_metadata(session, session_repr)457 self._restore_SessionState_metadata(session.metadata, session_repr)
458 logger.debug(_("restored metadata %r"), session.metadata)
261 logger.debug(_("Starting to restore desired job list..."))459 logger.debug(_("Starting to restore desired job list..."))
262 self._restore_SessionState_desired_job_list(session, session_repr)460 self._restore_SessionState_desired_job_list(session, session_repr)
263 logger.debug(_("Starting to restore job list..."))461 logger.debug(_("Starting to restore job list..."))
@@ -372,29 +570,6 @@
372 session.update_job_result(job, result_list[-1])570 session.update_job_result(job, result_list[-1])
373571
374 @classmethod572 @classmethod
375 def _restore_SessionState_metadata(cls, session, session_repr):
376 """
377 Extract meta-data information from the representation of the session
378 and set it in the given session object
379 """
380 # Get the representation of the meta-data
381 metadata_repr = _validate(
382 session_repr, key='metadata', value_type=dict)
383 # Set each bit back to the session
384 session.metadata.title = _validate(
385 metadata_repr, key='title', value_type=str, value_none=True)
386 session.metadata.flags = set([
387 _validate(
388 flag, value_type=str,
389 value_type_msg=_("Each flag must be a string"))
390 for flag in _validate(
391 metadata_repr, key='flags', value_type=list)])
392 session.metadata.running_job_name = _validate(
393 metadata_repr, key='running_job_name', value_type=str,
394 value_none=True)
395 logger.debug(_("restored metadata %r"), session.metadata)
396
397 @classmethod
398 def _restore_SessionState_desired_job_list(cls, session, session_repr):573 def _restore_SessionState_desired_job_list(cls, session, session_repr):
399 """574 """
400 Extract the representation of desired_job_list from the session and575 Extract the representation of desired_job_list from the session and
@@ -512,6 +687,44 @@
512 return IOLogRecord(delay, stream_name, data)687 return IOLogRecord(delay, stream_name, data)
513688
514689
690class SessionResumeHelper2(MetaDataHelper2MixIn, SessionResumeHelper1):
691 """
692 Helper class for implementing session resume feature
693
694 This class works with data constructed by
695 :class:`~plainbox.impl.session.suspend.SessionSuspendHelper2` which has
696 been pre-processed by :class:`SessionResumeHelper` (to strip the initial
697 envelope).
698
699 Due to the constraints of what can be represented in a suspended session,
700 this class cannot work in isolation. It must operate with a list of know
701 jobs.
702
703 Since (most of the) jobs are being provided externally (as they represent
704 the non-serialized parts of checkbox or other job providers) several
705 failure modes are possible. Those are documented in :meth:`resume()`
706 """
707
708
709class SessionResumeHelper3(MetaDataHelper3MixIn, SessionResumeHelper2):
710 """
711 Helper class for implementing session resume feature
712
713 This class works with data constructed by
714 :class:`~plainbox.impl.session.suspend.SessionSuspendHelper3` which has
715 been pre-processed by :class:`SessionResumeHelper` (to strip the initial
716 envelope).
717
718 Due to the constraints of what can be represented in a suspended session,
719 this class cannot work in isolation. It must operate with a list of know
720 jobs.
721
722 Since (most of the) jobs are being provided externally (as they represent
723 the non-serialized parts of checkbox or other job providers) several
724 failure modes are possible. Those are documented in :meth:`resume()`
725 """
726
727
515def _validate(obj, **flags):728def _validate(obj, **flags):
516 """729 """
517 Multi-purpose extraction and validation function.730 Multi-purpose extraction and validation function.
@@ -556,120 +769,3 @@
556 obj_name, value_choice))769 obj_name, value_choice))
557 raise CorruptedSessionError(error_msg)770 raise CorruptedSessionError(error_msg)
558 return value771 return value
559
560
561class SessionResumeHelper2(SessionResumeHelper1):
562 """
563 Helper class for implementing session resume feature
564
565 This class works with data constructed by
566 :class:`~plainbox.impl.session.suspend.SessionSuspendHelper2` which has
567 been pre-processed by :class:`SessionResumeHelper` (to strip the initial
568 envelope).
569
570 Due to the constraints of what can be represented in a suspended session,
571 this class cannot work in isolation. It must operate with a list of know
572 jobs.
573
574 Since (most of the) jobs are being provided externally (as they represent
575 the non-serialized parts of checkbox or other job providers) several
576 failure modes are possible. Those are documented in :meth:`resume()`
577 """
578
579 @classmethod
580 def _restore_SessionState_metadata(cls, session, session_repr):
581 """
582 Extract meta-data information from the representation of the session
583 and set it in the given session object
584 """
585 # Get the representation of the meta-data
586 metadata_repr = _validate(
587 session_repr, key='metadata', value_type=dict)
588 # Set each bit back to the session
589 session.metadata.title = _validate(
590 metadata_repr, key='title', value_type=str, value_none=True)
591 session.metadata.flags = set([
592 _validate(
593 flag, value_type=str,
594 value_type_msg=_("Each flag must be a string"))
595 for flag in _validate(
596 metadata_repr, key='flags', value_type=list)])
597 session.metadata.running_job_name = _validate(
598 metadata_repr, key='running_job_name', value_type=str,
599 value_none=True)
600 app_blob = _validate(
601 metadata_repr, key='app_blob', value_type=str,
602 value_none=True)
603 if app_blob is not None:
604 try:
605 app_blob = app_blob.encode("ASCII")
606 except UnicodeEncodeError:
607 # TRANSLATORS: please don't translate app_blob
608 raise CorruptedSessionError(_("app_blob is not ASCII"))
609 try:
610 app_blob = base64.standard_b64decode(app_blob)
611 except binascii.Error:
612 # TRANSLATORS: please don't translate app_blob
613 raise CorruptedSessionError(_("Cannot base64 decode app_blob"))
614 session.metadata.app_blob = app_blob
615 logger.debug(_("restored metadata %r"), session.metadata)
616
617
618class SessionResumeHelper3(SessionResumeHelper2):
619 """
620 Helper class for implementing session resume feature
621
622 This class works with data constructed by
623 :class:`~plainbox.impl.session.suspend.SessionSuspendHelper3` which has
624 been pre-processed by :class:`SessionResumeHelper` (to strip the initial
625 envelope).
626
627 Due to the constraints of what can be represented in a suspended session,
628 this class cannot work in isolation. It must operate with a list of know
629 jobs.
630
631 Since (most of the) jobs are being provided externally (as they represent
632 the non-serialized parts of checkbox or other job providers) several
633 failure modes are possible. Those are documented in :meth:`resume()`
634 """
635
636 @classmethod
637 def _restore_SessionState_metadata(cls, session, session_repr):
638 """
639 Extract meta-data information from the representation of the session
640 and set it in the given session object
641 """
642 # Get the representation of the meta-data
643 metadata_repr = _validate(
644 session_repr, key='metadata', value_type=dict)
645 # Set each bit back to the session
646 session.metadata.title = _validate(
647 metadata_repr, key='title', value_type=str, value_none=True)
648 session.metadata.flags = set([
649 _validate(
650 flag, value_type=str,
651 value_type_msg=_("Each flag must be a string"))
652 for flag in _validate(
653 metadata_repr, key='flags', value_type=list)])
654 session.metadata.running_job_name = _validate(
655 metadata_repr, key='running_job_name', value_type=str,
656 value_none=True)
657 app_blob = _validate(
658 metadata_repr, key='app_blob', value_type=str,
659 value_none=True)
660 if app_blob is not None:
661 try:
662 app_blob = app_blob.encode("ASCII")
663 except UnicodeEncodeError:
664 # TRANSLATORS: please don't translate app_blob
665 raise CorruptedSessionError(_("app_blob is not ASCII"))
666 try:
667 app_blob = base64.standard_b64decode(app_blob)
668 except binascii.Error:
669 # TRANSLATORS: please don't translate app_blob
670 raise CorruptedSessionError(_("Cannot base64 decode app_blob"))
671 session.metadata.app_blob = app_blob
672 session.metadata.app_id = _validate(
673 metadata_repr, key='app_id', value_type=str,
674 value_none=True)
675 logger.debug(_("restored metadata %r"), session.metadata)
676772
=== modified file 'plainbox/plainbox/impl/session/storage.py'
--- plainbox/plainbox/impl/session/storage.py 2014-02-20 21:43:55 +0000
+++ plainbox/plainbox/impl/session/storage.py 2014-04-30 19:25:48 +0000
@@ -206,6 +206,13 @@
206 return self._location206 return self._location
207207
208 @property208 @property
209 def id(self):
210 """
211 identifier of the session storage (name of the random directory)
212 """
213 return os.path.splitext(os.path.basename(self.location))[0]
214
215 @property
209 def session_file(self):216 def session_file(self):
210 """217 """
211 pathname of the session state file218 pathname of the session state file
@@ -404,6 +411,11 @@
404 # Close the session file411 # Close the session file
405 logger.debug(_("Closed descriptor %d"), session_fd)412 logger.debug(_("Closed descriptor %d"), session_fd)
406 os.close(session_fd)413 os.close(session_fd)
414 except IOError as exc:
415 if exc.errno == errno.ENOENT:
416 # Treat lack of 'session' file as an empty file
417 return b''
418 raise
407 finally:419 finally:
408 # Close the location directory420 # Close the location directory
409 logger.debug(_("Closed descriptor %d"), location_fd)421 logger.debug(_("Closed descriptor %d"), location_fd)
@@ -427,6 +439,11 @@
427 finally:439 finally:
428 # Close the session file440 # Close the session file
429 os.close(session_fd)441 os.close(session_fd)
442 except IOError as exc:
443 if exc.errno == errno.ENOENT:
444 # Treat lack of 'session' file as an empty file
445 return b''
446 raise
430 finally:447 finally:
431 # Close the location directory448 # Close the location directory
432 os.close(location_fd)449 os.close(location_fd)
433450
=== modified file 'plainbox/plainbox/impl/session/test_resume.py'
--- plainbox/plainbox/impl/session/test_resume.py 2014-03-26 20:01:03 +0000
+++ plainbox/plainbox/impl/session/test_resume.py 2014-04-30 19:25:48 +0000
@@ -957,7 +957,7 @@
957 """957 """
958 with self.assertRaises(CorruptedSessionError) as boom:958 with self.assertRaises(CorruptedSessionError) as boom:
959 self.good_repr['metadata'] = 1959 self.good_repr['metadata'] = 1
960 self.resume_fn(self.session, self.good_repr)960 self.resume_fn(self.session.metadata, self.good_repr)
961 self.assertEqual(961 self.assertEqual(
962 str(boom.exception),962 str(boom.exception),
963 "Value of key 'metadata' is of incorrect type int")963 "Value of key 'metadata' is of incorrect type int")
@@ -969,7 +969,7 @@
969 """969 """
970 with self.assertRaises(CorruptedSessionError) as boom:970 with self.assertRaises(CorruptedSessionError) as boom:
971 self.good_repr['metadata']['title'] = 1971 self.good_repr['metadata']['title'] = 1
972 self.resume_fn(self.session, self.good_repr)972 self.resume_fn(self.session.metadata, self.good_repr)
973 self.assertEqual(973 self.assertEqual(
974 str(boom.exception),974 str(boom.exception),
975 "Value of key 'title' is of incorrect type int")975 "Value of key 'title' is of incorrect type int")
@@ -980,7 +980,7 @@
980 ``title`` to be None980 ``title`` to be None
981 """981 """
982 self.good_repr['metadata']['title'] = None982 self.good_repr['metadata']['title'] = None
983 self.resume_fn(self.session, self.good_repr)983 self.resume_fn(self.session.metadata, self.good_repr)
984 self.assertEqual(self.session.metadata.title, None)984 self.assertEqual(self.session.metadata.title, None)
985985
986 def test_restore_SessionState_metadata_restores_title(self):986 def test_restore_SessionState_metadata_restores_title(self):
@@ -988,7 +988,7 @@
988 verify that _restore_SessionState_metadata() restores ``title``988 verify that _restore_SessionState_metadata() restores ``title``
989 """989 """
990 self.good_repr['metadata']['title'] = "a title"990 self.good_repr['metadata']['title'] = "a title"
991 self.resume_fn(self.session, self.good_repr)991 self.resume_fn(self.session.metadata, self.good_repr)
992 self.assertEqual(self.session.metadata.title, "a title")992 self.assertEqual(self.session.metadata.title, "a title")
993993
994 def test_restore_SessionState_metadata_checks_flags_type(self):994 def test_restore_SessionState_metadata_checks_flags_type(self):
@@ -998,7 +998,7 @@
998 """998 """
999 with self.assertRaises(CorruptedSessionError) as boom:999 with self.assertRaises(CorruptedSessionError) as boom:
1000 self.good_repr['metadata']['flags'] = 11000 self.good_repr['metadata']['flags'] = 1
1001 self.resume_fn(self.session, self.good_repr)1001 self.resume_fn(self.session.metadata, self.good_repr)
1002 self.assertEqual(1002 self.assertEqual(
1003 str(boom.exception),1003 str(boom.exception),
1004 "Value of key 'flags' is of incorrect type int")1004 "Value of key 'flags' is of incorrect type int")
@@ -1010,7 +1010,7 @@
1010 """1010 """
1011 with self.assertRaises(CorruptedSessionError) as boom:1011 with self.assertRaises(CorruptedSessionError) as boom:
1012 self.good_repr['metadata']['flags'] = None1012 self.good_repr['metadata']['flags'] = None
1013 self.resume_fn(self.session, self.good_repr)1013 self.resume_fn(self.session.metadata, self.good_repr)
1014 self.assertEqual(1014 self.assertEqual(
1015 str(boom.exception),1015 str(boom.exception),
1016 "Value of key 'flags' cannot be None")1016 "Value of key 'flags' cannot be None")
@@ -1022,7 +1022,7 @@
1022 """1022 """
1023 with self.assertRaises(CorruptedSessionError) as boom:1023 with self.assertRaises(CorruptedSessionError) as boom:
1024 self.good_repr['metadata']['flags'] = [1]1024 self.good_repr['metadata']['flags'] = [1]
1025 self.resume_fn(self.session, self.good_repr)1025 self.resume_fn(self.session.metadata, self.good_repr)
1026 self.assertEqual(1026 self.assertEqual(
1027 str(boom.exception),1027 str(boom.exception),
1028 "Each flag must be a string")1028 "Each flag must be a string")
@@ -1032,7 +1032,7 @@
1032 verify that _restore_SessionState_metadata() restores ``flags``1032 verify that _restore_SessionState_metadata() restores ``flags``
1033 """1033 """
1034 self.good_repr['metadata']['flags'] = ["flag1", "flag2"]1034 self.good_repr['metadata']['flags'] = ["flag1", "flag2"]
1035 self.resume_fn(self.session, self.good_repr)1035 self.resume_fn(self.session.metadata, self.good_repr)
1036 self.assertEqual(self.session.metadata.flags, set(['flag1', 'flag2']))1036 self.assertEqual(self.session.metadata.flags, set(['flag1', 'flag2']))
10371037
1038 def test_restore_SessionState_metadata_checks_running_job_name_type(self):1038 def test_restore_SessionState_metadata_checks_running_job_name_type(self):
@@ -1042,7 +1042,7 @@
1042 """1042 """
1043 with self.assertRaises(CorruptedSessionError) as boom:1043 with self.assertRaises(CorruptedSessionError) as boom:
1044 self.good_repr['metadata']['running_job_name'] = 11044 self.good_repr['metadata']['running_job_name'] = 1
1045 self.resume_fn(self.session, self.good_repr)1045 self.resume_fn(self.session.metadata, self.good_repr)
1046 self.assertEqual(1046 self.assertEqual(
1047 str(boom.exception),1047 str(boom.exception),
1048 "Value of key 'running_job_name' is of incorrect type int")1048 "Value of key 'running_job_name' is of incorrect type int")
@@ -1053,7 +1053,7 @@
1053 ``running_job_name`` to be None1053 ``running_job_name`` to be None
1054 """1054 """
1055 self.good_repr['metadata']['running_job_name'] = None1055 self.good_repr['metadata']['running_job_name'] = None
1056 self.resume_fn(self.session, self.good_repr)1056 self.resume_fn(self.session.metadata, self.good_repr)
1057 self.assertEqual(self.session.metadata.running_job_name, None)1057 self.assertEqual(self.session.metadata.running_job_name, None)
10581058
1059 def test_restore_SessionState_metadata_restores_running_job_name(self):1059 def test_restore_SessionState_metadata_restores_running_job_name(self):
@@ -1062,7 +1062,7 @@
1062 the value of ``running_job_name``1062 the value of ``running_job_name``
1063 """1063 """
1064 self.good_repr['metadata']['running_job_name'] = "a job"1064 self.good_repr['metadata']['running_job_name'] = "a job"
1065 self.resume_fn(self.session, self.good_repr)1065 self.resume_fn(self.session.metadata, self.good_repr)
1066 self.assertEqual(self.session.metadata.running_job_name, "a job")1066 self.assertEqual(self.session.metadata.running_job_name, "a job")
10671067
10681068
@@ -1093,7 +1093,7 @@
1093 with self.assertRaises(CorruptedSessionError) as boom:1093 with self.assertRaises(CorruptedSessionError) as boom:
1094 obj_repr = copy.copy(self.good_repr)1094 obj_repr = copy.copy(self.good_repr)
1095 obj_repr['metadata']['app_blob'] = 11095 obj_repr['metadata']['app_blob'] = 1
1096 self.resume_fn(self.session, obj_repr)1096 self.resume_fn(self.session.metadata, obj_repr)
1097 self.assertEqual(1097 self.assertEqual(
1098 str(boom.exception),1098 str(boom.exception),
1099 "Value of key 'app_blob' is of incorrect type int")1099 "Value of key 'app_blob' is of incorrect type int")
@@ -1105,7 +1105,7 @@
1105 """1105 """
1106 obj_repr = copy.copy(self.good_repr)1106 obj_repr = copy.copy(self.good_repr)
1107 obj_repr['metadata']['app_blob'] = None1107 obj_repr['metadata']['app_blob'] = None
1108 self.resume_fn(self.session, obj_repr)1108 self.resume_fn(self.session.metadata, obj_repr)
1109 self.assertEqual(self.session.metadata.app_blob, None)1109 self.assertEqual(self.session.metadata.app_blob, None)
11101110
1111 def test_restore_SessionState_metadata_restores_app_blob(self):1111 def test_restore_SessionState_metadata_restores_app_blob(self):
@@ -1114,7 +1114,7 @@
1114 """1114 """
1115 obj_repr = copy.copy(self.good_repr)1115 obj_repr = copy.copy(self.good_repr)
1116 obj_repr['metadata']['app_blob'] = "YmxvYg=="1116 obj_repr['metadata']['app_blob'] = "YmxvYg=="
1117 self.resume_fn(self.session, obj_repr)1117 self.resume_fn(self.session.metadata, obj_repr)
1118 self.assertEqual(self.session.metadata.app_blob, b"blob")1118 self.assertEqual(self.session.metadata.app_blob, b"blob")
11191119
1120 def test_restore_SessionState_metadata_non_ascii_app_blob(self):1120 def test_restore_SessionState_metadata_non_ascii_app_blob(self):
@@ -1125,7 +1125,7 @@
1125 with self.assertRaises(CorruptedSessionError) as boom:1125 with self.assertRaises(CorruptedSessionError) as boom:
1126 obj_repr = copy.copy(self.good_repr)1126 obj_repr = copy.copy(self.good_repr)
1127 obj_repr['metadata']['app_blob'] = '\uFFFD'1127 obj_repr['metadata']['app_blob'] = '\uFFFD'
1128 self.resume_fn(self.session, obj_repr)1128 self.resume_fn(self.session.metadata, obj_repr)
1129 self.assertEqual(str(boom.exception), "app_blob is not ASCII")1129 self.assertEqual(str(boom.exception), "app_blob is not ASCII")
1130 self.assertIsInstance(boom.exception.__context__, UnicodeEncodeError)1130 self.assertIsInstance(boom.exception.__context__, UnicodeEncodeError)
11311131
@@ -1137,7 +1137,7 @@
1137 with self.assertRaises(CorruptedSessionError) as boom:1137 with self.assertRaises(CorruptedSessionError) as boom:
1138 obj_repr = copy.copy(self.good_repr)1138 obj_repr = copy.copy(self.good_repr)
1139 obj_repr['metadata']['app_blob'] = '==broken'1139 obj_repr['metadata']['app_blob'] = '==broken'
1140 self.resume_fn(self.session, obj_repr)1140 self.resume_fn(self.session.metadata, obj_repr)
1141 self.assertEqual(str(boom.exception), "Cannot base64 decode app_blob")1141 self.assertEqual(str(boom.exception), "Cannot base64 decode app_blob")
1142 # base64.standard_b64decode() raises binascii.Error1142 # base64.standard_b64decode() raises binascii.Error
1143 self.assertIsInstance(boom.exception.__context__, binascii.Error)1143 self.assertIsInstance(boom.exception.__context__, binascii.Error)
@@ -1171,7 +1171,7 @@
1171 with self.assertRaises(CorruptedSessionError) as boom:1171 with self.assertRaises(CorruptedSessionError) as boom:
1172 obj_repr = copy.copy(self.good_repr)1172 obj_repr = copy.copy(self.good_repr)
1173 obj_repr['metadata']['app_id'] = 11173 obj_repr['metadata']['app_id'] = 1
1174 self.resume_fn(self.session, obj_repr)1174 self.resume_fn(self.session.metadata, obj_repr)
1175 self.assertEqual(1175 self.assertEqual(
1176 str(boom.exception),1176 str(boom.exception),
1177 "Value of key 'app_id' is of incorrect type int")1177 "Value of key 'app_id' is of incorrect type int")
@@ -1183,7 +1183,7 @@
1183 """1183 """
1184 obj_repr = copy.copy(self.good_repr)1184 obj_repr = copy.copy(self.good_repr)
1185 obj_repr['metadata']['app_id'] = None1185 obj_repr['metadata']['app_id'] = None
1186 self.resume_fn(self.session, obj_repr)1186 self.resume_fn(self.session.metadata, obj_repr)
1187 self.assertEqual(self.session.metadata.app_id, None)1187 self.assertEqual(self.session.metadata.app_id, None)
11881188
1189 def test_restore_SessionState_metadata_restores_app_id(self):1189 def test_restore_SessionState_metadata_restores_app_id(self):
@@ -1192,7 +1192,7 @@
1192 """1192 """
1193 obj_repr = copy.copy(self.good_repr)1193 obj_repr = copy.copy(self.good_repr)
1194 obj_repr['metadata']['app_id'] = "id"1194 obj_repr['metadata']['app_id'] = "id"
1195 self.resume_fn(self.session, obj_repr)1195 self.resume_fn(self.session.metadata, obj_repr)
1196 self.assertEqual(self.session.metadata.app_id, "id")1196 self.assertEqual(self.session.metadata.app_id, "id")
11971197
11981198

Subscribers

People subscribed via source and target branches