Skip to content

Commit dd2101b

Browse files
committed
fixes #772
1 parent fd227d2 commit dd2101b

3 files changed

Lines changed: 248 additions & 71 deletions

File tree

fastcore/_modidx.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,10 +457,13 @@
457457
'fastcore.meta.NewChkMeta.__call__': ('meta.html#newchkmeta.__call__', 'fastcore/meta.py'),
458458
'fastcore.meta.PrePostInitMeta': ('meta.html#prepostinitmeta', 'fastcore/meta.py'),
459459
'fastcore.meta.PrePostInitMeta.__call__': ('meta.html#prepostinitmeta.__call__', 'fastcore/meta.py'),
460+
'fastcore.meta._del_funcs': ('meta.html#_del_funcs', 'fastcore/meta.py'),
461+
'fastcore.meta._del_params': ('meta.html#_del_params', 'fastcore/meta.py'),
460462
'fastcore.meta._funcs_kwargs': ('meta.html#_funcs_kwargs', 'fastcore/meta.py'),
461463
'fastcore.meta._mk_param': ('meta.html#_mk_param', 'fastcore/meta.py'),
462464
'fastcore.meta._rm_self': ('meta.html#_rm_self', 'fastcore/meta.py'),
463465
'fastcore.meta.anno_dict': ('meta.html#anno_dict', 'fastcore/meta.py'),
466+
'fastcore.meta.delegated': ('meta.html#delegated', 'fastcore/meta.py'),
464467
'fastcore.meta.delegates': ('meta.html#delegates', 'fastcore/meta.py'),
465468
'fastcore.meta.empty2none': ('meta.html#empty2none', 'fastcore/meta.py'),
466469
'fastcore.meta.funcs_kwargs': ('meta.html#funcs_kwargs', 'fastcore/meta.py'),

fastcore/meta.py

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
# %% auto #0
66
__all__ = ['test_sig', 'FixSigMeta', 'PrePostInitMeta', 'AutoInit', 'NewChkMeta', 'BypassNewMeta', 'empty2none', 'anno_dict',
7-
'use_kwargs_dict', 'use_kwargs', 'delegates', 'method', 'funcs_kwargs', 'splice_sig']
7+
'use_kwargs_dict', 'use_kwargs', 'delegates', 'method', 'delegated', 'funcs_kwargs', 'splice_sig']
88

99
# %% ../nbs/05_meta.ipynb #d26e95dd
1010
from .imports import *
@@ -111,24 +111,44 @@ def _f(f):
111111
return f
112112
return _f
113113

114-
# %% ../nbs/05_meta.ipynb #11583391
115-
def delegates(to:FunctionType=None, # Delegatee
116-
keep=False, # Keep `kwargs` in decorated function?
117-
but:list=None, # Exclude these parameters from signature
118-
sort_args=False): # Sort arguments alphabetically, doesn't work with call_parse
114+
# %% ../nbs/05_meta.ipynb #90afe67f
115+
def _del_funcs(
116+
to, # Delegatee function/class/None
117+
f # Decorated function/class
118+
)->tuple: # (to_f, from_f) resolved underlying functions
119+
"Resolve delegate source and target functions, unwrapping bound methods"
120+
if to is None: to_f,from_f = f.__base__.__init__,f.__init__
121+
else: to_f,from_f = to.__init__ if isinstance(to,type) else to,f
122+
return getattr(to_f,'__func__',to_f), getattr(from_f,'__func__',from_f)
123+
124+
# %% ../nbs/05_meta.ipynb #7341bbbc
125+
def _del_params(
126+
to_f, # Delegatee function
127+
sig, # Signature of decorated function
128+
but:list=None, # Params to exclude
129+
sort_args=False # Sort params alphabetically?
130+
):
131+
"Get keyword-only params from `to_f` not already in `sig`, excluding `but`"
132+
s2 = {k:v.replace(kind=Parameter.KEYWORD_ONLY) for k,v in inspect.signature(to_f).parameters.items()
133+
if v.default != Parameter.empty and k not in sig.parameters and k not in listify(but)}
134+
if sort_args: s2 = dict(sorted(s2.items()))
135+
return s2
136+
137+
# %% ../nbs/05_meta.ipynb #a621d150
138+
def delegates(
139+
to:FunctionType=None, # Delegatee
140+
keep=False, # Keep `kwargs` in decorated function?
141+
but:list=None, # Exclude these parameters from signature
142+
sort_args=False # Sort arguments alphabetically, doesn't work with call_parse
143+
):
119144
"Decorator: replace `**kwargs` in signature with params from `to`"
120-
if but is None: but = []
121145
def _f(f):
122-
if to is None: to_f,from_f = f.__base__.__init__,f.__init__
123-
else: to_f,from_f = to.__init__ if isinstance(to,type) else to,f
124-
from_f = getattr(from_f,'__func__',from_f)
125-
to_f = getattr(to_f,'__func__',to_f)
146+
to_f,from_f = _del_funcs(to, f)
126147
if hasattr(from_f,'__delwrap__'): return f
127148
sig = inspect.signature(from_f)
128-
s2 = {k:v.replace(kind=inspect.Parameter.KEYWORD_ONLY) for k,v in inspect.signature(to_f).parameters.items()
129-
if v.default != inspect.Parameter.empty and k not in sig.parameters and k not in but}
130-
if sort_args: s2 = dict(sorted(s2.items()))
131-
anno = {k:v for k,v in getattr(to_f, "__annotations__", {}).items() if k not in sig.parameters and k not in but}
149+
s2 = _del_params(to_f, sig, but, sort_args)
150+
anno = {k:v for k,v in getattr(to_f, "__annotations__", {}).items()
151+
if k not in sig.parameters and k not in listify(but)}
132152
from_f.__signature__ = sig_with_params(sig, remove=None if keep else 'kwargs', keep='kwargs' if keep else None, **s2)
133153
if not keep: from_f.__delwrap__ = to_f
134154
if hasattr(from_f, '__annotations__'): from_f.__annotations__.update(anno)
@@ -141,6 +161,27 @@ def method(f):
141161
# `1` is a dummy instance since Py3 doesn't allow `None` any more
142162
return MethodType(f, 1)
143163

164+
# %% ../nbs/05_meta.ipynb #3aad059d
165+
def delegated(
166+
to:FunctionType=None, # Delegatee
167+
keep=False, # Keep `kwargs` in decorated function?
168+
but:list=None, # Exclude these parameters from signature
169+
sort_args=False # Sort arguments alphabetically, doesn't work with call_parse
170+
):
171+
"Like `delegates` but also populates delegated default kwargs at call time"
172+
def _f(f):
173+
to_f,from_f = _del_funcs(to, f)
174+
dp = _del_params(to_f, inspect.signature(from_f), but, sort_args)
175+
f = delegates(to, keep=keep, but=but, sort_args=sort_args)(f)
176+
dflts = {k:v.default for k,v in dp.items()}
177+
@wraps(f)
178+
def _inner(*args, **kwargs):
179+
for k,v in dflts.items():
180+
if k not in kwargs: kwargs[k] = v
181+
return f(*args, **kwargs)
182+
return _inner
183+
return _f
184+
144185
# %% ../nbs/05_meta.ipynb #0d934a51
145186
def _funcs_kwargs(cls, as_method):
146187
old_init = cls.__init__

0 commit comments

Comments
 (0)