From eb1040ddb7445a49168792871ab417360bdfa4a1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 Dec 2019 13:34:37 +0100 Subject: [PATCH] Convert resolvers to classmethods --- freqtrade/resolvers/exchange_resolver.py | 1 + freqtrade/resolvers/hyperopt_resolver.py | 23 +++++++++------- freqtrade/resolvers/iresolver.py | 34 +++++++++++++----------- freqtrade/resolvers/pairlist_resolver.py | 6 +++-- freqtrade/resolvers/strategy_resolver.py | 12 +++++---- tests/strategy/test_strategy.py | 2 -- 6 files changed, 43 insertions(+), 35 deletions(-) diff --git a/freqtrade/resolvers/exchange_resolver.py b/freqtrade/resolvers/exchange_resolver.py index e28a5cf80..2b6a731a9 100644 --- a/freqtrade/resolvers/exchange_resolver.py +++ b/freqtrade/resolvers/exchange_resolver.py @@ -14,6 +14,7 @@ class ExchangeResolver(IResolver): """ This class contains all the logic to load a custom exchange class """ + object_type = Exchange @staticmethod def load_exchange(exchange_name: str, config: dict, validate: bool = True) -> Exchange: diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py index 0726b0627..b9c750251 100644 --- a/freqtrade/resolvers/hyperopt_resolver.py +++ b/freqtrade/resolvers/hyperopt_resolver.py @@ -20,6 +20,7 @@ class HyperOptResolver(IResolver): """ This class contains all the logic to load custom hyperopt class """ + object_type = IHyperOpt @staticmethod def load_hyperopt(config: Dict) -> IHyperOpt: @@ -59,12 +60,13 @@ class HyperOptResolver(IResolver): """ current_path = Path(__file__).parent.parent.joinpath('optimize').resolve() - abs_paths = IResolver.build_search_paths(config, current_path=current_path, - user_subdir=USERPATH_HYPEROPTS, - extra_dir=extra_dir) + abs_paths = HyperOptResolver.build_search_paths(config, current_path=current_path, + user_subdir=USERPATH_HYPEROPTS, + extra_dir=extra_dir) - hyperopt = IResolver._load_object(paths=abs_paths, object_type=IHyperOpt, - object_name=hyperopt_name, kwargs={'config': config}) + hyperopt = HyperOptResolver._load_object(paths=abs_paths, + object_name=hyperopt_name, + kwargs={'config': config}) if hyperopt: return hyperopt raise OperationalException( @@ -77,6 +79,7 @@ class HyperOptLossResolver(IResolver): """ This class contains all the logic to load custom hyperopt loss class """ + object_type = IHyperOptLoss @staticmethod def load_hyperoptloss(config: Dict) -> IHyperOptLoss: @@ -113,12 +116,12 @@ class HyperOptLossResolver(IResolver): """ current_path = Path(__file__).parent.parent.joinpath('optimize').resolve() - abs_paths = IResolver.build_search_paths(config, current_path=current_path, - user_subdir=USERPATH_HYPEROPTS, - extra_dir=extra_dir) + abs_paths = HyperOptLossResolver.build_search_paths(config, current_path=current_path, + user_subdir=USERPATH_HYPEROPTS, + extra_dir=extra_dir) - hyperoptloss = IResolver._load_object(paths=abs_paths, object_type=IHyperOptLoss, - object_name=hyper_loss_name) + hyperoptloss = HyperOptLossResolver._load_object(paths=abs_paths, + object_name=hyper_loss_name) if hyperoptloss: return hyperoptloss diff --git a/freqtrade/resolvers/iresolver.py b/freqtrade/resolvers/iresolver.py index 0b986debb..11937c1da 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 Any, List, Optional, Tuple, Union, Generator +from typing import Any, Generator, List, Optional, Tuple, Type, Union logger = logging.getLogger(__name__) @@ -16,6 +16,8 @@ class IResolver: """ This class contains all the logic to load custom classes """ + # Childclasses need to override this + object_type: Type[Any] @staticmethod def build_search_paths(config, current_path: Path, user_subdir: Optional[str] = None, @@ -32,12 +34,11 @@ class IResolver: return abs_paths - @staticmethod - def _get_valid_object(object_type, module_path: Path, + @classmethod + def _get_valid_object(cls, module_path: Path, object_name: str) -> Generator[Any, None, None]: """ Generator returning objects with matching object_type and object_name in the path given. - :param object_type: object_type (class) :param module_path: absolute path to the module :param object_name: Class name of the object :return: generator containing matching objects @@ -55,19 +56,21 @@ class IResolver: valid_objects_gen = ( obj for name, obj in inspect.getmembers(module, inspect.isclass) - if object_name == name and object_type in obj.__bases__ + if object_name == name and cls.object_type in obj.__bases__ ) return valid_objects_gen - @staticmethod - def _search_object(directory: Path, object_type, object_name: str, + @classmethod + def _search_object(cls, directory: Path, object_name: str, kwargs: dict = {}) -> Union[Tuple[Any, Path], Tuple[None, None]]: """ Search for the objectname in the given directory :param directory: relative or absolute directory path + :param object_name: ClassName of the object to load :return: object instance """ - logger.debug("Searching for %s %s in '%s'", object_type.__name__, object_name, directory) + logger.debug("Searching for %s %s in '%s'", + cls.object_type.__name__, object_name, directory) for entry in directory.iterdir(): # Only consider python files if not str(entry).endswith('.py'): @@ -75,14 +78,14 @@ class IResolver: continue module_path = entry.resolve() - obj = next(IResolver._get_valid_object(object_type, module_path, object_name), None) + obj = next(cls._get_valid_object(module_path, object_name), None) if obj: return (obj(**kwargs), module_path) return (None, None) - @staticmethod - def _load_object(paths: List[Path], object_type, object_name: str, + @classmethod + def _load_object(cls, paths: List[Path], object_name: str, kwargs: dict = {}) -> Optional[Any]: """ Try to load object from path list. @@ -90,13 +93,12 @@ class IResolver: for _path in paths: try: - (module, module_path) = IResolver._search_object(directory=_path, - object_type=object_type, - object_name=object_name, - kwargs=kwargs) + (module, module_path) = cls._search_object(directory=_path, + object_name=object_name, + kwargs=kwargs) if module: logger.info( - f"Using resolved {object_type.__name__.lower()[1:]} {object_name} " + f"Using resolved {cls.object_type.__name__.lower()[1:]} {object_name} " f"from '{module_path}'...") return module except FileNotFoundError: diff --git a/freqtrade/resolvers/pairlist_resolver.py b/freqtrade/resolvers/pairlist_resolver.py index 611660ff4..00ebc03aa 100644 --- a/freqtrade/resolvers/pairlist_resolver.py +++ b/freqtrade/resolvers/pairlist_resolver.py @@ -17,6 +17,7 @@ class PairListResolver(IResolver): """ This class contains all the logic to load custom PairList class """ + object_type = IPairList @staticmethod def load_pairlist(pairlist_name: str, exchange, pairlistmanager, @@ -53,8 +54,9 @@ class PairListResolver(IResolver): abs_paths = IResolver.build_search_paths(config, current_path=current_path, user_subdir=None, extra_dir=None) - pairlist = IResolver._load_object(paths=abs_paths, object_type=IPairList, - object_name=pairlist_name, kwargs=kwargs) + pairlist = PairListResolver._load_object(paths=abs_paths, + object_name=pairlist_name, + kwargs=kwargs) if pairlist: return pairlist raise OperationalException( diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 6d3fe5ff9..654103377 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -22,6 +22,7 @@ class StrategyResolver(IResolver): """ This class contains the logic to load custom strategy class """ + object_type = IStrategy @staticmethod def load_strategy(config: Optional[Dict] = None) -> IStrategy: @@ -134,9 +135,9 @@ class StrategyResolver(IResolver): """ current_path = Path(__file__).parent.parent.joinpath('strategy').resolve() - abs_paths = IResolver.build_search_paths(config, current_path=current_path, - user_subdir=constants.USERPATH_STRATEGY, - extra_dir=extra_dir) + abs_paths = StrategyResolver.build_search_paths(config, current_path=current_path, + user_subdir=constants.USERPATH_STRATEGY, + extra_dir=extra_dir) if ":" in strategy_name: logger.info("loading base64 encoded strategy") @@ -154,8 +155,9 @@ class StrategyResolver(IResolver): # register temp path with the bot abs_paths.insert(0, temp.resolve()) - strategy = IResolver._load_object(paths=abs_paths, object_type=IStrategy, - object_name=strategy_name, kwargs={'config': config}) + strategy = StrategyResolver._load_object(paths=abs_paths, + object_name=strategy_name, + kwargs={'config': config}) if strategy: strategy._populate_fun_len = len(getfullargspec(strategy.populate_indicators).args) strategy._buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args) diff --git a/tests/strategy/test_strategy.py b/tests/strategy/test_strategy.py index ce7ac1741..dba816621 100644 --- a/tests/strategy/test_strategy.py +++ b/tests/strategy/test_strategy.py @@ -20,7 +20,6 @@ def test_search_strategy(): s, _ = StrategyResolver._search_object( directory=default_location, - object_type=IStrategy, kwargs={'config': default_config}, object_name='DefaultStrategy' ) @@ -28,7 +27,6 @@ def test_search_strategy(): s, _ = StrategyResolver._search_object( directory=default_location, - object_type=IStrategy, kwargs={'config': default_config}, object_name='NotFoundStrategy' )