Convert resolvers to classmethods

This commit is contained in:
Matthias 2019-12-24 13:34:37 +01:00
parent a68445692b
commit eb1040ddb7
6 changed files with 43 additions and 35 deletions

View File

@ -14,6 +14,7 @@ class ExchangeResolver(IResolver):
""" """
This class contains all the logic to load a custom exchange class This class contains all the logic to load a custom exchange class
""" """
object_type = Exchange
@staticmethod @staticmethod
def load_exchange(exchange_name: str, config: dict, validate: bool = True) -> Exchange: def load_exchange(exchange_name: str, config: dict, validate: bool = True) -> Exchange:

View File

@ -20,6 +20,7 @@ class HyperOptResolver(IResolver):
""" """
This class contains all the logic to load custom hyperopt class This class contains all the logic to load custom hyperopt class
""" """
object_type = IHyperOpt
@staticmethod @staticmethod
def load_hyperopt(config: Dict) -> IHyperOpt: def load_hyperopt(config: Dict) -> IHyperOpt:
@ -59,12 +60,13 @@ class HyperOptResolver(IResolver):
""" """
current_path = Path(__file__).parent.parent.joinpath('optimize').resolve() current_path = Path(__file__).parent.parent.joinpath('optimize').resolve()
abs_paths = IResolver.build_search_paths(config, current_path=current_path, abs_paths = HyperOptResolver.build_search_paths(config, current_path=current_path,
user_subdir=USERPATH_HYPEROPTS, user_subdir=USERPATH_HYPEROPTS,
extra_dir=extra_dir) extra_dir=extra_dir)
hyperopt = IResolver._load_object(paths=abs_paths, object_type=IHyperOpt, hyperopt = HyperOptResolver._load_object(paths=abs_paths,
object_name=hyperopt_name, kwargs={'config': config}) object_name=hyperopt_name,
kwargs={'config': config})
if hyperopt: if hyperopt:
return hyperopt return hyperopt
raise OperationalException( raise OperationalException(
@ -77,6 +79,7 @@ class HyperOptLossResolver(IResolver):
""" """
This class contains all the logic to load custom hyperopt loss class This class contains all the logic to load custom hyperopt loss class
""" """
object_type = IHyperOptLoss
@staticmethod @staticmethod
def load_hyperoptloss(config: Dict) -> IHyperOptLoss: def load_hyperoptloss(config: Dict) -> IHyperOptLoss:
@ -113,12 +116,12 @@ class HyperOptLossResolver(IResolver):
""" """
current_path = Path(__file__).parent.parent.joinpath('optimize').resolve() current_path = Path(__file__).parent.parent.joinpath('optimize').resolve()
abs_paths = IResolver.build_search_paths(config, current_path=current_path, abs_paths = HyperOptLossResolver.build_search_paths(config, current_path=current_path,
user_subdir=USERPATH_HYPEROPTS, user_subdir=USERPATH_HYPEROPTS,
extra_dir=extra_dir) extra_dir=extra_dir)
hyperoptloss = IResolver._load_object(paths=abs_paths, object_type=IHyperOptLoss, hyperoptloss = HyperOptLossResolver._load_object(paths=abs_paths,
object_name=hyper_loss_name) object_name=hyper_loss_name)
if hyperoptloss: if hyperoptloss:
return hyperoptloss return hyperoptloss

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 Any, List, Optional, Tuple, Union, Generator from typing import Any, Generator, List, Optional, Tuple, Type, Union
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -16,6 +16,8 @@ class IResolver:
""" """
This class contains all the logic to load custom classes This class contains all the logic to load custom classes
""" """
# Childclasses need to override this
object_type: Type[Any]
@staticmethod @staticmethod
def build_search_paths(config, current_path: Path, user_subdir: Optional[str] = None, def build_search_paths(config, current_path: Path, user_subdir: Optional[str] = None,
@ -32,12 +34,11 @@ class IResolver:
return abs_paths return abs_paths
@staticmethod @classmethod
def _get_valid_object(object_type, module_path: Path, def _get_valid_object(cls, module_path: Path,
object_name: str) -> Generator[Any, None, None]: object_name: str) -> Generator[Any, None, None]:
""" """
Generator returning objects with matching object_type and object_name in the path given. 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 module_path: absolute path to the module
:param object_name: Class name of the object :param object_name: Class name of the object
:return: generator containing matching objects :return: generator containing matching objects
@ -55,19 +56,21 @@ class IResolver:
valid_objects_gen = ( valid_objects_gen = (
obj for name, obj in inspect.getmembers(module, inspect.isclass) 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 return valid_objects_gen
@staticmethod @classmethod
def _search_object(directory: Path, object_type, object_name: str, def _search_object(cls, directory: Path, object_name: str,
kwargs: dict = {}) -> Union[Tuple[Any, Path], Tuple[None, None]]: 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
:param object_name: ClassName of the object to load
:return: object instance :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(): for entry in directory.iterdir():
# Only consider python files # Only consider python files
if not str(entry).endswith('.py'): if not str(entry).endswith('.py'):
@ -75,14 +78,14 @@ class IResolver:
continue continue
module_path = entry.resolve() 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: if obj:
return (obj(**kwargs), module_path) return (obj(**kwargs), module_path)
return (None, None) return (None, None)
@staticmethod @classmethod
def _load_object(paths: List[Path], object_type, object_name: str, def _load_object(cls, paths: List[Path], object_name: str,
kwargs: dict = {}) -> Optional[Any]: kwargs: dict = {}) -> Optional[Any]:
""" """
Try to load object from path list. Try to load object from path list.
@ -90,13 +93,12 @@ class IResolver:
for _path in paths: for _path in paths:
try: try:
(module, module_path) = IResolver._search_object(directory=_path, (module, module_path) = cls._search_object(directory=_path,
object_type=object_type, object_name=object_name,
object_name=object_name, kwargs=kwargs)
kwargs=kwargs)
if module: if module:
logger.info( 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}'...") f"from '{module_path}'...")
return module return module
except FileNotFoundError: except FileNotFoundError:

View File

@ -17,6 +17,7 @@ class PairListResolver(IResolver):
""" """
This class contains all the logic to load custom PairList class This class contains all the logic to load custom PairList class
""" """
object_type = IPairList
@staticmethod @staticmethod
def load_pairlist(pairlist_name: str, exchange, pairlistmanager, 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, abs_paths = IResolver.build_search_paths(config, current_path=current_path,
user_subdir=None, extra_dir=None) user_subdir=None, extra_dir=None)
pairlist = IResolver._load_object(paths=abs_paths, object_type=IPairList, pairlist = PairListResolver._load_object(paths=abs_paths,
object_name=pairlist_name, kwargs=kwargs) object_name=pairlist_name,
kwargs=kwargs)
if pairlist: if pairlist:
return pairlist return pairlist
raise OperationalException( raise OperationalException(

View File

@ -22,6 +22,7 @@ class StrategyResolver(IResolver):
""" """
This class contains the logic to load custom strategy class This class contains the logic to load custom strategy class
""" """
object_type = IStrategy
@staticmethod @staticmethod
def load_strategy(config: Optional[Dict] = None) -> IStrategy: def load_strategy(config: Optional[Dict] = None) -> IStrategy:
@ -134,9 +135,9 @@ class StrategyResolver(IResolver):
""" """
current_path = Path(__file__).parent.parent.joinpath('strategy').resolve() current_path = Path(__file__).parent.parent.joinpath('strategy').resolve()
abs_paths = IResolver.build_search_paths(config, current_path=current_path, abs_paths = StrategyResolver.build_search_paths(config, current_path=current_path,
user_subdir=constants.USERPATH_STRATEGY, user_subdir=constants.USERPATH_STRATEGY,
extra_dir=extra_dir) extra_dir=extra_dir)
if ":" in strategy_name: if ":" in strategy_name:
logger.info("loading base64 encoded strategy") logger.info("loading base64 encoded strategy")
@ -154,8 +155,9 @@ class StrategyResolver(IResolver):
# register temp path with the bot # register temp path with the bot
abs_paths.insert(0, temp.resolve()) abs_paths.insert(0, temp.resolve())
strategy = IResolver._load_object(paths=abs_paths, object_type=IStrategy, strategy = StrategyResolver._load_object(paths=abs_paths,
object_name=strategy_name, kwargs={'config': config}) object_name=strategy_name,
kwargs={'config': config})
if strategy: if strategy:
strategy._populate_fun_len = len(getfullargspec(strategy.populate_indicators).args) strategy._populate_fun_len = len(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)

View File

@ -20,7 +20,6 @@ def test_search_strategy():
s, _ = StrategyResolver._search_object( s, _ = StrategyResolver._search_object(
directory=default_location, directory=default_location,
object_type=IStrategy,
kwargs={'config': default_config}, kwargs={'config': default_config},
object_name='DefaultStrategy' object_name='DefaultStrategy'
) )
@ -28,7 +27,6 @@ def test_search_strategy():
s, _ = StrategyResolver._search_object( s, _ = StrategyResolver._search_object(
directory=default_location, directory=default_location,
object_type=IStrategy,
kwargs={'config': default_config}, kwargs={'config': default_config},
object_name='NotFoundStrategy' object_name='NotFoundStrategy'
) )