commit 72fbbd05ae128cca208632073b20a4b2cafea3ff Author: Damian Johnson atagar@torproject.org Date: Fri Nov 3 09:32:08 2017 -0700
Don't require sqlite3 unless used
Though sqlite3 is usually built in, it's an optional module that's commonly missing on FreeBSD and Gentoo...
https://lists.torproject.org/pipermail/tor-relays/2017-October/013433.html https://lists.torproject.org/pipermail/tor-relays/2017-October/013440.html
Like the cryptography module making this a soft dependency that's only required if it's used. --- stem/descriptor/__init__.py | 2 ++ stem/manual.py | 31 ++++++++++++++++++++++++++----- stem/prereq.py | 20 ++++++++++++++++++++ test/settings.cfg | 1 + 4 files changed, 49 insertions(+), 5 deletions(-)
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py index 5c6e5f3d..1fb2bb2c 100644 --- a/stem/descriptor/__init__.py +++ b/stem/descriptor/__init__.py @@ -982,6 +982,8 @@ def create_signing_key(private_key = None):
:returns: :class:`~stem.descriptor.__init__.SigningKey` that can be used to create descriptors + + :raises: **ImportError** if the cryptography module is unavailable """
if not stem.prereq.is_crypto_available(): diff --git a/stem/manual.py b/stem/manual.py index df074c1b..df55e3cb 100644 --- a/stem/manual.py +++ b/stem/manual.py @@ -50,7 +50,6 @@ us what our torrc options do...
import os import shutil -import sqlite3 import sys import tempfile
@@ -138,9 +137,16 @@ def query(query, *param):
:returns: :class:`sqlite3.Cursor` with the query results
- :raises: **sqlite3.OperationalError** if query fails + :raises: + * **ImportError** if the sqlite3 module is unavailable + * **sqlite3.OperationalError** if query fails """
+ if not stem.prereq.is_sqlite_available(): + raise ImportError('Querying requires the sqlite3 module') + + import sqlite3 + # The only reason to explicitly close the sqlite connection is to ensure # transactions are committed. Since we're only using read-only access this # doesn't matter, and can allow interpreter shutdown to do the needful. @@ -387,8 +393,11 @@ class Manual(object):
:returns: :class:`~stem.manual.Manual` with our bundled manual information
- :raises: **IOError** if a **path** was provided and we were unable to read - it or the schema is out of date + :raises: + * **ImportError** if cache is sqlite and the sqlite3 module is + unavailable + * **IOError** if a **path** was provided and we were unable to read + it or the schema is out of date """
# TODO: drop _from_config_cache() with stem 2.x @@ -403,6 +412,11 @@ class Manual(object):
@staticmethod def _from_sqlite_cache(path): + if not stem.prereq.is_sqlite_available(): + raise ImportError('Reading a sqlite cache requires the sqlite3 module') + + import sqlite3 + if not os.path.exists(path): raise IOError("%s doesn't exist" % path)
@@ -556,7 +570,10 @@ class Manual(object):
:param str path: path to save our manual content to
- :raises: **IOError** if unsuccessful + :raises: + * **ImportError** if saving as sqlite and the sqlite3 module is + unavailable + * **IOError** if unsuccessful """
# TODO: drop _save_as_config() with stem 2.x @@ -567,6 +584,10 @@ class Manual(object): return self._save_as_config(path)
def _save_as_sqlite(self, path): + if not stem.prereq.is_sqlite_available(): + raise ImportError('Saving a sqlite cache requires the sqlite3 module') + + import sqlite3 tmp_path = path + '.new'
if os.path.exists(tmp_path): diff --git a/stem/prereq.py b/stem/prereq.py index f09870cf..9f896a83 100644 --- a/stem/prereq.py +++ b/stem/prereq.py @@ -14,7 +14,9 @@ Checks for stem dependencies. We require python 2.6 or greater (including the
check_requirements - checks for minimum requirements for running stem is_python_3 - checks if python 3.0 or later is available + is_sqlite_available - checks if the sqlite3 module is available is_crypto_available - checks if the cryptography module is available + is_mock_available - checks if the mock module is available """
import inspect @@ -84,6 +86,24 @@ def is_python_3():
@lru_cache() +def is_sqlite_available(): + """ + Checks if the sqlite3 module is available. Usually this is built in, but some + platforms such as FreeBSD and Gentoo exclude it by default. + + .. versionadded:: 1.6.0 + + :returns: **True** if we can use the sqlite3 module and **False** otherwise + """ + + try: + import sqlite3 + return True + except ImportError: + return False + + +@lru_cache() def is_crypto_available(): """ Checks if the cryptography functions we use are available. This is used for diff --git a/test/settings.cfg b/test/settings.cfg index 5b3ba6bc..4e9b2ab9 100644 --- a/test/settings.cfg +++ b/test/settings.cfg @@ -162,6 +162,7 @@ pyflakes.ignore stem/prereq.py => 'unittest.mock' imported but unused pyflakes.ignore stem/prereq.py => 'long_to_bytes' imported but unused pyflakes.ignore stem/prereq.py => 'encoding' imported but unused pyflakes.ignore stem/prereq.py => 'signing' imported but unused +pyflakes.ignore stem/prereq.py => 'sqlite3' imported but unused pyflakes.ignore stem/prereq.py => 'cryptography.utils.int_to_bytes' imported but unused pyflakes.ignore stem/prereq.py => 'cryptography.utils.int_from_bytes' imported but unused pyflakes.ignore stem/prereq.py => 'cryptography.hazmat.backends.default_backend' imported but unused