handle sqlalchemy NoSuchModuleError
This commit is contained in:
parent
3f5efef6e5
commit
d41f71bc34
@ -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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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()
|
||||||
|
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user