summaryrefslogtreecommitdiffstats
path: root/addons/metadata.tvshows.themoviedb.org.python/libs/debugger.py
diff options
context:
space:
mode:
Diffstat (limited to 'addons/metadata.tvshows.themoviedb.org.python/libs/debugger.py')
-rw-r--r--addons/metadata.tvshows.themoviedb.org.python/libs/debugger.py115
1 files changed, 115 insertions, 0 deletions
diff --git a/addons/metadata.tvshows.themoviedb.org.python/libs/debugger.py b/addons/metadata.tvshows.themoviedb.org.python/libs/debugger.py
new file mode 100644
index 0000000..6414722
--- /dev/null
+++ b/addons/metadata.tvshows.themoviedb.org.python/libs/debugger.py
@@ -0,0 +1,115 @@
+# -*- coding: UTF-8 -*-
+#
+# Copyright (C) 2020, Team Kodi
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+# pylint: disable=missing-docstring
+#
+# This is based on the metadata.tvmaze scrapper by Roman Miroshnychenko aka Roman V.M.
+
+"""
+Provides a context manager that writes extended debugging info
+in the Kodi log on unhandled exceptions
+"""
+from __future__ import absolute_import, unicode_literals
+
+import inspect
+from contextlib import contextmanager
+from platform import uname
+from pprint import pformat
+
+import xbmc
+
+from .utils import logger
+
+try:
+ from typing import Text, Generator, Callable, Dict, Any # pylint: disable=unused-import
+except ImportError:
+ pass
+
+
+def _format_vars(variables):
+ # type: (Dict[Text, Any]) -> Text
+ """
+ Format variables dictionary
+
+ :param variables: variables dict
+ :type variables: dict
+ :return: formatted string with sorted ``var = val`` pairs
+ :rtype: str
+ """
+ var_list = [(var, val) for var, val in variables.items()
+ if not (var.startswith('__') or var.endswith('__'))]
+ var_list.sort(key=lambda i: i[0])
+ lines = []
+ for var, val in var_list:
+ lines.append('{0} = {1}'.format(var, pformat(val)))
+ return '\n'.join(lines)
+
+
+@contextmanager
+def debug_exception(logger_func=logger.error):
+ # type: (Callable[[Text], None]) -> Generator[None]
+ """
+ Diagnostic helper context manager
+
+ It controls execution within its context and writes extended
+ diagnostic info to the Kodi log if an unhandled exception
+ happens within the context. The info includes the following items:
+
+ - System info
+ - Kodi version
+ - Module path.
+ - Code fragment where the exception has happened.
+ - Global variables.
+ - Local variables.
+
+ After logging the diagnostic info the exception is re-raised.
+
+ Example::
+
+ with debug_exception():
+ # Some risky code
+ raise RuntimeError('Fatal error!')
+
+ :param logger_func: logger function which must accept a single argument
+ which is a log message.
+ """
+ try:
+ yield
+ except Exception as exc:
+ frame_info = inspect.trace(5)[-1]
+ logger_func(
+ '*** Unhandled exception detected: {} {} ***'.format(type(exc), exc))
+ logger_func('*** Start diagnostic info ***')
+ logger_func('System info: {0}'.format(uname()))
+ logger_func('OS info: {0}'.format(
+ xbmc.getInfoLabel('System.OSVersionInfo')))
+ logger_func('Kodi version: {0}'.format(
+ xbmc.getInfoLabel('System.BuildVersion')))
+ logger_func('File: {0}'.format(frame_info[1]))
+ context = ''
+ if frame_info[4] is not None:
+ for i, line in enumerate(frame_info[4], frame_info[2] - frame_info[5]):
+ if i == frame_info[2]:
+ context += '{0}:>{1}'.format(str(i).rjust(5), line)
+ else:
+ context += '{0}: {1}'.format(str(i).rjust(5), line)
+ logger_func('Code context:\n' + context)
+ logger_func('Global variables:\n' +
+ _format_vars(frame_info[0].f_globals))
+ logger_func('Local variables:\n' +
+ _format_vars(frame_info[0].f_locals))
+ logger_func('**** End diagnostic info ****')
+ raise exc