#!/usr/bin/env python
from abc import ABC
################################################################
[docs]
class parameter(property):
def __init__(self, func, optional=False, last=False) -> None:
self.optional = optional
self.key = func.__name__
self.name_var = "_" + self.key
self.func = func
self.last = last
super().__init__(self.foo, self.foo_setter)
self.__doc__ = func.__doc__
[docs]
def foo(self, obj, *args, **kwargs):
if not hasattr(obj, self.name_var):
value = self.func(*args, **kwargs)
if value is None:
raise ValueError(f"{self.key} has no default parameter")
self.foo_setter(obj, value)
return getattr(obj, self.name_var)
[docs]
def foo_setter(self, obj, value, *args, **kwargs) -> None:
setattr(obj, self.name_var, value)
[docs]
class optional_parameter(parameter):
def __init__(self, func) -> None:
super().__init__(func, optional=True)
[docs]
class last_parameter(parameter):
def __init__(self, func) -> None:
super().__init__(func, last=True)
[docs]
class last_parameter_with_default(last_parameter):
def __init__(self, func) -> None:
super().__init__(func)
################################################################
[docs]
def populate_parser(cls, parser) -> None:
_parameters = dict()
_groups = dict()
import typing
def _register(key, _cls, v):
if key not in _parameters:
_parameters[key] = (_cls, v)
if _cls not in _groups:
_groups[_cls] = parser.add_argument_group(_cls.__name__)
cls.apply_to_parameters(_register)
for key, (_cls, v) in _parameters.items():
func = getattr(_cls, key)
type_hints = typing.get_type_hints(func.func)
arg_name = func.key
if func.optional:
arg_name = "--" + arg_name
try:
ret_type = type_hints["return"]
except KeyError:
raise RuntimeError(f"Parameter {cls.__name__}.{key} was not annotated with a return type")
_parser = _groups[_cls]
if ret_type is bool:
if func.func():
action = "store_false"
else:
action = "store_true"
_parser.add_argument(arg_name, help=func.__doc__, action=action)
elif isinstance(v, last_parameter_with_default):
_parser.add_argument(
arg_name,
nargs="?",
default="TBD",
help=func.__doc__,
type=type_hints["return"],
)
else:
_parser.add_argument(arg_name, help=func.__doc__, type=type_hints["return"])
################################################################
[docs]
def get_key_to_parsables(name, cls):
from solidipes.plugins.discovery import (
get_subclasses_from_plugins,
plugin_package_names,
)
parsable_subclasses = get_subclasses_from_plugins(plugin_package_names, name, cls)
key_to_parsable = {}
for e in parsable_subclasses:
if e.parser_key is None:
continue
if not isinstance(e.parser_key, list):
e.parser_key = [e.parser_key]
for k in e.parser_key:
key_to_parsable[k] = e
return key_to_parsable
################################################################
[docs]
class Parsable(ABC):
def __init__(self, **kwargs):
for k, v in kwargs.items():
if v == "TBD":
continue
if v is not None:
setattr(self, k, v)
[docs]
@classmethod
def populate_parser(cls, parser) -> None:
populate_parser(cls, parser)
[docs]
@classmethod
def run_and_check_return(cls, command, **kwargs) -> None:
from solidipes.utils.utils import run_and_check_return
return run_and_check_return(command, **kwargs)
[docs]
@classmethod
def apply_to_parameters(cls, func) -> None:
_parameters = set()
last_parameters = dict()
_groups = dict()
from ..plugins.discovery import apply_to_object_parent_classes
def filter_parameters(_cls) -> None:
for key, v in _cls.__dict__.items():
if key in _parameters:
continue
_parameters.add(key)
if isinstance(v, parameter):
if v.last:
last_parameters[key] = (_cls, v)
continue
func(key, _cls, v)
apply_to_object_parent_classes(cls, filter_parameters)
for k, (_cls, v) in last_parameters.items():
func(k, _cls, v)
################################################################