Implement exception holder

This commit is contained in:
taizan-hokouto
2020-10-29 01:39:07 +09:00
parent 499cf26fa8
commit 15a1d5c210
2 changed files with 52 additions and 21 deletions

View File

@@ -42,6 +42,10 @@ class PytchatCore:
topchat_only : bool topchat_only : bool
If True, get only top chat. If True, get only top chat.
hold_exception : bool [default:True]
If True, when exceptions occur, the exception is holded internally,
and can be raised by raise_for_status().
Attributes Attributes
--------- ---------
_is_alive : bool _is_alive : bool
@@ -56,7 +60,8 @@ class PytchatCore:
interruptable=True, interruptable=True,
force_replay=False, force_replay=False,
topchat_only=False, topchat_only=False,
logger=config.logger(__name__) hold_exception=True,
logger=config.logger(__name__),
): ):
self._video_id = extract_video_id(video_id) self._video_id = extract_video_id(video_id)
self.seektime = seektime self.seektime = seektime
@@ -66,12 +71,16 @@ class PytchatCore:
self.processor = processor self.processor = processor
self._is_alive = True self._is_alive = True
self._is_replay = force_replay self._is_replay = force_replay
self._parser = Parser(is_replay=self._is_replay) self._hold_exception = hold_exception
self._exception_holder = None
self._parser = Parser(
is_replay=self._is_replay,
exception_holder=self._exception_holder
)
self._first_fetch = True self._first_fetch = True
self._fetch_url = "live_chat/get_live_chat?continuation=" self._fetch_url = "live_chat/get_live_chat?continuation="
self._topchat_only = topchat_only self._topchat_only = topchat_only
self._logger = logger self._logger = logger
self.exception = None
if interruptable: if interruptable:
signal.signal(signal.SIGINT, lambda a, b: self.terminate()) signal.signal(signal.SIGINT, lambda a, b: self.terminate())
self._setup() self._setup()
@@ -108,13 +117,13 @@ class PytchatCore:
return chat_component return chat_component
except exceptions.ChatParseException as e: except exceptions.ChatParseException as e:
self._logger.debug(f"[{self._video_id}]{str(e)}") self._logger.debug(f"[{self._video_id}]{str(e)}")
raise self._raise_exception(e)
except (TypeError, json.JSONDecodeError): except (TypeError, json.JSONDecodeError) as e:
self._logger.error(f"{traceback.format_exc(limit=-1)}") self._logger.error(f"{traceback.format_exc(limit=-1)}")
raise self._raise_exception(e)
self._logger.debug(f"[{self._video_id}]finished fetching chat.") self._logger.debug(f"[{self._video_id}]finished fetching chat.")
raise exceptions.ChatDataFinished self._raise_exception(exceptions.ChatDataFinished)
def _get_contents(self, continuation, client, headers): def _get_contents(self, continuation, client, headers):
'''Get 'continuationContents' from livechat json. '''Get 'continuationContents' from livechat json.
@@ -167,7 +176,7 @@ class PytchatCore:
else: else:
self._logger.error(f"[{self._video_id}]" self._logger.error(f"[{self._video_id}]"
f"Exceeded retry count. Last error: {str(err)}") f"Exceeded retry count. Last error: {str(err)}")
raise exceptions.RetryExceedMaxCount() self._raise_exception(exceptions.RetryExceedMaxCount())
return livechat_json return livechat_json
def get(self): def get(self):
@@ -188,5 +197,11 @@ class PytchatCore:
self.processor.finalize() self.processor.finalize()
def raise_for_status(self): def raise_for_status(self):
if self.exception is not None: if self._exception_holder is not None:
raise self.exception raise self._exception_holder
def _raise_exception(self, exception: Exception = None):
self._is_alive = False
if self._hold_exception is False:
raise exception
self._exception_holder = exception

View File

@@ -8,15 +8,26 @@ from .. import exceptions
class Parser: class Parser:
'''
Parser of chat json.
Parameter
----------
is_replay : bool
__slots__ = ['is_replay'] exception_holder : Object [default:Npne]
The object holding exceptions.
This is passed from the parent livechat object.
'''
__slots__ = ['is_replay', 'exception_holder']
def __init__(self, is_replay): def __init__(self, is_replay, exception_holder=None):
self.is_replay = is_replay self.is_replay = is_replay
self.exception_holder = exception_holder
def get_contents(self, jsn): def get_contents(self, jsn):
if jsn is None: if jsn is None:
raise exceptions.IllegalFunctionCall('Called with none JSON object.') self.raise_exception(exceptions.IllegalFunctionCall('Called with none JSON object.'))
if jsn['response']['responseContext'].get('errors'): if jsn['response']['responseContext'].get('errors'):
raise exceptions.ResponseContextError( raise exceptions.ResponseContextError(
'The video_id would be wrong, or video is deleted or private.') 'The video_id would be wrong, or video is deleted or private.')
@@ -42,11 +53,11 @@ class Parser:
if contents is None: if contents is None:
'''Broadcasting end or cannot fetch chat stream''' '''Broadcasting end or cannot fetch chat stream'''
raise exceptions.NoContents('Chat data stream is empty.') self.raise_exception(exceptions.NoContents('Chat data stream is empty.'))
cont = contents['liveChatContinuation']['continuations'][0] cont = contents['liveChatContinuation']['continuations'][0]
if cont is None: if cont is None:
raise exceptions.NoContinuation('No Continuation') self.raise_exception(exceptions.NoContinuation('No Continuation'))
metadata = (cont.get('invalidationContinuationData') metadata = (cont.get('invalidationContinuationData')
or cont.get('timedContinuationData') or cont.get('timedContinuationData')
or cont.get('reloadContinuationData') or cont.get('reloadContinuationData')
@@ -54,13 +65,13 @@ class Parser:
) )
if metadata is None: if metadata is None:
if cont.get("playerSeekContinuationData"): if cont.get("playerSeekContinuationData"):
raise exceptions.ChatDataFinished('Finished chat data') self.raise_exception(exceptions.ChatDataFinished('Finished chat data'))
unknown = list(cont.keys())[0] unknown = list(cont.keys())[0]
if unknown: if unknown:
raise exceptions.ReceivedUnknownContinuation( self.raise_exception(exceptions.ReceivedUnknownContinuation(
f"Received unknown continuation type:{unknown}") f"Received unknown continuation type:{unknown}"))
else: else:
raise exceptions.FailedExtractContinuation('Cannot extract continuation data') self.raise_exception(exceptions.FailedExtractContinuation('Cannot extract continuation data'))
return self._create_data(metadata, contents) return self._create_data(metadata, contents)
def reload_continuation(self, contents): def reload_continuation(self, contents):
@@ -72,7 +83,7 @@ class Parser:
""" """
if contents is None: if contents is None:
'''Broadcasting end or cannot fetch chat stream''' '''Broadcasting end or cannot fetch chat stream'''
raise exceptions.NoContents('Chat data stream is empty.') self.raise_exception(exceptions.NoContents('Chat data stream is empty.'))
cont = contents['liveChatContinuation']['continuations'][0] cont = contents['liveChatContinuation']['continuations'][0]
if cont.get("liveChatReplayContinuationData"): if cont.get("liveChatReplayContinuationData"):
# chat data exist. # chat data exist.
@@ -81,7 +92,7 @@ class Parser:
init_cont = cont.get("playerSeekContinuationData") init_cont = cont.get("playerSeekContinuationData")
if init_cont: if init_cont:
return init_cont.get("continuation") return init_cont.get("continuation")
raise exceptions.ChatDataFinished('Finished chat data') self.raise_exception(exceptions.ChatDataFinished('Finished chat data'))
def _create_data(self, metadata, contents): def _create_data(self, metadata, contents):
actions = contents['liveChatContinuation'].get('actions') actions = contents['liveChatContinuation'].get('actions')
@@ -103,3 +114,8 @@ class Parser:
start = int(actions[0]["replayChatItemAction"]["videoOffsetTimeMsec"]) start = int(actions[0]["replayChatItemAction"]["videoOffsetTimeMsec"])
last = int(actions[-1]["replayChatItemAction"]["videoOffsetTimeMsec"]) last = int(actions[-1]["replayChatItemAction"]["videoOffsetTimeMsec"])
return (last - start) return (last - start)
def raise_exception(self, exception):
if self.exception_holder is None:
raise exception
self.exception_holder = exception