From d41f71bc34c961fe3075cc77071abc645461db7b Mon Sep 17 00:00:00 2001 From: gcarq Date: Thu, 7 Jun 2018 21:35:57 +0200 Subject: [PATCH] handle sqlalchemy NoSuchModuleError --- freqtrade/__init__.py | 3 ++- freqtrade/main.py | 4 ++++ freqtrade/persistence.py | 12 +++++++++++- freqtrade/tests/test_persistence.py | 17 ++++++++++++++--- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 37187a404..7cf0fa996 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -12,7 +12,8 @@ class DependencyException(BaseException): class OperationalException(BaseException): """ Requires manual intervention. - This happens when an exchange returns an unexpected error during runtime. + This happens when an exchange returns an unexpected error during runtime + or given configuration is invalid. """ diff --git a/freqtrade/main.py b/freqtrade/main.py index 973ed031d..81e578810 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -7,6 +7,7 @@ import logging import sys from typing import List +from freqtrade import OperationalException from freqtrade.arguments import Arguments from freqtrade.configuration import Configuration from freqtrade.freqtradebot import FreqtradeBot @@ -47,6 +48,9 @@ def main(sysargv: List[str]) -> None: except KeyboardInterrupt: logger.info('SIGINT received, aborting ...') return_code = 0 + except OperationalException as e: + logger.error(str(e)) + return_code = 2 except BaseException: logger.exception('Fatal exception!') finally: diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index ce834bced..7fd8fdeb9 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -11,11 +11,14 @@ import arrow from sqlalchemy import (Boolean, Column, DateTime, Float, Integer, String, create_engine) from sqlalchemy import inspect +from sqlalchemy.exc import NoSuchModuleError from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm.scoping import scoped_session from sqlalchemy.orm.session import sessionmaker from sqlalchemy.pool import StaticPool +from freqtrade import OperationalException + logger = logging.getLogger(__name__) _CONF = {} @@ -43,7 +46,14 @@ def init(config: Dict) -> None: 'echo': False, }) - engine = create_engine(db_url, **kwargs) + try: + engine = create_engine(db_url, **kwargs) + except NoSuchModuleError: + error = 'Given value for db_url: \'{}\' is no valid database URL! (See {}).'.format( + db_url, 'http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls' + ) + raise OperationalException(error) + session = scoped_session(sessionmaker(bind=engine, autoflush=True, autocommit=True)) Trade.session = session() Trade.query = session.query_property() diff --git a/freqtrade/tests/test_persistence.py b/freqtrade/tests/test_persistence.py index d8d42461b..c50ad7d2c 100644 --- a/freqtrade/tests/test_persistence.py +++ b/freqtrade/tests/test_persistence.py @@ -5,7 +5,7 @@ from unittest.mock import MagicMock import pytest from sqlalchemy import create_engine -from freqtrade import constants +from freqtrade import constants, OperationalException from freqtrade.persistence import Trade, init, clean_dry_run_db @@ -27,13 +27,24 @@ def test_init_custom_db_url(default_conf, mocker): conf = deepcopy(default_conf) # Update path to a value other than default, but still in-memory - conf.update({'db_url': 'sqlite:////tmp/freqtrade2_test.sqlite'}) + conf.update({'db_url': 'sqlite:///tmp/freqtrade2_test.sqlite'}) create_engine_mock = mocker.patch('freqtrade.persistence.create_engine', MagicMock()) mocker.patch.dict('freqtrade.persistence._CONF', conf) init(conf) assert create_engine_mock.call_count == 1 - assert create_engine_mock.mock_calls[0][1][0] == 'sqlite:////tmp/freqtrade2_test.sqlite' + assert create_engine_mock.mock_calls[0][1][0] == 'sqlite:///tmp/freqtrade2_test.sqlite' + + +def test_init_invalid_db_url(default_conf, mocker): + conf = deepcopy(default_conf) + + # Update path to a value other than default, but still in-memory + conf.update({'db_url': 'unknown:///some.url'}) + mocker.patch.dict('freqtrade.persistence._CONF', conf) + + with pytest.raises(OperationalException, match=r'.*no valid database URL*'): + init(conf) def test_init_prod_db(default_conf, mocker):