diff --git a/freqtrade/resolvers/exchange_resolver.py b/freqtrade/resolvers/exchange_resolver.py index 089f6306f..6fb12a65f 100644 --- a/freqtrade/resolvers/exchange_resolver.py +++ b/freqtrade/resolvers/exchange_resolver.py @@ -45,13 +45,13 @@ class ExchangeResolver(IResolver): exchange = ex_class(kwargs['config']) if exchange: - logger.info("Using resolved exchange %s", exchange_name) + logger.info(f"Using resolved exchange '{exchange_name}'...") return exchange except AttributeError: # Pass and raise ImportError instead pass raise ImportError( - "Impossible to load Exchange '{}'. This class does not exist" - " or contains Python code errors".format(exchange_name) + f"Impossible to load Exchange '{exchange_name}'. This class does not exist " + "or contains Python code errors." ) diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py index 9333bb09a..30e097f3f 100644 --- a/freqtrade/resolvers/hyperopt_resolver.py +++ b/freqtrade/resolvers/hyperopt_resolver.py @@ -7,6 +7,7 @@ import logging from pathlib import Path from typing import Optional, Dict +from freqtrade import OperationalException from freqtrade.constants import DEFAULT_HYPEROPT from freqtrade.optimize.hyperopt_interface import IHyperOpt from freqtrade.resolvers import IResolver @@ -63,15 +64,16 @@ class HyperOptResolver(IResolver): for _path in abs_paths: try: - hyperopt = self._search_object(directory=_path, object_type=IHyperOpt, - object_name=hyperopt_name) + (hyperopt, module_path) = self._search_object(directory=_path, + object_type=IHyperOpt, + object_name=hyperopt_name) 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 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( - "Impossible to load Hyperopt '{}'. This class does not exist" - " or contains Python code errors".format(hyperopt_name) + raise OperationalException( + f"Impossible to load Hyperopt '{hyperopt_name}'. This class does not exist " + "or contains Python code errors." ) diff --git a/freqtrade/resolvers/iresolver.py b/freqtrade/resolvers/iresolver.py index 86b9a799b..1065abba7 100644 --- a/freqtrade/resolvers/iresolver.py +++ b/freqtrade/resolvers/iresolver.py @@ -7,7 +7,7 @@ import importlib.util import inspect import logging from pathlib import Path -from typing import Optional, Type, Any +from typing import Any, Optional, Tuple, Type, Union logger = logging.getLogger(__name__) @@ -45,7 +45,7 @@ class IResolver(object): @staticmethod 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 :param directory: relative or absolute directory path @@ -57,9 +57,10 @@ class IResolver(object): if not str(entry).endswith('.py'): logger.debug('Ignoring %s', entry) continue + module_path = Path.resolve(directory.joinpath(entry)) obj = IResolver._get_valid_object( - object_type, Path.resolve(directory.joinpath(entry)), object_name + object_type, module_path, object_name ) if obj: - return obj(**kwargs) - return None + return (obj(**kwargs), module_path) + return (None, None) diff --git a/freqtrade/resolvers/pairlist_resolver.py b/freqtrade/resolvers/pairlist_resolver.py index 4306a9669..ac67ca496 100644 --- a/freqtrade/resolvers/pairlist_resolver.py +++ b/freqtrade/resolvers/pairlist_resolver.py @@ -6,6 +6,7 @@ This module load custom hyperopts import logging from pathlib import Path +from freqtrade import OperationalException from freqtrade.pairlist.IPairList import IPairList from freqtrade.resolvers import IResolver @@ -44,16 +45,17 @@ class PairListResolver(IResolver): for _path in abs_paths: try: - pairlist = self._search_object(directory=_path, object_type=IPairList, - object_name=pairlist_name, - kwargs=kwargs) + (pairlist, module_path) = self._search_object(directory=_path, + object_type=IPairList, + object_name=pairlist_name, + kwargs=kwargs) 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 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( - "Impossible to load Pairlist '{}'. This class does not exist" - " or contains Python code errors".format(pairlist_name) + raise OperationalException( + f"Impossible to load Pairlist '{pairlist_name}'. This class does not exist " + "or contains Python code errors." ) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 47218bef4..4a5604db8 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -11,7 +11,7 @@ from inspect import getfullargspec from pathlib import Path from typing import Dict, Optional -from freqtrade import constants +from freqtrade import constants, OperationalException from freqtrade.resolvers import IResolver from freqtrade.strategy import import_strategy from freqtrade.strategy.interface import IStrategy @@ -149,10 +149,12 @@ class StrategyResolver(IResolver): for _path in abs_paths: try: - strategy = self._search_object(directory=_path, object_type=IStrategy, - object_name=strategy_name, kwargs={'config': config}) + (strategy, module_path) = self._search_object(directory=_path, + object_type=IStrategy, + object_name=strategy_name, + kwargs={'config': config}) 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( getfullargspec(strategy.populate_indicators).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) except TypeError as e: 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: - 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( - f"Impossible to load Strategy '{strategy_name}'. This class does not exist" - " or contains Python code errors" + raise OperationalException( + f"Impossible to load Strategy '{strategy_name}'. This class does not exist " + "or contains Python code errors." ) diff --git a/freqtrade/tests/pairlist/test_pairlist.py b/freqtrade/tests/pairlist/test_pairlist.py index 38a8d78c7..e7439bb51 100644 --- a/freqtrade/tests/pairlist/test_pairlist.py +++ b/freqtrade/tests/pairlist/test_pairlist.py @@ -34,9 +34,9 @@ def whitelist_conf(default_conf): def test_load_pairlist_noexist(mocker, markets, default_conf): freqtradebot = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) - with pytest.raises(ImportError, - match=r"Impossible to load Pairlist 'NonexistingPairList'." - r" This class does not exist or contains Python code errors"): + with pytest.raises(OperationalException, + match=r"Impossible to load Pairlist 'NonexistingPairList'. " + r"This class does not exist or contains Python code errors."): PairListResolver('NonexistingPairList', freqtradebot, default_conf).pairlist diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py index 8971b041c..904f84946 100644 --- a/freqtrade/tests/strategy/test_strategy.py +++ b/freqtrade/tests/strategy/test_strategy.py @@ -9,6 +9,7 @@ from unittest.mock import Mock import pytest from pandas import DataFrame +from freqtrade import OperationalException from freqtrade.resolvers import StrategyResolver from freqtrade.strategy import import_strategy from freqtrade.strategy.default_strategy import DefaultStrategy @@ -44,21 +45,22 @@ def test_import_strategy(caplog): def test_search_strategy(): default_config = {} default_location = Path(__file__).parent.parent.joinpath('strategy').resolve() - assert isinstance( - StrategyResolver._search_object( - directory=default_location, - object_type=IStrategy, - kwargs={'config': default_config}, - object_name='DefaultStrategy' - ), - IStrategy + + s, _ = StrategyResolver._search_object( + directory=default_location, + object_type=IStrategy, + kwargs={'config': default_config}, + object_name='DefaultStrategy' ) - assert StrategyResolver._search_object( + assert isinstance(s, IStrategy) + + s, _ = StrategyResolver._search_object( directory=default_location, object_type=IStrategy, kwargs={'config': default_config}, object_name='NotFoundStrategy' - ) is None + ) + assert s is None def test_load_strategy(result): @@ -85,18 +87,18 @@ def test_load_strategy_invalid_directory(result, caplog): def test_load_not_found_strategy(): strategy = StrategyResolver() - with pytest.raises(ImportError, - match=r"Impossible to load Strategy 'NotFoundStrategy'." - r" This class does not exist or contains Python code errors"): + with pytest.raises(OperationalException, + match=r"Impossible to load Strategy 'NotFoundStrategy'. " + r"This class does not exist or contains Python code errors."): strategy._load_strategy(strategy_name='NotFoundStrategy', config={}) def test_load_staticmethod_importerror(mocker, caplog): mocker.patch("freqtrade.resolvers.strategy_resolver.import_strategy", Mock( side_effect=TypeError("can't pickle staticmethod objects"))) - with pytest.raises(ImportError, - match=r"Impossible to load Strategy 'DefaultStrategy'." - r" This class does not exist or contains Python code errors"): + with pytest.raises(OperationalException, + match=r"Impossible to load Strategy 'DefaultStrategy'. " + r"This class does not exist or contains Python code errors."): StrategyResolver() assert log_has_re(r".*Error: can't pickle staticmethod objects", caplog.record_tuples)