33from pathlib import Path
44from contextlib import suppress , closing
55from collections .abc import MutableMapping
6+ from threading import current_thread
7+ from weakref import ref
68
79BUILD_TABLE = """
810 CREATE TABLE IF NOT EXISTS Dict (
@@ -32,6 +34,34 @@ def _normalize_uri(path):
3234 uri = uri .replace ("//" , "/" )
3335 return uri
3436
37+ class _ThreadLocalSqliteConnection :
38+ def __init__ (self , uri : str ):
39+ self ._uri = uri
40+ self ._conn = {}
41+
42+ def conn (self ):
43+ thread = current_thread ()
44+ idt = id (thread )
45+ def thread_deleted (_ , idt = idt ):
46+ # When the thread is deleted, remove the local dict.
47+ # Note that this is suboptimal if the thread object gets
48+ # caught in a reference loop. We would like to be called
49+ # as soon as the OS-level thread ends instead.
50+ if self ._conn is not None :
51+ dct = self ._conn .dicts .pop (idt )
52+ wrthread = ref (thread , thread_deleted )
53+ try :
54+ conn = sqlite3 .connect (self ._uri , autocommit = True , uri = True )
55+ self ._conn [id (thread )] = conn
56+ return conn
57+ except sqlite3 .Error as exc :
58+ raise error (str (exc ))
59+
60+ def close (self ):
61+ for t , conn in self ._conn .items ():
62+ conn .close ()
63+ del self ._conn [t ]
64+
3565
3666class _Database (MutableMapping ):
3767
@@ -59,15 +89,11 @@ def __init__(self, path, /, *, flag, mode):
5989 # We use the URI format when opening the database.
6090 uri = _normalize_uri (path )
6191 uri = f"{ uri } ?mode={ flag } "
62-
63- try :
64- self ._cx = sqlite3 .connect (uri , autocommit = True , uri = True )
65- except sqlite3 .Error as exc :
66- raise error (str (exc ))
92+ self ._cx = _ThreadLocalSqliteConnection (uri )
6793
6894 # This is an optimization only; it's ok if it fails.
6995 with suppress (sqlite3 .OperationalError ):
70- self ._cx .execute ("PRAGMA journal_mode = wal" )
96+ self ._cx .conn (). execute ("PRAGMA journal_mode = wal" )
7197
7298 if flag == "rwc" :
7399 self ._execute (BUILD_TABLE )
@@ -76,7 +102,7 @@ def _execute(self, *args, **kwargs):
76102 if not self ._cx :
77103 raise error (_ERR_CLOSED )
78104 try :
79- return closing (self ._cx .execute (* args , ** kwargs ))
105+ return closing (self ._cx .conn (). execute (* args , ** kwargs ))
80106 except sqlite3 .Error as exc :
81107 raise error (str (exc ))
82108
0 commit comments