minor improvements to resolvers

This commit is contained in:
hroff-1902 2019-07-12 23:45:49 +03:00
parent 7536f6adbd
commit b499e74502
7 changed files with 62 additions and 52 deletions

View File

@ -45,13 +45,13 @@ class ExchangeResolver(IResolver):
exchange = ex_class(kwargs['config']) exchange = ex_class(kwargs['config'])
if exchange: if exchange:
logger.info("Using resolved exchange %s", exchange_name) logger.info(f"Using resolved exchange '{exchange_name}'...")
return exchange return exchange
except AttributeError: except AttributeError:
# Pass and raise ImportError instead # Pass and raise OperationalException instead
pass pass
raise ImportError( raise ImportError(
"Impossible to load Exchange '{}'. This class does not exist" f"Impossible to load Exchange '{exchange_name}'. This class does not exist "
" or contains Python code errors".format(exchange_name) "or contains Python code errors."
) )

View File

@ -7,6 +7,7 @@ import logging
from pathlib import Path from pathlib import Path
from typing import Optional, Dict from typing import Optional, Dict
from freqtrade import OperationalException
from freqtrade.constants import DEFAULT_HYPEROPT from freqtrade.constants import DEFAULT_HYPEROPT
from freqtrade.optimize.hyperopt_interface import IHyperOpt from freqtrade.optimize.hyperopt_interface import IHyperOpt
from freqtrade.resolvers import IResolver from freqtrade.resolvers import IResolver
@ -63,15 +64,16 @@ class HyperOptResolver(IResolver):
for _path in abs_paths: for _path in abs_paths:
try: try:
hyperopt = self._search_object(directory=_path, object_type=IHyperOpt, (hyperopt, module_path) = self._search_object(directory=_path,
object_name=hyperopt_name) object_type=IHyperOpt,
object_name=hyperopt_name)
if hyperopt: if hyperopt:
logger.info("Using resolved hyperopt %s from '%s'", hyperopt_name, _path) logger.info(f"Using resolved hyperopt {hyperopt_name} from '{module_path}'...")
return hyperopt return hyperopt
except FileNotFoundError: except FileNotFoundError:
logger.warning('Path "%s" does not exist', _path.relative_to(Path.cwd())) logger.warning('Path "%s" does not exist.', _path.relative_to(Path.cwd()))
raise ImportError( raise OperationalException(
"Impossible to load Hyperopt '{}'. This class does not exist" f"Impossible to load Hyperopt '{hyperopt_name}'. This class does not exist "
" or contains Python code errors".format(hyperopt_name) "or contains Python code errors."
) )

View File

@ -7,7 +7,7 @@ import importlib.util
import inspect import inspect
import logging import logging
from pathlib import Path from pathlib import Path
from typing import Optional, Type, Any from typing import Any, Optional, Tuple, Type, Union
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -45,7 +45,7 @@ class IResolver(object):
@staticmethod @staticmethod
def _search_object(directory: Path, object_type, object_name: str, def _search_object(directory: Path, object_type, object_name: str,
kwargs: dict = {}) -> Optional[Any]: kwargs: dict = {}) -> Union[Tuple[Any, Path], Tuple[None, None]]:
""" """
Search for the objectname in the given directory Search for the objectname in the given directory
:param directory: relative or absolute directory path :param directory: relative or absolute directory path
@ -57,9 +57,10 @@ class IResolver(object):
if not str(entry).endswith('.py'): if not str(entry).endswith('.py'):
logger.debug('Ignoring %s', entry) logger.debug('Ignoring %s', entry)
continue continue
module_path = Path.resolve(directory.joinpath(entry))
obj = IResolver._get_valid_object( obj = IResolver._get_valid_object(
object_type, Path.resolve(directory.joinpath(entry)), object_name object_type, module_path, object_name
) )
if obj: if obj:
return obj(**kwargs) return (obj(**kwargs), module_path)
return None return (None, None)

View File

@ -6,6 +6,7 @@ This module load custom hyperopts
import logging import logging
from pathlib import Path from pathlib import Path
from freqtrade import OperationalException
from freqtrade.pairlist.IPairList import IPairList from freqtrade.pairlist.IPairList import IPairList
from freqtrade.resolvers import IResolver from freqtrade.resolvers import IResolver
@ -44,16 +45,17 @@ class PairListResolver(IResolver):
for _path in abs_paths: for _path in abs_paths:
try: try:
pairlist = self._search_object(directory=_path, object_type=IPairList, (pairlist, module_path) = self._search_object(directory=_path,
object_name=pairlist_name, object_type=IPairList,
kwargs=kwargs) object_name=pairlist_name,
kwargs=kwargs)
if pairlist: if pairlist:
logger.info("Using resolved pairlist %s from '%s'", pairlist_name, _path) logger.info(f"Using resolved pairlist {pairlist_name} from '{module_path}'...")
return pairlist return pairlist
except FileNotFoundError: except FileNotFoundError:
logger.warning('Path "%s" does not exist', _path.relative_to(Path.cwd())) logger.warning('Path "%s" does not exist.', _path.relative_to(Path.cwd()))
raise ImportError( raise OperationalException(
"Impossible to load Pairlist '{}'. This class does not exist" f"Impossible to load Pairlist '{pairlist_name}'. This class does not exist "
" or contains Python code errors".format(pairlist_name) "or contains Python code errors."
) )

View File

@ -11,7 +11,7 @@ from inspect import getfullargspec
from pathlib import Path from pathlib import Path
from typing import Dict, Optional from typing import Dict, Optional
from freqtrade import constants from freqtrade import constants, OperationalException
from freqtrade.resolvers import IResolver from freqtrade.resolvers import IResolver
from freqtrade.strategy import import_strategy from freqtrade.strategy import import_strategy
from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.interface import IStrategy
@ -149,10 +149,12 @@ class StrategyResolver(IResolver):
for _path in abs_paths: for _path in abs_paths:
try: try:
strategy = self._search_object(directory=_path, object_type=IStrategy, (strategy, module_path) = self._search_object(directory=_path,
object_name=strategy_name, kwargs={'config': config}) object_type=IStrategy,
object_name=strategy_name,
kwargs={'config': config})
if strategy: if strategy:
logger.info("Using resolved strategy %s from '%s'", strategy_name, _path) logger.info(f"Using resolved strategy {strategy_name} from '{module_path}'...")
strategy._populate_fun_len = len( strategy._populate_fun_len = len(
getfullargspec(strategy.populate_indicators).args) getfullargspec(strategy.populate_indicators).args)
strategy._buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args) strategy._buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args)
@ -161,11 +163,12 @@ class StrategyResolver(IResolver):
return import_strategy(strategy, config=config) return import_strategy(strategy, config=config)
except TypeError as e: except TypeError as e:
logger.warning( logger.warning(
f"Impossible to load strategy '{strategy}' from {_path}. Error: {e}") f"Impossible to load strategy '{strategy_name}' from {module_path}. "
f"Error: {e}")
except FileNotFoundError: except FileNotFoundError:
logger.warning('Path "%s" does not exist', _path.relative_to(Path.cwd())) logger.warning('Path "%s" does not exist.', _path.relative_to(Path.cwd()))
raise ImportError( raise OperationalException(
f"Impossible to load Strategy '{strategy_name}'. This class does not exist" f"Impossible to load Strategy '{strategy_name}'. This class does not exist "
" or contains Python code errors" "or contains Python code errors."
) )

View File

@ -34,9 +34,9 @@ def whitelist_conf(default_conf):
def test_load_pairlist_noexist(mocker, markets, default_conf): def test_load_pairlist_noexist(mocker, markets, default_conf):
freqtradebot = get_patched_freqtradebot(mocker, default_conf) freqtradebot = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets))
with pytest.raises(ImportError, with pytest.raises(OperationalException,
match=r"Impossible to load Pairlist 'NonexistingPairList'." match=r"Impossible to load Pairlist 'NonexistingPairList'. "
r" This class does not exist or contains Python code errors"): r"This class does not exist or contains Python code errors."):
PairListResolver('NonexistingPairList', freqtradebot, default_conf).pairlist PairListResolver('NonexistingPairList', freqtradebot, default_conf).pairlist

View File

@ -9,6 +9,7 @@ from unittest.mock import Mock
import pytest import pytest
from pandas import DataFrame from pandas import DataFrame
from freqtrade import OperationalException
from freqtrade.resolvers import StrategyResolver from freqtrade.resolvers import StrategyResolver
from freqtrade.strategy import import_strategy from freqtrade.strategy import import_strategy
from freqtrade.strategy.default_strategy import DefaultStrategy from freqtrade.strategy.default_strategy import DefaultStrategy
@ -44,21 +45,22 @@ def test_import_strategy(caplog):
def test_search_strategy(): def test_search_strategy():
default_config = {} default_config = {}
default_location = Path(__file__).parent.parent.joinpath('strategy').resolve() default_location = Path(__file__).parent.parent.joinpath('strategy').resolve()
assert isinstance(
StrategyResolver._search_object( s, _ = StrategyResolver._search_object(
directory=default_location, directory=default_location,
object_type=IStrategy, object_type=IStrategy,
kwargs={'config': default_config}, kwargs={'config': default_config},
object_name='DefaultStrategy' object_name='DefaultStrategy'
),
IStrategy
) )
assert StrategyResolver._search_object( assert isinstance(s, IStrategy)
s, _ = StrategyResolver._search_object(
directory=default_location, directory=default_location,
object_type=IStrategy, object_type=IStrategy,
kwargs={'config': default_config}, kwargs={'config': default_config},
object_name='NotFoundStrategy' object_name='NotFoundStrategy'
) is None )
assert s is None
def test_load_strategy(result): def test_load_strategy(result):
@ -85,18 +87,18 @@ def test_load_strategy_invalid_directory(result, caplog):
def test_load_not_found_strategy(): def test_load_not_found_strategy():
strategy = StrategyResolver() strategy = StrategyResolver()
with pytest.raises(ImportError, with pytest.raises(OperationalException,
match=r"Impossible to load Strategy 'NotFoundStrategy'." match=r"Impossible to load Strategy 'NotFoundStrategy'. "
r" This class does not exist or contains Python code errors"): r"This class does not exist or contains Python code errors."):
strategy._load_strategy(strategy_name='NotFoundStrategy', config={}) strategy._load_strategy(strategy_name='NotFoundStrategy', config={})
def test_load_staticmethod_importerror(mocker, caplog): def test_load_staticmethod_importerror(mocker, caplog):
mocker.patch("freqtrade.resolvers.strategy_resolver.import_strategy", Mock( mocker.patch("freqtrade.resolvers.strategy_resolver.import_strategy", Mock(
side_effect=TypeError("can't pickle staticmethod objects"))) side_effect=TypeError("can't pickle staticmethod objects")))
with pytest.raises(ImportError, with pytest.raises(OperationalException,
match=r"Impossible to load Strategy 'DefaultStrategy'." match=r"Impossible to load Strategy 'DefaultStrategy'. "
r" This class does not exist or contains Python code errors"): r"This class does not exist or contains Python code errors."):
StrategyResolver() StrategyResolver()
assert log_has_re(r".*Error: can't pickle staticmethod objects", caplog.record_tuples) assert log_has_re(r".*Error: can't pickle staticmethod objects", caplog.record_tuples)