diff --git a/news/5635.vendor.rst b/news/5635.vendor.rst new file mode 100644 index 00000000..e0c48bab --- /dev/null +++ b/news/5635.vendor.rst @@ -0,0 +1 @@ +Bump vistir to 0.8.0, requirementslib to 2.2.4. diff --git a/pipenv/utils/resolver.py b/pipenv/utils/resolver.py index 7fdbaec4..df3df280 100644 --- a/pipenv/utils/resolver.py +++ b/pipenv/utils/resolver.py @@ -32,7 +32,7 @@ from pipenv.vendor import click from pipenv.vendor.requirementslib import Requirement from pipenv.vendor.requirementslib.models.requirements import Line from pipenv.vendor.requirementslib.models.utils import DIRECT_URL_RE -from pipenv.vendor.vistir import open_file +from pipenv.vendor.vistir.contextmanagers import open_file from pipenv.vendor.vistir.path import create_tracked_tempdir try: diff --git a/pipenv/vendor/requirementslib/__init__.py b/pipenv/vendor/requirementslib/__init__.py index 16479c39..f0f834cb 100644 --- a/pipenv/vendor/requirementslib/__init__.py +++ b/pipenv/vendor/requirementslib/__init__.py @@ -5,7 +5,7 @@ from .models.lockfile import Lockfile from .models.pipfile import Pipfile from .models.requirements import Requirement -__version__ = "2.2.3" +__version__ = "2.2.4" logger = logging.getLogger(__name__) diff --git a/pipenv/vendor/requirementslib/funktools.py b/pipenv/vendor/requirementslib/funktools.py new file mode 100644 index 00000000..4c4cdcb2 --- /dev/null +++ b/pipenv/vendor/requirementslib/funktools.py @@ -0,0 +1,77 @@ +"""A small collection of useful functional tools for working with iterables. + +unnest, chunked and take are used by pipenv. However, since pipenv +relies on requirementslib, we can them here. +""" +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)) diff --git a/pipenv/vendor/requirementslib/models/requirements.py b/pipenv/vendor/requirementslib/models/requirements.py index 51b5cf66..3f1ae40e 100644 --- a/pipenv/vendor/requirementslib/models/requirements.py +++ b/pipenv/vendor/requirementslib/models/requirements.py @@ -32,7 +32,6 @@ from pipenv.patched.pip._vendor.packaging.specifiers import ( from pipenv.patched.pip._vendor.packaging.utils import canonicalize_name from pipenv.patched.pip._vendor.packaging.version import parse from pipenv.vendor.vistir.contextmanagers import temp_path -from pipenv.vendor.vistir.misc import dedup from pipenv.vendor.vistir.path import ( create_tracked_tempdir, get_converted_relative_path, @@ -43,6 +42,7 @@ from pipenv.vendor.vistir.path import ( from ..environment import MYPY_RUNNING from ..exceptions import RequirementError +from ..funktools import dedup from ..utils import ( VCS_LIST, add_ssh_scheme_to_git_uri, @@ -213,7 +213,7 @@ class Line(object): line = self.line extras_str = extras_to_string(self.extras) with_hashes = False if self.editable or self.is_vcs else with_hashes - hash_list = ["--hash={0}".format(h) for h in self.hashes] + hash_list = ["--hash={0}".format(h) for h in sorted(self.hashes)] if self.is_named: line = self.name_and_specifier elif self.is_direct_url: diff --git a/pipenv/vendor/vendor.txt b/pipenv/vendor/vendor.txt index b116dcee..50518628 100644 --- a/pipenv/vendor/vendor.txt +++ b/pipenv/vendor/vendor.txt @@ -13,9 +13,9 @@ ptyprocess==0.7.0 pyparsing==3.0.9 python-dotenv==0.19.0 pythonfinder==1.3.2 -requirementslib==2.2.3 +requirementslib==2.2.4 ruamel.yaml==0.17.21 shellingham==1.5.0 toml==0.10.2 tomlkit==0.9.2 -vistir==0.7.5 +vistir==0.8.0 diff --git a/pipenv/vendor/vistir/__init__.py b/pipenv/vendor/vistir/__init__.py index 9d09aaeb..7014814e 100644 --- a/pipenv/vendor/vistir/__init__.py +++ b/pipenv/vendor/vistir/__init__.py @@ -1,42 +1,3 @@ # -*- coding=utf-8 -*- -import importlib -__newpaths = { - 'create_spinner': 'vistir.spin', - 'cd': 'vistir.contextmanagers', - 'atomic_open_for_write': 'vistir.contextmanagers', - 'open_file': 'vistir.contextmanagers', - 'replaced_stream': 'vistir.contextmanagers', - 'replaced_streams': 'vistir.contextmanagers', - 'spinner': 'vistir.contextmanagers', - 'temp_environ': 'vistir.contextmanagers', - 'temp_path': 'vistir.contextmanagers', - 'hide_cursor': 'vistir.cursor', - 'show_cursor': 'vistir.cursor', - 'StreamWrapper': 'vistir.misc', - 'chunked':'vistir.misc', - 'decode_for_output': 'vistir.misc', - 'divide': 'vistir.misc', - 'get_wrapped_stream': 'vistir.misc', - 'load_path': 'vistir.misc', - 'partialclass': 'vistir.misc', - 'run': 'vistir.misc', - 'shell_escape': 'vistir.misc', - 'take': 'vistir.misc', - 'to_bytes': 'vistir.misc', - 'to_text': 'vistir.misc', - 'create_tracked_tempdir': 'vistir.path', - 'create_tracked_tempfile': 'vistir.path', - 'mkdir_p': 'vistir.path', - 'rmtree': 'vistir.path', -} - -from warnings import warn - -def __getattr__(name): - warn((f"Importing {name} directly from vistir is deprecated.\nUse 'from {__newpaths[name]} import {name}' instead.\n" - "This import path will be removed in vistir 0.8"), - DeprecationWarning) - return getattr(importlib.import_module(__newpaths[name]), name) - -__version__ = "0.7.5" +__version__ = "0.8.0" diff --git a/pipenv/vendor/vistir/contextmanagers.py b/pipenv/vendor/vistir/contextmanagers.py index 0e5626f7..20b4d64d 100644 --- a/pipenv/vendor/vistir/contextmanagers.py +++ b/pipenv/vendor/vistir/contextmanagers.py @@ -44,8 +44,6 @@ __all__ = [ "cd", "atomic_open_for_write", "open_file", - "spinner", - "dummy_spinner", "replaced_stream", "replaced_streams", ] @@ -127,85 +125,6 @@ def cd(path): os.chdir(prev_cwd) -@contextmanager -def dummy_spinner(spin_type, text, **kwargs): - # type: (str, str, Any) - class FakeClass(object): - def __init__(self, text=""): - self.text = text - - def fail(self, exitcode=1, text=None): - if text: - print(text) - raise SystemExit(exitcode, text) - - def ok(self, text): - print(text) - return 0 - - def write(self, text): - print(text) - - myobj = FakeClass(text) - yield myobj - - -@contextmanager -def spinner( - spinner_name=None, # type: Optional[str] - start_text=None, # type: Optional[str] - handler_map=None, # type: Optional[Dict[str, Callable]] - nospin=False, # type: bool - write_to_stdout=True, # type: bool -): - # type: (...) -> ContextManager[TSpinner] - """Get a spinner object or a dummy spinner to wrap a context. - - :param str spinner_name: A spinner type e.g. "dots" or "bouncingBar" (default: {"bouncingBar"}) - :param str start_text: Text to start off the spinner with (default: {None}) - :param dict handler_map: Handler map for signals to be handled gracefully (default: {None}) - :param bool nospin: If true, use the dummy spinner (default: {False}) - :param bool write_to_stdout: Writes to stdout if true, otherwise writes to stderr (default: True) - :return: A spinner object which can be manipulated while alive - :rtype: :class:`~vistir.spin.VistirSpinner` - - Raises: - RuntimeError -- Raised if the spinner extra is not installed - """ - - from .spin import create_spinner - - has_yaspin = None - try: - import yaspin - except (ImportError, ModuleNotFoundError): # noqa - has_yaspin = False - if not nospin: - raise RuntimeError( - "Failed to import spinner! Reinstall vistir with command:" - " pip install --upgrade vistir[spinner]" - ) - else: - spinner_name = "" - else: - has_yaspin = True - spinner_name = "" - use_yaspin = (has_yaspin is False) or (nospin is True) - if has_yaspin is None or has_yaspin is True and not nospin: - use_yaspin = True - if start_text is None and use_yaspin is True: - start_text = "Running..." - with create_spinner( - spinner_name=spinner_name, - text=start_text, - handler_map=handler_map, - nospin=nospin, - use_yaspin=use_yaspin, - write_to_stdout=write_to_stdout, - ) as _spinner: - yield _spinner - - @contextmanager def atomic_open_for_write(target, binary=False, newline=None, encoding=None): # type: (str, bool, Optional[str], Optional[str]) -> None diff --git a/pipenv/vendor/vistir/misc.py b/pipenv/vendor/vistir/misc.py index 24f82676..6f9abf44 100644 --- a/pipenv/vendor/vistir/misc.py +++ b/pipenv/vendor/vistir/misc.py @@ -1,9 +1,4 @@ -# -*- coding=utf-8 -*- -from __future__ import absolute_import, print_function, unicode_literals - -import atexit import io -import itertools import json import locale import logging @@ -11,41 +6,28 @@ import os import subprocess import sys import threading -import typing import warnings -from collections import OrderedDict -from functools import partial -from itertools import islice, tee +from itertools import tee from weakref import WeakKeyDictionary from queue import Empty, Queue -from typing import Iterable +from typing import Iterable, List, Optional, Union from .cmdparse import Script -from .contextmanagers import spinner as spinner _fs_encode_errors = "surrogatepass" -if os.name != "nt": - - class WindowsError(OSError): - pass - __all__ = [ "shell_escape", "unnest", - "dedup", "run", "load_path", "partialclass", "to_text", "to_bytes", "locale_encoding", - "chunked", - "take", - "divide", "getpreferredencoding", "decode_for_output", "get_canonical_encoding_name", @@ -54,11 +36,6 @@ __all__ = [ ] -if typing.TYPE_CHECKING: - from typing import Any, Dict, Generator, IO, List, Optional, Text, Tuple, Union - from .spin import VistirSpinner - - def _get_logger(name=None, level="ERROR"): # type: (Optional[str], str) -> logging.Logger if not name: @@ -75,8 +52,7 @@ def _get_logger(name=None, level="ERROR"): return logger -def shell_escape(cmd): - # type: (Union[str, List[str]]) -> str +def shell_escape(cmd: Union[str, List[str]]) -> str: """Escape strings for use in :func:`~subprocess.Popen` and :func:`run`. This is a passthrough method for instantiating a @@ -133,18 +109,6 @@ def _is_iterable(elem): return False -def dedup(iterable): - # type: (Iterable) -> Iterable - """Deduplicate an iterable object like iter(set(iterable)) but order- - preserved.""" - warnings.warn( - ('This function is deprecated and will be removed in version 0.8.' - 'Use instead: sorted(iter(dict.fromkeys(iterable)))'), - DeprecationWarning, stacklevel=2) - - return iter(OrderedDict.fromkeys(iterable)) - - def _spawn_subprocess( script, # type: Union[str, List[str]] env=None, # type: Optional[Dict[str, str]] @@ -153,9 +117,12 @@ def _spawn_subprocess( combine_stderr=True, # type: bool encoding="utf-8", # type: str ): - # type: (...) -> subprocess.Popen from shutil import which + if os.name != "nt": + class WindowsError(OSError): + """this exception is only available on windows""" + if not env: env = os.environ.copy() command = which(script.command) @@ -185,8 +152,8 @@ def _spawn_subprocess( # a "command" that is non-executable. See pypa/pipenv#2727. try: return subprocess.Popen(cmd, **options) - except WindowsError as e: # pragma: no cover - if getattr(e, "winerror", 9999) != 193: + except WindowsError as err: # pragma: no cover + if getattr(err, "winerror", 9999) != 193: raise options["shell"] = True # Try shell mode to use Windows's file association for file launch. @@ -604,25 +571,19 @@ def run( if block or not return_object: combine_stderr = False start_text = "" - with spinner( - spinner_name=spinner_name, + return _create_subprocess( + cmd, + env=_env, + return_object=return_object, + block=block, + cwd=cwd, + verbose=verbose, + spinner=None, + combine_stderr=combine_stderr, start_text=start_text, - nospin=nospin, write_to_stdout=write_to_stdout, - ) as sp: - return _create_subprocess( - cmd, - env=_env, - return_object=return_object, - block=block, - cwd=cwd, - verbose=verbose, - spinner=sp, - combine_stderr=combine_stderr, - start_text=start_text, - write_to_stdout=write_to_stdout, - encoding=encoding, - ) + encoding=encoding, + ) def load_path(python): @@ -776,64 +737,6 @@ def to_text(string, encoding="utf-8", errors=None): return string -def divide(n, iterable): - """split an iterable into n groups, per https://more- - itertools.readthedocs.io/en/latest/api.html#grouping. - - :param int n: Number of unique groups - :param iter iterable: An iterable to split up - :return: a list of new iterables derived from the original iterable - :rtype: list - """ - warnings.warn( - ('This function is deprecated and will be removed in version 0.8.' - 'Use instead: more_itertools.divide(n, iterable)))'), - DeprecationWarning, stacklevel=2) - seq = tuple(iterable) - q, r = divmod(len(seq), n) - - ret = [] - for i in range(n): - start = (i * q) + (i if i < r else r) - stop = ((i + 1) * q) + (i + 1 if i + 1 < r else r) - ret.append(iter(seq[start:stop])) - - return ret - - -def take(n, 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 - - from https://github.com/erikrose/more-itertools/blob/master/more_itertools/recipes.py - """ - - warnings.warn( - ('This function is deprecated and will be removed in version 0.8.' - 'Use instead: list(islice(iterable, n))'), - DeprecationWarning, stacklevel=2) - - return list(islice(iterable, n)) - - -def chunked(n, iterable): - """Split an iterable into lists of length *n*. - - :param int n: Number of unique groups - :param iter iterable: An iterable to split up - - from https://github.com/erikrose/more-itertools/blob/master/more_itertools/more.py - """ - warnings.warn( - ('This function is deprecated and will be removed in version 0.8.' - 'Use instead: more_itertools.chunked(iterable, n)))'), - DeprecationWarning, stacklevel=2) - - return iter(partial(take, n, iter(iterable)), []) - - try: locale_encoding = locale.getdefaultlocale()[1] or "ascii" except Exception: