commit 17ab84ac5ebf896a77d1ce67bee54bb00d3c0bfd Author: Damian Johnson atagar@torproject.org Date: Sat Aug 26 14:59:08 2017 -0700
Provide a connection with stem.manual.database
In dabbling with this more I've been finding the context manager enforced by stem.manual.database() annoying. When on an interactive prompt I want a method to quickly get a no-hassle database connection - not a cursor that's only usable within a 'with' block.
Reason I provided a cursor was to ensure callers had read-only access. However, just checked and when the database file is read-only (as our cached_tor_manual.sqlite should be when installed) attempting to write to it behaves how I'd expect...
>>> import stem.manual >>> conn = stem.manual.database() >>> conn.execute('CREATE TABLE new_table(name TEXT PRIMARY KEY, description TEXT)') Traceback (most recent call last): File "<stdin>", line 1, in <module> sqlite3.OperationalError: attempt to write a readonly database --- stem/manual.py | 18 ++++++++++++------ test/unit/manual.py | 8 +++++--- 2 files changed, 17 insertions(+), 9 deletions(-)
diff --git a/stem/manual.py b/stem/manual.py index fc648cc9..e78f917c 100644 --- a/stem/manual.py +++ b/stem/manual.py @@ -47,7 +47,6 @@ us what our torrc options do... .. versionadded:: 1.5.0 """
-import contextlib import os import shutil import sqlite3 @@ -95,26 +94,33 @@ CATEGORY_SECTIONS = OrderedDict(( ))
-@contextlib.contextmanager def database(path = None): """ - Provides a database cursor for a sqlite cache. + Provides database connection for our sqlite cache. This database should be + treated as being read-only, with this restriction being enforced in the + future.
.. versionadded:: 1.6.0
:param str path: cached manual content to read, if not provided this uses the bundled manual information
- :returns: :class:`sqlite3.Cursor` for the database cache + :returns: :class:`sqlite3.Connection` for the database cache
:raises: **IOError** if a **path** was provided and we were unable to read it """
if path is None: path = CACHE_PATH + elif not os.path.exists(path): + raise IOError("%s doesn't exist" % path)
- with sqlite3.connect(path) as conn: - yield conn.cursor() + # TODO: When we only support python 3.4+ we can use sqlite's uri argument to + # get a read-only connection... + # + # https://docs.python.org/3/library/sqlite3.html#sqlite3.connect + + return sqlite3.connect(path)
class ConfigOption(object): diff --git a/test/unit/manual.py b/test/unit/manual.py index eab9bdfc..eaca3629 100644 --- a/test/unit/manual.py +++ b/test/unit/manual.py @@ -103,9 +103,11 @@ def _cached_manual():
class TestManual(unittest.TestCase): def test_database(self): - with stem.manual.database() as cursor: - cursor.execute('SELECT description FROM torrc WHERE name="CookieAuthFile"') - self.assertEqual("If set, this option overrides the default location and file name for Tor's cookie file. (See CookieAuthentication above.)", cursor.fetchone()[0]) + with stem.manual.database() as conn: + self.assertEqual("If set, this option overrides the default location and file name for Tor's cookie file. (See CookieAuthentication above.)", conn.execute('SELECT description FROM torrc WHERE name="CookieAuthFile"').fetchone()[0]) + + def test_missing_database(self): + self.assertRaisesRegexp(IOError, "/no/such/path doesn't exist", stem.manual.database, '/no/such/path')
def test_has_all_summaries(self): """
tor-commits@lists.torproject.org