diff --git a/pipenv/environment.py b/pipenv/environment.py index 94193b28..f3c2e898 100644 --- a/pipenv/environment.py +++ b/pipenv/environment.py @@ -18,6 +18,7 @@ from pipenv.patched.pip._internal.req.req_uninstall import UninstallPathSet from pipenv.patched.pip._vendor import pkg_resources from pipenv.patched.pip._vendor.packaging.utils import canonicalize_name from pipenv.utils.constants import is_type_checking +from pipenv.utils.funktools import chunked, unnest from pipenv.utils.indexes import prepare_pip_source_args from pipenv.utils.processes import subprocess_run from pipenv.utils.shell import make_posix, normalize_path @@ -714,8 +715,6 @@ class Environment: yield new_node def reverse_dependencies(self): - from vistir.misc import chunked, unnest - rdeps = {} for req in self.get_package_requirements(): for d in self.reverse_dependency(req): diff --git a/pipenv/utils/funktools.py b/pipenv/utils/funktools.py new file mode 100644 index 00000000..bcfeae5e --- /dev/null +++ b/pipenv/utils/funktools.py @@ -0,0 +1,69 @@ +""" +A small collection of useful functional tools for working with iterables. +""" +from functools import islice, partial, tee + +from typings import Any, Iterable + + +def _is_iterable(elem: Any) -> bool: + if getattr(elem, "__iter__", False) or isinstance(elem, Iterable): + return True + return False + + +def take(n: int, iterable: Iterable) -> Iterable: + """Take n elements from the supplied iterable without consuming it. + + :param int n: Number of unique groups + :param iter iterable: An iterable to split up + """ + return list(islice(iterable, n)) + + +def chunked(n: int, iterable: Iterable) -> Iterable: + """Split an iterable into lists of length *n*. + + :param int n: Number of unique groups + :param iter iterable: An iterable to split up + + """ + return iter(partial(take, n, iter(iterable)), []) + + +def unnest(elem: Iterable) -> Any: + # type: (Iterable) -> Any + """Flatten an arbitrarily nested iterable. + + :param elem: An iterable to flatten + :type elem: :class:`~collections.Iterable` + >>> nested_iterable = ( + 1234, (3456, 4398345, (234234)), ( + 2396, ( + 928379, 29384, ( + 293759, 2347, ( + 2098, 7987, 27599 + ) + ) + ) + ) + ) + >>> list(unnest(nested_iterable)) + [1234, 3456, 4398345, 234234, 2396, 928379, 29384, 293759, + 2347, 2098, 7987, 27599] + """ + + if isinstance(elem, Iterable) and not isinstance(elem, str): + elem, target = tee(elem, 2) + else: + target = elem + if not target or not _is_iterable(target): + yield target + else: + for el in target: + if isinstance(el, Iterable) and not isinstance(el, str): + el, el_copy = tee(el, 2) + for sub in unnest(el_copy): + yield sub + else: + yield el