|
10 | 10 | 'filter_keys', 'filter_values', 'cycle', 'zip_cycle', 'sorted_ex', 'negate_func', 'argwhere', 'filter_ex', |
11 | 11 | 'range_of', 'renumerate', 'first', 'nested_attr', 'nested_idx', 'val2idx', 'uniqueify', 'num_methods', |
12 | 12 | 'rnum_methods', 'inum_methods', 'fastuple', 'arg0', 'arg1', 'arg2', 'arg3', 'arg4', 'bind', 'map_ex', |
13 | | - 'compose', 'maps', 'partialler', 'instantiate', 'using_attr', 'Self', 'Self', 'Stateful', 'PrettyString', |
14 | | - 'even_mults', 'num_cpus', 'add_props', 'typed'] |
| 13 | + 'compose', 'maps', 'partialler', 'instantiate', 'using_attr', 'Self', 'Self', 'copy_func', 'patch_to', |
| 14 | + 'patch', 'patch_property', 'Stateful', 'PrettyString', 'even_mults', 'num_cpus', 'add_props', 'typed'] |
15 | 15 |
|
16 | 16 | # Cell |
17 | 17 | from .imports import * |
| 18 | +import builtins |
18 | 19 |
|
19 | 20 | # Cell |
20 | 21 | defaults = SimpleNamespace() |
@@ -688,6 +689,48 @@ def __call__(self,*args,**kwargs): return self.__getattr__('_call')(*args,**kwar |
688 | 689 | # Cell |
689 | 690 | #nbdev_comment _all_ = ['Self'] |
690 | 691 |
|
| 692 | +# Cell |
| 693 | +def copy_func(f): |
| 694 | + "Copy a non-builtin function (NB `copy.copy` does not work for this)" |
| 695 | + if not isinstance(f,FunctionType): return copy(f) |
| 696 | + fn = FunctionType(f.__code__, f.__globals__, f.__name__, f.__defaults__, f.__closure__) |
| 697 | + fn.__kwdefaults__ = f.__kwdefaults__ |
| 698 | + fn.__dict__.update(f.__dict__) |
| 699 | + return fn |
| 700 | + |
| 701 | +# Cell |
| 702 | +def patch_to(cls, as_prop=False, cls_method=False): |
| 703 | + "Decorator: add `f` to `cls`" |
| 704 | + if not isinstance(cls, (tuple,list)): cls=(cls,) |
| 705 | + def _inner(f): |
| 706 | + for c_ in cls: |
| 707 | + nf = copy_func(f) |
| 708 | + nm = f.__name__ |
| 709 | + # `functools.update_wrapper` when passing patched function to `Pipeline`, so we do it manually |
| 710 | + for o in functools.WRAPPER_ASSIGNMENTS: setattr(nf, o, getattr(f,o)) |
| 711 | + nf.__qualname__ = f"{c_.__name__}.{nm}" |
| 712 | + if cls_method: |
| 713 | + setattr(c_, nm, MethodType(nf, c_)) |
| 714 | + else: |
| 715 | + setattr(c_, nm, property(nf) if as_prop else nf) |
| 716 | + # Avoid clobbering existing functions |
| 717 | + return globals().get(nm, builtins.__dict__.get(nm, None)) |
| 718 | + return _inner |
| 719 | + |
| 720 | +# Cell |
| 721 | +def patch(f=None, *, as_prop=False, cls_method=False): |
| 722 | + "Decorator: add `f` to the first parameter's class (based on f's type annotations)" |
| 723 | + if f is None: return partial(patch, as_prop=as_prop, cls_method=cls_method) |
| 724 | + cls = next(iter(f.__annotations__.values())) |
| 725 | + return patch_to(cls, as_prop=as_prop, cls_method=cls_method)(f) |
| 726 | + |
| 727 | +# Cell |
| 728 | +def patch_property(f): |
| 729 | + "Deprecated; use `patch(as_prop=True)` instead" |
| 730 | + warnings.warn("`patch_property` is deprecated and will be removed; use `patch(as_prop=True)` instead") |
| 731 | + cls = next(iter(f.__annotations__.values())) |
| 732 | + return patch_to(cls, as_prop=True)(f) |
| 733 | + |
691 | 734 | # Cell |
692 | 735 | class Stateful: |
693 | 736 | "A base class/mixin for objects that should not serialize all their state" |
|
0 commit comments