From 97e24f4311e20a857cdc43516b29529d82fb2c19 Mon Sep 17 00:00:00 2001 From: Michael Wayne Goodman Date: Tue, 26 Jan 2021 18:45:58 +0800 Subject: [PATCH] Fix #86: add wn.config.allow_multithreading When set to `True`, the `check_same_thread` parameter to `sqlite3.connect()` is set to `False` --- CHANGELOG.md | 3 ++- docs/api/wn.rst | 7 +++++++ tests/db_test.py | 32 ++++++++++++++++++++++++++++++++ wn/_config.py | 1 + wn/_db.py | 6 +++++- 5 files changed, 47 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bf567b..f0d1792 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Added * `wn.Lexicon.specifier()` - +* `wn.config.allow_multithreading` ([#86]) ### Removed @@ -192,6 +192,7 @@ abandoned, but this is an entirely new codebase. [#79]: https://github.com/goodmami/wn/issues/79 [#81]: https://github.com/goodmami/wn/issues/81 [#83]: https://github.com/goodmami/wn/issues/83 +[#86]: https://github.com/goodmami/wn/issues/86 [#90]: https://github.com/goodmami/wn/issues/90 [#92]: https://github.com/goodmami/wn/issues/92 [#93]: https://github.com/goodmami/wn/issues/93 diff --git a/docs/api/wn.rst b/docs/api/wn.rst index 920c80e..68bcaee 100644 --- a/docs/api/wn.rst +++ b/docs/api/wn.rst @@ -177,6 +177,13 @@ directly. Configuration should occur through the single .. autoattribute:: data_directory .. autoattribute:: database_path + .. attribute:: allow_multithreading + + If set to :python:`True`, the database connection may be shared + across threads. In this case, it is the user's responsibility to + ensure that multiple threads don't try to write to the database + at the same time. The default is :python:`False`. + .. autoattribute:: downloads_directory .. automethod:: add_project .. automethod:: add_project_version diff --git a/tests/db_test.py b/tests/db_test.py index 6c43873..d531e6a 100644 --- a/tests/db_test.py +++ b/tests/db_test.py @@ -1,5 +1,6 @@ import sqlite3 +import threading import pytest @@ -11,3 +12,34 @@ def test_schema_compatibility(): conn = sqlite3.connect(str(wn.config.database_path)) schema_hash = wn._db.schema_hash(conn) assert schema_hash in wn._db.COMPATIBLE_SCHEMA_HASHES + + +@pytest.mark.usefixtures('mini_db') +def test_db_multithreading(): + """ + See https://github.com/goodmami/wn/issues/86 + Thanks: @fushinari + """ + + class WNThread: + w = None + + def __init__(self): + w_thread = threading.Thread(target=self.set_w) + w_thread.start() + w_thread.join() + self.w.synsets() + + def set_w(self): + if self.w is None: + self.w = wn.Wordnet() + + # close the connections by resetting the pool + wn._db.pool = {} + with pytest.raises(sqlite3.ProgrammingError): + WNThread() + wn._db.pool = {} + wn.config.allow_multithreading = True + WNThread() # no error + wn.config.allow_multithreading = False + wn._db.pool = {} diff --git a/wn/_config.py b/wn/_config.py index 0234219..ae3fcbe 100644 --- a/wn/_config.py +++ b/wn/_config.py @@ -23,6 +23,7 @@ def __init__(self): self._data_directory = DEFAULT_DATA_DIRECTORY self._projects = {} self._dbpath = self._data_directory / DATABASE_FILENAME + self.allow_multithreading = False @property def data_directory(self) -> Path: diff --git a/wn/_db.py b/wn/_db.py index 228706e..d66183b 100644 --- a/wn/_db.py +++ b/wn/_db.py @@ -72,7 +72,11 @@ def connect() -> sqlite3.Connection: dbpath = wn.config.database_path if dbpath not in pool: initialized = dbpath.is_file() - conn = sqlite3.connect(str(dbpath), detect_types=sqlite3.PARSE_DECLTYPES) + conn = sqlite3.connect( + str(dbpath), + detect_types=sqlite3.PARSE_DECLTYPES, + check_same_thread=not wn.config.allow_multithreading, + ) # foreign key support needs to be enabled for each connection conn.execute('PRAGMA foreign_keys = ON') if DEBUG: