Merge pull request #3226 from jackwilsdon/loadext

Add loadext plugin
This commit is contained in:
Adrian Sampson 2019-04-21 12:22:49 -04:00 committed by GitHub
commit 84c29d0275
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 128 additions and 0 deletions

View file

@ -850,16 +850,21 @@ class Database(object):
"""A container for Model objects that wraps an SQLite database as
the backend.
"""
_models = ()
"""The Model subclasses representing tables in this database.
"""
supports_extensions = hasattr(sqlite3.Connection, 'enable_load_extension')
"""Whether or not the current version of SQLite supports extensions"""
def __init__(self, path, timeout=5.0):
self.path = path
self.timeout = timeout
self._connections = {}
self._tx_stacks = defaultdict(list)
self._extensions = []
# A lock to protect the _connections and _tx_stacks maps, which
# both map thread IDs to private resources.
@ -909,6 +914,13 @@ class Database(object):
py3_path(self.path), timeout=self.timeout
)
if self.supports_extensions:
conn.enable_load_extension(True)
# Load any extension that are already loaded for other connections.
for path in self._extensions:
conn.load_extension(path)
# Access SELECT results like dictionaries.
conn.row_factory = sqlite3.Row
return conn
@ -937,6 +949,18 @@ class Database(object):
"""
return Transaction(self)
def load_extension(self, path):
"""Load an SQLite extension into all open connections."""
if not self.supports_extensions:
raise ValueError(
'this sqlite3 installation does not support extensions')
self._extensions.append(path)
# Load the extension into every open connection.
for conn in self._connections.values():
conn.load_extension(path)
# Schema setup and migration.
def _make_table(self, table, fields):

46
beetsplug/loadext.py Normal file
View file

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
# This file is part of beets.
# Copyright 2019, Jack Wilsdon <jack.wilsdon@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
"""Load SQLite extensions.
"""
from __future__ import division, absolute_import, print_function
from beets.dbcore import Database
from beets.plugins import BeetsPlugin
import sqlite3
class LoadExtPlugin(BeetsPlugin):
def __init__(self):
super(LoadExtPlugin, self).__init__()
if not Database.supports_extensions:
self._log.warn('loadext is enabled but the current SQLite '
'installation does not support extensions')
return
self.register_listener('library_opened', self.library_opened)
def library_opened(self, lib):
for v in self.config:
ext = v.as_filename()
self._log.debug(u'loading extension {}', ext)
try:
lib.load_extension(ext)
except sqlite3.OperationalError as e:
self._log.error(u'failed to load extension {}: {}', ext, e)

View file

@ -94,6 +94,9 @@ New features:
:bug:`3205` :bug:`800`
* :doc:`/plugins/bpd`: MPD protocol command ``decoders`` is now supported.
:bug:`3222`
* The new :doc:`/plugins/loadext` allows loading of SQLite extensions, primarily
for use with the ICU SQLite extension for internationalization.
:bug:`3160` :bug:`3226`
Changes:

View file

@ -71,6 +71,7 @@ like this::
kodiupdate
lastgenre
lastimport
loadext
lyrics
mbcollection
mbsubmit
@ -189,6 +190,7 @@ Miscellaneous
* :doc:`hook`: Run a command when an event is emitted by beets.
* :doc:`ihate`: Automatically skip albums and tracks during the import process.
* :doc:`info`: Print music files' tags to the console.
* :doc:`loadext`: Load SQLite extensions.
* :doc:`mbcollection`: Maintain your MusicBrainz collection list.
* :doc:`mbsubmit`: Print an album's tracks in a MusicBrainz-friendly format.
* :doc:`missing`: List missing tracks.

53
docs/plugins/loadext.rst Normal file
View file

@ -0,0 +1,53 @@
Load Extension Plugin
=====================
Beets uses an SQLite database to store and query library information, which
has support for extensions to extend its functionality. The ``loadext`` plugin
lets you enable these SQLite extensions within beets.
One of the primary uses of this within beets is with the `"ICU" extension`_,
which adds support for case insensitive querying of non-ASCII characters.
.. _"ICU" extension: https://www.sqlite.org/src/dir?ci=7461d2e120f21493&name=ext/icu
Configuration
-------------
To configure the plugin, make a ``loadext`` section in your configuration
file. The section must consist of a list of paths to extensions to load, which
looks like this:
.. code-block:: yaml
loadext:
- libicu
If a relative path is specified, it is resolved relative to the beets
configuration directory.
If no file extension is specified, the default dynamic library extension for
the current platform will be used.
Building the ICU extension
--------------------------
This section is for **advanced** users only, and is not an in-depth guide on
building the extension.
To compile the ICU extension, you will need a few dependencies:
- gcc
- icu-devtools
- libicu
- libicu-dev
- libsqlite3-dev
Here's roughly how to download, build and install the extension (although the
specifics may vary from system to system):
.. code-block:: shell
$ wget https://sqlite.org/2019/sqlite-src-3280000.zip
$ unzip sqlite-src-3280000.zip
$ cd sqlite-src-3280000/ext/icu
$ gcc -shared -fPIC icu.c `icu-config --ldflags` -o libicu.so
$ cp libicu.so ~/.config/beets