diff --git a/pipenv/_compat.py b/pipenv/_compat.py deleted file mode 100644 index 610d886947..0000000000 --- a/pipenv/_compat.py +++ /dev/null @@ -1,132 +0,0 @@ -"""A compatibility module for pipenv's backports and manipulations. - -Exposes a standard API that enables compatibility across python versions, -operating systems, etc. -""" -import sys -import warnings - -from pipenv.vendor import vistir - -warnings.filterwarnings("ignore", category=ResourceWarning) - - -__all__ = [ - "getpreferredencoding", - "DEFAULT_ENCODING", - "canonical_encoding_name", - "force_encoding", - "UNICODE_TO_ASCII_TRANSLATION_MAP", - "decode_output", - "fix_utf8", -] - - -def getpreferredencoding(): - import locale - - # Borrowed from Invoke - # (see https://github.com/pyinvoke/invoke/blob/93af29d/invoke/runners.py#L881) - return locale.getpreferredencoding(False) - - -DEFAULT_ENCODING = getpreferredencoding() - - -def canonical_encoding_name(name): - import codecs - - try: - codec = codecs.lookup(name) - except LookupError: - return name - else: - return codec.name - - -# From https://github.com/CarlFK/veyepar/blob/5c5de47/dj/scripts/fixunicode.py -# MIT LIcensed, thanks Carl! -def force_encoding(): - try: - stdout_isatty = sys.stdout.isatty - stderr_isatty = sys.stderr.isatty - except AttributeError: - return DEFAULT_ENCODING, DEFAULT_ENCODING - else: - if not (stdout_isatty() and stderr_isatty()): - return DEFAULT_ENCODING, DEFAULT_ENCODING - stdout_encoding = canonical_encoding_name(sys.stdout.encoding) - stderr_encoding = canonical_encoding_name(sys.stderr.encoding) - if sys.platform == "win32": - return DEFAULT_ENCODING, DEFAULT_ENCODING - if stdout_encoding != "utf-8" or stderr_encoding != "utf-8": - - try: - from ctypes import c_char_p, py_object, pythonapi - except ImportError: - return DEFAULT_ENCODING, DEFAULT_ENCODING - try: - PyFile_SetEncoding = pythonapi.PyFile_SetEncoding - except AttributeError: - return DEFAULT_ENCODING, DEFAULT_ENCODING - else: - PyFile_SetEncoding.argtypes = (py_object, c_char_p) - if stdout_encoding != "utf-8": - try: - was_set = PyFile_SetEncoding(sys.stdout, "utf-8") - except OSError: - was_set = False - if not was_set: - stdout_encoding = DEFAULT_ENCODING - else: - stdout_encoding = "utf-8" - - if stderr_encoding != "utf-8": - try: - was_set = PyFile_SetEncoding(sys.stderr, "utf-8") - except OSError: - was_set = False - if not was_set: - stderr_encoding = DEFAULT_ENCODING - else: - stderr_encoding = "utf-8" - - return stdout_encoding, stderr_encoding - - -OUT_ENCODING, ERR_ENCODING = force_encoding() - - -UNICODE_TO_ASCII_TRANSLATION_MAP = { - 8230: "...", - 8211: "-", - 10004: "OK", - 10008: "x", -} - - -def decode_for_output(output, target=sys.stdout): - return vistir.misc.decode_for_output( - output, sys.stdout, translation_map=UNICODE_TO_ASCII_TRANSLATION_MAP - ) - - -def decode_output(output): - if not isinstance(output, str): - return output - try: - output = output.encode(DEFAULT_ENCODING) - except (AttributeError, UnicodeDecodeError, UnicodeEncodeError): - output = output.translate(UNICODE_TO_ASCII_TRANSLATION_MAP) - output = output.encode(DEFAULT_ENCODING, "replace") - return vistir.misc.to_text(output, encoding=DEFAULT_ENCODING, errors="replace") - - -def fix_utf8(text): - if not isinstance(text, str): - return text - try: - text = decode_output(text) - except UnicodeDecodeError: - pass - return text diff --git a/pipenv/cli/command.py b/pipenv/cli/command.py index c7fdfc9a87..5eef271c7b 100644 --- a/pipenv/cli/command.py +++ b/pipenv/cli/command.py @@ -3,7 +3,6 @@ from pipenv import environments from pipenv.__version__ import __version__ -from pipenv._compat import fix_utf8 from pipenv.cli.options import ( CONTEXT_SETTINGS, PipenvGroup, @@ -725,7 +724,7 @@ def scripts(state): "{0:<{width}} {1}".format(name, script, width=first_column_width) for name, script in scripts.items() ) - echo("\n".join(fix_utf8(line) for line in lines)) + echo("\n".join(line for line in lines)) @cli.command( diff --git a/pipenv/core.py b/pipenv/core.py index 36f4c6d5d9..d3b2882b6d 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -16,7 +16,6 @@ from typing import Dict, List, Optional, Union from pipenv import environments, exceptions, pep508checker -from pipenv._compat import fix_utf8 from pipenv.patched.pip._internal.build_env import get_runnable_pip from pipenv.patched.pip._internal.exceptions import PipError from pipenv.patched.pip._internal.network.session import PipSession @@ -102,7 +101,7 @@ def do_clear(project): from pipenv.patched.pip._internal import locations - click.secho(fix_utf8("Clearing caches..."), bold=True) + click.secho("Clearing caches...", bold=True) try: shutil.rmtree( project.s.PIPENV_CACHE_DIR, onerror=vistir.path.handle_remove_readonly @@ -274,12 +273,7 @@ def ensure_pipfile(project, validate=True, skip_requirements=False, system=False ) ) else: - click.echo( - click.style( - fix_utf8("Creating a Pipfile for this project..."), bold=True - ), - err=True, - ) + click.secho("Creating a Pipfile for this project...", bold=True, err=True) # Create the pipfile if it doesn't exist. project.create_pipfile(python=python) # Validate the Pipfile's contents. @@ -354,7 +348,7 @@ def abort(msg=""): "{}: Python {} {}".format( click.style("Warning", fg="red", bold=True), click.style(python, fg="cyan"), - fix_utf8("was not found on your system..."), + "was not found on your system...", ), err=True, ) @@ -414,7 +408,7 @@ def abort(msg=""): err.print( environments.PIPENV_SPINNER_FAIL_TEXT.format("Failed...") ) - click.echo(fix_utf8("Something went wrong..."), err=True) + click.echo("Something went wrong...", err=True) click.secho(e.err, fg="cyan", err=True) else: console.print( @@ -497,9 +491,7 @@ def abort(): or click.confirm("Use existing virtualenv?", default=True) ): abort() - click.echo( - click.style(fix_utf8("Using existing virtualenv..."), bold=True), err=True - ) + click.echo(click.style("Using existing virtualenv...", bold=True), err=True) # Remove the virtualenv. cleanup_virtualenv(project, bare=True) # Call this function again. @@ -859,11 +851,7 @@ def do_install_dependencies( # Load the lockfile if it exists, or if dev_only is being used. if skip_lock or not project.lockfile_exists: if not bare: - click.echo( - click.style( - fix_utf8("Installing dependencies from Pipfile..."), bold=True - ) - ) + click.secho("Installing dependencies from Pipfile...", bold=True) # skip_lock should completely bypass the lockfile (broken in 4dac1676) lockfile = project.get_or_create_lockfile( categories=categories, from_pipfile=True @@ -871,15 +859,11 @@ def do_install_dependencies( else: lockfile = project.get_or_create_lockfile(categories=categories) if not bare: - click.echo( - click.style( - fix_utf8( - "Installing dependencies from Pipfile.lock ({})...".format( - lockfile["_meta"].get("hash", {}).get("sha256")[-6:] - ) - ), - bold=True, - ) + click.secho( + "Installing dependencies from Pipfile.lock ({})...".format( + lockfile["_meta"].get("hash", {}).get("sha256")[-6:] + ), + bold=True, ) dev = dev or dev_only deps_list = list( @@ -913,11 +897,7 @@ def do_install_dependencies( # Iterate over the hopefully-poorly-packaged dependencies... if not failed_deps_queue.empty(): - click.echo( - click.style( - fix_utf8("Installing initially failed dependencies..."), bold=True - ) - ) + click.secho("Installing initially failed dependencies...", bold=True) retry_list = [] while not failed_deps_queue.empty(): failed_dep = failed_deps_queue.get() @@ -973,10 +953,7 @@ def _create_virtualenv_cmd(project, python, site_packages=False): def do_create_virtualenv(project, python=None, site_packages=None, pypi_mirror=None): """Creates a virtualenv.""" - click.echo( - click.style(fix_utf8("Creating a virtualenv for this project..."), bold=True), - err=True, - ) + click.secho("Creating a virtualenv for this project...", bold=True, err=True) click.echo( "Pipfile: " + click.style(project.pipfile_location, fg="yellow", bold=True), @@ -992,17 +969,14 @@ def do_create_virtualenv(project, python=None, site_packages=None, pypi_mirror=N "{0} {1} {3} {2}".format( click.style(using_string, bold=True), click.style(python, fg="yellow", bold=True), - click.style(fix_utf8("to create virtualenv..."), bold=True), + click.style("to create virtualenv...", bold=True), click.style(f"({python_version(python)})", fg="green"), ), err=True, ) if site_packages: - click.echo( - click.style(fix_utf8("Making site-packages available..."), bold=True), - err=True, - ) + click.secho("Making site-packages available...", bold=True, err=True) if pypi_mirror: pip_config = {"PIP_INDEX_URL": pypi_mirror} @@ -1160,7 +1134,7 @@ def do_lock( "{} {} {}".format( click.style("Locking"), click.style("[{}]".format(pipfile_category), fg="yellow"), - click.style(fix_utf8("dependencies...")), + click.style("dependencies..."), ), err=True, ) @@ -1238,9 +1212,7 @@ def do_purge(project, bare=False, downloads=False, allow_global=False): if downloads: if not bare: - click.echo( - click.style(fix_utf8("Clearing out downloads directory..."), bold=True) - ) + click.secho("Clearing out downloads directory...", bold=True) shutil.rmtree(project.download_location) return @@ -1261,7 +1233,7 @@ def do_purge(project, bare=False, downloads=False, allow_global=False): return installed if not bare: - click.echo(fix_utf8(f"Found {len(to_remove)} installed package(s), purging...")) + click.echo(f"Found {len(to_remove)} installed package(s), purging...") command = [ project_python(project, system=allow_global), @@ -1336,20 +1308,18 @@ def do_init( raise exceptions.DeployException elif (system or allow_global) and not (project.s.PIPENV_VIRTUALENV): click.secho( - fix_utf8( - "Pipfile.lock ({}) out of date, but installation " - "uses {} re-building lockfile must happen in " - "isolation. Please rebuild lockfile in a virtualenv. " - "Continuing anyway...".format(old_hash[-6:], "--system") - ), + "Pipfile.lock ({}) out of date, but installation " + "uses {} re-building lockfile must happen in " + "isolation. Please rebuild lockfile in a virtualenv. " + "Continuing anyway...".format(old_hash[-6:], "--system"), fg="yellow", err=True, ) else: if old_hash: - msg = fix_utf8("Pipfile.lock ({0}) out of date, updating to ({1})...") + msg = "Pipfile.lock ({0}) out of date, updating to ({1})..." else: - msg = fix_utf8("Pipfile.lock is corrupted, replaced with ({1})...") + msg = "Pipfile.lock is corrupt, replaced with ({1})..." click.secho( msg.format(old_hash[-6:], new_hash[-6:]), fg="yellow", @@ -1377,8 +1347,9 @@ def do_init( "See also: --deploy flag.", ) else: - click.echo( - click.style(fix_utf8("Pipfile.lock not found, creating..."), bold=True), + click.secho( + "Pipfile.lock not found, creating...", + bold=True, err=True, ) do_lock( @@ -1974,16 +1945,12 @@ def ensure_lockfile(project, keep_outdated=False, pypi_mirror=None): old_hash = project.get_lockfile_hash() new_hash = project.calculate_pipfile_hash() if new_hash != old_hash: - click.echo( - click.style( - fix_utf8( - "Pipfile.lock ({}) out of date, updating to ({})...".format( - old_hash[-6:], new_hash[-6:] - ) - ), - fg="yellow", - bold=True, + click.secho( + "Pipfile.lock ({}) out of date, updating to ({})...".format( + old_hash[-6:], new_hash[-6:] ), + fg="yellow", + bold=True, err=True, ) do_lock(project, keep_outdated=keep_outdated, pypi_mirror=pypi_mirror) @@ -2170,7 +2137,7 @@ def do_install( # Check if the file is remote or not if remote: click.secho( - fix_utf8("Remote requirements file provided! Downloading..."), + "Remote requirements file provided! Downloading...", bold=True, err=True, ) @@ -2199,7 +2166,7 @@ def do_install( if requirementstxt: error, traceback = None, None click.secho( - fix_utf8("Requirements file provided! Importing into Pipfile..."), + "Requirements file provided! Importing into Pipfile...", bold=True, err=True, ) @@ -2295,7 +2262,7 @@ def do_install( for pkg_line in pkg_list: click.secho( - fix_utf8(f"Installing {pkg_line}..."), + f"Installing {pkg_line}...", fg="green", bold=True, ) @@ -2497,13 +2464,9 @@ def do_uninstall( ) ) return - click.echo( + click.secho( click.style( - fix_utf8( - "Un-installing {}...".format( - click.style("[dev-packages]", fg="yellow") - ) - ), + "Un-installing {}...".format(click.style("[dev-packages]", fg="yellow")), bold=True, ) ) @@ -2530,11 +2493,9 @@ def do_uninstall( if all: click.echo( click.style( - fix_utf8( - "Un-installing all {} and {}...".format( - click.style("[dev-packages]", fg="yellow"), - click.style("[packages]", fg="yellow"), - ) + "Un-installing all {} and {}...".format( + click.style("[dev-packages]", fg="yellow"), + click.style("[packages]", fg="yellow"), ), bold=True, ) @@ -2558,7 +2519,7 @@ def do_uninstall( click.style("Removing", fg="cyan"), click.style(package_name, fg="green"), click.style("from", fg="cyan"), - click.style(fix_utf8("Pipfile.lock..."), fg="white"), + click.style("Pipfile.lock...", fg="white"), ) ) if normalized_name in lockfile[category]: @@ -2570,9 +2531,7 @@ def do_uninstall( package_name, category=pipfile_category ): click.secho( - fix_utf8( - f"Removed {package_name} from Pipfile category {pipfile_category}" - ), + f"Removed {package_name} from Pipfile category {pipfile_category}", fg="green", ) @@ -2585,7 +2544,7 @@ def do_uninstall( # Uninstall the package. if package_name in packages_to_remove: click.secho( - fix_utf8(f"Uninstalling {click.style(package_name)}..."), + f"Uninstalling {click.style(package_name)}...", fg="green", bold=True, ) @@ -2625,7 +2584,7 @@ def do_shell(project, python=False, fancy=False, shell_args=None, pypi_mirror=No from .shells import choose_shell shell = choose_shell(project) - click.echo(fix_utf8("Launching subshell in virtual environment..."), err=True) + click.echo("Launching subshell in virtual environment...", err=True) fork_args = ( project.virtualenv_location, @@ -2646,10 +2605,8 @@ def do_shell(project, python=False, fancy=False, shell_args=None, pypi_mirror=No shell.fork_compat(*fork_args) except (AttributeError, ImportError): click.echo( - fix_utf8( - "Compatibility mode not supported. " - "Trying to continue as well-configured shell..." - ), + "Compatibility mode not supported. " + "Trying to continue as well-configured shell...", err=True, ) shell.fork(*fork_args) @@ -3272,7 +3229,7 @@ def do_clean( else: if not bare: click.secho( - fix_utf8(f"Uninstalling {apparent_bad_package}..."), + f"Uninstalling {apparent_bad_package}...", fg="white", bold=True, ) diff --git a/pipenv/environments.py b/pipenv/environments.py index c253bcdd63..3622811438 100644 --- a/pipenv/environments.py +++ b/pipenv/environments.py @@ -6,7 +6,6 @@ from vistir.path import normalize_drive -from pipenv._compat import fix_utf8 from pipenv.patched.pip._vendor.platformdirs import user_cache_dir from pipenv.utils.shell import env_to_bool, is_env_truthy from pipenv.vendor.vistir.misc import _isatty @@ -427,5 +426,5 @@ def is_in_virtualenv(): return virtual_env and not (pipenv_active or ignore_virtualenvs) -PIPENV_SPINNER_FAIL_TEXT = fix_utf8("✘ {0}") if not PIPENV_HIDE_EMOJIS else "{0}" -PIPENV_SPINNER_OK_TEXT = fix_utf8("✔ {0}") if not PIPENV_HIDE_EMOJIS else "{0}" +PIPENV_SPINNER_FAIL_TEXT = "✘ {0}" if not PIPENV_HIDE_EMOJIS else "{0}" +PIPENV_SPINNER_OK_TEXT = "✔ {0}" if not PIPENV_HIDE_EMOJIS else "{0}" diff --git a/pipenv/exceptions.py b/pipenv/exceptions.py index 99e477bacf..21c0ae7797 100644 --- a/pipenv/exceptions.py +++ b/pipenv/exceptions.py @@ -4,7 +4,6 @@ from traceback import format_tb from pipenv import environments -from pipenv._compat import decode_for_output from pipenv.vendor import click, vistir from pipenv.vendor.click.exceptions import ClickException, FileError, UsageError @@ -48,7 +47,7 @@ def handle_exception(exc_type, exception, traceback, hook=sys.excepthook): formatted_lines.append(line) # use new exception prettification rules to format exceptions according to # UX rules - click.echo(decode_for_output(prettify_exc("\n".join(formatted_lines))), err=True) + click.echo(prettify_exc("\n".join(formatted_lines)), err=True) exception.show() @@ -124,14 +123,14 @@ def show(self, file=None): file = vistir.misc.get_text_stderr() message = "{}\n{}".format( click.style("Failed parsing JSON results:", bold=True), - decode_for_output(self.message.strip(), file), + print(self.message.strip(), file=file), ) click.echo(message, err=True) if self.error_text: click.echo( "{} {}".format( click.style("ERROR TEXT:", bold=True), - decode_for_output(self.error_text, file), + print(self.error_text, file=file), ), err=True, ) @@ -146,7 +145,7 @@ def __init__(self, message=None, ctx=None, **kwargs): message = formatted_message.format(msg_prefix, click.style(message, bold=True)) self.message = message extra = kwargs.pop("extra", []) - UsageError.__init__(self, decode_for_output(message), ctx) + UsageError.__init__(self, message, ctx) self.extra = extra def show(self, file=None): @@ -185,9 +184,7 @@ def __init__(self, filename, message=None, **kwargs): message = self.formatted_message.format( click.style(f"{filename} not found!", bold=True), message ) - FileError.__init__( - self, filename=filename, hint=decode_for_output(message), **kwargs - ) + FileError.__init__(self, filename=filename, hint=message, **kwargs) self.extra = extra def show(self, file=None): @@ -197,7 +194,7 @@ def show(self, file=None): if isinstance(self.extra, str): self.extra = [self.extra] for extra in self.extra: - click.echo(decode_for_output(extra, file), file=file) + click.echo(extra, file=file) click.echo(self.message, file=file) @@ -455,6 +452,6 @@ def prettify_exc(error): _, error, info = error.rpartition(split_string) errors.append(f"{error} {info}") if not errors: - return f"{vistir.misc.decode_for_output(error)}" + return error return "\n".join(errors) diff --git a/pipenv/utils/environment.py b/pipenv/utils/environment.py index abcbe4a559..669d37ce94 100644 --- a/pipenv/utils/environment.py +++ b/pipenv/utils/environment.py @@ -1,7 +1,6 @@ import os from pipenv import environments -from pipenv._compat import fix_utf8 from pipenv.vendor import click, dotenv @@ -30,10 +29,9 @@ def load_dot_env(project, as_dict=False, quiet=False): return dotenv.dotenv_values(dotenv_file) elif os.path.isfile(dotenv_file): if not quiet: - click.echo( - click.style( - fix_utf8("Loading .env environment variables..."), bold=True - ), + click.secho( + "Loading .env environment variables...", + bold=True, err=True, ) dotenv.load_dotenv(dotenv_file, override=True) diff --git a/pipenv/utils/processes.py b/pipenv/utils/processes.py index fb96e30ab5..5a38290af1 100644 --- a/pipenv/utils/processes.py +++ b/pipenv/utils/processes.py @@ -21,7 +21,6 @@ def run_command(cmd, *args, is_verbose=False, **kwargs): :raises: exceptions.PipenvCmdError """ - from pipenv._compat import decode_for_output from pipenv.cmdparse import Script catch_exceptions = kwargs.pop("catch_exceptions", True) @@ -38,9 +37,7 @@ def run_command(cmd, *args, is_verbose=False, **kwargs): c = subprocess_run(command, *args, **kwargs) if is_verbose: click.echo( - "Command output: {}".format( - click.style(decode_for_output(c.stdout), fg="cyan") - ), + "Command output: {}".format(click.style(c.stdout, fg="cyan")), err=True, ) if c.returncode and catch_exceptions: diff --git a/pipenv/utils/resolver.py b/pipenv/utils/resolver.py index b48f320965..f0283218df 100644 --- a/pipenv/utils/resolver.py +++ b/pipenv/utils/resolver.py @@ -912,7 +912,6 @@ def actually_resolve_deps( def resolve(cmd, st, project): - from pipenv._compat import decode_output from pipenv.cmdparse import Script from pipenv.vendor.click import echo @@ -920,7 +919,6 @@ def resolve(cmd, st, project): is_verbose = project.s.is_verbose() err = "" for line in iter(c.stderr.readline, ""): - line = decode_output(line) if not line.rstrip(): continue err += line