commit c4072bd1705e1996eecc3236995c26e9a3fbf8bd Author: Damian Johnson atagar@torproject.org Date: Tue Feb 18 14:45:57 2020 -0800
Tests required sqlite3
Python includes sqlite3 as a builtin, but when you compile python yourself it isn't included by default. Our stem.manual attempted to account for this but our tests didn't, and with python3 the module raises a ModuleNotFoundError rather than an ImportError.
Traceback (most recent call last): File "/home/atagar/Desktop/stem/test/task.py", line 160, in _import_tests importlib.import_module(module.rsplit('.', 1)[0]) File "/home/atagar/Python-3.8.1/Lib/importlib/__init__.py", line 127, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "<frozen importlib._bootstrap>", line 1014, in _gcd_import File "<frozen importlib._bootstrap>", line 991, in _find_and_load File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 671, in _load_unlocked File "<frozen importlib._bootstrap_external>", line 783, in exec_module File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed File "/home/atagar/Desktop/stem/test/unit/manual.py", line 8, in <module> import sqlite3 File "/home/atagar/Python-3.8.1/Lib/sqlite3/__init__.py", line 23, in <module> from sqlite3.dbapi2 import * File "/home/atagar/Python-3.8.1/Lib/sqlite3/dbapi2.py", line 27, in <module> from _sqlite3 import * ModuleNotFoundError: No module named '_sqlite3' --- stem/manual.py | 6 +++--- test/require.py | 14 ++++++++++++++ test/unit/doctest.py | 6 ++++++ test/unit/manual.py | 9 ++++++++- 4 files changed, 31 insertions(+), 4 deletions(-)
diff --git a/stem/manual.py b/stem/manual.py index 44117f93..367b6d7e 100644 --- a/stem/manual.py +++ b/stem/manual.py @@ -135,7 +135,7 @@ def query(query, *param):
try: import sqlite3 - except ImportError: + except (ImportError, ModuleNotFoundError): raise ImportError('Querying requires the sqlite3 module')
# The only reason to explicitly close the sqlite connection is to ensure @@ -386,7 +386,7 @@ class Manual(object):
try: import sqlite3 - except ImportError: + except (ImportError, ModuleNotFoundError): raise ImportError('Reading a sqlite cache requires the sqlite3 module')
if path is None: @@ -518,7 +518,7 @@ class Manual(object):
try: import sqlite3 - except ImportError: + except (ImportError, ModuleNotFoundError): raise ImportError('Saving a sqlite cache requires the sqlite3 module')
tmp_path = path + '.new' diff --git a/test/require.py b/test/require.py index 53d66f62..8054bfad 100644 --- a/test/require.py +++ b/test/require.py @@ -99,6 +99,20 @@ def command(cmd): return needs(lambda: stem.util.system.is_available(cmd), '%s unavailable' % cmd)
+def module(module_name): + """ + Skip test unless this module is available. + """ + + try: + import module_name + available = True + except ImportError: + available = False + + return needs(lambda: available, '%s unavailable' % module_name) + + def version(req_version): """ Skips the test unless we meet the required version. diff --git a/test/unit/doctest.py b/test/unit/doctest.py index 84712dc2..120940ba 100644 --- a/test/unit/doctest.py +++ b/test/unit/doctest.py @@ -119,6 +119,12 @@ class TestDocumentation(unittest.TestCase): elif path.endswith('/stem/version.py'): with patch('stem.version.get_system_tor_version', Mock(return_value = stem.version.Version('0.2.1.30'))): test_run = doctest.testfile(path, **args) + elif path.endswith('/stem/manual.py'): + try: + import sqlite3 + test_run = doctest.testfile(path, **args) + except ImportError: + pass # manual module requires sqlite3 else: test_run = doctest.testfile(path, **args)
diff --git a/test/unit/manual.py b/test/unit/manual.py index 006e0b3e..26cb83d8 100644 --- a/test/unit/manual.py +++ b/test/unit/manual.py @@ -5,7 +5,6 @@ Unit testing for the stem.manual module. import collections import io import os -import sqlite3 import tempfile import unittest import urllib.request @@ -90,13 +89,18 @@ def _cached_manual():
class TestManual(unittest.TestCase): + @test.require.module('sqlite3') def test_query(self): self.assertEqual("If set, this option overrides the default location and file name for Tor's cookie file. (See CookieAuthentication above.)", stem.manual.query('SELECT description FROM torrc WHERE name="CookieAuthFile"').fetchone()[0]) self.assertEqual("If set, this option overrides the default location and file name for Tor's cookie file. (See CookieAuthentication above.)", stem.manual.query('SELECT description FROM torrc WHERE name=?', 'CookieAuthFile').fetchone()[0])
+ @test.require.module('sqlite3') def test_query_on_failure(self): + import sqlite3 + self.assertRaisesWith(sqlite3.OperationalError, 'near "hello": syntax error', stem.manual.query, 'hello world')
+ @test.require.module('sqlite3') def test_has_all_summaries(self): """ Check that we have brief, human readable summaries for all of tor's @@ -182,6 +186,7 @@ class TestManual(unittest.TestCase): self.assertEqual('Description of this new option.', option.description)
@test.require.command('man') + @test.require.module('sqlite3') def test_saving_manual_as_config(self): """ Check that we can save and reload manuals as a config. @@ -198,6 +203,7 @@ class TestManual(unittest.TestCase): self.assertEqual(manual, loaded_manual)
@test.require.command('man') + @test.require.module('sqlite3') def test_saving_manual_as_sqlite(self): """ Check that we can save and reload manuals as sqlite. @@ -213,6 +219,7 @@ class TestManual(unittest.TestCase): loaded_manual = stem.manual.Manual.from_cache(tmp.name) self.assertEqual(manual, loaded_manual)
+ @test.require.module('sqlite3') def test_cached_manual(self): manual = _cached_manual()