Skip to content

Commit 29d3ab1

Browse files
committed
fix: resolve Windows database locking and Unicode encoding issues
- Call teardown_sqlalchemy() before os.remove() in initialize_storage() to release SQLite file locks on Windows (PermissionError WinError 32) - Use remove_database_if_exists=True in test setUp to prevent stale DB state between tests - Replace manual os.remove/os.rmdir loops with shutil.rmtree in web controller test teardowns - Open verbose output file with encoding='utf-8' in validate_backtest_checkpoints to fix UnicodeEncodeError on Windows (cp1252 cannot encode checkmark/cross characters)
1 parent 7aa0070 commit 29d3ab1

6 files changed

Lines changed: 16 additions & 24 deletions

File tree

investing_algorithm_framework/app/app.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,13 @@ def initialize_storage(self, remove_database_if_exists: bool = False):
368368
logger.info(
369369
f"Removing existing database at {database_path}"
370370
)
371+
372+
# Dispose the existing engine to release file locks
373+
# (required on Windows where locks are mandatory)
374+
from investing_algorithm_framework.infrastructure.database \
375+
import teardown_sqlalchemy
376+
teardown_sqlalchemy()
377+
371378
os.remove(database_path)
372379

373380
# Create the sqlalchemy database uri

investing_algorithm_framework/cli/validate_backtest_checkpoints.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def validate_and_create_checkpoints(
5050
verbose_file_handle = None
5151

5252
if verbose_output_file is not None:
53-
verbose_file_handle = open(verbose_output_file, 'w')
53+
verbose_file_handle = open(verbose_output_file, 'w', encoding='utf-8')
5454

5555
def echo(msg):
5656
verbose_file_handle.write(msg + "\n")

tests/app/web/controllers/order_controller/test_list_orders.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
import os
3+
import shutil
34

45
from investing_algorithm_framework import PortfolioConfiguration, \
56
MarketCredential
@@ -33,13 +34,7 @@ def tearDown(self) -> None:
3334
)
3435

3536
if os.path.exists(database_dir):
36-
for root, dirs, files in os.walk(database_dir, topdown=False):
37-
for name in files:
38-
os.remove(os.path.join(root, name))
39-
for name in dirs:
40-
os.rmdir(os.path.join(root, name))
41-
42-
os.rmdir(database_dir)
37+
shutil.rmtree(database_dir, ignore_errors=True)
4338

4439
def test_list_portfolios(self):
4540
self.iaf_app.add_strategy(StrategyOne)

tests/app/web/controllers/portfolio_controller/test_list_portfolio.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
import os
3+
import shutil
34

45
from investing_algorithm_framework import MarketCredential, \
56
PortfolioConfiguration, DataSource, INDEX_DATETIME
@@ -33,13 +34,7 @@ def tearDown(self) -> None:
3334
)
3435

3536
if os.path.exists(database_dir):
36-
for root, dirs, files in os.walk(database_dir, topdown=False):
37-
for name in files:
38-
os.remove(os.path.join(root, name))
39-
for name in dirs:
40-
os.rmdir(os.path.join(root, name))
41-
42-
os.rmdir(database_dir)
37+
shutil.rmtree(database_dir, ignore_errors=True)
4338

4439
def test_list_portfolios(self):
4540
strategy = StrategyOne()

tests/app/web/controllers/position_controller/test_list_positions.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
import os
3+
import shutil
34

45
from investing_algorithm_framework import PortfolioConfiguration, \
56
MarketCredential
@@ -33,13 +34,7 @@ def tearDown(self) -> None:
3334
)
3435

3536
if os.path.exists(database_dir):
36-
for root, dirs, files in os.walk(database_dir, topdown=False):
37-
for name in files:
38-
os.remove(os.path.join(root, name))
39-
for name in dirs:
40-
os.rmdir(os.path.join(root, name))
41-
42-
os.rmdir(database_dir)
37+
shutil.rmtree(database_dir, ignore_errors=True)
4338

4439
def test_list_portfolios(self):
4540
self.iaf_app.add_strategy(StrategyOne)

tests/resources/test_base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def setUp(self) -> None:
8383

8484
if self.initialize:
8585
self.app.initialize_config()
86-
self.app.initialize_storage()
86+
self.app.initialize_storage(remove_database_if_exists=True)
8787
self.app.initialize_services()
8888
self.app.initialize_portfolios()
8989

@@ -215,7 +215,7 @@ def create_app(self):
215215

216216
if self.initialize:
217217
self.iaf_app.initialize_config()
218-
self.iaf_app.initialize_storage()
218+
self.iaf_app.initialize_storage(remove_database_if_exists=True)
219219
self.iaf_app.initialize_services()
220220
self.iaf_app.initialize_portfolios()
221221

0 commit comments

Comments
 (0)