handle sqlalchemy NoSuchModuleError

This commit is contained in:
gcarq 2018-06-07 21:35:57 +02:00
parent 3f5efef6e5
commit d41f71bc34
4 changed files with 31 additions and 5 deletions

View File

@ -12,7 +12,8 @@ class DependencyException(BaseException):
class OperationalException(BaseException): class OperationalException(BaseException):
""" """
Requires manual intervention. 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.
""" """

View File

@ -7,6 +7,7 @@ import logging
import sys import sys
from typing import List from typing import List
from freqtrade import OperationalException
from freqtrade.arguments import Arguments from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration from freqtrade.configuration import Configuration
from freqtrade.freqtradebot import FreqtradeBot from freqtrade.freqtradebot import FreqtradeBot
@ -47,6 +48,9 @@ def main(sysargv: List[str]) -> None:
except KeyboardInterrupt: except KeyboardInterrupt:
logger.info('SIGINT received, aborting ...') logger.info('SIGINT received, aborting ...')
return_code = 0 return_code = 0
except OperationalException as e:
logger.error(str(e))
return_code = 2
except BaseException: except BaseException:
logger.exception('Fatal exception!') logger.exception('Fatal exception!')
finally: finally:

View File

@ -11,11 +11,14 @@ import arrow
from sqlalchemy import (Boolean, Column, DateTime, Float, Integer, String, from sqlalchemy import (Boolean, Column, DateTime, Float, Integer, String,
create_engine) create_engine)
from sqlalchemy import inspect from sqlalchemy import inspect
from sqlalchemy.exc import NoSuchModuleError
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm.scoping import scoped_session from sqlalchemy.orm.scoping import scoped_session
from sqlalchemy.orm.session import sessionmaker from sqlalchemy.orm.session import sessionmaker
from sqlalchemy.pool import StaticPool from sqlalchemy.pool import StaticPool
from freqtrade import OperationalException
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_CONF = {} _CONF = {}
@ -43,7 +46,14 @@ def init(config: Dict) -> None:
'echo': False, '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)) session = scoped_session(sessionmaker(bind=engine, autoflush=True, autocommit=True))
Trade.session = session() Trade.session = session()
Trade.query = session.query_property() Trade.query = session.query_property()

View File

@ -5,7 +5,7 @@ from unittest.mock import MagicMock
import pytest import pytest
from sqlalchemy import create_engine from sqlalchemy import create_engine
from freqtrade import constants from freqtrade import constants, OperationalException
from freqtrade.persistence import Trade, init, clean_dry_run_db 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) conf = deepcopy(default_conf)
# Update path to a value other than default, but still in-memory # 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()) create_engine_mock = mocker.patch('freqtrade.persistence.create_engine', MagicMock())
mocker.patch.dict('freqtrade.persistence._CONF', conf) mocker.patch.dict('freqtrade.persistence._CONF', conf)
init(conf) init(conf)
assert create_engine_mock.call_count == 1 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): def test_init_prod_db(default_conf, mocker):