Toolz Composition Poser
import toolz, abc, inspect, functools, typing, importlib, urllib, builtins, json, pathlib, operator, itertools, fnmatch
from toolz.curried import *
What is does a toolz
bound version of poser look like? That means none of my nonsense only critical logic.
class Compose(toolz.functoolz.Compose):
__slots__ = toolz.functoolz.Compose.__slots__ + tuple("args kwargs exceptions".split())
def __init__(self, funcs=None, *args, **kwargs):
"""`Compose` stores `args` and `kwargs` like a partial."""
super().__init__(funcs or (I,)); self.args, self.exceptions, self.kwargs = args, kwargs.pop('exceptions', tuple()), kwargs
def __call__(self, *args, **kwargs):
args, kwargs = self.args + args, {**self.kwargs, **kwargs}
for callable in (self.first,) + self.funcs:
try: args, kwargs = (callable(*args, **kwargs),), {}; object = args[0]
except self.exceptions as Exception: return Ø(Exception)
return object
compute = __call__
"""`__add__ or pipe` a function into the composition."""
def pipe(this, object=None, *args, **kwargs):
"""`append` an `object` to `this` composition."""
if isinstance(this, type) and issubclass(this, Compose): this = this()
if object == slice(None): return this
if not object: return this
object = forward(object)
if args or kwargs: object = toolz.partial(object, *args, **kwargs)
if this.first == I: this.first = object
else: this.funcs += object,
return this
__add__ = __radd__ = __iadd__ = __getitem__ = pipe
def skip(this, *args, **kwargs): return this
__sub__ = __rsub__ = __isub__ = pipe
__pow__ = isinstance = functools.partialmethod(pipe, flip(isinstance))
for key, value in toolz.merge(
toolz.pipe(toolz, vars, toolz.curried.valfilter(callable), toolz.curried.keyfilter(toolz.compose(str.islower, toolz.first))),
toolz.pipe(builtins, vars, toolz.curried.valfilter(callable), toolz.curried.keyfilter(toolz.compose(str.islower, toolz.first)))).items():
if not hasattr(Compose, key):
setattr(Compose, key, getattr(Compose, key, functools.partialmethod(Compose.pipe, value)))
getattr(Compose, key).__doc__ = inspect.getdoc(value)
for attr, symbol in map(str.split, "map mul;filter truediv;groupby matmul;reduce mod".split(';')):
if attr not in Compose.__slots__:
[setattr(Compose, format.format(symbol), getattr(Compose, attr)) for format in "__{}__ __i{}__ __r{}__".split()]
Compose.__mul__ = Compose.__imul__ = Compose.__rmul__ =Compose.map
class Type(abc.ABCMeta):
def __getattribute__(cls, str):
if str in _type_method_names: return object.__getattribute__(cls, str)
return object.__getattribute__(cls(), str)
_type_method_names = set(dir(Type))
for attr in set(dir(Compose))-(set(dir(toolz.functoolz.Compose)))-set("__weakref__ __dict__".split()):
setattr(Type, attr, getattr(Type, attr, getattr(Compose, attr)))
class λ(Compose, metaclass=Type):
def __init__(self, *args, **kwargs): super().__init__(None, *args, **kwargs)
Sometimes we have to write our own utility functions.
def I(*args, **kwargs): "A nothing special identity function, does pep8 peph8 me?"; return args[0] if args else None
def forward(module, *, property='', period='.'):
"""Load string forward references"""
if not isinstance(module, str): return module
while period:
try:
if not property: raise ModuleNotFoundError
return operator.attrgetter(property)(importlib.import_module(module))
except ModuleNotFoundError as BaseException:
module, period, rest = module.rpartition('.')
property = '.'.join((rest, property)).rstrip('.')
if not module: raise BaseException
class juxt(toolz.functoolz.juxt):
def __new__(self, funcs):
if isinstance(funcs, str): funcs = forward(funcs)
if callable(funcs) or not toolz.isiterable(funcs): return funcs
self = super().__new__(self)
return self.__init__(funcs) or self
def __init__(self, object): self.funcs = object
def __call__(self, *args, **kwargs):
if isinstance(self.funcs, typing.Mapping):
object = type(self.funcs)()
for key, value in self.funcs.items():
if callable(key): key = key(*args, **kwargs)
if callable(value): value = value(*args, **kwargs)
object[key] = value
else: return object
if toolz.isiterable(self.funcs): return type(self.funcs)(x(*args, **kwargs) if callable(x) else x for x in self.funcs)
if callable(self.funcs): return self.funcs(*args, **kwargs)
return self.funcs
class Ø(BaseException):
def __bool__(self): return False
def stars(callable):
@functools.wraps(callable)
def call(*iter, **kwargs):
args, iter = list(), list(iter)
while iter:
if isinstance(iter[-1], typing.Mapping): kwargs.update(iter.pop())
else: args.extend(iter.pop())
return callable(*args, **kwargs)
return call
"__main__"
tests.
__test__ = globals().get('__test__', {}); __test__[__name__] = """
#### Tests
Initializing a composition.
>>> assert λ[:] == λ() == λ[::]
>>> λ[:]
λ(<function I at ...>,)
Composing compositions.
>>> λ[callable]
λ(<built-in function callable>,)
>>> assert λ[callable] == λ+callable == callable+λ == λ.pipe(callable)
>>> assert λ[callable] != λ[callable][range]
>>> assert λ-callable == callable-λ
Juxtapositions.
>>> λ[juxt((type, str))]
λ(<__main__.juxt object at ...>,)
>>> λ[juxt((type, str))](10)
(<class 'int'>, '10')
Mapping.
>>> (λ[range] * type + list)(3)
[<class 'int'>, <class 'int'>, <class 'int'>]
>>> λ[range].map(juxt((type, str)))[list](3)
[(<class 'int'>, '0'), (<class 'int'>, '1'), (<class 'int'>, '2')]
Filtering
>>> (λ[range] / λ[juxt(((3).__lt__, (2).__rfloordiv__))][all] + list)(10)
[4, 5, 6, 7, 8, 9]
>>> (λ[range] / (λ[juxt(((3).__lt__, (2).__rmod__))][all]) + list)(10)
[5, 7, 9]
Filtering Mappings
>>> λ('abc').enumerate().dict().valfilter('ab'.__contains__)()
{0: 'a', 1: 'b'}
>>> λ('abc').enumerate().dict().keyfilter((1).__lt__).valfilter(λ().pipe(operator.__contains__, 'bc'))()
{2: 'c'}
>>> λ('abc').enumerate().dict().keyfilter((1).__lt__)()
{2: 'c'}
Groupby
> assert λ[range] @ (2).__rmod__ == λ[range].groupby((2).__rmod__)
>>> (λ[range] @ (2).__rmod__)(10)
{0: [0, 2, 4, 6, 8], 1: [1, 3, 5, 7, 9]}
Reduce
> assert λ[range]%int.__add__ == λ[range].reduce(int.__add__)
>>> (λ[range] % int.__add__)(10)
45
Conditionals
>>> λ[juxt((λ**int+bool, λ**str))](10)
(True, False)
Forward references.
>>> λ['random.random']()
0...
Loading files.
>>> read = λ[pathlib.Path][pathlib.Path.read_text][json.loads]
>>> (λ('2019-10-18-another-bout-with-poser.ipynb')[read][
... juxt((type, λ.get('cells')[toolz.first].get('cell_type')))
... ])()
(<class 'dict'>, 'markdown')
Starred functions allows arguments and dictionaries to be defined in iterables.
>>> stars(range)([0,10])
range(0, 10)
>>> stars(λ[dict])(λ[range][reversed][enumerate][juxt((list,))](3))
{0: 2, 1: 1, 2: 0}
Some recipes.
Load a bunch of notebooks as objects.
>>> λ[λ[pathlib.Path].pipe(toolz.flip(pathlib.Path.glob), '*.ipynb').take(2)[list] * juxt((I, read)) + dict + 'pandas.Series'][juxt((type, len))]('')
(<class 'pandas.core.series.Series'>, ...)
"""
import doctest, IPython; __name__ == '__main__' and display(doctest.testmod(optionflags=doctest.ELLIPSIS), IPython.display.Markdown(__test__[__name__]))
TestResults(failed=0, attempted=24)
Tests
Initializing a composition.
>>> assert λ[:] == λ() == λ[::]
>>> λ[:]
λ(<function I at ...>,)
Composing compositions.
>>> λ[callable]
λ(<built-in function callable>,)
>>> assert λ[callable] == λ+callable == callable+λ == λ.pipe(callable)
>>> assert λ[callable] != λ[callable][range]
>>> assert λ-callable == callable-λ
Juxtapositions.
>>> λ[juxt((type, str))]
λ(<__main__.juxt object at ...>,)
>>> λ[juxt((type, str))](10)
(<class 'int'>, '10')
Mapping.
>>> (λ[range] * type + list)(3)
[<class 'int'>, <class 'int'>, <class 'int'>]
>>> λ[range].map(juxt((type, str)))[list](3)
[(<class 'int'>, '0'), (<class 'int'>, '1'), (<class 'int'>, '2')]
Filtering
>>> (λ[range] / λ[juxt(((3).__lt__, (2).__rfloordiv__))][all] + list)(10)
[4, 5, 6, 7, 8, 9]
>>> (λ[range] / (λ[juxt(((3).__lt__, (2).__rmod__))][all]) + list)(10)
[5, 7, 9]
Filtering Mappings
>>> λ('abc').enumerate().dict().valfilter('ab'.__contains__)()
{0: 'a', 1: 'b'}
>>> λ('abc').enumerate().dict().keyfilter((1).__lt__).valfilter(λ().pipe(operator.__contains__, 'bc'))()
{2: 'c'}
>>> λ('abc').enumerate().dict().keyfilter((1).__lt__)()
{2: 'c'}
Groupby
> assert λ[range] @ (2).__rmod__ == λ[range].groupby((2).__rmod__)
>>> (λ[range] @ (2).__rmod__)(10)
{0: [0, 2, 4, 6, 8], 1: [1, 3, 5, 7, 9]}
Reduce
> assert λ[range]%int.__add__ == λ[range].reduce(int.__add__)
>>> (λ[range] % int.__add__)(10)
45
Conditionals
>>> λ[juxt((λ**int+bool, λ**str))](10)
(True, False)
Forward references.
>>> λ['random.random']()
0...
Loading files.
>>> read = λ[pathlib.Path][pathlib.Path.read_text][json.loads]
>>> (λ('2019-10-18-another-bout-with-poser.ipynb')[read][
... juxt((type, λ.get('cells')[toolz.first].get('cell_type')))
... ])()
(<class 'dict'>, 'markdown')
Starred functions allows arguments and dictionaries to be defined in iterables.
>>> stars(range)([0,10])
range(0, 10)
>>> stars(λ[dict])(λ[range][reversed][enumerate][juxt((list,))](3))
{0: 2, 1: 1, 2: 0}
Some recipes.
Load a bunch of notebooks as objects.
>>> λ[λ[pathlib.Path].pipe(toolz.flip(pathlib.Path.glob), '*.ipynb').take(2)[list] * juxt((I, read)) + dict + 'pandas.Series'][juxt((type, len))]('')
(<class 'pandas.core.series.Series'>, ...)
Written on October 23, 2019