From 464762cf689d4ed86b87860237cc61b93b18beef Mon Sep 17 00:00:00 2001 From: Martin Matyasek Date: Wed, 10 Apr 2019 20:57:36 +0200 Subject: [PATCH] Atomic bomb --- .coveragerc | 23 ++ .flake8 | 5 + .gitignore | 110 ++++++ .pylintrc | 51 +++ Makefile | 59 ++++ README.md | 111 ++++++ mypy.ini | 24 ++ pytoolz/__init__.py | 1 + pytoolz/itertoolz.py | 426 +++++++++++++++++++++++ pytoolz/predicates.py | 47 +++ pytoolz/typing.py | 27 ++ setup.py | 56 +++ stubs/cytoolz/__init__.pyi | 10 + stubs/cytoolz/_pipe.pyi | 146 ++++++++ stubs/cytoolz/curried/__init__.pyi | 49 +++ stubs/cytoolz/curried/exceptions.pyi | 11 + stubs/cytoolz/dicttoolz.pyi | 110 ++++++ stubs/cytoolz/functoolz.pyi | 136 ++++++++ stubs/cytoolz/itertoolz.pyi | 500 +++++++++++++++++++++++++++ stubs/cytoolz/recipes.pyi | 20 ++ stubs/cytoolz/utils.pyi | 10 + stubs/nose/__init__.pyi | 6 + tests/__init__.py | 0 tests/run.py | 11 + unittest.cfg | 11 + 25 files changed, 1960 insertions(+) create mode 100644 .coveragerc create mode 100644 .flake8 create mode 100644 .gitignore create mode 100644 .pylintrc create mode 100644 Makefile create mode 100644 README.md create mode 100644 mypy.ini create mode 100644 pytoolz/__init__.py create mode 100644 pytoolz/itertoolz.py create mode 100644 pytoolz/predicates.py create mode 100644 pytoolz/typing.py create mode 100644 setup.py create mode 100644 stubs/cytoolz/__init__.pyi create mode 100644 stubs/cytoolz/_pipe.pyi create mode 100644 stubs/cytoolz/curried/__init__.pyi create mode 100644 stubs/cytoolz/curried/exceptions.pyi create mode 100644 stubs/cytoolz/dicttoolz.pyi create mode 100644 stubs/cytoolz/functoolz.pyi create mode 100644 stubs/cytoolz/itertoolz.pyi create mode 100644 stubs/cytoolz/recipes.pyi create mode 100644 stubs/cytoolz/utils.pyi create mode 100644 stubs/nose/__init__.pyi create mode 100644 tests/__init__.py create mode 100644 tests/run.py create mode 100644 unittest.cfg diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..d09e430 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,23 @@ +[run] +branch = True +source = + pytoolz/ +omit = + +[report] +# Regexes for lines to exclude from consideration +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + + # Don't complain if tests don't hit defensive assertion code: + raise AssertionError + raise NotImplementedError + + # Don't complain if non-runnable code isn't run: + if 0: + if __name__ == .__main__.: + +ignore_errors = True +skip_covered = True +fail_under = 95 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..61634e0 --- /dev/null +++ b/.flake8 @@ -0,0 +1,5 @@ +[flake8] +doctests = True +exclude = __pycache__ +max-complexity = 10 +statistics = True diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..88621d8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,110 @@ +# IDEs +.idea/ +.vscode/ + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +.static_storage/ +.media/ +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache +flask_session/ + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +/foobar.sqlite diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..9ac93af --- /dev/null +++ b/.pylintrc @@ -0,0 +1,51 @@ +# http://pylint-messages.wikidot.com/all-codes +# https://github.com/PyCQA/pylint/blob/master/pylintrc + +[MASTER] +extension-pkg-whitelist=cytoolz,nose + +[MESSAGES CONTROL] +disable= + missing-docstring, + too-few-public-methods, + wrong-import-order, + R, + logging-fstring-interpolation + +msg-template= + {abspath}:{line}:{column}: [{msg_id}({symbol}), {obj}] {msg} + +output-format=colorized + +[BASIC] +# Regular expression which should only match correct function names +# UPDATED: max length to 60 +function-rgx=[a-z_][a-z0-9_]{2,60}$ + +# Regular expression which should only match correct method names +# UPDATED: max length to 60 +method-rgx=[a-z_][a-z0-9_]{2,60}$ + +# Good variable names which should always be accepted, separated by a comma +# defaults were i,j,k,ex,Run,_ +good-names= + i,j,k,ex,Run,_,e,op,m,t,it,id,fn,f,g,h,n,v,ds,xs,ys,zs,x,y,z, + ff,fa,fb,fc,a,b,c,A,B,C,E,F,G,K,R,T,R,V,Z + +const-naming-style=any + +[TYPECHECK] +ignored-classes= + +[DESIGN] +max-args=10 +max-attributes=15 +max-locals=20 + +[MISCELLANEOUS] +# dont list TODOs or FIXMEs +notes= + +[FORMAT] +# Maximum number of characters on a single line. +max-line-length=79 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7a778f5 --- /dev/null +++ b/Makefile @@ -0,0 +1,59 @@ +.PHONY: help clean setup setup-dev install release-check type-check flake8-check lint tests + +.DEFAULT: help +help: + @echo "make clean" + @echo " clean virtual environment" + @echo "make setup" + @echo " setup development environment" + @echo "make setup-dev" + @echo " setup virtualenv and development environment" + @echo "make install" + @echo " install dependencies" + @echo "make type-check" + @echo " run mypy type checking" + @echo "make flake8-check" + @echo " run flake8 code style check" + @echo "make lint" + @echo " run pylint" + @echo "make tests" + @echo " run unit and doc tests" + @echo "make release-check" + @echo " run type-check, flake8 check, linting and tests" + +clean: + rm -rf venv + rm -rf dist + rm -rf build + rm -rf *.egg-info + +setup: clean + python setup.py develop + +setup-dev: clean + virtualenv -p python3 venv + ./venv/bin/pip install -U pip + ./venv/bin/pip install -U setuptools + ./venv/bin/python setup.py develop + +install: clean + python setup.py install + +type-check: + @echo ">>> checking types in pytoolz and tests" + MYPYPATH=./stubs mypy pytoolz tests || ( echo ">>> type check failed"; exit 1; ) + +flake8-check: + @echo ">>> enforcing PEP 8 style with flake8 in pytoolz and tests" + flake8 --config=.flake8 pytoolz/ tests/ || ( echo ">>> flake8 check failed"; exit 1; ) + +lint: + @echo ">>> linting code" + pylint -j 0 --rcfile .pylintrc pytoolz tests || ( echo ">>> linting failed"; exit 1; ) + +tests: + @echo ">>> running tests" + python tests/run.py || ( echo ">>> tests failed"; exit 1; ) +# python setup.py test + +release-check: type-check flake8-check lint tests diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f9f8b0 --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +# pytoolz + +Collection of higher-order and utility functions built on top of `cytoolz`. + +## Module overview +Pytoolz are split into few generic modules. + +### itertoolz +This module contains functions that work with `Iterable` instances. + +Table of contents + +| Function | Description | +|----------|-------------| +| `associate(key_fn, iterable)` | associate elements of iterable to keys selected by `key_fn` | +| `associate_to(key_fn, value_fn, iterable)` | associate values obtained from iterable by `value_fn` to keys +selected by `key_fn` | +| `collect(iterable)` | materialize iterable into a sequence if it's not one already | +| `empty(iterable)` | check if iterable is empty, returns flag and unchanged iterable | +| `enumerate_with_final(iterable)` | same as `iter_with_final` but adds index as third part | +| `filter_not_none(iterable)` | filter out `None` elements from iterable | +| `find(predicate, iterable)` | find first element of iterable satisfying predicate | +| `first(sequence)` | return first element of a sequence or `None` | +| `head_tail(iterable)` | split iterable into head element and tail iterable | +| `head_tail_list(iterable)` | same as `head_tail` but materialized tail into list | +| `iter_with_final(iterable)` | creates iterable of tuples of original element and final flag | +| `last(sequence)` | return last element of a sequence or `None` | +| `make_str(iterable, key_fn, separator)` | create string of tokens from iterable selected by `key_fn` with separator | +| `split_by(predicate, iterable)` | split elements of iterable by predicate to positives and negatives | +| `take(n, iterable)` | take first n elements of an iterable | +| `take_first(iterable)` | take first element of an iterable or fail | +| `try_take_first(iterable)` | same as `take_first` but returns `None` | +| `try_take_last(iterable)` | take last element of an iterable or `None` | +| `unique_list(iterable)` | return distinct elements of an iterable as `Seq` | +| `unique_sorted(iterable)` | return distinct elements of an iterable in natural order as `Seq` | + +### predicates +This module contains common `Predicate`s, i.e. functions from generic or concrete `A` to `bool`. + +Table of contents + +| Predicate | Description | +|-----------|-------------| +| `some(optional)` | `True` iff optional *is not* `None` | +| `none(optional)` | `True` iff optional *is* `None` | +| `even(integer)` | `True` iff integer is even | +| `odd(integer)` | `True` iff integer is odd | + +### typing +Typing contains helpful type aliases and other type-related definitions. + +## cytoolz +Cytoolz is a cython implementation of a python library supporting functional style called +[toolz](https://toolz.readthedocs.io). + +We highly recommend reading the API docs and using it in your project. + +Pytoolz does not fork but rather extends cytoolz and provides typed stubs for it's API. +Please note that the typed stubs do not cover all the functions from cytoolz. + +Also some valid cases might not be covered due to Python's restricted typing capabilities. + +## Setup development environment +It is highly recommended to use virtual environment to develop and test `pytoolz`. For making things easy there are +two make targets to setup `pytoolz`: +* `make setup-dev` which creates new virtual environment in `./venv` +* `make setup` that just installs dependencies for development + +Of course one can use his/her own favourite tool to create and manage python venv. + +To activate the prepared venv run: +```bash +source venv/bin/activate +``` +and for deactivation simply `deactivate`. + +## Running checks and tests + +### Type checking +Type checking is done using `mypy` (for configuration see `mypy.ini`) and can be executed by: +```bash +make type-check +``` + +### Code style checking +Pytoolz uses [Flake8](http://flake8.pycqa.org/en/latest/index.html) for enforcing PEP 8 and other code smells. +```bash +make flake8-check +``` + +### Linting +Linting is configured in `.pylintrc` and can be run via: +```bash +make lint +``` + +### Tests +Unit and doc tests with coverage can be run by +```bash +make tests +``` + +One can also run all checks and tests at once via +```bash +make release-check +``` + +*Note*: Make sure you run these commands in an activate venv or a container. + +## Distribution +Project uses `setuptools` for distribution. Check settings in `setup.py`. diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..88e9f03 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,24 @@ +[mypy] +ignore_missing_imports = False + +# throws errors for subclassing Resource from flask_restful or Module from +# injector +disallow_subclassing_any = False + +# equivalent to --strict except for the modifications above: +disallow_untyped_calls = True +disallow_untyped_defs = True +disallow_incomplete_defs = True +check_untyped_defs = True +disallow_untyped_decorators = False +warn_redundant_casts = True +warn_return_any = True +warn_unused_ignores = True +warn_unused_configs = True +no_implicit_optional = True + +[mypy-cytoolz.*] +ignore_missing_imports = True + +[mypy-tests.*] +disallow_untyped_defs = False diff --git a/pytoolz/__init__.py b/pytoolz/__init__.py new file mode 100644 index 0000000..b8023d8 --- /dev/null +++ b/pytoolz/__init__.py @@ -0,0 +1 @@ +__version__ = '0.0.1' diff --git a/pytoolz/itertoolz.py b/pytoolz/itertoolz.py new file mode 100644 index 0000000..65715df --- /dev/null +++ b/pytoolz/itertoolz.py @@ -0,0 +1,426 @@ +from itertools import islice +from typing import Callable, Iterable, List, Optional, Tuple, TypeVar + +from cytoolz.functoolz import complement, compose +from cytoolz.itertoolz import drop, identity, last as clast, peek, unique + +from pytoolz.typing import Map, Seq + +A = TypeVar('A') +B = TypeVar('B') +C = TypeVar('C') +E = TypeVar('E') + + +def associate(key: Callable[[B], A], values: Iterable[B]) -> Map[A, B]: + """ + Collect values into a :class:`Map` using provided key function. + + >>> key = lambda x: x[0] + + >>> associate(key, iter([])) + {} + + >>> values = iter([('a', 1), ('b', 2), ('c', 3)]) + >>> associate(key, values) + {'a': ('a', 1), 'b': ('b', 2), 'c': ('c', 3)} + + This operation is terminal in values. + + >>> next(values) + Traceback (most recent call last): + ... + StopIteration + + Values are assumed to be unique with respect to the keys. Latter value is + kept on key collision. + + >>> values = iter([('a', 1), ('a', 2)]) + >>> associate(key, values) + {'a': ('a', 2)} + """ + return associate_to(key, identity, values) + + +def associate_to( + key: Callable[[B], A], + value: Callable[[B], C], + values: Iterable[B] +) -> Map[A, C]: + """ + Collect values into a Map using provided key_func and value_func. + + >>> key = lambda x: x[0] + >>> value = lambda x: x[1] + + >>> associate_to(key, value, iter([])) + {} + + >>> values = iter([('a', 1), ('b', 2), ('c', 3)]) + >>> associate_to(key, value, values) + {'a': 1, 'b': 2, 'c': 3} + + This operation is terminal in values. + + >>> next(values) + Traceback (most recent call last): + ... + StopIteration + + Values are assumed to be unique with respect to the keys. Latter value is + kept on key collision. + + >>> values = iter([('a', 1), ('a', 2)]) + >>> associate_to(key, value, values) + {'a': 2} + """ + return {key(v): value(v) for v in values} + + +def collect(items: Iterable[E]) -> Seq[E]: + """ + Collect given `items` into a sequence (list). If the input iterable already + is a sequence, it is just returned. Otherwise it is materialized. + + >>> it = iter([1, 2, 3]) + >>> collect(it) + [1, 2, 3] + + This operation is terminal. + + >>> next(it) + Traceback (most recent call last): + ... + StopIteration + + >>> in_seq = [1, 2, 3] + >>> out_seq = collect(in_seq) + >>> out_seq is in_seq + True + """ + return items if isinstance(items, Seq) else list(items) + + +def empty(it: Iterable[E]) -> Tuple[bool, Iterable[E]]: + """ + Checks, whether the sequence is empty or not. + + NOTE: This method modifies the original sequence (takes the first element), + use the returned one, which contains the original items. + + >>> it_orig = iter([1, 2, 3]) + >>> is_empty, it_new = empty(it_orig) + >>> is_empty, list(it_new) + (False, [1, 2, 3]) + + >>> is_empty, it_empty = empty(iter([])) + >>> is_empty, list(it_empty) + (True, []) + """ + try: + _, it = peek(it) + return False, it + except StopIteration: + return True, iter([]) + + +def enumerate_with_final(it: Iterable[E]) -> Iterable[Tuple[E, bool, int]]: + """ + Change given iterator to new one yielding additional *final* flag and + iteration order. + + >>> list(enumerate_with_final(iter(['a', 'b', 'c']))) + [('a', False, 0), ('b', False, 1), ('c', True, 2)] + + >>> list(enumerate_with_final(iter(['a']))) + [('a', True, 0)] + + >>> list(enumerate_with_final(iter([]))) + [] + """ + it_final = iter_with_final(it) + for i, (item, final) in enumerate(it_final): + yield item, final, i + + +def filter_not_none(it: Iterable[Optional[E]]) -> Iterable[E]: + """ + Filter items of given iterable which are not `None`, inferring non-optional + return type `E`. + + >>> list(filter_not_none(iter([1, None, 3]))) + [1, 3] + + >>> list(filter_not_none([])) + [] + + >>> list(filter_not_none([None, None, None])) + [] + """ + for e in it: + if e is not None: + yield e + + +def find(pred: Callable[[E], bool], it: Iterable[E]) -> Optional[E]: + """ + Find first item from give iterable that satisfies `predicate`. + + >>> even = lambda x: x % 2 == 0 + >>> find(even, iter([1, 5, 4, 7, 2])) + 4 + >>> find(even, iter([1, 3, 5])) + >>> find(even, iter([])) + + This operation is terminal in provided iterable. + + >>> it = iter([1, 2]) + >>> find(even, it) + 2 + >>> next(it) + Traceback (most recent call last): + ... + StopIteration + """ + return try_take_first(e for e in it if pred(e)) + + +def first(seq: Seq[E]) -> Optional[E]: + """ + Return first element of a sequence or `None` if empty. + + >>> first([1, 2, 3]) + 1 + >>> first([]) + """ + return seq[0] if seq else None + + +def head_tail(it: Iterable[E]) -> Tuple[E, Iterable[E]]: + """ + Split provided iterable into head element and tail iterable. + + >>> head, tail = head_tail(iter([1, 2, 3])) + >>> head, list(tail) + (1, [2, 3]) + + >>> head, tail = head_tail(iter([42])) + >>> head, list(tail) + (42, []) + + Raises :class:`StopIteration` if the original iterable is empty. + + >>> head_tail(iter([])) + Traceback (most recent call last): + ... + StopIteration + """ + head, seq = peek(it) + tail = drop(1, seq) + return head, tail + + +def head_tail_list(it: Iterable[E]) -> Tuple[E, List[E]]: + """ + Split given iterable into head and tail :class:`List`. + + >>> head_tail_list(iter([1, 2, 3])) + (1, [2, 3]) + + >>> head_tail_list(iter([42])) + (42, []) + + >>> head_tail_list(iter([])) + Traceback (most recent call last): + ... + StopIteration + """ + head, tail = head_tail(it) + return head, list(tail) + + +def iter_with_final(it: Iterable[E]) -> Iterable[Tuple[E, bool]]: + """ + Change given iterator to new one yielding additional *final* flag. + + >>> list(iter_with_final(iter([1, 2, 3]))) + [(1, False), (2, False), (3, True)] + + >>> list(iter_with_final(iter([42]))) + [(42, True)] + + >>> list(iter_with_final(iter([]))) + [] + """ + + is_empty, iterable = empty(it) + if is_empty: + return iter([]) + + it = iter(iterable) + buffer = [next(it)] + for i in it: + buffer.append(i) + item, buffer = head_tail_list(buffer) + yield item, False + yield buffer[0], True + + +def last(seq: Seq[E]) -> Optional[E]: + """ + Return last element of a sequence or `None` if empty. + + >>> last([1, 2, 3]) + 3 + >>> last([]) + """ + return seq[-1] if seq else None + + +def make_str( + it: Iterable[E], + key: Callable[[E], str] = str, + sep: str = ',' +) -> str: + """ + Serialize objects selected by key from iterable to a string using separator + + >>> make_str(iter([1, 2, 3])) + '1,2,3' + >>> make_str(iter([])) + '' + >>> make_str(iter([1, 2, 3]), sep='; ') + '1; 2; 3' + + >>> it = iter([{'k': 'a', 'v': 1}, {'k': 'b', 'v': 2}, {'k': 'c', 'v': 3}]) + >>> make_str(it, key=lambda x: x['k'], sep='-') + 'a-b-c' + + This operation is terminal in the iterable. + + >>> next(it) + Traceback (most recent call last): + ... + StopIteration + """ + tokens = map(key, it) + return sep.join(tokens) + + +def split_by( + pred: Callable[[E], bool], + it: Iterable[E] +) -> Tuple[Iterable[E], Iterable[E]]: + """ + Split given items by a predicate into (positives, negatives). + + >>> even = lambda x: x % 2 == 0 + >>> pos, neg = split_by(even, iter([1, 5, 4, 7, 2])) + >>> list(pos), list(neg) + ([4, 2], [1, 5, 7]) + + >>> pos, neg = split_by(even, iter([1, 3, 5])) + >>> list(pos), list(neg) + ([], [1, 3, 5]) + + >>> pos, neg = split_by(even, iter([])) + >>> list(pos), list(neg) + ([], []) + + This operation is terminal in given iterable. + + >>> it = iter([1, 2, 3]) + >>> _ = split_by(even, it) + >>> next(it) + Traceback (most recent call last): + ... + StopIteration + """ + items = collect(it) + rest = complement(pred) + return filter(pred, items), filter(rest, items) + + +def take(n: int, it: Iterable[E]) -> Seq[E]: + """ + Return first n items of the iterable as a list. + + >>> it = iter([1, 2, 3]) + >>> take(2, it) + [1, 2] + >>> next(it) + 3 + >>> take(2, it) + [] + + >>> take(0, [1, 2, 3]) + [] + + >>> take(-1, []) + Traceback (most recent call last): + ... + ValueError: n must be non-negative integer + """ + if n < 0: + raise ValueError('n must be non-negative integer') + return list(islice(it, n)) + + +def take_first(it: Iterable[E]) -> E: + """ + Return the first item of the iterable or raise StopIteration if empty. + + >>> it = iter([1, 2]) + >>> take_first(it) + 1 + >>> next(it) + 2 + >>> take_first(it) + Traceback (most recent call last): + ... + StopIteration + """ + return next(iter(it)) + + +def try_take_first(it: Iterable[E]) -> Optional[E]: + """ + Return the first item of the iterable or None if empty. + + >>> it = iter([1, 2, 3]) + >>> try_take_first(it) + 1 + + This operation does not change given iterable. + + >>> next(it) + 2 + + >>> try_take_first(iter([])) + """ + return next(iter(it), None) + + +def try_take_last(seq: Iterable[E]) -> Optional[E]: + """ + Get last element of given sequence or return `None`. + + >>> it = iter([1, 2, 3]) + >>> try_take_last(it) + 3 + + This operation is terminal. + + >>> try_take_last(it) + """ + try: + return clast(seq) + except IndexError: + return None + + +# Common composition of extracting unique items followed by list +unique_list: Callable[[Iterable[E]], Seq[E]] = compose(collect, unique) + +# Common composition of extracting unique items followed by natural sort +unique_sorted: Callable[[Iterable[E]], Seq[E]] = compose(sorted, unique) diff --git a/pytoolz/predicates.py b/pytoolz/predicates.py new file mode 100644 index 0000000..e2f41df --- /dev/null +++ b/pytoolz/predicates.py @@ -0,0 +1,47 @@ +from typing import Optional, TypeVar + +A = TypeVar('A') + + +def some(a: Optional[A]) -> bool: + """ + Check if given optional argument contains some value. + + >>> some('a') + True + >>> some(None) + False + """ + return a is not None + + +def none(a: Optional[A]) -> bool: + """ + Check if given optional argument contains no value. + + >>> none(None) + True + >>> none('a') + False + """ + return a is None + + +def even(n: int) -> bool: + """ + Check that given number is even. + + >>> [(n, even(n)) for n in range(5)] + [(0, True), (1, False), (2, True), (3, False), (4, True)] + """ + return n % 2 == 0 + + +def odd(n: int) -> bool: + """ + Check that given number is odd. + + >>> [(n, odd(n)) for n in range(5)] + [(0, False), (1, True), (2, False), (3, True), (4, False)] + """ + return n % 2 != 0 diff --git a/pytoolz/typing.py b/pytoolz/typing.py new file mode 100644 index 0000000..a58c059 --- /dev/null +++ b/pytoolz/typing.py @@ -0,0 +1,27 @@ +from typing import Mapping, Optional, Sequence, TypeVar + +A = TypeVar('A') +B = TypeVar('B') + +Map = Mapping +Seq = Sequence + + +class TypingError(RuntimeError): + pass + + +def assert_some(a: Optional[A]) -> A: + """ + Assert given optional has some value or raise :class:`TypingError`. + + >>> assert_some('a') + 'a' + >>> assert_some(None) + Traceback (most recent call last): + ... + pytoolz.typing.TypingError: Item expected to be not None, None given. + """ + if a is None: + raise TypingError('Item expected to be not None, None given.') + return a diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..4115e97 --- /dev/null +++ b/setup.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +from os.path import exists + +from setuptools import find_packages, setup +from setuptools.dist import Distribution + +import pytoolz + + +class BinaryDistribution(Distribution): + """Distribution which always forces a binary package with platform name""" + + def has_ext_modules(self) -> bool: + return True + + +test_requirements = [ + 'coverage==4.5.1', + 'flake8==3.7.7', + 'mypy==0.700', + 'nose==1.3.7', + # 'nose2==0.8.0', + # 'nose2[coverage_plugin]>=0.6.5', + 'pylint==2.3.0', +] + +setup( + name='pytoolz', + version=pytoolz.__version__, + description='Collection of higher-order and utility functions', + long_description=(open('README.md').read() if exists('README.md') else ''), + classifiers=[ + 'Development Status :: 2 - Pre-Alpha', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'Natural Language :: English', + 'Operating System :: Unix', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + ], + url='https://bitbucket.org/blindspotsolutions/pytoolz/', + maintainer='Martin Matyasek', + maintainer_email='martin.matyasek@blindspot.ai', + keywords='functional utility cytoolz itertools functools', + packages=find_packages(include=['pytoolz']), + include_package_data=True, + distclass=BinaryDistribution, + zip_safe=False, + python_requires='>=3.6', + install_requires=['cytoolz==0.9.0.1'] + test_requirements, + test_suite='tests', + tests_require=test_requirements, +) diff --git a/stubs/cytoolz/__init__.pyi b/stubs/cytoolz/__init__.pyi new file mode 100644 index 0000000..3d6d889 --- /dev/null +++ b/stubs/cytoolz/__init__.pyi @@ -0,0 +1,10 @@ +from typing import Any + +from .dicttoolz import * +from .functoolz import * +from .itertoolz import * +from .recipes import * + +comp = compose +flip: Any +memoize: Any diff --git a/stubs/cytoolz/_pipe.pyi b/stubs/cytoolz/_pipe.pyi new file mode 100644 index 0000000..30d19b7 --- /dev/null +++ b/stubs/cytoolz/_pipe.pyi @@ -0,0 +1,146 @@ +from typing import Any, Callable, TypeVar, overload + +_T1 = TypeVar('_T1') +_T2 = TypeVar('_T2') +_T3 = TypeVar('_T3') +_T4 = TypeVar('_T4') +_T5 = TypeVar('_T5') +_T6 = TypeVar('_T6') +_T7 = TypeVar('_T7') +_T8 = TypeVar('_T8') +_T9 = TypeVar('_T9') +_T10 = TypeVar('_T10') +_T11 = TypeVar('_T11') + + +@overload +def pipe( + item: _T1, + func1: Callable[[_T1], _T2], +) -> _T2: + ... + + +@overload +def pipe( + item: _T1, + func1: Callable[[_T1], _T2], + func2: Callable[[_T2], _T3], +) -> _T3: + ... + + +@overload +def pipe( + item: _T1, + func1: Callable[[_T1], _T2], + func2: Callable[[_T2], _T3], + func3: Callable[[_T3], _T4], +) -> _T4: + ... + + +@overload +def pipe( + item: _T1, + func1: Callable[[_T1], _T2], + func2: Callable[[_T2], _T3], + func3: Callable[[_T3], _T4], + func4: Callable[[_T4], _T5], +) -> _T5: + ... + + +@overload +def pipe( + item: _T1, + func1: Callable[[_T1], _T2], + func2: Callable[[_T2], _T3], + func3: Callable[[_T3], _T4], + func4: Callable[[_T4], _T5], + func5: Callable[[_T5], _T6], +) -> _T6: + ... + + +@overload +def pipe( + item: _T1, + func1: Callable[[_T1], _T2], + func2: Callable[[_T2], _T3], + func3: Callable[[_T3], _T4], + func4: Callable[[_T4], _T5], + func5: Callable[[_T5], _T6], + func6: Callable[[_T6], _T7], +) -> _T7: + ... + + +@overload +def pipe( + item: _T1, + func1: Callable[[_T1], _T2], + func2: Callable[[_T2], _T3], + func3: Callable[[_T3], _T4], + func4: Callable[[_T4], _T5], + func5: Callable[[_T5], _T6], + func6: Callable[[_T6], _T7], + func7: Callable[[_T7], _T8], +) -> _T8: + ... + + +@overload +def pipe( + item: _T1, + func1: Callable[[_T1], _T2], + func2: Callable[[_T2], _T3], + func3: Callable[[_T3], _T4], + func4: Callable[[_T4], _T5], + func5: Callable[[_T5], _T6], + func6: Callable[[_T6], _T7], + func7: Callable[[_T7], _T8], + func8: Callable[[_T8], _T9], +) -> _T9: + ... + + +@overload +def pipe( + item: _T1, + func1: Callable[[_T1], _T2], + func2: Callable[[_T2], _T3], + func3: Callable[[_T3], _T4], + func4: Callable[[_T4], _T5], + func5: Callable[[_T5], _T6], + func6: Callable[[_T6], _T7], + func7: Callable[[_T7], _T8], + func8: Callable[[_T8], _T9], + func9: Callable[[_T9], _T10], +) -> _T10: + ... + + +@overload +def pipe( + item: _T1, + func1: Callable[[_T1], _T2], + func2: Callable[[_T2], _T3], + func3: Callable[[_T3], _T4], + func4: Callable[[_T4], _T5], + func5: Callable[[_T5], _T6], + func6: Callable[[_T6], _T7], + func7: Callable[[_T7], _T8], + func8: Callable[[_T8], _T9], + func9: Callable[[_T9], _T10], + func10: Callable[[_T10], _T11], +) -> _T11: + ... + + +@overload +def pipe( + item: Any, + *funcs: Callable[[Any], Any], +) -> Any: + ... diff --git a/stubs/cytoolz/curried/__init__.pyi b/stubs/cytoolz/curried/__init__.pyi new file mode 100644 index 0000000..80d3984 --- /dev/null +++ b/stubs/cytoolz/curried/__init__.pyi @@ -0,0 +1,49 @@ +# Stubs for cytoolz.curried (Python 3.6) +# +# NOTE: This dynamically typed stub was automatically generated by stubgen. + +from typing import Any + +from .exceptions import merge, merge_with + +accumulate: Any +assoc: Any +assoc_in: Any +cons: Any +countby: Any +do: Any +drop: Any +excepts: Any +filter: Any +get: Any +get_in: Any +groupby: Any +interpose: Any +itemfilter: Any +itemmap: Any +iterate: Any +join: Any +keyfilter: Any +keymap: Any +map: Any +mapcat: Any +nth: Any +partial: Any +partition: Any +partition_all: Any +partitionby: Any +pluck: Any +random_sample: Any +reduce: Any +reduceby: Any +remove: Any +sliding_window: Any +sorted: Any +tail: Any +take: Any +take_nth: Any +topk: Any +unique: Any +update_in: Any +valfilter: Any +valmap: Any diff --git a/stubs/cytoolz/curried/exceptions.pyi b/stubs/cytoolz/curried/exceptions.pyi new file mode 100644 index 0000000..577e93e --- /dev/null +++ b/stubs/cytoolz/curried/exceptions.pyi @@ -0,0 +1,11 @@ +# Stubs for cytoolz.curried.exceptions (Python 3.6) +# +# NOTE: This dynamically typed stub was automatically generated by stubgen. + +from typing import Any + + +def merge(d: Any, *dicts: Any, **kwargs: Any) -> Any: ... + + +def merge_with(func: Any, d: Any, *dicts: Any, **kwargs: Any) -> Any: ... diff --git a/stubs/cytoolz/dicttoolz.pyi b/stubs/cytoolz/dicttoolz.pyi new file mode 100644 index 0000000..5b0fc20 --- /dev/null +++ b/stubs/cytoolz/dicttoolz.pyi @@ -0,0 +1,110 @@ +from typing import Any, Callable, Dict, Mapping, Sequence, Tuple, TypeVar, \ + Union, overload + +_K = TypeVar('_K') +_K1 = TypeVar('_K1') +_V = TypeVar('_V') +_V1 = TypeVar('_V1') + + +def assoc( + d: Mapping[_K, _V], + key: _K1, + value: _V1, + factory: Callable[[], Dict[Union[_K, _K1], Union[_V, _V1]]] = dict +) -> Dict[Union[_K, _K1], Union[_V, _V1]]: ... + + +def assoc_in( + d: Mapping[_K, _V], + keys: Sequence[Any], + value: Any, + factory: Callable[[], Dict[_K, Any]] = dict +) -> Dict[_K, Any]: ... + + +def dissoc( + d: Mapping[_K, _V], + *keys: _K, +) -> Dict[_K, _V]: ... + + +def get_in( + keys: Sequence[Any], + coll: Mapping[_K, _V], + default: Any = None, + no_default: bool = False, +) -> Any: ... + + +def itemfilter( + predicate: Callable[[Tuple[_K, _V]], bool], + d: Mapping[_K, _V], + factory: Callable[[], Dict[_K, _V]] = dict, +) -> Dict[_K, _V]: ... + + +def itemmap( + func: Callable[[Tuple[_K, _V]], Tuple[_K1, _V1]], + d: Mapping[_K, _V], + factory: Callable[[], Dict[_K1, _V1]] = dict +) -> Dict[_K1, _V1]: ... + + +def keyfilter( + predicate: Callable[[_K], bool], + d: Mapping[_K, _V], + factory: Callable[[], Dict[_K, _V]] = dict, +) -> Dict[_K, _V]: ... + + +def keymap( + func: Callable[[_K], _K1], + d: Mapping[_K, _V], + factory: Callable[[], Dict[_K1, _V]] = dict +) -> Dict[_K1, _V]: ... + + +def merge( + *dicts: Mapping[_K, _V], + factory: Callable[[], Dict[_K, _V]] = dict, +) -> Dict[_K, _V]: ... + + +@overload +def merge_with( + func: Callable[[Sequence[_V]], _V1], + *dicts: Mapping[_K, _V], + factory: Callable[[], Dict[_K, _V1]] = dict, +) -> Dict[_K, _V1]: ... + + +@overload +def merge_with( + func: Callable[[Sequence[Any]], Any], + *dicts: Mapping[_K, Any], + factory: Callable[[], Dict[_K, Any]] = dict, +) -> Dict[_K, Any]: ... + + +def update_in( + d: Mapping[_K, _V], + keys: Sequence[Any], + func: Callable[[Any], Any], + default: Any = None, + factory: Callable[[], Dict[_K, Any]] = dict, +) -> Dict[_K, Any]: ... + + +def valfilter( + predicate: Callable[[_V], bool], + d: Mapping[_K, _V], + factory: Callable[[], Dict[_K, _V]] = dict, +) -> Dict[_K, _V]: ... + + +def valmap( + func: Callable[[_V], _V1], + d: Mapping[_K, _V], + factory: Callable[[], Dict[_K, _V1]] = dict +) -> Dict[_K, _V1]: ... diff --git a/stubs/cytoolz/functoolz.pyi b/stubs/cytoolz/functoolz.pyi new file mode 100644 index 0000000..3a8b701 --- /dev/null +++ b/stubs/cytoolz/functoolz.pyi @@ -0,0 +1,136 @@ +# Stubs for cytoolz.functoolz (Python 3.6) +# +# NOTE: This dynamically typed stub was automatically generated by stubgen. + +from typing import Any, Callable, Iterator, TypeVar +from ._pipe import * + +_T = TypeVar('_T') +_T1 = TypeVar('_T1') + +compose: Any +dedent: Any + + +def do(func: Callable[[_T], _T1], x: _T) -> _T: ... + + +flip: Any +has_keywords: Any +has_varargs: Any +identity: Any +import_module: Any +inspect: Any +instanceproperty: Any +is_arity: Any +is_partial_args: Any +is_valid_args: Any +juxt: Any +memoize: Any +no_default: str +num_required_args: Any +return_none: Any +thread_first: Any +thread_last: Any + + +def reduce(*args: Any, **kwargs: Any) -> Any: ... + + +class complement: + __reduce__: Any = ... + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __call__(self, *args: Any, **kwargs: Any) -> Any: ... + + +class curry: + _has_unknown_args: Any = ... + _should_curry: Any = ... + _should_curry_internal: Any = ... + _sigspec: Any = ... + args: Any = ... + bind: Any = ... + call: Any = ... + func: Any = ... + keywords: Any = ... + __name__: Any = ... + __qualname__: Any = ... + __reduce__: Any = ... + __signature__: Any = ... + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __call__(self, *args: Any, **kwargs: Any) -> Any: ... + + def __eq__(self, other: Any) -> Any: ... + + def __ge__(self, other: Any) -> Any: ... + + def __get__(self, *args: Any, **kwargs: Any) -> Any: ... + + def __gt__(self, other: Any) -> Any: ... + + def __hash__(self) -> Any: ... + + def __le__(self, other: Any) -> Any: ... + + def __lt__(self, other: Any) -> Any: ... + + def __ne__(self, other: Any) -> Any: ... + + +class excepts: + exc: Any = ... + func: Any = ... + handler: Any = ... + __name__: Any = ... + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __call__(self, *args: Any, **kwargs: Any) -> Any: ... + + def __reduce__(self) -> Any: ... + + def __setstate__(self, state: Any) -> Any: ... + + +class ifilter: + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __iter__(self) -> Iterator: ... + + def __next__(self) -> Any: ... + + def __reduce__(self) -> Any: ... + + +class imap: + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __iter__(self) -> Iterator: ... + + def __next__(self) -> Any: ... + + def __reduce__(self) -> Any: ... + + +class partial: + args: Any = ... + func: Any = ... + keywords: Any = ... + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __call__(self, *args: Any, **kwargs: Any) -> Any: ... + + def __delattr__(self, name: Any) -> Any: ... + + def __reduce__(self) -> Any: ... + + def __setattr__(self, name: Any, value: Any) -> Any: ... + + def __setstate__(self, state: Any) -> Any: ... diff --git a/stubs/cytoolz/itertoolz.pyi b/stubs/cytoolz/itertoolz.pyi new file mode 100644 index 0000000..d8a6d49 --- /dev/null +++ b/stubs/cytoolz/itertoolz.pyi @@ -0,0 +1,500 @@ +from random import Random as _Random +from typing import Any, Callable, Dict, Iterable, Iterator, List, Sequence, \ + Tuple, TypeVar, Union, overload + +_K = TypeVar('_K') +_V = TypeVar('_V') +# noinspection PyShadowingBuiltins +_T = TypeVar('_T') +# noinspection PyShadowingBuiltins +_T_co = TypeVar('_T_co', covariant=True) +# noinspection PyShadowingBuiltins +_T1 = TypeVar('_T1') +# noinspection PyShadowingBuiltins +_T2 = TypeVar('_T2') +no_default: str +no_pad: str + + +def concat(seqs: Iterable[Iterable[_T]]) -> Iterable[_T]: ... + + +def concatv(*seqs: Iterable[_T]) -> Iterable[_T]: ... + + +def cons(el: _T, seq: Iterable[_T]) -> Iterator[_T]: ... + + +def count(seq: Iterable) -> int: ... + + +@overload +def diff(*seqs: Sequence[_T]) -> Iterable[Tuple[_T, ...]]: ... + + +@overload +def diff( + *seqs: Sequence[_T], + key: Callable[[_T], _T1], +) -> Iterable[Tuple[_T1, ...]]: ... + + +@overload +def diff( + *seqs: Sequence[_T], + default: _T2, +) -> Iterable[Tuple[Union[_T, _T2], ...]]: ... + + +@overload +def diff( + *seqs: Sequence[_T], + key: Callable[[_T], _T1], + default: _T2, +) -> Iterable[Tuple[Union[_T1, _T2], ...]]: ... + + +def drop(n: int, seq: Iterable[_T]) -> Iterable[_T]: ... + + +def first(seq: Iterable[_T]) -> _T: ... + + +def frequencies(seq: Iterable[_T]) -> Dict[_T, int]: ... + + +@overload +def get(ind: int, seq: Iterable[_T]) -> _T: ... + + +@overload +def get( + ind: int, + seq: Iterable[_T], + default: Union[_T1, str] = no_default, +) -> Union[_T, _T1]: ... + + +# TODO +def getter(index: Any) -> Any: ... + + +@overload +def groupby( + key: _K, + seq: Iterable[Dict[_K, _V]], +) -> Dict[_K, List[Dict[_K, _V]]]: ... + + +@overload +def groupby(key: Callable[[_T], _T1], seq: Iterable[_T]) -> Dict[_T1, List[_T]]: ... + + +def identity(x: _T) -> _T: ... + + +def isdistinct(seq: Iterable) -> bool: ... + + +def isiterable(x: Any) -> bool: ... + + +# TODO +def join(leftkey: Any, leftseq: Any, rightkey: Any, rightseq: Any, + left_default: Any = no_default, + right_default: Any = no_default) -> Any: ... + + +def last(seq: Iterable[_T]) -> _T: ... + + +def mapcat(func: Callable[[_T_co], Iterable[_T1]], + seqs: Iterable[_T_co]) -> Iterable[_T1]: ... + + +# TODO replace kwargs with key +def merge_sorted(*seqs: Iterable[_T], **kwargs: Any) -> Iterable[_T]: ... + + +def nth(n: int, seq: Iterable[_T]) -> _T: ... + + +@overload +def partition(n: int, seq: Iterable[_T]) -> Iterable[Tuple[_T, ...]]: ... + + +@overload +def partition( + n: int, + seq: Iterable[_T], + pad: _T1, +) -> Iterable[Tuple[Union[_T, _T1], ...]]: ... + + +def partition_all(n: int, seq: Iterable[_T]) -> Iterable[Tuple[_T, ...]]: ... + + +def peek(seq: Iterable[_T]) -> Tuple[_T, Iterable[_T]]: ... + + +@overload +def pluck( + ind: Union[int, List[int], str, List[str]], + seqs: Union[List[_T], Dict[Union[str, int], _T]], +) -> Iterable[Tuple[_T, ...]]: ... + + +@overload +def pluck( + ind: Union[int, List[int], str, List[str]], + seqs: Union[List[_T], Dict[Union[str, int], _T]], + default: Union[_T1, str] = no_default, +) -> Iterable[Tuple[Union[_T, _T1], ...]]: ... + + +@overload +def reduceby( + key: Callable[[_T], _T1], + binop: Callable[[_T, _T], _T], + seq: Iterable[_T], +) -> Dict[_T1, _T]: ... + + +# Causes error in mypy by conflicting with first overload +# @overload +# def reduceby( +# key: _K, +# binop: Callable[[Dict[_K, _V], Dict[_K, _V]], Dict[_K, _V]], +# seq: Dict[_K, _V], +# ) -> Dict[_V, Dict[_K, _V]]: ... + + +@overload +def reduceby( + key: _K, + binop: Callable[[_T, Dict[_K, _V]], _T], + seq: Dict[_K, _V], + init: Union[Callable[[], _T], _T], +) -> Dict[_V, _T]: ... + + +@overload +def reduceby( + key: Callable[[_T], _T1], + binop: Callable[[_T2, _T], _T2], + seq: Iterable[_T], + init: Union[Callable[[], _T2], _T2], +) -> Dict[_T1, _T2]: ... + + +# uncomment if necessary +# @overload +# def reduceby(key: Any, binop: Any, seq: Any, init: Any = no_default) -> +# Any: ... + + +# TODO - no documentation +def rest(seq: Any) -> Any: ... + + +def second(seq: Iterable[_T]) -> _T: ... + + +def tail(n: int, seq: Iterable[_T]) -> Iterable[_T]: ... + + +def take(n: int, seq: Iterable[_T]) -> Iterable[_T]: ... + + +def take_nth(n: int, seq: Iterable[_T]) -> Iterable[_T]: ... + + +def topk(k: int, seq: Iterable[_T], key: Any = None) -> Iterable[_T]: ... + + +def unique(seq: Iterable[_T], key: Any = None) -> Iterable[_T]: ... + + +#################################################################### +# TBD +#################################################################### + +def heapify(*args: Any, **kwargs: Any) -> Any: ... + + +def heappop(*args: Any, **kwargs: Any) -> Any: ... + + +def heapreplace(heap: Any, item: Any) -> Any: ... + + +class Random(_Random): + __init__: Any = ... + VERSION: Any = ... + _randbelow: Any = ... + betavariate: Any = ... + choice: Any = ... + choices: Any = ... + expovariate: Any = ... + gammavariate: Any = ... + gauss: Any = ... + getstate: Any = ... + lognormvariate: Any = ... + normalvariate: Any = ... + paretovariate: Any = ... + randint: Any = ... + randrange: Any = ... + sample: Any = ... + seed: Any = ... + setstate: Any = ... + shuffle: Any = ... + triangular: Any = ... + uniform: Any = ... + vonmisesvariate: Any = ... + weibullvariate: Any = ... + __getstate__: Any = ... + __reduce__: Any = ... + __setstate__: Any = ... + + +class accumulate: + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __iter__(self) -> Iterator: ... + + def __next__(self) -> Any: ... + + def __reduce__(self) -> Any: ... + + def __setstate__(self, state: Any) -> Any: ... + + +class chain: + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + @classmethod + def from_iterable(cls, *args: Any, **kwargs: Any) -> Any: ... + + def __iter__(self) -> Iterator: ... + + def __next__(self) -> Any: ... + + def __reduce__(self) -> Any: ... + + def __setstate__(self, state: Any) -> Any: ... + + +class deque: + maxlen: Any = ... + __hash__: Any = ... + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def append(self, *args: Any, **kwargs: Any) -> Any: ... + + def appendleft(self, *args: Any, **kwargs: Any) -> Any: ... + + def clear(self, *args: Any, **kwargs: Any) -> Any: ... + + def copy(self, *args: Any, **kwargs: Any) -> Any: ... + + def count(self, *args: Any, **kwargs: Any) -> Any: ... + + def extend(self, *args: Any, **kwargs: Any) -> Any: ... + + def extendleft(self, *args: Any, **kwargs: Any) -> Any: ... + + def index(self, *args: Any, **kwargs: Any) -> Any: ... + + def insert(self, *args: Any, **kwargs: Any) -> Any: ... + + def pop(self, *args: Any, **kwargs: Any) -> Any: ... + + def popleft(self, *args: Any, **kwargs: Any) -> Any: ... + + def remove(self, *args: Any, **kwargs: Any) -> Any: ... + + def reverse(self, *args: Any, **kwargs: Any) -> Any: ... + + def rotate(self, *args: Any, **kwargs: Any) -> Any: ... + + def __add__(self, other: Any) -> Any: ... + + def __bool__(self) -> Any: ... + + def __contains__(self, *args: Any, **kwargs: Any) -> Any: ... + + def __copy__(self) -> Any: ... + + def __delitem__(self, *args: Any, **kwargs: Any) -> Any: ... + + def __eq__(self, other: Any) -> Any: ... + + def __ge__(self, other: Any) -> Any: ... + + def __getitem__(self, index: Any) -> Any: ... + + def __gt__(self, other: Any) -> Any: ... + + def __iadd__(self, *args: Any, **kwargs: Any) -> Any: ... + + def __imul__(self, *args: Any, **kwargs: Any) -> Any: ... + + def __iter__(self) -> Iterator: ... + + def __le__(self, other: Any) -> Any: ... + + def __len__(self, *args: Any, **kwargs: Any) -> Any: ... + + def __lt__(self, other: Any) -> Any: ... + + def __mul__(self, other: Any) -> Any: ... + + def __ne__(self, other: Any) -> Any: ... + + def __reduce__(self) -> Any: ... + + def __reversed__(self, *args: Any, **kwargs: Any) -> Any: ... + + def __rmul__(self, other: Any) -> Any: ... + + def __setitem__(self, index: Any, object: Any) -> Any: ... + + def __sizeof__(self) -> Any: ... + + +class interleave: + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __iter__(self) -> Iterator: ... + + def __next__(self) -> Any: ... + + def __reduce__(self) -> Any: ... + + def __setstate__(self, state: Any) -> Any: ... + + +class interpose: + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __iter__(self) -> Iterator: ... + + def __next__(self) -> Any: ... + + def __reduce__(self) -> Any: ... + + def __setstate__(self, state: Any) -> Any: ... + + +class islice: + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __iter__(self) -> Iterator: ... + + def __next__(self) -> Any: ... + + def __reduce__(self) -> Any: ... + + def __setstate__(self, state: Any) -> Any: ... + + +class itemgetter: + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __call__(self, *args: Any, **kwargs: Any) -> Any: ... + + def __reduce__(self) -> Any: ... + + +class iterate: + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __iter__(self) -> Iterator: ... + + def __next__(self) -> Any: ... + + def __reduce__(self) -> Any: ... + + def __setstate__(self, state: Any) -> Any: ... + + +class map: + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __iter__(self) -> Iterator: ... + + def __next__(self) -> Any: ... + + def __reduce__(self) -> Any: ... + + +class random_sample: + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __iter__(self) -> Iterator: ... + + def __next__(self) -> Any: ... + + def __reduce__(self) -> Any: ... + + def __setstate__(self, state: Any) -> Any: ... + + +class remove: + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __iter__(self) -> Iterator: ... + + def __next__(self) -> Any: ... + + def __reduce__(self) -> Any: ... + + def __setstate__(self, state: Any) -> Any: ... + + +class sliding_window: + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __iter__(self) -> Iterator: ... + + def __next__(self) -> Any: ... + + def __reduce__(self) -> Any: ... + + def __setstate__(self, state: Any) -> Any: ... + + +class zip: + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __iter__(self) -> Iterator: ... + + def __next__(self) -> Any: ... + + def __reduce__(self) -> Any: ... + + +class zip_longest: + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __iter__(self) -> Iterator: ... + + def __next__(self) -> Any: ... + + def __reduce__(self) -> Any: ... + + def __setstate__(self, state: Any) -> Any: ... diff --git a/stubs/cytoolz/recipes.pyi b/stubs/cytoolz/recipes.pyi new file mode 100644 index 0000000..3ce895f --- /dev/null +++ b/stubs/cytoolz/recipes.pyi @@ -0,0 +1,20 @@ +# Stubs for cytoolz.recipes (Python 3.6) +# +# NOTE: This dynamically typed stub was automatically generated by stubgen. + +from typing import Any + +countby: Any + + +class partitionby: + + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + + def __iter__(self) -> Any: ... + + def __next__(self) -> Any: ... + + def __reduce__(self) -> Any: ... + + def __setstate__(self, state: Any) -> Any: ... diff --git a/stubs/cytoolz/utils.pyi b/stubs/cytoolz/utils.pyi new file mode 100644 index 0000000..a2bf304 --- /dev/null +++ b/stubs/cytoolz/utils.pyi @@ -0,0 +1,10 @@ +# Stubs for cytoolz.utils (Python 3.6) +# +# NOTE: This dynamically typed stub was automatically generated by stubgen. + +from typing import Any + +consume: Any +include_dirs: Any +no_default: str +raises: Any diff --git a/stubs/nose/__init__.pyi b/stubs/nose/__init__.pyi new file mode 100644 index 0000000..011c2c4 --- /dev/null +++ b/stubs/nose/__init__.pyi @@ -0,0 +1,6 @@ +from typing import Any + +from pytoolz.typing import Seq + + +def main(defaultTest: Seq[str], **kwargs: Any) -> None: ... diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/run.py b/tests/run.py new file mode 100644 index 0000000..88da5c4 --- /dev/null +++ b/tests/run.py @@ -0,0 +1,11 @@ +if __name__ == '__main__': + import sys + import nose + + sys.argv.append('--with-doctest') + sys.argv.append('--with-coverage') + sys.argv.append('--cover-branches') + sys.argv.append('--cover-erase') + sys.argv.append('--cover-package=pytoolz') + + nose.main(defaultTest=['pytoolz', '.']) diff --git a/unittest.cfg b/unittest.cfg new file mode 100644 index 0000000..b640d31 --- /dev/null +++ b/unittest.cfg @@ -0,0 +1,11 @@ +[unittest] +start-dir = tests +code-directories = pytoolz +plugins = nose2.plugins.doctests + +[doctest] +always-on = True +extensions = .py + +[coverage] +always-on = True