[tor-commits] [stem/master] Don't require sqlite3 unless used

atagar at torproject.org atagar at torproject.org
Fri Nov 3 16:31:35 UTC 2017


commit 72fbbd05ae128cca208632073b20a4b2cafea3ff
Author: Damian Johnson <atagar at 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
+
+
+ at 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



More information about the tor-commits mailing list