Пример #1
0
 def start_services(self):
     """Start the background services"""
     self.dial_srv_instance.server_activate()
     self.dial_srv_thread.start()
     LOG.info('[DialServer] service started')
     self.ssdp_udp_srv_thread.start()
     LOG.info('[SSDPUDPServer] service started')
Пример #2
0
def reporthook(num_blocks, block_size, file_size, dlg, start_time, filename):
    try:
        percent = min(num_blocks * block_size * 100 / file_size, 100)
        currently_downloaded = float(num_blocks) * block_size / (1024 * 1024)
        kbps_speed = num_blocks * block_size / (time.time() - start_time)
        eta = 0
        if kbps_speed > 0:
            eta = (file_size - num_blocks * block_size) / kbps_speed
            if eta < 0:
                eta = 0
        kbps_speed = kbps_speed / 1024
        eta = divmod(eta, 60)
        file_size_mb = float(file_size) / (1024 * 1024)
        status = '{:.02f} MB of {:.02f} MB '.format(currently_downloaded,
                                                    file_size_mb)
        status += '{} {:.02f} Kb/s '.format(get_local_string(30501),
                                            kbps_speed)
        status += '{} {:02d}:{:02d}'.format(get_local_string(30502),
                                            int(eta[0]), int(eta[1]))
        dlg.update(
            int(percent), '{}[CR]{}[CR]{}'.format(get_local_string(30500),
                                                  filename, status))
    except Exception as exc:
        LOG.error('[download_file] reporthook raised an error: {}', exc)
        dlg.update(100)
    if dlg.iscanceled():
        raise InterruptedError
Пример #3
0
def register_internal_apps(kodi_interface):
    """Register the internal DIAL applications based on the installed Kodi add-ons"""
    # The DIAL apps will be loaded from "resources/lib/apps" sub-folders
    directory_base = file_ops.join_folders_paths(G.ADDON_DATA_PATH,
                                                 'resources/lib/apps')
    dirs, _ = file_ops.list_dir(directory_base)
    MUTEX.acquire()
    for dir_name in dirs:
        try:
            if dir_name.startswith('_') or dir_name == 'dial_app_test':
                continue
            class_file_path = file_ops.join_folders_paths(
                directory_base, dir_name, dir_name + '.py')
            if not file_ops.file_exists(class_file_path):
                LOG.error(
                    'register_internal_apps: missing module file {}.py on {} folder',
                    dir_name, dir_name)
                continue
            # Load the internal module
            loaded_module = importlib.import_module('resources.lib.apps.' +
                                                    dir_name + '.' + dir_name)
            app_class = getattr(loaded_module, 'DialApp', None)
            if app_class is None:
                LOGGER.error(
                    'register_internal_apps: "DialApp" class not found in {}.py file',
                    dir_name)
                continue
            register_app(app_class, kodi_interface)
        except Exception:
            LOGGER.error(
                'register_internal_apps: could not import the DIAL app from {} folder',
                dir_name)
            import traceback
            LOG.error(traceback.format_exc())
    MUTEX.release()
Пример #4
0
 def init_globals(self):
     """Initialized globally used module variables. Needs to be called at start of each plugin instance!"""
     # xbmcaddon.Addon must be created at every instance otherwise it does not read any new changes to the settings
     self.ADDON = xbmcaddon.Addon()
     self.ADDON_ID = self.ADDON.getAddonInfo('id')
     self.PLUGIN = self.ADDON.getAddonInfo('name')
     self.ICON = self.ADDON.getAddonInfo('icon')
     self.ADDON_DATA_PATH = self.ADDON.getAddonInfo('path')  # Add-on folder
     self.DATA_PATH = self.ADDON.getAddonInfo(
         'profile')  # Add-on user data folder
     try:
         self.IS_SERVICE = False
     except IndexError:
         self.IS_SERVICE = True
     # Initialize the log
     from resources.lib.helpers.logging import LOG
     LOG.initialize(self.ADDON_ID, G.ADDON.getSettingBool('enable_debug'),
                    G.ADDON.getSettingBool('debug_dial_server'),
                    G.ADDON.getSettingBool('debug_ssdp_server'),
                    G.ADDON.getSettingBool('debug_apps'))
     # Set SSDP server variables
     self.SP_FRIENDLY_NAME = xbmc.getInfoLabel(
         "System.FriendlyName") or 'Kodi (AppCast)'
     self.SP_MODEL_NAME = 'MyDeviceModel'
     self.SP_MANUFACTURER_NAME = ' '
     self.DEVICE_UUID = get_system_appcast_uuid()
Пример #5
0
def register_addonsignals_slot(callback, signal=None, source_id=None):
    """Register a callback with AddonSignals for return calls"""
    name = signal if signal else callback.__name__
    AddonSignals.registerSlot(signaler_id=source_id or IPC_ADDON_ID,
                              signal=name,
                              callback=callback)
    LOG.debug('Registered AddonSignals slot {} to {}'.format(name, callback))
Пример #6
0
def register_apps(kodi_interface):
    """Register DIAL applications based on the Kodi add-ons that support Cast feature"""
    # We find out which add-ons has "script.appcast" optional dependency,
    # then for each add-on we try import the "DialApp" class included in the path "resources/lib/dial_app_test/"
    addons = kodi_ops.json_rpc(
        'Addons.GetAddons', {
            'type': 'unknown',
            'properties': ['name', 'dependencies', 'enabled', 'path']
        })
    MUTEX.acquire()
    # # Clear previous sys modules added
    # for name in list(sys.modules.keys()):
    #     if name.startswith('dial_app_'):
    #         del sys.modules[name]

    for addon in addons['addons']:
        if not addon['enabled']:
            continue
        if any(dep['addonid'] == 'script.appcast'
               for dep in addon['dependencies']):
            # Try add the DIAL app included in the add-on
            try:
                name = addon['addonid'].split('.')[-1]
                package = 'dial_app_' + name
                module_path_folder1 = file_ops.join_folders_paths(
                    addon['path'], package, package + '.py')
                module_path_folder2 = file_ops.join_folders_paths(
                    addon['path'], 'resources/lib/' + package, package + '.py')
                # Check if the add-on has the package file
                if file_ops.file_exists(module_path_folder1):
                    module_path = module_path_folder1
                elif file_ops.file_exists(module_path_folder2):
                    module_path = module_path_folder2
                else:
                    LOGGER.error(
                        'register_apps: missing module file {}.py on {} add-on',
                        package, addon['addonid'])
                    continue
                # Load the external module (and allow it's own relative imports)
                spec = importlib.util.spec_from_file_location(
                    package, module_path, submodule_search_locations=[])
                module = importlib.util.module_from_spec(spec)
                sys.modules[module.__name__] = module
                spec.loader.exec_module(module)
                # Get the "DialApp" class from the loaded module
                app_class = getattr(module, 'DialApp', None)
                if app_class is None:
                    LOGGER.error(
                        'register_apps: "DialApp" class not found in {}.py file of {} add-on',
                        package, addon['addonid'])
                    continue
                register_app(app_class, kodi_interface)
            except Exception:
                LOGGER.error(
                    'register_apps: could not import the DIAL app from {}',
                    addon['addonid'])
                import traceback
                LOG.error(traceback.format_exc())
    MUTEX.release()
Пример #7
0
 def run(self):
     while not self._stop_event.is_set():
         if not self._notify_apps(
                 'on_playback_tick',
             {'is_playback_paused': self.is_playback_paused}):
             LOG.warn('PlaybackTick: Interrupted due to an error')
             break
         if self._stop_event.wait(self._timeout_secs):
             break  # Stop requested by stop_join
Пример #8
0
 def _notify_all_apps(self, callback_name, data=None, extra_data_app=None):
     for _app in self._apps:
         _data = deepcopy(data)
         if extra_data_app[0] == self._active_app:
             # If current app then send extra data only for this app
             _data.update(extra_data_app[1])
         LOG.debug('Notify Kodi callback {} to {} with data: {}',
                   callback_name, _app.DIAL_APP_NAME, _data)
         self._execute_notify(_app, callback_name, _data)
Пример #9
0
def _execute(executor_type, pathitems, params):
    """Execute an action as specified by the path"""
    try:
        executor = executor_type(params).__getattribute__(
            pathitems[0] if pathitems else 'root')
    except AttributeError as exc:
        raise InvalidPathError('Unknown action {}'.format(
            '/'.join(pathitems))) from exc
    LOG.debug('Invoking action: {}', executor.__name__)
    executor(pathitems=pathitems[1:])
Пример #10
0
 def _notify_apps(self, callback_name, data=None):
     if self._active_app is None:
         LOG.warn('Ignored Kodi callback {}, no app currently active',
                  callback_name)
         return False
     self._mutex.acquire()
     LOG.debug('Notify Kodi callback {} to {} with data: {}', callback_name,
               self._active_app.DIAL_APP_NAME, data)
     ret = self._execute_notify(self._active_app, callback_name, data)
     self._mutex.release()
     return ret
Пример #11
0
 def run(self):
     """Main loop. Runs until xbmc.Monitor requests abort"""
     try:
         self.start_services()
         monitor = xbmc.Monitor()
         while not monitor.abortRequested():
             monitor.waitForAbort(1)
         self.shutdown()
     except Exception:  # pylint: disable=broad-except
         import traceback
         LOG.error(traceback.format_exc())
Пример #12
0
def route(pathitems):
    """Route to the appropriate handler"""
    LOG.debug('Routing navigation request')
    root_handler = pathitems[0] if pathitems else G.MODE_DIRECTORY
    if root_handler == G.MODE_INSTALL:
        from resources.lib.navigation.install import install
        install(pathitems[1:], G.REQUEST_PARAMS)
    else:
        nav_handler = _get_nav_handler(root_handler, pathitems)
        _execute(nav_handler, pathitems[1:], G.REQUEST_PARAMS)
    return True
Пример #13
0
def _perform_ipc_return_call_instance(instance, func, data):
    try:
        result = _call_with_instance(instance, func, data)
    except Exception as exc:  # pylint: disable=broad-except
        LOG.error('IPC callback raised exception: {exc}', exc=exc)
        import traceback
        LOG.error(traceback.format_exc())
        result = ipc_convert_exc_to_json(exc)
    AddonSignals.returnCall(signal=func.__name__,
                            source_id=IPC_ADDON_ID,
                            data=result)
    return result
Пример #14
0
 def _execute_notify(app, callback_name, data):
     try:
         method = getattr(app, callback_name)
         method(data)
         return True
     except Exception:  # pylint: disable=broad-except
         LOG.error(
             'The app {} has raised the following error on {} callback:',
             app.DIAL_APP_NAME, callback_name)
         import traceback
         LOG.error(traceback.format_exc())
         return False
Пример #15
0
def install(pathitems, params):
    LOG.info('Start install Kodi "{}" (params "{}")', pathitems[-1], params)
    use_task_scheduler = G.ADDON.getSettingBool('usetaskscheduler')
    save_downloads = G.ADDON.getSettingBool('save_downloads')
    # Download the file
    if not kodi_ops.dlg_confirm(
            kodi_ops.get_local_string(30070),
            kodi_ops.get_local_string(30071).format(pathitems[-1])):
        return
    # Check if the task is installed
    if use_task_scheduler and not misc.check_task():
        kodi_ops.dlg_ok(kodi_ops.get_local_string(30070),
                        kodi_ops.get_local_string(30072))
        return
    # Check if destination path exist
    if not folder_exists(G.INSTALLER_TEMP_PATH):
        create_folder(G.INSTALLER_TEMP_PATH)
    # Check if the setup installer is already downloaded
    dwn_filepath = join_folders_paths(G.DOWNLOADS_PATH,
                                      '/'.join(pathitems[:-1]), pathitems[-1])
    # Temp file path will be used by the Windows Task scheduled
    temp_filepath = join_folders_paths(G.INSTALLER_TEMP_PATH,
                                       G.INSTALLER_TEMP_NAME)
    if params.get('is_local', 'False') == 'True':
        # Get the file to install from "downloads" folder
        if not file_exists(dwn_filepath):
            raise FileExistsError('The file {] not exists'.format(
                pathitems[:-1]))
        copy_file(dwn_filepath, temp_filepath)
    else:
        # Download the file
        if save_downloads and file_exists(dwn_filepath):
            # Copy the existing file to the temp file path
            copy_file(dwn_filepath, temp_filepath)
        else:
            # Download the setup installer file
            url_file_path = '/'.join(pathitems)
            if not download_file(G.MIRROR_BASE_URL + url_file_path,
                                 temp_filepath, pathitems[-1]):
                # Download cancelled
                kodi_ops.show_notification(kodi_ops.get_local_string(30073))
                return
            # Save the setup installer file
            if save_downloads:
                copy_file(temp_filepath, dwn_filepath)
    with kodi_ops.show_busy_dialog():
        # Run the "AutoUpdateWorker" bash script
        _run_auto_update_worker(use_task=use_task_scheduler)
        # Wait a bit before close Kodi,
        # the bash script have to read the executable path from the Kodi process before continue
        time.sleep(2)
        kodi_ops.json_rpc('Application.Quit')
Пример #16
0
def make_addonsignals_call(callname, data=None):
    """Make an IPC call via AddonSignals and wait for it to return.
    The contents of data will be expanded to kwargs and passed into the target function."""
    LOG.debug('Handling AddonSignals IPC call to {}'.format(callname))
    try:
        result = AddonSignals.makeCall(source_id=IPC_ADDON_ID,
                                       signal=callname,
                                       data=data,
                                       timeout_ms=IPC_TIMEOUT_SECS * 1000,
                                       use_timeout_exception=True)
        _raise_for_error(result)
    except AddonSignals.WaitTimeoutError:
        raise Exception('Addon Signals call timeout')
    return result
Пример #17
0
 def shutdown(self):
     """Stop the background services"""
     self.dial_srv_instance.shutdown()
     self.dial_srv_instance.server_close()
     self.dial_srv_instance = None
     self.dial_srv_thread.join()
     self.dial_srv_thread = None
     LOG.info('[DialServer] service stopped')
     self.ssdp_udp_srv_instance.shutdown()
     self.ssdp_udp_srv_instance.server_close()
     self.ssdp_udp_srv_instance = None
     self.ssdp_udp_srv_thread.join()
     self.ssdp_udp_srv_thread = None
     LOG.info('[SSDPUPDServer] service stopped')
Пример #18
0
 def init_servers(self):
     """Initialize the servers"""
     try:
         self.dial_srv_instance = dial_server.DialTCPServer((self.HOST_ADDRESS, G.DIAL_SERVER_PORT))
         self.dial_srv_instance.allow_reuse_address = True
         self.dial_srv_thread = threading.Thread(target=self.dial_srv_instance.serve_forever)
         self.ssdp_udp_srv_instance = ssdp_server.SSDPUDPServer()
         self.ssdp_udp_srv_instance.allow_reuse_address = True
         self.ssdp_udp_srv_thread = threading.Thread(target=self.ssdp_udp_srv_instance.serve_forever)
         return True
     except Exception:  # pylint: disable=broad-except
         import traceback
         LOG.error(traceback.format_exc())
     return False
Пример #19
0
def convert_to_string(value):
    if value is None:
        return None
    data_type = type(value)
    if data_type == str:
        return value
    converter = None
    if data_type in (int, float, bool, tuple, datetime.datetime):
        converter = _conv_standard_to_string
    if data_type in (list, dict, OrderedDict):
        converter = _conv_json_to_string
    if not converter:
        LOG.error(
            'convert_to_string: Data type {} not mapped'.format(data_type))
        raise DataTypeNotMapped
    return converter(value)
Пример #20
0
def _get_system_uuid():
    uuid_value = None
    system = get_system_platform()
    if system in ['windows', 'uwp']:
        uuid_value = _get_windows_uuid()
    elif system == 'android':
        uuid_value = _get_android_uuid()
    elif system == 'linux':
        uuid_value = _get_linux_uuid()
    elif system == 'osx':
        # Due to OS restrictions on 'ios' and 'tvos' is not possible to use _get_macos_uuid()
        # See python limits in the wiki development page
        uuid_value = _get_macos_uuid()
    if not uuid_value:
        LOG.debug('It is not possible to get a system UUID creating a new UUID')
        uuid_value = _get_fake_uuid(system not in ['android', 'linux'])
    return str(uuid_value)
Пример #21
0
def convert_from_string(value, to_data_type):
    if value is None:
        return None
    if to_data_type in (str, int, float):
        return to_data_type(value)
    if to_data_type in (bool, list, tuple):
        return literal_eval(value)
    converter = None
    if to_data_type == dict:
        converter = _conv_string_to_json
    if to_data_type == datetime.datetime:
        converter = _conv_string_to_datetime
    if not converter:
        LOG.error('convert_from_string: Data type {} not mapped'.format(
            to_data_type))
        raise DataTypeNotMapped
    return converter(value)
Пример #22
0
def download_file(url, dest_path, filename):
    start_time = time.time()
    dlg = xbmcgui.DialogProgress()
    dlg.create(G.ADDON_ID, get_local_string(30499))
    try:
        urlretrieve(
            url.rstrip('/'), dest_path,
            lambda num_blocks, block_size, file_size: reporthook(
                num_blocks, block_size, file_size, dlg, start_time, filename))
        return True
    except InterruptedError:
        LOG.error('Download interrupted by user')
    except Exception as exc:
        LOG.error('Download failed due to an error: {}', exc)
        raise Exception('Download failed') from exc
    finally:
        dlg.close()
    return False
Пример #23
0
 def init_globals(self, argv):
     """Initialized globally used module variables. Needs to be called at start of each plugin instance!"""
     # IS_ADDON_FIRSTRUN: specifies if the add-on has been initialized for the first time
     #                    (reuseLanguageInvoker not used yet)
     self.IS_ADDON_FIRSTRUN = self.IS_ADDON_FIRSTRUN is None
     # xbmcaddon.Addon must be created at every instance otherwise it does not read any new changes to the settings
     self.ADDON = xbmcaddon.Addon()
     self.URL = urlparse(argv[0])
     self.REQUEST_PATH = unquote(self.URL[2][1:])
     try:
         self.PARAM_STRING = argv[2][1:]
     except IndexError:
         self.PARAM_STRING = ''
     self.REQUEST_PARAMS = dict(parse_qsl(self.PARAM_STRING))
     if self.IS_ADDON_FIRSTRUN:
         # Global variables that do not need to be generated at every instance
         self.ADDON_ID = self.ADDON.getAddonInfo('id')
         self.PLUGIN = self.ADDON.getAddonInfo('name')
         self.VERSION_RAW = self.ADDON.getAddonInfo('version')
         self.VERSION = remove_ver_suffix(self.VERSION_RAW)
         self.ICON = self.ADDON.getAddonInfo('icon')
         self.ADDON_DATA_PATH = self.ADDON.getAddonInfo('path')  # Add-on folder
         self.DATA_PATH = self.ADDON.getAddonInfo('profile')  # Add-on user data folder
         try:
             self.PLUGIN_HANDLE = int(argv[1])
             self.IS_SERVICE = False
             self.BASE_URL = '{scheme}://{netloc}'.format(scheme=self.URL[0],
                                                          netloc=self.URL[1])
         except IndexError:
             self.PLUGIN_HANDLE = 0
             self.IS_SERVICE = True
             self.BASE_URL = '{scheme}://{netloc}'.format(scheme='plugin',
                                                          netloc=self.ADDON_ID)
     # Initialize the log
     from resources.lib.helpers.logging import LOG
     LOG.initialize(self.ADDON_ID, self.PLUGIN_HANDLE,
                    self.ADDON.getSettingString('debug_log_level'),
                    self.ADDON.getSettingBool('enable_timing'))
     # Temporary file path (use to download and run the installer)
     from resources.lib.helpers.file_ops import translate_path
     self.INSTALLER_TEMP_PATH = translate_path(G.DATA_PATH) + 'temp/'
     self.INSTALLER_TEMP_NAME = 'KodiInstaller.exe'  # Mush be equal to all scripts
     self.DOWNLOADS_PATH = translate_path(G.DATA_PATH) + 'downloads/'
Пример #24
0
def _get_macos_uuid():
    # pylint: disable=broad-except
    import subprocess
    sp_dict_values = None
    try:
        proc = subprocess.Popen(
            ['/usr/sbin/system_profiler', 'SPHardwareDataType', '-detaillevel', 'full', '-xml'],
            stdout=subprocess.PIPE)
        output_data = proc.communicate()[0].decode('utf-8')
        if output_data:
            sp_dict_values = _parse_osx_xml_plist_data(output_data)
    except Exception as exc:
        LOG.debug('Failed to fetch OSX/IOS system profile {}'.format(exc))
    if sp_dict_values:
        if 'UUID' in list(sp_dict_values.keys()):
            return sp_dict_values['UUID']
        if 'serialnumber' in list(sp_dict_values.keys()):
            return sp_dict_values['serialnumber']
    return None
Пример #25
0
 def initialize_connection(self):
     """Checks database existence and performs first connection tests"""
     try:
         LOG.debug('Trying connection to the database {}', self.db_filename)
         self.conn = sql.connect(self.db_file_path, check_same_thread=False)
         cur = self.conn.cursor()
         cur.execute(str('SELECT SQLITE_VERSION()'))
         LOG.debug('Database connection {} was successful (SQLite ver. {})',
                   self.db_filename,
                   cur.fetchone()[0])
         cur.row_factory = lambda cursor, row: row[0]
         cur.execute(
             str('SELECT name FROM sqlite_master WHERE type=\'table\' '
                 'AND name NOT LIKE \'sqlite_%\''))
         list_tables = cur.fetchall()
         if not list_tables:
             # If no tables exist create a new one
             self.conn.close()
             db_create_sqlite.create_database(self.db_file_path,
                                              self.db_filename)
     except sql.Error as exc:
         LOG.error('SQLite error {}:', exc.args[0])
         raise DBSQLiteConnectionError from exc
     finally:
         if self.conn:
             self.conn.close()
Пример #26
0
    def wrapper(*args, **kwargs):
        if args[0].is_mysql_database:
            # If database is mysql pass to next decorator
            return func(*args, **kwargs)
        conn = None
        try:
            if not args[0].is_connected:
                args[0].mutex.acquire()
                args[0].conn = sql.connect(
                    args[0].db_file_path, isolation_level=CONN_ISOLATION_LEVEL)
                args[0].is_connected = True
                conn = args[0].conn

            return func(*args, **kwargs)
        except sql.Error as exc:
            LOG.error('SQLite error {}:', exc.args[0])
            raise DBSQLiteConnectionError from exc
        finally:
            if conn:
                args[0].is_connected = False
                conn.close()
                args[0].mutex.release()
Пример #27
0
 def _executemany_non_query(self, query, params, cursor=None):
     try:
         if cursor is None:
             cursor = self.get_cursor()
         cursor.executemany(query, params)
     except sql.Error as exc:
         LOG.error('SQLite error {}:', exc.args[0])
         raise DBSQLiteError from exc
     except ValueError:
         LOG.error('Value {}', str(params))
         LOG.error('Value type {}', type(params))
         raise
Пример #28
0
def run(argv):
    # Initialize globals right away to avoid stale values from the last addon invocation.
    # Otherwise Kodi's reuseLanguageInvoker will cause some really quirky behavior!
    # PR: https://github.com/xbmc/xbmc/pull/13814
    G.init_globals(argv)
    LOG.info('Started (Version {})'.format(G.VERSION_RAW))
    LOG.info('URL is {}'.format(G.URL))
    success = False
    try:
        pathitems = [part for part in G.REQUEST_PATH.split('/') if part]
        success = route(pathitems)
    except Exception as exc:
        import traceback
        LOG.error(traceback.format_exc())
        kodi_ops.dlg_ok(
            'AutoUpdateKodi',
            kodi_ops.get_local_string(30700).format('[{}] {}'.format(
                exc.__class__.__name__, exc)))
    if not success:
        from xbmcplugin import endOfDirectory
        endOfDirectory(handle=G.PLUGIN_HANDLE, succeeded=False)
    LOG.log_time_trace()
Пример #29
0
def _get_linux_uuid():
    # pylint: disable=broad-except
    import subprocess
    uuid_value = None
    try:
        uuid_value = subprocess.check_output(['cat', '/var/lib/dbus/machine-id']).decode('utf-8')
    except Exception as exc:
        import traceback
        LOG.error('_get_linux_uuid first attempt returned: {}', exc)
        LOG.error(traceback.format_exc())
    if not uuid_value:
        try:
            # Fedora linux
            uuid_value = subprocess.check_output(['cat', '/etc/machine-id']).decode('utf-8')
        except Exception as exc:
            LOG.error('_get_linux_uuid second attempt returned: {}', exc)
    return uuid_value
Пример #30
0
 def _execute_non_query(self, query, params=None, cursor=None, **kwargs):
     """
     Execute a query without returning a value
     :param query: sql query
     :param params: tuple of values
     :param cursor: a cursor, if None get a instance of standard cursor
     """
     try:
         if cursor is None:
             cursor = self.get_cursor()
         if params is not None:
             cursor.execute(query, params)
         else:
             cursor.execute(query)
     except sql.Error as exc:
         LOG.error('SQLite error {}:', exc.args[0])
         raise DBSQLiteError from exc
     except ValueError:
         LOG.error('Value {}', str(params))
         LOG.error('Value type {}', type(params))
         raise