Merge pull request #2017 from hroff-1902/resolver-filename
minor: improvements to resolvers
This commit is contained in:
		| @@ -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." | ||||
|         ) | ||||
|   | ||||
| @@ -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." | ||||
|         ) | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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." | ||||
|         ) | ||||
|   | ||||
| @@ -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." | ||||
|         ) | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user