Created
April 23, 2019 23:52
-
-
Save bigjason/3b1ae586fe81556d273a7e8890168150 to your computer and use it in GitHub Desktop.
Partial function application supporting any arg order (WIP)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
_sentinal = object() | |
@dataclass(frozen=True) | |
class Placeholder(DataClassEnrichment): | |
keyword: Optional[str] = None | |
default: Optional[Any] = _sentinal | |
annotation: Type = Any | |
def type_repr_of(self, obj): | |
"""Return the repr() of an object, special-casing types (internal helper). | |
If obj is a type, we return a shorter version than the default | |
type.__repr__, based on the module and qualified name, which is | |
typically enough to uniquely identify a type. For everything | |
else, we fall back on repr(obj). | |
""" | |
if isinstance(obj, type): | |
if obj.__module__ == 'builtins': | |
return obj.__qualname__ | |
return f'{obj.__module__}.{obj.__qualname__}' | |
if obj is ...: | |
return ('...') | |
if isinstance(obj, FunctionType): | |
return obj.__name__ | |
return repr(obj) | |
def _as_arg(self): | |
assert self.keyword is not None | |
assert self.keyword.strip() != '' | |
result = f"{self.keyword}: {self.type_repr_of(self.annotation)}" | |
if self.default is _sentinal: | |
result += f" = {repr(self.default)}" | |
return result | |
Arg = Placeholder('__') | |
__ = Arg | |
def partlet(func: Callable[..., Any], *args_template, **kwargs_template): | |
""" | |
>>> checker = partlet(isinstance, __, str) | |
>>> checker("Asd") | |
True | |
>>> checker(22) | |
False | |
""" | |
args = list(copy.deepcopy(args_template)) | |
kwargs = copy.deepcopy(kwargs_template) | |
parameters = [] | |
i = 0 | |
for f in args: | |
if isinstance(f, Placeholder): | |
if f.keyword == __.keyword or f.keyword is None: | |
new = f.replace(keyword=f"arg{i}") | |
args[args.index(f)] = new | |
i += 1 | |
else: | |
new = f | |
parameters.append(new) | |
def wrapped(*args_internal, **kwargs_internal): | |
vals = deque(args_internal) | |
new_args = deque() | |
new_kwargs = copy.deepcopy(kwargs) | |
new_kwargs.update(kwargs_internal) | |
for val in args: | |
if isinstance(val, Placeholder): | |
try: | |
new_args.append(vals.popleft()) | |
except IndexError as exc: | |
raise TypeError(f"Missing argument {val.keyword}") from exc | |
else: | |
new_args.append(val) | |
return func(*new_args, **kwargs) | |
wrapped.__doc__ = f""" | |
Has an effective signature similar to: | |
def {func.__name__}({', '.join((f._as_arg() for f in parameters))}): | |
... | |
""" | |
wrapped.__name__ = func.__name__ | |
return wrapped |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment