Skip to content

Commit

Permalink
Explicitly import and curry objects in toolz.curried for nicer IDE …
Browse files Browse the repository at this point in the history
…integration. pytoolz#323

We could either have a script to create the curried/__init__.py file or
test that the file is correct.  I opted for the latter that uses our
original logic to construct curried namespace, and the test includes
very informative error messages.

The purpose of this is to remove the red squiggles in editors that try to
inspect Python files and warn on mispelled names.  This is a serious
quality-of-life issue for some people.
  • Loading branch information
eriknw committed Nov 3, 2016
1 parent fe56571 commit 20d8aef
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 25 deletions.
95 changes: 70 additions & 25 deletions toolz/curried/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,33 +23,78 @@
See Also:
toolz.functoolz.curry
"""
from . import exceptions
from . import operator
import toolz
from . import operator
from toolz import (
comp,
complement,
compose,
concat,
concatv,
count,
curry,
diff,
dissoc,
first,
flip,
frequencies,
identity,
interleave,
isdistinct,
isiterable,
juxt,
last,
memoize,
merge_sorted,
peek,
pipe,
second,
thread_first,
thread_last,
)
from .exceptions import merge, merge_with

accumulate = toolz.curry(toolz.accumulate)
assoc = toolz.curry(toolz.assoc)
assoc_in = toolz.curry(toolz.assoc_in)
cons = toolz.curry(toolz.cons)
countby = toolz.curry(toolz.countby)
do = toolz.curry(toolz.do)
drop = toolz.curry(toolz.drop)
excepts = toolz.curry(toolz.excepts)
filter = toolz.curry(toolz.filter)
get = toolz.curry(toolz.get)
get_in = toolz.curry(toolz.get_in)
groupby = toolz.curry(toolz.groupby)
interpose = toolz.curry(toolz.interpose)
itemfilter = toolz.curry(toolz.itemfilter)
itemmap = toolz.curry(toolz.itemmap)
iterate = toolz.curry(toolz.iterate)
join = toolz.curry(toolz.join)
keyfilter = toolz.curry(toolz.keyfilter)
keymap = toolz.curry(toolz.keymap)
map = toolz.curry(toolz.map)
mapcat = toolz.curry(toolz.mapcat)
nth = toolz.curry(toolz.nth)
partial = toolz.curry(toolz.partial)
partition = toolz.curry(toolz.partition)
partition_all = toolz.curry(toolz.partition_all)
partitionby = toolz.curry(toolz.partitionby)
pluck = toolz.curry(toolz.pluck)
random_sample = toolz.curry(toolz.random_sample)
reduce = toolz.curry(toolz.reduce)
reduceby = toolz.curry(toolz.reduceby)
remove = toolz.curry(toolz.remove)
sliding_window = toolz.curry(toolz.sliding_window)
sorted = toolz.curry(toolz.sorted)
tail = toolz.curry(toolz.tail)
take = toolz.curry(toolz.take)
take_nth = toolz.curry(toolz.take_nth)
topk = toolz.curry(toolz.topk)
unique = toolz.curry(toolz.unique)
update_in = toolz.curry(toolz.update_in)
valfilter = toolz.curry(toolz.valfilter)
valmap = toolz.curry(toolz.valmap)

def _should_curry(func):
if not callable(func) or isinstance(func, toolz.curry):
return False
nargs = toolz.functoolz.num_required_args(func)
if nargs is None or nargs > 1:
return True
return nargs == 1 and toolz.functoolz.has_keywords(func)


def _curry_namespace(ns):
return dict(
(name, toolz.curry(f) if _should_curry(f) else f)
for name, f in ns.items() if '__' not in name
)


locals().update(toolz.merge(
_curry_namespace(vars(toolz)),
_curry_namespace(vars(exceptions)),
))

# Clean up the namespace.
del _should_curry
del exceptions
del toolz
1 change: 1 addition & 0 deletions toolz/curried/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ def merge_with(func, d, *dicts, **kwargs):
def merge(d, *dicts, **kwargs):
return toolz.merge(d, *dicts, **kwargs)


merge_with.__doc__ = toolz.merge_with.__doc__
merge.__doc__ = toolz.merge.__doc__
49 changes: 49 additions & 0 deletions toolz/tests/test_curried.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import toolz.curried
from toolz.curried import (take, first, second, sorted, merge_with, reduce,
merge, operator as cop)
from toolz.compatibility import import_module
from collections import defaultdict
from operator import add

Expand Down Expand Up @@ -62,3 +63,51 @@ def test_curried_operator():

# Make sure this isn't totally empty.
assert len(set(vars(cop)) & set(['add', 'sub', 'mul'])) == 3


def test_curried_namespace():
exceptions = import_module('toolz.curried.exceptions')
namespace = {}

def should_curry(func):
if not callable(func) or isinstance(func, toolz.curry):
return False
nargs = toolz.functoolz.num_required_args(func)
if nargs is None or nargs > 1:
return True
return nargs == 1 and toolz.functoolz.has_keywords(func)


def curry_namespace(ns):
return dict(
(name, toolz.curry(f) if should_curry(f) else f)
for name, f in ns.items() if '__' not in name
)

from_toolz = curry_namespace(vars(toolz))
from_exceptions = curry_namespace(vars(exceptions))
namespace.update(toolz.merge(from_toolz, from_exceptions))

namespace = toolz.valfilter(callable, namespace)
curried_namespace = toolz.valfilter(callable, toolz.curried.__dict__)

if namespace != curried_namespace:
missing = set(namespace) - set(curried_namespace)
if missing:
raise AssertionError('There are missing functions in toolz.curried:\n %s'
% ' \n'.join(sorted(missing)))
extra = set(curried_namespace) - set(namespace)
if extra:
raise AssertionError('There are extra functions in toolz.curried:\n %s'
% ' \n'.join(sorted(extra)))
unequal = toolz.merge_with(list, namespace, curried_namespace)
unequal = toolz.valfilter(lambda x: x[0] != x[1], unequal)
messages = []
for name, (orig_func, auto_func) in sorted(unequal.items()):
if name in from_exceptions:
messages.append('%s should come from toolz.curried.exceptions' % name)
elif should_curry(getattr(toolz, name)):
messages.append('%s should be curried from toolz' % name)
else:
messages.append('%s should come from toolz and NOT be curried' % name)
raise AssertionError('\n'.join(messages))

0 comments on commit 20d8aef

Please sign in to comment.