From 5a9bc1374a6c9d1184c00ca2d1df1d38cbd5c5bd Mon Sep 17 00:00:00 2001 From: Oz Tiram Date: Wed, 9 Nov 2022 23:43:12 +0100 Subject: [PATCH 01/13] install command: remove vistir spinner in favor of rich --- pipenv/core.py | 54 +++++++++++++++++++++++------------------ pipenv/utils/spinner.py | 2 ++ 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/pipenv/core.py b/pipenv/core.py index c51eb689..52ffd8c8 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -2277,6 +2277,10 @@ def do_install( extra_pip_args=extra_pip_args, categories=categories, ) + from pipenv.patched.pip._vendor import rich + + console = rich.console.Console() + err = rich.console.Console(stderr=True).print for pkg_line in pkg_list: click.secho( fix_utf8(f"Installing {pkg_line}..."), @@ -2284,29 +2288,32 @@ def do_install( bold=True, ) # pip install: - with vistir.contextmanagers.temp_environ(), create_spinner( - "Installing...", project.s - ) as sp: + # TODO: console.status() accepts: + # spinner='dots', spinner_style='status.spinner' + # we should use pipenv.project.settings to configure these. + with vistir.contextmanagers.temp_environ(), console.status( + "Installing..." + ) as st: if not system: os.environ["PIP_USER"] = "0" if "PYTHONHOME" in os.environ: del os.environ["PYTHONHOME"] - sp.text = f"Resolving {pkg_line}..." + st.update(f"Resolving {pkg_line}...") try: pkg_requirement = Requirement.from_line(pkg_line) except ValueError as e: - sp.write_err("{}: {}".format(click.style("WARNING", fg="red"), e)) - sp.red.fail( + err("{}: {}".format(click.style("WARNING", fg="red"), e)) + err( environments.PIPENV_SPINNER_FAIL_TEXT.format( "Installation Failed" ) ) sys.exit(1) - sp.text = "Installing..." + st.update("Installing...") try: - sp.text = f"Installing {pkg_requirement.name}..." + st.update(f"Installing {pkg_requirement.name}...") if project.s.is_verbose(): - sp.hide_and_write( + st.update( f"Installing package: {pkg_requirement.as_line(include_hashes=False)}" ) c = pip_install( @@ -2325,34 +2332,32 @@ def do_install( extra_pip_args=extra_pip_args, ) if c.returncode: - sp.write_err( + err( "{} An error occurred while installing {}!".format( click.style("Error: ", fg="red", bold=True), click.style(pkg_line, fg="green"), ), ) - sp.write_err(f"Error text: {c.stdout}") - sp.write_err(click.style(format_pip_error(c.stderr), fg="cyan")) + err(f"Error text: {c.stdout}") + err(click.style(format_pip_error(c.stderr), fg="cyan")) if project.s.is_verbose(): - sp.write_err( - click.style(format_pip_output(c.stdout), fg="cyan") - ) + err(click.style(format_pip_output(c.stdout), fg="cyan")) if "setup.py egg_info" in c.stderr: - sp.write_err( + err( "This is likely caused by a bug in {}. " "Report this to its maintainers.".format( click.style(pkg_requirement.name, fg="green") ) ) - sp.red.fail( + err( environments.PIPENV_SPINNER_FAIL_TEXT.format( "Installation Failed" ) ) sys.exit(1) except (ValueError, RuntimeError) as e: - sp.write_err("{}: {}".format(click.style("WARNING", fg="red"), e)) - sp.red.fail( + err("{}: {}".format(click.style("WARNING", fg="red"), e)) + err( environments.PIPENV_SPINNER_FAIL_TEXT.format( "Installation Failed", ) @@ -2364,7 +2369,7 @@ def do_install( and not pkg_requirement.editable and not project.s.PIPENV_RESOLVE_VCS ): - sp.write_err( + err( "{}: You installed a VCS dependency in non-editable mode. " "This will work fine, but sub-dependencies will not be resolved by {}." "\n To enable this sub-dependency functionality, specify that this dependency is editable." @@ -2381,7 +2386,7 @@ def do_install( pipfile_sections = "[dev-packages]" else: pipfile_sections = "[packages]" - sp.write( + st.update( "{} {} {} {}{}".format( click.style("Adding", bold=True), click.style(f"{pkg_requirement.name}", fg="green", bold=True), @@ -2409,18 +2414,19 @@ def do_install( except ValueError: import traceback - sp.write_err( + err( "{} {}".format( click.style("Error:", fg="red", bold=True), traceback.format_exc(), ) ) - sp.fail( + err( environments.PIPENV_SPINNER_FAIL_TEXT.format( "Failed adding package to Pipfile" ) ) - sp.ok( + # ok has a nice v in front, should do something similir with rich + st.update( environments.PIPENV_SPINNER_OK_TEXT.format("Installation Succeeded") ) # Update project settings with pre preference. diff --git a/pipenv/utils/spinner.py b/pipenv/utils/spinner.py index 489d096e..47e4d884 100644 --- a/pipenv/utils/spinner.py +++ b/pipenv/utils/spinner.py @@ -3,12 +3,14 @@ import contextlib @contextlib.contextmanager def create_spinner(text, setting, nospin=None, spinner_name=None): + from pipenv.vendor.vistir import spin if not spinner_name: spinner_name = setting.PIPENV_SPINNER if nospin is None: nospin = setting.PIPENV_NOSPIN + with spin.create_spinner( spinner_name=spinner_name, start_text=text, From c1a2fc849de9736fa589b53aeaba650308ca9fd1 Mon Sep 17 00:00:00 2001 From: Oz Tiram Date: Sun, 13 Nov 2022 10:03:35 +0100 Subject: [PATCH 02/13] cli: remove create_spinner from --rm This is in favour of `rich.console.Console`. --- pipenv/cli/command.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pipenv/cli/command.py b/pipenv/cli/command.py index d5047adc..ab665de5 100644 --- a/pipenv/cli/command.py +++ b/pipenv/cli/command.py @@ -80,8 +80,8 @@ def cli( site_packages=None, **kwargs, ): + from pipenv.patched.pip._vendor import rich from pipenv.utils.shell import system_which - from pipenv.utils.spinner import create_spinner load_dot_env(state.project, quiet=state.quiet) @@ -188,7 +188,10 @@ def cli( ) ) ) - with create_spinner(text="Running...", setting=state.project.s): + + console = rich.console.Console() + # TODO: add state.project.s to spinner status + with console.status("Running..."): # Remove the virtualenv. cleanup_virtualenv(state.project, bare=True) return 0 From 352ff9f16abebc63542d2c8047eb24e6d998787e Mon Sep 17 00:00:00 2001 From: Oz Tiram Date: Sun, 13 Nov 2022 16:14:47 +0100 Subject: [PATCH 03/13] core: remove yaspin spinner in ensure_pipfile This is in favor of rich.status. --- pipenv/core.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/pipenv/core.py b/pipenv/core.py index 52ffd8c8..f19de6b7 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -24,6 +24,7 @@ from pipenv.patched.pip._internal.req.constructors import ( ) from pipenv.patched.pip._internal.req.req_file import parse_requirements from pipenv.patched.pip._internal.utils.misc import split_auth_from_netloc +from pipenv.patched.pip._vendor import rich from pipenv.patched.pip._vendor.packaging.utils import canonicalize_name from pipenv.project import Project from pipenv.utils.constants import MYPY_RUNNING @@ -94,6 +95,10 @@ else: STARTING_LABEL = " " +console = rich.console.Console() +err = rich.console.Console(stderr=True) + + def do_clear(project): from pipenv.patched.pip._internal import locations @@ -245,14 +250,15 @@ def ensure_pipfile(project, validate=True, skip_requirements=False, system=False ) # Create a Pipfile... project.create_pipfile(python=python) - with create_spinner("Importing requirements...", project.s) as sp: + # TODO: pass project settings to status here + with console.status("Importing requirements...") as st: # Import requirements.txt. try: import_requirements(project) except Exception: - sp.fail(environments.PIPENV_SPINNER_FAIL_TEXT.format("Failed...")) + err.print(environments.PIPENV_SPINNER_FAIL_TEXT.format("Failed...")) else: - sp.ok(environments.PIPENV_SPINNER_OK_TEXT.format("Success!")) + st.update(environments.PIPENV_SPINNER_OK_TEXT.format("Success!")) # Warn the user of side-effects. click.echo( "{0}: Your {1} now contains pinned versions, if your {2} did. \n" @@ -2277,10 +2283,7 @@ def do_install( extra_pip_args=extra_pip_args, categories=categories, ) - from pipenv.patched.pip._vendor import rich - console = rich.console.Console() - err = rich.console.Console(stderr=True).print for pkg_line in pkg_list: click.secho( fix_utf8(f"Installing {pkg_line}..."), @@ -2302,8 +2305,8 @@ def do_install( try: pkg_requirement = Requirement.from_line(pkg_line) except ValueError as e: - err("{}: {}".format(click.style("WARNING", fg="red"), e)) - err( + err.print("{}: {}".format(click.style("WARNING", fg="red"), e)) + err.print( environments.PIPENV_SPINNER_FAIL_TEXT.format( "Installation Failed" ) From 5758231bc404ed58ff72fb9942717e0b070f318d Mon Sep 17 00:00:00 2001 From: Oz Tiram Date: Sun, 13 Nov 2022 16:42:01 +0100 Subject: [PATCH 04/13] core: remove yaspin spinner from install python This is in favor of rich.status --- pipenv/core.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pipenv/core.py b/pipenv/core.py index f19de6b7..82356724 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -404,17 +404,18 @@ def ensure_python(project, three=None, python=None): click.style("...", bold=True), ) ) - with create_spinner("Installing python...", project.s) as sp: + # TOOD: pass project settings to console.status + with console.status("Installing python...") as st: try: c = installer.install(version) except InstallerError as e: - sp.fail( + err.print( environments.PIPENV_SPINNER_FAIL_TEXT.format("Failed...") ) click.echo(fix_utf8("Something went wrong..."), err=True) click.secho(e.err, fg="cyan", err=True) else: - sp.ok(environments.PIPENV_SPINNER_OK_TEXT.format("Success!")) + st(environments.PIPENV_SPINNER_OK_TEXT.format("Success!")) # Print the results, in a beautiful blue... click.secho(c.stdout, fg="cyan", err=True) # Clear the pythonfinder caches From 23dcc30005485c5d9ee0611d8dd738b8d1a76374 Mon Sep 17 00:00:00 2001 From: Oz Tiram Date: Sun, 13 Nov 2022 22:28:07 +0100 Subject: [PATCH 05/13] core: fix calls to err.print This broke after importing cosole at the top level. --- pipenv/core.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pipenv/core.py b/pipenv/core.py index 82356724..ec1a8c1a 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -2336,32 +2336,32 @@ def do_install( extra_pip_args=extra_pip_args, ) if c.returncode: - err( + err.print( "{} An error occurred while installing {}!".format( click.style("Error: ", fg="red", bold=True), click.style(pkg_line, fg="green"), ), ) - err(f"Error text: {c.stdout}") - err(click.style(format_pip_error(c.stderr), fg="cyan")) + err.print(f"Error text: {c.stdout}") + err.print(click.style(format_pip_error(c.stderr), fg="cyan")) if project.s.is_verbose(): - err(click.style(format_pip_output(c.stdout), fg="cyan")) + err.print(click.style(format_pip_output(c.stdout), fg="cyan")) if "setup.py egg_info" in c.stderr: - err( + err.print( "This is likely caused by a bug in {}. " "Report this to its maintainers.".format( click.style(pkg_requirement.name, fg="green") ) ) - err( + err.print( environments.PIPENV_SPINNER_FAIL_TEXT.format( "Installation Failed" ) ) sys.exit(1) except (ValueError, RuntimeError) as e: - err("{}: {}".format(click.style("WARNING", fg="red"), e)) - err( + err.print("{}: {}".format(click.style("WARNING", fg="red"), e)) + err.print( environments.PIPENV_SPINNER_FAIL_TEXT.format( "Installation Failed", ) @@ -2373,7 +2373,7 @@ def do_install( and not pkg_requirement.editable and not project.s.PIPENV_RESOLVE_VCS ): - err( + err.print( "{}: You installed a VCS dependency in non-editable mode. " "This will work fine, but sub-dependencies will not be resolved by {}." "\n To enable this sub-dependency functionality, specify that this dependency is editable." @@ -2418,13 +2418,13 @@ def do_install( except ValueError: import traceback - err( + err.print( "{} {}".format( click.style("Error:", fg="red", bold=True), traceback.format_exc(), ) ) - err( + err.print( environments.PIPENV_SPINNER_FAIL_TEXT.format( "Failed adding package to Pipfile" ) From f64bef7df9bfc88dfd63a3180df148901c81138c Mon Sep 17 00:00:00 2001 From: Oz Tiram Date: Sun, 13 Nov 2022 22:32:23 +0100 Subject: [PATCH 06/13] Remove create_spinner from create_virtualenv Use console.status instead. --- pipenv/core.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pipenv/core.py b/pipenv/core.py index ec1a8c1a..b435330f 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -53,7 +53,6 @@ from pipenv.utils.shell import ( subprocess_run, system_which, ) -from pipenv.utils.spinner import create_spinner from pipenv.vendor import click, plette, vistir from pipenv.vendor.requirementslib.models.requirements import Requirement @@ -1010,20 +1009,20 @@ def do_create_virtualenv(project, python=None, site_packages=None, pypi_mirror=N # Actually create the virtualenv. error = None - with create_spinner("Creating virtual environment...", project.s) as sp: + with console.status("Creating virtual environment...", project.s) as st: c = subprocess_run(cmd, env=pip_config) click.secho(f"{c.stdout}", fg="cyan", err=True) if c.returncode != 0: error = ( c.stderr if project.s.is_verbose() else exceptions.prettify_exc(c.stderr) ) - sp.fail( + err.print( environments.PIPENV_SPINNER_FAIL_TEXT.format( "Failed creating virtual environment" ) ) else: - sp.green.ok( + st.update( environments.PIPENV_SPINNER_OK_TEXT.format( "Successfully created virtual environment!" ) From da41e09dcbc2b39e919af845bd628959fd46d28f Mon Sep 17 00:00:00 2001 From: Oz Tiram Date: Sun, 13 Nov 2022 22:38:57 +0100 Subject: [PATCH 07/13] Resolver: remove yaspin spinner for rich.status --- pipenv/utils/resolver.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/pipenv/utils/resolver.py b/pipenv/utils/resolver.py index e45e541d..649c684f 100644 --- a/pipenv/utils/resolver.py +++ b/pipenv/utils/resolver.py @@ -11,7 +11,6 @@ from pathlib import Path from typing import Dict, List, Optional, Set, Tuple, Union from pipenv import environments -from pipenv._compat import decode_for_output from pipenv.exceptions import RequirementError, ResolutionFailure from pipenv.patched.pip._internal.cache import WheelCache from pipenv.patched.pip._internal.commands.install import InstallCommand @@ -27,7 +26,7 @@ from pipenv.patched.pip._internal.req.constructors import ( from pipenv.patched.pip._internal.req.req_file import parse_requirements from pipenv.patched.pip._internal.utils.hashes import FAVORITE_HASH from pipenv.patched.pip._internal.utils.temp_dir import global_tempdir_manager -from pipenv.patched.pip._vendor import pkg_resources +from pipenv.patched.pip._vendor import pkg_resources, rich from pipenv.project import Project from pipenv.vendor import click from pipenv.vendor.requirementslib import Requirement @@ -59,7 +58,9 @@ from .indexes import parse_indexes, prepare_pip_source_args from .internet import _get_requests_session, is_pypi_url from .locking import format_requirement_for_lockfile, prepare_lockfile from .shell import make_posix, subprocess_run, temp_environ -from .spinner import create_spinner + +console = rich.console.Console() +err = rich.console.Console(stderr=True) def get_package_finder( @@ -911,7 +912,7 @@ def actually_resolve_deps( return (results, hashes, resolver.markers_lookup, resolver, resolver.skipped) -def resolve(cmd, sp, project): +def resolve(cmd, st, project): from pipenv._compat import decode_output from pipenv.cmdparse import Script from pipenv.vendor.vistir.misc import echo @@ -925,13 +926,13 @@ def resolve(cmd, sp, project): continue err += line if is_verbose: - sp.hide_and_write(line.rstrip()) + st.update(line.rstrip()) c.wait() returncode = c.poll() out = c.stdout.read() if returncode != 0: - sp.red.fail(environments.PIPENV_SPINNER_FAIL_TEXT.format("Locking Failed!")) + st.update(environments.PIPENV_SPINNER_FAIL_TEXT.format("Locking Failed!")) echo(out.strip(), err=True) if not is_verbose: echo(err, err=True) @@ -1026,14 +1027,13 @@ def venv_resolve_deps( os.environ.pop("PIPENV_SITE_DIR", None) if keep_outdated: os.environ["PIPENV_KEEP_OUTDATED"] = "1" - with create_spinner( - text=decode_for_output("Locking..."), setting=project.s - ) as sp: + # TODO: add settings to console.status + with console.status("Locking...") as st: # This conversion is somewhat slow on local and file-type requirements since # we now download those requirements / make temporary folders to perform # dependency resolution on them, so we are including this step inside the # spinner context manager for the UX improvement - sp.write(decode_for_output("Building requirements...")) + st.update("Building requirements...") deps = convert_deps_to_pip(deps, project, include_index=True) constraints = set(deps) with tempfile.NamedTemporaryFile( @@ -1042,16 +1042,14 @@ def venv_resolve_deps( constraints_file.write(str("\n".join(constraints))) cmd.append("--constraints-file") cmd.append(constraints_file.name) - sp.write(decode_for_output("Resolving dependencies...")) - c = resolve(cmd, sp, project=project) + st.update("Resolving dependencies...") + c = resolve(cmd, st, project=project) if c.returncode == 0: - sp.green.ok(environments.PIPENV_SPINNER_OK_TEXT.format("Success!")) + st.update(environments.PIPENV_SPINNER_OK_TEXT.format("Success!")) if not project.s.is_verbose() and c.stderr.strip(): click.echo(click.style(f"Warning: {c.stderr.strip()}"), err=True) else: - sp.red.fail( - environments.PIPENV_SPINNER_FAIL_TEXT.format("Locking Failed!") - ) + st.update(environments.PIPENV_SPINNER_FAIL_TEXT.format("Locking Failed!")) click.echo(f"Output: {c.stdout.strip()}", err=True) click.echo(f"Error: {c.stderr.strip()}", err=True) try: From a1b141cfee99130fbecb1bd1da17a6c404608df5 Mon Sep 17 00:00:00 2001 From: Oz Tiram Date: Sun, 13 Nov 2022 22:42:05 +0100 Subject: [PATCH 08/13] Core: fix usage of console.status --- pipenv/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pipenv/core.py b/pipenv/core.py index b435330f..e9846fba 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -1009,7 +1009,8 @@ def do_create_virtualenv(project, python=None, site_packages=None, pypi_mirror=N # Actually create the virtualenv. error = None - with console.status("Creating virtual environment...", project.s) as st: + # TODO: update project setting here + with console.status("Creating virtual environment...") as st: c = subprocess_run(cmd, env=pip_config) click.secho(f"{c.stdout}", fg="cyan", err=True) if c.returncode != 0: From 349b6edf168f9f2f38e7440fb0c68fc1bcdef14f Mon Sep 17 00:00:00 2001 From: Oz Tiram Date: Sun, 13 Nov 2022 22:54:22 +0100 Subject: [PATCH 09/13] Fix failing tests --- pipenv/core.py | 4 ++-- tests/integration/test_cli.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pipenv/core.py b/pipenv/core.py index e9846fba..35a4833c 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -1010,7 +1010,7 @@ def do_create_virtualenv(project, python=None, site_packages=None, pypi_mirror=N # Actually create the virtualenv. error = None # TODO: update project setting here - with console.status("Creating virtual environment...") as st: + with console.status("Creating virtual environment..."): c = subprocess_run(cmd, env=pip_config) click.secho(f"{c.stdout}", fg="cyan", err=True) if c.returncode != 0: @@ -1023,7 +1023,7 @@ def do_create_virtualenv(project, python=None, site_packages=None, pypi_mirror=N ) ) else: - st.update( + console.print( environments.PIPENV_SPINNER_OK_TEXT.format( "Successfully created virtual environment!" ) diff --git a/tests/integration/test_cli.py b/tests/integration/test_cli.py index 61d3f459..f485152c 100644 --- a/tests/integration/test_cli.py +++ b/tests/integration/test_cli.py @@ -248,7 +248,7 @@ def test_pipenv_three(pipenv_instance_pypi): with pipenv_instance_pypi() as p: c = p.pipenv('--three') assert c.returncode == 0 - assert 'Successfully created virtual environment' in c.stderr + assert 'Successfully created virtual environment' in c.stdout @pytest.mark.outdated From 0bd03e62b6848b852bea164b814a83d6ab83a522 Mon Sep 17 00:00:00 2001 From: Oz Tiram Date: Sun, 13 Nov 2022 22:57:23 +0100 Subject: [PATCH 10/13] Fix failing tests --- tests/integration/test_run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_run.py b/tests/integration/test_run.py index 31f6de63..ae3eee34 100644 --- a/tests/integration/test_run.py +++ b/tests/integration/test_run.py @@ -135,7 +135,7 @@ hello = "echo $HELLO_VAR" else: c = p.pipenv('run hello') assert c.returncode == 0 - assert 'WORLD\n' == c.stdout + assert 'WORLD\n' in c.stdout @pytest.mark.run From 74b64f8bae56e8f7bfa34994a760d2cc936c1632 Mon Sep 17 00:00:00 2001 From: Oz Tiram Date: Mon, 14 Nov 2022 00:01:02 +0100 Subject: [PATCH 11/13] spinner: allow disabling or overriding --- pipenv/core.py | 15 +++++++-------- pipenv/environments.py | 19 +++++++++++++------ pipenv/utils/resolver.py | 3 +-- pipenv/utils/spinner.py | 20 -------------------- 4 files changed, 21 insertions(+), 36 deletions(-) delete mode 100644 pipenv/utils/spinner.py diff --git a/pipenv/core.py b/pipenv/core.py index 35a4833c..11f71ef6 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -249,8 +249,9 @@ def ensure_pipfile(project, validate=True, skip_requirements=False, system=False ) # Create a Pipfile... project.create_pipfile(python=python) - # TODO: pass project settings to status here - with console.status("Importing requirements...") as st: + with console.status( + "Importing requirements...", spinner=project.s.PIPENV_SPINNER + ) as st: # Import requirements.txt. try: import_requirements(project) @@ -1009,8 +1010,9 @@ def do_create_virtualenv(project, python=None, site_packages=None, pypi_mirror=N # Actually create the virtualenv. error = None - # TODO: update project setting here - with console.status("Creating virtual environment..."): + with console.status( + "Creating virtual environment...", spinner=project.s.PIPENV_SPINNER + ): c = subprocess_run(cmd, env=pip_config) click.secho(f"{c.stdout}", fg="cyan", err=True) if c.returncode != 0: @@ -2292,11 +2294,8 @@ def do_install( bold=True, ) # pip install: - # TODO: console.status() accepts: - # spinner='dots', spinner_style='status.spinner' - # we should use pipenv.project.settings to configure these. with vistir.contextmanagers.temp_environ(), console.status( - "Installing..." + "Installing...", spinner=project.s.PIPENV_SPINNER ) as st: if not system: os.environ["PIP_USER"] = "0" diff --git a/pipenv/environments.py b/pipenv/environments.py index f0c4667e..3d14a219 100644 --- a/pipenv/environments.py +++ b/pipenv/environments.py @@ -218,14 +218,21 @@ class Setting: if PIPENV_IS_CI: self.PIPENV_NOSPIN = True - pipenv_spinner = "dots" if not os.name == "nt" else "bouncingBar" - self.PIPENV_SPINNER = get_from_env( - "SPINNER", check_for_negation=False, default=pipenv_spinner - ) + if self.PIPENV_NOSPIN: + from pipenv.patched.pip._vendor.rich import _spinners + + _spinners.SPINNERS[None] = {"interval": 80, "frames": " "} + self.PIPENV_SPINNER = None + else: + pipenv_spinner = "dots" if not os.name == "nt" else "bouncingBar" + self.PIPENV_SPINNER = get_from_env( + "SPINNER", check_for_negation=False, default=pipenv_spinner + ) """Sets the default spinner type. - Spinners are identical to the ``node.js`` spinners and can be found at - https://github.com/sindresorhus/cli-spinners + You can see which spinners are available by running:: + + $ python -m pipenv.patched.pip._vendor.rich.spinner """ pipenv_pipfile = get_from_env("PIPFILE", check_for_negation=False) diff --git a/pipenv/utils/resolver.py b/pipenv/utils/resolver.py index 649c684f..ff38fb79 100644 --- a/pipenv/utils/resolver.py +++ b/pipenv/utils/resolver.py @@ -1027,8 +1027,7 @@ def venv_resolve_deps( os.environ.pop("PIPENV_SITE_DIR", None) if keep_outdated: os.environ["PIPENV_KEEP_OUTDATED"] = "1" - # TODO: add settings to console.status - with console.status("Locking...") as st: + with console.status("Locking...", spinner=project.s.PIPENV_SPINNER) as st: # This conversion is somewhat slow on local and file-type requirements since # we now download those requirements / make temporary folders to perform # dependency resolution on them, so we are including this step inside the diff --git a/pipenv/utils/spinner.py b/pipenv/utils/spinner.py deleted file mode 100644 index 47e4d884..00000000 --- a/pipenv/utils/spinner.py +++ /dev/null @@ -1,20 +0,0 @@ -import contextlib - - -@contextlib.contextmanager -def create_spinner(text, setting, nospin=None, spinner_name=None): - - from pipenv.vendor.vistir import spin - - if not spinner_name: - spinner_name = setting.PIPENV_SPINNER - if nospin is None: - nospin = setting.PIPENV_NOSPIN - - with spin.create_spinner( - spinner_name=spinner_name, - start_text=text, - nospin=nospin, - write_to_stdout=False, - ) as sp: - yield sp From eff7aa0933daa980da1a5d33d0ef0563e901ee47 Mon Sep 17 00:00:00 2001 From: Oz Tiram Date: Mon, 14 Nov 2022 00:47:35 +0100 Subject: [PATCH 12/13] vendor: bump vistir to version 0.7.4 --- pipenv/vendor/vendor.txt | 2 +- pipenv/vendor/vistir/__init__.py | 11 +- pipenv/vendor/vistir/_winconsole.py | 4 +- pipenv/vendor/vistir/compat.py | 320 ------------------------ pipenv/vendor/vistir/contextmanagers.py | 9 +- pipenv/vendor/vistir/environment.py | 6 - pipenv/vendor/vistir/misc.py | 118 +++------ pipenv/vendor/vistir/path.py | 63 ++--- pipenv/vendor/vistir/spin.py | 24 +- pipenv/vendor/vistir/termcolors.py | 4 +- 10 files changed, 84 insertions(+), 477 deletions(-) delete mode 100644 pipenv/vendor/vistir/compat.py delete mode 100644 pipenv/vendor/vistir/environment.py diff --git a/pipenv/vendor/vendor.txt b/pipenv/vendor/vendor.txt index 15311e5c..1c070d58 100644 --- a/pipenv/vendor/vendor.txt +++ b/pipenv/vendor/vendor.txt @@ -19,6 +19,6 @@ shellingham==1.5.0 termcolor==1.1.0 toml==0.10.2 tomlkit==0.9.2 -vistir==0.6.1 +vistir==0.7.4 wheel==0.37.1 yaspin==2.0.0 diff --git a/pipenv/vendor/vistir/__init__.py b/pipenv/vendor/vistir/__init__.py index e715298b..a584da36 100644 --- a/pipenv/vendor/vistir/__init__.py +++ b/pipenv/vendor/vistir/__init__.py @@ -1,13 +1,5 @@ # -*- coding=utf-8 -*- -from __future__ import absolute_import, unicode_literals -from .compat import ( - NamedTemporaryFile, - StringIO, - TemporaryDirectory, - partialmethod, - to_native_string, -) from .contextmanagers import ( atomic_open_for_write, cd, @@ -36,7 +28,7 @@ from .misc import ( from .path import create_tracked_tempdir, create_tracked_tempfile, mkdir_p, rmtree from .spin import create_spinner -__version__ = "0.6.1" +__version__ = "0.7.4" __all__ = [ @@ -58,7 +50,6 @@ __all__ = [ "create_spinner", "create_tracked_tempdir", "create_tracked_tempfile", - "to_native_string", "decode_for_output", "to_text", "to_bytes", diff --git a/pipenv/vendor/vistir/_winconsole.py b/pipenv/vendor/vistir/_winconsole.py index 6ebc89bc..922f6952 100644 --- a/pipenv/vendor/vistir/_winconsole.py +++ b/pipenv/vendor/vistir/_winconsole.py @@ -44,6 +44,7 @@ import io import os import sys import time +import typing import zlib from ctypes import ( POINTER, @@ -65,7 +66,6 @@ from itertools import count import msvcrt -from .compat import IS_TYPE_CHECKING from .misc import StreamWrapper, run, to_text try: @@ -77,7 +77,7 @@ except ImportError: pythonapi = None -if IS_TYPE_CHECKING: +if typing.TYPE_CHECKING: from typing import Text diff --git a/pipenv/vendor/vistir/compat.py b/pipenv/vendor/vistir/compat.py deleted file mode 100644 index 9292e1dd..00000000 --- a/pipenv/vendor/vistir/compat.py +++ /dev/null @@ -1,320 +0,0 @@ -# -*- coding=utf-8 -*- -from __future__ import absolute_import, print_function, unicode_literals - -import codecs -import errno -import os -import sys -import warnings -from tempfile import mkdtemp - -__all__ = [ - "Path", - "get_terminal_size", - "finalize", - "partialmethod", - "JSONDecodeError", - "FileNotFoundError", - "ResourceWarning", - "PermissionError", - "is_type_checking", - "IS_TYPE_CHECKING", - "IsADirectoryError", - "fs_str", - "lru_cache", - "TemporaryDirectory", - "NamedTemporaryFile", - "to_native_string", - "samefile", - "Mapping", - "Hashable", - "MutableMapping", - "Container", - "Iterator", - "KeysView", - "ItemsView", - "MappingView", - "Iterable", - "Set", - "Sequence", - "Sized", - "ValuesView", - "MutableSet", - "MutableSequence", - "Callable", - "fs_encode", - "fs_decode", - "_fs_encode_errors", - "_fs_decode_errors", -] - -from pathlib import Path - -from functools import lru_cache, partialmethod -from tempfile import NamedTemporaryFile -from shutil import get_terminal_size -from weakref import finalize -from collections.abc import ( - Mapping, - Hashable, - MutableMapping, - Container, - Iterator, - KeysView, - ItemsView, - MappingView, - Iterable, - Set, - Sequence, - Sized, - ValuesView, - MutableSet, - MutableSequence, - Callable, -) -from os.path import samefile - - -from json import JSONDecodeError - -from builtins import ( - ResourceWarning, - FileNotFoundError, - PermissionError, - IsADirectoryError, - FileExistsError, - TimeoutError, -) -from io import StringIO - -if not sys.warnoptions: - warnings.simplefilter("default", ResourceWarning) - - -def is_type_checking(): - try: - from typing import TYPE_CHECKING - except ImportError: - return False - return TYPE_CHECKING - - -IS_TYPE_CHECKING = os.environ.get("MYPY_RUNNING", is_type_checking()) - - -class TemporaryDirectory(object): - - """ - Create and return a temporary directory. This has the same - behavior as mkdtemp but can be used as a context manager. For - example: - - with TemporaryDirectory() as tmpdir: - ... - - Upon exiting the context, the directory and everything contained - in it are removed. - """ - - def __init__(self, suffix="", prefix=None, dir=None): - if "RAM_DISK" in os.environ: - import uuid - - name = uuid.uuid4().hex - dir_name = os.path.join(os.environ["RAM_DISK"].strip(), name) - os.mkdir(dir_name) - self.name = dir_name - else: - suffix = suffix if suffix else "" - if not prefix: - self.name = mkdtemp(suffix=suffix, dir=dir) - else: - self.name = mkdtemp(suffix, prefix, dir) - self._finalizer = finalize( - self, - self._cleanup, - self.name, - warn_message="Implicitly cleaning up {!r}".format(self), - ) - - @classmethod - def _rmtree(cls, name): - from .path import rmtree - - rmtree(name) - - @classmethod - def _cleanup(cls, name, warn_message): - cls._rmtree(name) - warnings.warn(warn_message, ResourceWarning) - - def __repr__(self): - return "<{} {!r}>".format(self.__class__.__name__, self.name) - - def __enter__(self): - return self - - def __exit__(self, exc, value, tb): - self.cleanup() - - def cleanup(self): - if self._finalizer.detach(): - self._rmtree(self.name) - - -def is_bytes(string): - """Check if a string is a bytes instance. - - :param Union[str, bytes] string: A string that may be string or bytes like - :return: Whether the provided string is a bytes type or not - :rtype: bool - """ - if isinstance(string, (bytes, memoryview, bytearray)): # noqa - return True - return False - - -def fs_str(string): - """Encodes a string into the proper filesystem encoding. - - Borrowed from pip-tools - """ - - if isinstance(string, str): - return string - assert not isinstance(string, bytes) - return string.encode(_fs_encoding) - - -def _get_path(path): - """Fetch the string value from a path-like object. - - Returns **None** if there is no string value. - """ - - if isinstance(path, (str, bytes)): - return path - path_type = type(path) - try: - path_repr = path_type.__fspath__(path) - except AttributeError: - return - if isinstance(path_repr, (str, bytes)): - return path_repr - return - - -# copied from the os backport which in turn copied this from -# the pyutf8 package -- -# URL: https://github.com/etrepum/pyutf8/blob/master/pyutf8/ref.py -# -def _invalid_utf8_indexes(bytes): - skips = [] - i = 0 - len_bytes = len(bytes) - while i < len_bytes: - c1 = bytes[i] - if c1 < 0x80: - # U+0000 - U+007F - 7 bits - i += 1 - continue - try: - c2 = bytes[i + 1] - if (c1 & 0xE0 == 0xC0) and (c2 & 0xC0 == 0x80): - # U+0080 - U+07FF - 11 bits - c = ((c1 & 0x1F) << 6) | (c2 & 0x3F) - if c < 0x80: # pragma: no cover - # Overlong encoding - skips.extend([i, i + 1]) # pragma: no cover - i += 2 - continue - c3 = bytes[i + 2] - if (c1 & 0xF0 == 0xE0) and (c2 & 0xC0 == 0x80) and (c3 & 0xC0 == 0x80): - # U+0800 - U+FFFF - 16 bits - c = ((((c1 & 0x0F) << 6) | (c2 & 0x3F)) << 6) | (c3 & 0x3F) - if (c < 0x800) or (0xD800 <= c <= 0xDFFF): - # Overlong encoding or surrogate. - skips.extend([i, i + 1, i + 2]) - i += 3 - continue - c4 = bytes[i + 3] - if ( - (c1 & 0xF8 == 0xF0) - and (c2 & 0xC0 == 0x80) - and (c3 & 0xC0 == 0x80) - and (c4 & 0xC0 == 0x80) - ): - # U+10000 - U+10FFFF - 21 bits - c = ((((((c1 & 0x0F) << 6) | (c2 & 0x3F)) << 6) | (c3 & 0x3F)) << 6) | ( - c4 & 0x3F - ) - if (c < 0x10000) or (c > 0x10FFFF): # pragma: no cover - # Overlong encoding or invalid code point. - skips.extend([i, i + 1, i + 2, i + 3]) - i += 4 - continue - except IndexError: - pass - skips.append(i) - i += 1 - return skips - - -def fs_encode(path): - """Encode a filesystem path to the proper filesystem encoding. - - :param Union[str, bytes] path: A string-like path - :returns: A bytes-encoded filesystem path representation - """ - - path = _get_path(path) - if path is None: - raise TypeError("expected a valid path to encode") - if isinstance(path, str): - return path.encode(_fs_encoding, _fs_encode_errors) - return path - - -def fs_decode(path): - """Decode a filesystem path using the proper filesystem encoding. - - :param path: The filesystem path to decode from bytes or string - :return: The filesystem path, decoded with the determined encoding - :rtype: Text - """ - - path = _get_path(path) - if path is None: - raise TypeError("expected a valid path to decode") - if isinstance(path, bytes): - import array - - indexes = _invalid_utf8_indexes(array.array(str("B"), path)) - if indexes and os.name == "nt": - return path.decode(_fs_encoding, "surrogateescape") - return path.decode(_fs_encoding, _fs_decode_errors) - return path - - -_fs_encoding = "utf-8" -_fs_decode_errors = "surrogateescape" -if sys.platform.startswith("win"): - _fs_error_fn = None - _fs_encode_errors = "surrogatepass" -else: - if sys.version_info >= (3, 3): - _fs_encoding = sys.getfilesystemencoding() - if not _fs_encoding: - _fs_encoding = sys.getdefaultencoding() - alt_strategy = "surrogateescape" - _fs_error_fn = getattr(sys, "getfilesystemencodeerrors", None) - _fs_encode_errors = _fs_error_fn() if _fs_error_fn else alt_strategy - _fs_decode_errors = _fs_error_fn() if _fs_error_fn else _fs_decode_errors - -_byte = chr if sys.version_info < (3,) else lambda i: bytes([i]) - - -def to_native_string(string): - from .misc import to_text, to_bytes - - return to_text(string) diff --git a/pipenv/vendor/vistir/contextmanagers.py b/pipenv/vendor/vistir/contextmanagers.py index de0d6b5d..451a2331 100644 --- a/pipenv/vendor/vistir/contextmanagers.py +++ b/pipenv/vendor/vistir/contextmanagers.py @@ -1,18 +1,19 @@ # -*- coding=utf-8 -*- -from __future__ import absolute_import, print_function, unicode_literals - import io import os + import stat import sys +import typing from contextlib import closing, contextmanager +from pathlib import Path +from tempfile import NamedTemporaryFile from urllib import request -from .compat import IS_TYPE_CHECKING, NamedTemporaryFile, Path from .path import is_file_url, is_valid_url, path_to_url, url_to_path -if IS_TYPE_CHECKING: +if typing.TYPE_CHECKING: from typing import ( Any, Bytes, diff --git a/pipenv/vendor/vistir/environment.py b/pipenv/vendor/vistir/environment.py deleted file mode 100644 index b8490c00..00000000 --- a/pipenv/vendor/vistir/environment.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, print_function - -from .compat import IS_TYPE_CHECKING - -MYPY_RUNNING = IS_TYPE_CHECKING diff --git a/pipenv/vendor/vistir/misc.py b/pipenv/vendor/vistir/misc.py index 57ba9227..24f82676 100644 --- a/pipenv/vendor/vistir/misc.py +++ b/pipenv/vendor/vistir/misc.py @@ -11,29 +11,21 @@ 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 weakref import WeakKeyDictionary from queue import Empty, Queue +from typing import Iterable from .cmdparse import Script -from .compat import ( - Iterable, - Path, - StringIO, - TimeoutError, - _fs_decode_errors, - _fs_encode_errors, - fs_str, - is_bytes, - partialmethod, - to_native_string, -) from .contextmanagers import spinner as spinner -from .environment import MYPY_RUNNING -from .termcolors import ANSI_REMOVAL_RE, colorize + +_fs_encode_errors = "surrogatepass" if os.name != "nt": @@ -62,7 +54,7 @@ __all__ = [ ] -if MYPY_RUNNING: +if typing.TYPE_CHECKING: from typing import Any, Dict, Generator, IO, List, Optional, Text, Tuple, Union from .spin import VistirSpinner @@ -145,6 +137,11 @@ 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)) @@ -381,7 +378,7 @@ class SubprocessStreamWrapper(object): if self.display_line: if new_line != self.display_line: self.display_line_loops_displayed = 0 - new_line = fs_str("{}".format(new_line)) + new_line = "{}".format(new_line) if len(new_line) > self.display_line_max_len: new_line = "{}...".format(new_line[: self.display_line_max_len]) self.display_line = new_line @@ -447,9 +444,7 @@ class SubprocessStreamWrapper(object): line, "stderr", spinner=spinner, stdout_allowed=stdout_allowed ) if spinner: - spinner.text = to_native_string( - "{} {}".format(spinner.text, self.display_line) - ) + spinner.text = "{} {}".format(spinner.text, self.display_line) self.out = self.out.strip() self.err = self.err.strip() @@ -486,11 +481,11 @@ def _handle_nonblocking_subprocess(c, spinner=None): c.wait() if spinner: if c.returncode != 0: - spinner.fail(to_native_string("Failed...cleaning up...")) + spinner.fail("Failed...cleaning up...") elif c.returncode == 0 and not os.name == "nt": - spinner.ok(to_native_string("✔ Complete")) + spinner.ok("✔ Complete") else: - spinner.ok(to_native_string("Complete")) + spinner.ok("Complete") return c @@ -524,7 +519,7 @@ def _create_subprocess( formatted_tb = "".join(traceback.format_exception(*sys.exc_info())) sys.stderr.write( - "Error while executing command %s:" % to_native_string(" ".join(cmd._parts)) + "Error while executing command %s:" % " ".join(cmd._parts) ) sys.stderr.write(formatted_tb) raise exc @@ -600,7 +595,7 @@ def run( if env: _env.update(env) - _env = {k: fs_str(v) for k, v in _env.items()} + _env = {k: v for k, v in _env.items()} if not spinner_name: spinner_name = "bouncingBar" @@ -647,7 +642,10 @@ def load_path(python): '/home/user/.virtualenvs/requirementslib-5MhGuG3C/lib/python3.7/site-packages', '/home/user/git/requirementslib/src'] """ - + warnings.warn( + 'This function is deprecated and will be removed in version 0.8.', + DeprecationWarning, stacklevel=2) + from pathlib import Path python = Path(python).as_posix() out, err = run( [python, "-c", "import json, sys; print(json.dumps(sys.path))"], nospin=True @@ -684,7 +682,7 @@ def partialclass(cls, *args, **kwargs): >>> new_source.__dict__ {'url': 'https://pypi.org/simple', 'verify_ssl': True, 'name': 'pypi'} """ - + from functools import partialmethod name_attrs = [ n for n in (getattr(cls, name, str(cls)) for name in ("__name__", "__qualname__")) @@ -787,7 +785,10 @@ def divide(n, iterable): :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) @@ -809,6 +810,11 @@ def take(n, iterable): 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)) @@ -820,6 +826,10 @@ def chunked(n, iterable): 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)), []) @@ -893,7 +903,6 @@ def decode_for_output(output, target_stream=None, translation_map=None): try: output = _encode(output, encoding=encoding, translation_map=translation_map) except (UnicodeDecodeError, UnicodeEncodeError): - output = to_native_string(output) output = _encode( output, encoding=encoding, errors="replace", translation_map=translation_map ) @@ -1205,56 +1214,3 @@ def _can_use_color(stream=None, color=None): stream = sys.stdin return _isatty(stream) return bool(color) - - -def echo(text, fg=None, bg=None, style=None, file=None, err=False, color=None): - """Write the given text to the provided stream or **sys.stdout** by - default. - - Provides optional foreground and background colors from the ansi defaults: - **grey**, **red**, **green**, **yellow**, **blue**, **magenta**, **cyan** - or **white**. - - Available styles include **bold**, **dark**, **underline**, **blink**, **reverse**, - **concealed** - - :param str text: Text to write - :param str fg: Foreground color to use (default: None) - :param str bg: Foreground color to use (default: None) - :param str style: Style to use (default: None) - :param stream file: File to write to (default: None) - :param bool color: Whether to force color (i.e. ANSI codes are in the text) - """ - - if file and not hasattr(file, "write"): - raise TypeError("Expected a writable stream, received {!r}".format(file)) - if not file: - if err: - file = _text_stderr() - else: - file = _text_stdout() - if text and not isinstance(text, (str, bytes, bytearray)): - text = str(text) - text = "" if not text else text - if isinstance(text, str): - text += "\n" - else: - text += b"\n" - if text and is_bytes(text): - buffer = _get_binary_buffer(file) - if buffer is not None: - file.flush() - buffer.write(text) - buffer.flush() - return - if text and not is_bytes(text): - can_use_color = _can_use_color(file, color=color) - if any([fg, bg, style]): - text = colorize(text, fg=fg, bg=bg, attrs=style) - if not can_use_color or (os.name == "nt" and not _wrap_for_color): - text = ANSI_REMOVAL_RE.sub("", text) - elif os.name == "nt" and _wrap_for_color and not _is_wrapped_for_color(file): - file = _wrap_for_color(file, color=color) - if text: - file.write(text) - file.flush() diff --git a/pipenv/vendor/vistir/path.py b/pipenv/vendor/vistir/path.py index 8e44346d..2464cb23 100644 --- a/pipenv/vendor/vistir/path.py +++ b/pipenv/vendor/vistir/path.py @@ -10,43 +10,28 @@ import posixpath import shutil import stat import sys +import typing import time import unicodedata import warnings +from pathlib import Path +from tempfile import NamedTemporaryFile, TemporaryDirectory +from typing import Optional, Callable from urllib import parse as urllib_parse from urllib import request as urllib_request -from .compat import ( - IS_TYPE_CHECKING, - FileNotFoundError, - Path, - PermissionError, - ResourceWarning, - TemporaryDirectory, - _fs_encoding, - NamedTemporaryFile, - finalize, - fs_decode, - fs_encode, -) +from urllib.parse import quote -# fmt: off -from urllib.parse import quote_from_bytes as quote -# fmt: on - - -if IS_TYPE_CHECKING: +if typing.TYPE_CHECKING: from types import TracebackType from typing import ( Any, AnyStr, ByteString, - Callable, Generator, Iterator, List, - Optional, Text, Tuple, Type, @@ -56,6 +41,7 @@ if IS_TYPE_CHECKING: TPath = os.PathLike TFunc = Callable[..., Any] + __all__ = [ "check_for_unc_path", "get_converted_relative_path", @@ -188,13 +174,13 @@ def path_to_url(path): # XXX: This enables us to handle half-surrogates that were never # XXX: actually part of a surrogate pair, but were just incidentally # XXX: passed in as a piece of a filename - quoted_path = quote(fs_encode(path)) - return fs_decode("file:///{}:{}".format(drive, quoted_path)) + quoted_path = quote(path, errors="backslashreplace") + return "file:///{}:{}".format(drive, quoted_path) # XXX: This is also here to help deal with incidental dangling surrogates # XXX: on linux, by making sure they are preserved during encoding so that # XXX: we can urlencode the backslash correctly - bytes_path = to_bytes(normalized_path, errors="backslashreplace") - return fs_decode("file://{}".format(quote(bytes_path))) + # bytes_path = to_bytes(normalized_path, errors="backslashreplace") + return "file://{}".format(quote(path, errors="backslashreplace")) def url_to_path(url): @@ -248,8 +234,6 @@ def is_readonly_path(fn): Permissions check is `bool(path.stat & stat.S_IREAD)` or `not os.access(path, os.W_OK)` """ - - fn = fs_decode(fs_encode(fn)) if os.path.exists(fn): file_stat = os.stat(fn).st_mode return not bool(file_stat & stat.S_IWRITE) or not os.access(fn, os.W_OK) @@ -257,6 +241,10 @@ def is_readonly_path(fn): def mkdir_p(newdir, mode=0o777): + warnings.warn( + ('This function is deprecated and will be removed in version 0.8.' + 'Use os.makedirs instead'), DeprecationWarning, stacklevel=2) + # This exists in shutil already # type: (TPath, int) -> None """Recursively creates the target directory and all of its parents if they do not already exist. Fails silently if they do. @@ -264,12 +252,11 @@ def mkdir_p(newdir, mode=0o777): :param str newdir: The directory path to ensure :raises: OSError if a file is encountered along the way """ - newdir = fs_decode(fs_encode(newdir)) if os.path.exists(newdir): if not os.path.isdir(newdir): raise OSError( "a file with the same name as the desired dir, '{}', already exists.".format( - fs_decode(newdir) + newdir ) ) return None @@ -280,7 +267,10 @@ def ensure_mkdir_p(mode=0o777): # type: (int) -> Callable[Callable[..., Any], Callable[..., Any]] """Decorator to ensure `mkdir_p` is called to the function's return value.""" + warnings.warn('This function is deprecated and will be removed in version 0.8.', + DeprecationWarning, stacklevel=2) + # This exists in shutil already def decorator(f): # type: (Callable[..., Any]) -> Callable[..., Any] @functools.wraps(f) @@ -289,9 +279,7 @@ def ensure_mkdir_p(mode=0o777): path = f(*args, **kwargs) mkdir_p(path, mode=mode) return path - return decorated - return decorator @@ -346,16 +334,13 @@ def _find_icacls_exe(): return None -def set_write_bit(fn): - # type: (str) -> None +def set_write_bit(fn: str) -> None: """Set read-write permissions for the current user on the target path. Fail silently if the path doesn't exist. :param str fn: The target filename or path :return: None """ - - fn = fs_decode(fs_encode(fn)) if not os.path.exists(fn): return file_stat = os.stat(fn).st_mode @@ -411,8 +396,9 @@ def set_write_bit(fn): set_write_bit(file_) -def rmtree(directory, ignore_errors=False, onerror=None): - # type: (str, bool, Optional[Callable]) -> None +def rmtree(directory: str, + ignore_errors: bool = False, + onerror: Optional[Callable] = None) -> None : """Stand-in for :func:`~shutil.rmtree` with additional error-handling. This version of `rmtree` handles read-only paths, especially in the case of index @@ -427,7 +413,6 @@ def rmtree(directory, ignore_errors=False, onerror=None): Setting `ignore_errors=True` may cause this to silently fail to delete the path """ - directory = fs_decode(fs_encode(directory)) if onerror is None: onerror = handle_remove_readonly try: @@ -485,8 +470,6 @@ def handle_remove_readonly(func, path, exc): This function will call check :func:`is_readonly_path` before attempting to call :func:`set_write_bit` on the target path and try again. """ - # Check for read-only attribute - from .compat import ResourceWarning, FileNotFoundError, PermissionError PERM_ERRORS = (errno.EACCES, errno.EPERM, errno.ENOENT) default_warning_message = "Unable to remove file due to permissions restriction: {!r}" diff --git a/pipenv/vendor/vistir/spin.py b/pipenv/vendor/vistir/spin.py index d1e78418..adf48ce9 100644 --- a/pipenv/vendor/vistir/spin.py +++ b/pipenv/vendor/vistir/spin.py @@ -1,22 +1,22 @@ # -*- coding=utf-8 -*- -from __future__ import absolute_import, print_function - import functools import os import signal import sys import threading import time +import typing +import warnings + from io import StringIO import pipenv.vendor.colorama as colorama -from .compat import IS_TYPE_CHECKING, to_native_string from .cursor import hide_cursor, show_cursor from .misc import decode_for_output, to_text from .termcolors import COLOR_MAP, COLORS, DISABLE_COLORS, colored -if IS_TYPE_CHECKING: +if typing.TYPE_CHECKING: from typing import ( Any, Callable, @@ -38,17 +38,17 @@ if IS_TYPE_CHECKING: try: import pipenv.vendor.yaspin as yaspin -except ImportError: # pragma: no cover - yaspin = None - Spinners = None - SpinBase = None -else: # pragma: no cover import yaspin.spinners import yaspin.core Spinners = yaspin.spinners.Spinners SpinBase = yaspin.core.Yaspin +except ImportError: # pragma: no cover + yaspin = None + Spinners = None + SpinBase = None + if os.name == "nt": # pragma: no cover def handler(signum, frame, spinner): @@ -88,7 +88,7 @@ class DummySpinner(object): # type: (str, Any) -> None if DISABLE_COLORS: colorama.init() - self.text = to_native_string(decode_output(text)) if text else "" + self.text = decode_output(text) if text else "" self.stdout = kwargs.get("stdout", sys.stdout) self.stderr = kwargs.get("stderr", sys.stderr) self.out_buff = StringIO() @@ -484,6 +484,10 @@ class VistirSpinner(SpinBase): def create_spinner(*args, **kwargs): + warnings.warn( + ('This function is deprecated and will be removed in version 0.8.' + 'Consider using yaspin directly instead, or user rich.status'), + DeprecationWarning, stacklevel=2) # type: (Any, Any) -> Union[DummySpinner, VistirSpinner] nospin = kwargs.pop("nospin", False) use_yaspin = kwargs.pop("use_yaspin", not nospin) diff --git a/pipenv/vendor/vistir/termcolors.py b/pipenv/vendor/vistir/termcolors.py index 4807e551..d5b34cf4 100644 --- a/pipenv/vendor/vistir/termcolors.py +++ b/pipenv/vendor/vistir/termcolors.py @@ -1,12 +1,10 @@ # -*- coding=utf-8 -*- -from __future__ import absolute_import, print_function, unicode_literals - import os import re import pipenv.vendor.colorama as colorama -from .compat import to_native_string +from .misc import to_text as to_native_string DISABLE_COLORS = os.getenv("CI", False) or os.getenv( "ANSI_COLORS_DISABLED", os.getenv("VISTIR_DISABLE_COLORS", False) From baf1124ba20c34dd075b2b3b70b0be544822af02 Mon Sep 17 00:00:00 2001 From: Oz Tiram Date: Fri, 18 Nov 2022 10:24:51 +0100 Subject: [PATCH 13/13] Add news snippet --- news/5468.vendor.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 news/5468.vendor.rst diff --git a/news/5468.vendor.rst b/news/5468.vendor.rst new file mode 100644 index 00000000..a55aba1c --- /dev/null +++ b/news/5468.vendor.rst @@ -0,0 +1,2 @@ + * Replace yaspin spinner with rich spinner. + * Bump vistir version to 0.7.4