Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pipenv/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from pipenv.vendor import colorama

# Backward compatability with vistir
# These variables will be removed in vistir 0.8.0
no_color = False
for item in ("ANSI_COLORS_DISABLED", "VISTIR_DISABLE_COLORS"):
if os.getenv(item):
Expand Down
6 changes: 3 additions & 3 deletions pipenv/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1468,7 +1468,7 @@ def write_requirement_to_file(
click.echo(
f"Writing supplied requirement line to temporary file: {line!r}", err=True
)
f.write(vistir.misc.to_bytes(line))
f.write(line.encode())
r = f.name
f.close()
return r
Expand Down Expand Up @@ -1661,8 +1661,8 @@ def pip_install_deps(
err=True,
)
target = editable_requirements if vcs_or_editable else standard_requirements
target.write(vistir.misc.to_bytes(line))
target.write(vistir.misc.to_bytes("\n"))
target.write(line.encode())
target.write(b"\n")
standard_requirements.close()
editable_requirements.close()

Expand Down
7 changes: 3 additions & 4 deletions pipenv/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import os
import site
import sys
import typing
from pathlib import Path
from sysconfig import get_paths, get_python_version, get_scheme_names

Expand All @@ -19,7 +20,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
Expand All @@ -33,7 +34,7 @@
from pipenv.patched.pip._vendor.distlib.util import cached_property


if is_type_checking():
if typing.TYPE_CHECKING:
from types import ModuleType
from typing import ContextManager, Dict, Generator, List, Optional, Set, Union

Expand Down Expand Up @@ -716,8 +717,6 @@ def reverse_dependency(cls, node):
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):
Expand Down
12 changes: 6 additions & 6 deletions pipenv/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from traceback import format_tb

from pipenv import environments
from pipenv.vendor import click, vistir
from pipenv.vendor import click
from pipenv.vendor.click.exceptions import ClickException, FileError, UsageError

if sys.version_info[:2] >= (3, 7):
Expand Down Expand Up @@ -67,7 +67,7 @@ def __init__(self, message=None, **kwargs):

def show(self, file=None):
if file is None:
file = vistir.misc.get_text_stderr()
file = sys.stderr
if self.extra:
if isinstance(self.extra, str):
self.extra = [self.extra]
Expand All @@ -90,7 +90,7 @@ def __init__(self, cmd, out="", err="", exit_code=1):

def show(self, file=None):
if file is None:
file = vistir.misc.get_text_stderr()
file = sys.stderr
click.echo(
"{} {}".format(
click.style("Error running command: ", fg="red"),
Expand Down Expand Up @@ -120,7 +120,7 @@ def __init__(self, contents="", error_text=""):

def show(self, file=None):
if file is None:
file = vistir.misc.get_text_stderr()
file = sys.stderr
message = "{}\n{}".format(
click.style("Failed parsing JSON results:", bold=True),
print(self.message.strip(), file=file),
Expand Down Expand Up @@ -150,7 +150,7 @@ def __init__(self, message=None, ctx=None, **kwargs):

def show(self, file=None):
if file is None:
file = vistir.misc.get_text_stderr()
file = sys.stderr
color = None
if self.ctx is not None:
color = self.ctx.color
Expand Down Expand Up @@ -189,7 +189,7 @@ def __init__(self, filename, message=None, **kwargs):

def show(self, file=None):
if file is None:
file = vistir.misc.get_text_stderr()
file = sys.stderr
if self.extra:
if isinstance(self.extra, str):
self.extra = [self.extra]
Expand Down
80 changes: 80 additions & 0 deletions pipenv/utils/funktools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""
A small collection of useful functional tools for working with iterables.

This module should be in requirementslib. Once we release a new version of requirementslib
we can remove this file and use the one in requirementslib.
"""
from functools import partial
from itertools import islice, tee
from typing 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


def dedup(iterable: Iterable) -> Iterable:
# type: (Iterable) -> Iterable
"""Deduplicate an iterable object like iter(set(iterable)) but order-
preserved."""

return iter(dict.fromkeys(iterable))
50 changes: 50 additions & 0 deletions tests/unit/test_funktools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import pytest

from pipenv.utils.funktools import dedup, unnest, _is_iterable


def test_unnest():
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]


@pytest.mark.parametrize(
"iterable, result",
[
[["abc", "def"], True],
[("abc", "def"), True],
["abcdef", True],
[None, False],
[1234, False],
],
)
def test_is_iterable(iterable, result):
assert _is_iterable(iterable) is result


def test_unnest_none():
assert list(unnest(None)) == [None]


def test_dedup():
dup_strings = ["abcde", "fghij", "klmno", "pqrst", "abcde", "klmno"]
assert list(dedup(dup_strings)) == [
"abcde",
"fghij",
"klmno",
"pqrst",
]
dup_ints = (12345, 56789, 12345, 54321, 98765, 54321)
assert list(dedup(dup_ints)) == [12345, 56789, 54321, 98765]