Skip to content

Commit cf3b312

Browse files
authored
Always close thread-specific SQLite connections (#1222)
* Added close all DB clauses to the end of some QThreads * Created ABThread that auto-closed sqlite-connections * Added run_safely method to ABThread * Updated `CopyDatabaseThread()` as well * Added `python 3.10` to dependencies * Updated github workflows
1 parent ebbc322 commit cf3b312

File tree

8 files changed

+49
-17
lines changed

8 files changed

+49
-17
lines changed

.github/workflows/install-canary.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
fail-fast: false
1616
matrix:
1717
os: [ubuntu-latest, windows-latest, macos-latest]
18-
python-version: ['3.8', '3.9', '3.11']
18+
python-version: ['3.8', '3.9', '3.10', '3.11']
1919
defaults:
2020
run:
2121
shell: bash -l {0}

.github/workflows/main.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
fail-fast: false
3434
matrix:
3535
os: [ubuntu-latest, windows-latest, macos-latest]
36-
python-version: ['3.8', '3.9', '3.11']
36+
python-version: ['3.8', '3.9', '3.10', '3.11']
3737
defaults:
3838
run:
3939
shell: bash -l {0}

activity_browser/ui/threading.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from PySide2.QtCore import QThread
2+
import brightway2 as bw
3+
4+
5+
class ABThread(QThread):
6+
7+
def run(self):
8+
"""Reimplemented from QThread to close any database connections before finishing."""
9+
# call run_safely and finish by closing the connections
10+
try:
11+
self.run_safely()
12+
self.close_connections()
13+
# also close the connections if any exception occurs
14+
except Exception as e:
15+
self.close_connections()
16+
raise e
17+
18+
def close_connections(self):
19+
"""
20+
Closes all connections for this thread
21+
todo: move to an appropriate controller
22+
"""
23+
for _, SubstitutableDatabase in bw.config.sqlite3_databases:
24+
if not SubstitutableDatabase.db.is_closed():
25+
SubstitutableDatabase.db.close()
26+
27+
def run_safely(self):
28+
raise NotImplementedError

activity_browser/ui/widgets/biosphere_update.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import logging
1010
from activity_browser.logger import ABHandler
11+
from ..threading import ABThread
1112

1213
logger = logging.getLogger('ab_logs')
1314
log = ABHandler.setup_with_logger(logger, __name__)
@@ -40,7 +41,7 @@ def update_progress(self, current: int):
4041
self.setValue(current)
4142

4243

43-
class UpdateBiosphereThread(QtCore.QThread):
44+
class UpdateBiosphereThread(ABThread):
4445
PATCHES = [patch for patch in dir(data) if patch.startswith('add_ecoinvent') and patch.endswith('biosphere_flows')]
4546
progress = Signal(int)
4647
def __init__(self, ei_versions, parent=None):
@@ -51,7 +52,7 @@ def __init__(self, ei_versions, parent=None):
5152

5253
self.total_patches = len(self.PATCHES)
5354

54-
def run(self):
55+
def run_safely(self):
5556
try:
5657
for i, patch in enumerate(self.PATCHES):
5758
self.progress.emit(i)

activity_browser/ui/widgets/database_copy.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from PySide2 import QtCore, QtWidgets
44

55
from ...signals import signals
6+
from ..threading import ABThread
67

78

89
class CopyDatabaseDialog(QtWidgets.QProgressDialog):
@@ -36,7 +37,7 @@ def finished(self, result: int = None) -> None:
3637
signals.databases_changed.emit()
3738

3839

39-
class CopyDatabaseThread(QtCore.QThread):
40+
class CopyDatabaseThread(ABThread):
4041
def __init__(self, parent=None):
4142
super().__init__(parent=parent)
4243
self.copy_from = None
@@ -46,5 +47,5 @@ def configure(self, copy_from: str, copy_to: str):
4647
self.copy_from = copy_from
4748
self.copy_to = copy_to
4849

49-
def run(self):
50+
def run_safely(self):
5051
bw.Database(self.copy_from).copy(self.copy_to)

activity_browser/ui/widgets/dialog.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44

55
import brightway2 as bw
66
from PySide2 import QtGui, QtWidgets
7-
from PySide2.QtCore import QRegExp, QThread, Qt, Signal, Slot
7+
from PySide2.QtCore import Qt, Signal, Slot
88

99
from activity_browser.bwutils.superstructure import get_sheet_names
1010
from activity_browser.settings import project_settings
1111
from activity_browser.signals import signals
12+
from ..threading import ABThread
1213
from ..style import style_group_box, vertical_line
1314
from ...ui.icons import qicons
1415
from ...ui.widgets import BiosphereUpdater
@@ -549,14 +550,14 @@ def check_patches(self):
549550
dialog.show()
550551

551552

552-
class DefaultBiosphereThread(QThread):
553+
class DefaultBiosphereThread(ABThread):
553554
update = Signal(int, str)
554555

555556
def __init__(self, version, parent=None):
556557
super().__init__(parent=parent)
557558
self.version = version
558559

559-
def run(self):
560+
def run_safely(self):
560561
project = "<b>{}</b>".format(bw.projects.current)
561562
if "biosphere3" not in bw.databases:
562563
self.update.emit(0, "Creating default biosphere for {}".format(project))

activity_browser/ui/wizards/db_import_wizard.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from PySide2 import QtWidgets, QtCore
1717
from PySide2.QtCore import Signal, Slot
1818

19+
from ..threading import ABThread
1920
from ...bwutils.errors import ImportCanceledError, LinkingFailed
2021
from ...bwutils.importers import ABExcelImporter, ABPackage
2122
from ...signals import signals
@@ -707,7 +708,7 @@ def report_failed_unarchive(self, file: str) -> None:
707708
return
708709

709710

710-
class MainWorkerThread(QtCore.QThread):
711+
class MainWorkerThread(ABThread):
711712
def __init__(self, downloader, parent=None):
712713
super().__init__(parent)
713714
self.downloader = downloader
@@ -729,7 +730,7 @@ def update(self, db_name: str, archive_path=None, datasets_path=None,
729730
self.use_local = use_local
730731
self.relink = relink or {}
731732

732-
def run(self):
733+
def run_safely(self):
733734
# Set the cancel sentinal to false whenever the thread (re-)starts
734735
import_signals.cancel_sentinel = False
735736
if self.use_forwast:

recipe/meta.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,20 @@ requirements:
2424
- python
2525
- setuptools
2626
run:
27-
- python >=3.8,<=3.11,!=3.10
27+
- python >=3.8,<3.12
2828
- arrow
2929
- brightway2 =2.4.4
30-
- pyperclip
3130
- eidl >=2.0.1
31+
- libxml2 <=2.10.4
3232
- networkx
33+
- numpy >=1.23.5
34+
- pandas <=2.1.4
35+
- pint <=0.21
36+
- pyperclip
3337
- pyside2 >=5.15.5
3438
- qt-webengine
35-
- numpy >=1.23.5
3639
- salib >=1.4
3740
- seaborn
38-
- libxml2 <=2.10.4
39-
- pint <=0.21
40-
- pandas <=2.1.4
4141

4242
about:
4343
home: https://github.com/LCA-ActivityBrowser/activity-browser

0 commit comments

Comments
 (0)