Skip to content
This repository was archived by the owner on Aug 25, 2024. It is now read-only.

Commit d638e59

Browse files
committed
base: Instantiate config from kwargs
Fixes: #330 Signed-off-by: John Andersen <[email protected]>
1 parent c637850 commit d638e59

File tree

2 files changed

+44
-2
lines changed

2 files changed

+44
-2
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5454
- SciKit models use `make_config_numpy`.
5555
- Predictions in `repos` are now dictionary.
5656
- All instances of `label` changed to `tag`
57+
- Subclasses of `BaseConfigurable` will now auto instantiate their respective
58+
config classes using `kwargs` if the config argument isn't given and keyword
59+
arguments are.
5760
### Fixed
5861
- CONTRIBUTING.md has `-e` in the wrong place in the getting setup section.
5962
- Since moving to auto `args()` and `config()`, BaseConfigurable no longer

dffml/base.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import copy
88
import inspect
99
import argparse
10+
import functools
1011
import contextlib
1112
import dataclasses
1213
from argparse import ArgumentParser
@@ -278,7 +279,45 @@ def __init__(self):
278279
self.dest = None
279280

280281

281-
class BaseConfigurable(abc.ABC):
282+
class BaseConfigurableMetaClass(type, abc.ABC):
283+
def __new__(cls, name, bases, props, module=None):
284+
# Create the class
285+
cls = super(BaseConfigurableMetaClass, cls).__new__(
286+
cls, name, bases, props
287+
)
288+
# Wrap __init__
289+
setattr(cls, "__init__", cls.wrap(cls.__init__))
290+
return cls
291+
292+
@classmethod
293+
def wrap(cls, func):
294+
"""
295+
If a subclass of BaseConfigurable is passed keyword arguments, convert
296+
them into the instance of the CONFIG class.
297+
"""
298+
299+
@functools.wraps(func)
300+
def wrapper(self, config: Optional[BaseConfig] = None, **kwargs):
301+
if config is None and hasattr(self, "CONFIG") and kwargs:
302+
try:
303+
config = self.CONFIG(**kwargs)
304+
except TypeError as error:
305+
error.args = (
306+
error.args[0].replace(
307+
"__init__", f"{self.CONFIG.__qualname__}"
308+
),
309+
)
310+
raise
311+
if config is None:
312+
raise TypeError(
313+
"__init__() missing 1 required positional argument: 'config'"
314+
)
315+
return func(self, config)
316+
317+
return wrapper
318+
319+
320+
class BaseConfigurable(metaclass=BaseConfigurableMetaClass):
282321
"""
283322
Class which produces a config for itself by providing Args to a CMD (from
284323
dffml.util.cli.base) and then using a CMD after it contains parsed args to
@@ -459,7 +498,7 @@ async def __aexit__(self, exc_type, exc_value, traceback):
459498

460499

461500
class BaseDataFlowFacilitatorObject(
462-
BaseDataFlowFacilitatorObjectContext, BaseConfigurable, Entrypoint, abc.ABC
501+
BaseDataFlowFacilitatorObjectContext, BaseConfigurable, Entrypoint
463502
):
464503
"""
465504
Base class for all Data Flow Facilitator objects conforming to the

0 commit comments

Comments
 (0)