Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor to support the NO_COLOR argument and deprecate PIPENV_COLORBLIND #5185

Merged
merged 5 commits into from
Jul 24, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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
7 changes: 7 additions & 0 deletions pipenv/cli/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ def cli(
warn_in_virtualenv,
)

if "PIPENV_COLORBLIND" in os.environ:
echo(
"PIPENV_COLORBLIND is deprecated, use NO_COLOR instead"
"Per https://no-color.org/",
err=True,
)

if man:
if system_which("man"):
path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "pipenv.1")
Expand Down
9 changes: 2 additions & 7 deletions pipenv/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import pipfile
import vistir

import pipenv.utils.constants
from pipenv import environments, exceptions, pep508checker, progress
from pipenv._compat import decode_for_output, fix_utf8
from pipenv.utils.dependencies import (
Expand All @@ -39,7 +40,7 @@
from pipenv.utils.spinner import create_spinner
from pipenv.vendor import click

if environments.is_type_checking():
if pipenv.utils.constants.is_type_checking():
from typing import Dict, List, Optional, Union

from pipenv.project import Project
Expand Down Expand Up @@ -81,12 +82,6 @@
INSTALL_LABEL2 = " "
STARTING_LABEL = " "

# Disable colors, for the color blind and others who do not prefer colors.
# if environments.PIPENV_COLORBLIND:

# problem here, click.style and click.secho are doing other things besides just color
# crayons.disable()


def do_clear(project):
click.secho(fix_utf8("Clearing caches..."), bold=True)
Expand Down
2 changes: 1 addition & 1 deletion pipenv/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
import pkg_resources

import pipenv
from pipenv.environments import is_type_checking
from pipenv.patched.notpip._vendor.packaging.utils import canonicalize_name
from pipenv.utils.constants import is_type_checking
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 Down
48 changes: 12 additions & 36 deletions pipenv/environments.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,21 @@
from vistir.path import normalize_drive

from pipenv._compat import fix_utf8
from pipenv.utils.constants import FALSE_VALUES, TRUE_VALUES
from pipenv.utils.shell import env_to_bool
from pipenv.vendor.vistir.misc import _isatty

# HACK: avoid resolver.py uses the wrong byte code files.
# I hope I can remove this one day.

os.environ["PYTHONDONTWRITEBYTECODE"] = "1"
_false_values = ("0", "false", "no", "off")
_true_values = ("1", "true", "yes", "on")


def env_to_bool(val):
"""
Convert **val** to boolean, returning True if truthy or False if falsey

:param Any val: The value to convert
:return: False if Falsey, True if truthy
:rtype: bool
"""
if isinstance(val, bool):
return val
if val.lower() in _false_values:
return False
if val.lower() in _true_values:
return True
raise ValueError(f"Value is not a valid boolean-like: {val}")


def _is_env_truthy(name):
"""An environment variable is truthy if it exists and isn't one of (0, false, no, off)"""
if name not in os.environ:
return False
return os.environ.get(name).lower() not in _false_values
return os.environ.get(name).lower() not in FALSE_VALUES


def get_from_env(arg, prefix="PIPENV", check_for_negation=True):
Expand Down Expand Up @@ -99,12 +82,14 @@ def normalize_pipfile_path(p):
# Internal, to tell whether the command line session is interactive.
SESSION_IS_INTERACTIVE = _isatty(sys.stdout)
PIPENV_IS_CI = env_to_bool(os.environ.get("CI") or os.environ.get("TF_BUILD") or False)
PIPENV_COLORBLIND = bool(os.environ.get("PIPENV_COLORBLIND"))
"""If set, disable terminal colors.
NO_COLOR = False
if env_to_bool(os.getenv("NO_COLOR")) or env_to_bool(os.getenv("PIPENV_COLORBLIND")):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are going to refer to no-color.org, we should also adopt their convention.
NO_COLOR should be a non empty string:

Command-line software which adds ANSI color to its output by default should check for a NO_COLOR environment variable that, when present and not an empty string (regardless of its value), prevents the addition of ANSI color.

By using env_to_bool we deviate from this behavior. Personally, I think it's better to use explicit boolean string, but it's hard fixing the whole world. We should stick to non-empty string IMO.

NO_COLOR = True
from pipenv.utils.shell import style_no_color
from pipenv.vendor import click

Some people don't like colors in their terminals, for some reason. Default is
to show colors.
"""
click.original_style = click.style
click.style = style_no_color

PIPENV_HIDE_EMOJIS = (
os.environ.get("PIPENV_HIDE_EMOJIS") is None
Expand Down Expand Up @@ -294,9 +279,9 @@ def initialize(self):

self.PIPENV_VENV_IN_PROJECT = os.environ.get("PIPENV_VENV_IN_PROJECT")
if self.PIPENV_VENV_IN_PROJECT is not None:
if self.PIPENV_VENV_IN_PROJECT.lower() in _true_values:
if self.PIPENV_VENV_IN_PROJECT.lower() in TRUE_VALUES:
self.PIPENV_VENV_IN_PROJECT = True
elif self.PIPENV_VENV_IN_PROJECT.lower() in _false_values:
elif self.PIPENV_VENV_IN_PROJECT.lower() in FALSE_VALUES:
self.PIPENV_VENV_IN_PROJECT = False
else:
self.PIPENV_VENV_IN_PROJECT = None
Expand Down Expand Up @@ -422,14 +407,5 @@ def is_in_virtualenv():
return virtual_env and not (pipenv_active or ignore_virtualenvs)


def is_type_checking():
try:
from typing import TYPE_CHECKING
except ImportError:
return False
return TYPE_CHECKING


MYPY_RUNNING = is_type_checking()
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}"
6 changes: 3 additions & 3 deletions pipenv/progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@
import sys
import time

from pipenv.environments import PIPENV_COLORBLIND, PIPENV_HIDE_EMOJIS
from pipenv.environments import NO_COLOR, PIPENV_HIDE_EMOJIS
from pipenv.vendor import click

STREAM = sys.stderr
MILL_TEMPLATE = "%s %s %i/%i\r"
DOTS_CHAR = "."
if PIPENV_HIDE_EMOJIS:
if PIPENV_COLORBLIND:
if NO_COLOR:
BAR_FILLED_CHAR = "="
BAR_EMPTY_CHAR = "-"
else:
BAR_FILLED_CHAR = str(click.style("=", bold=True, fg="green"))
BAR_EMPTY_CHAR = str(click.style("-", fg="black"))
else:
if PIPENV_COLORBLIND:
if NO_COLOR:
BAR_FILLED_CHAR = "▉"
BAR_EMPTY_CHAR = " "
else:
Expand Down
8 changes: 2 additions & 6 deletions pipenv/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,8 @@
from pipenv.cmdparse import Script
from pipenv.core import system_which
from pipenv.environment import Environment
from pipenv.environments import (
Setting,
is_in_virtualenv,
is_type_checking,
normalize_pipfile_path,
)
from pipenv.environments import Setting, is_in_virtualenv, normalize_pipfile_path
from pipenv.utils.constants import is_type_checking
from pipenv.utils.dependencies import (
get_canonical_names,
is_editable,
Expand Down
13 changes: 13 additions & 0 deletions pipenv/utils/constants.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# List of version control systems we support.
VCS_LIST = ("git", "svn", "hg", "bzr")
SCHEME_LIST = ("http://", "https://", "ftp://", "ftps://", "file://")
FALSE_VALUES = ("0", "false", "no", "off")
TRUE_VALUES = ("1", "true", "yes", "on")


def is_type_checking():
try:
from typing import TYPE_CHECKING
except ImportError:
return False
return TYPE_CHECKING


MYPY_RUNNING = is_type_checking()
4 changes: 2 additions & 2 deletions pipenv/utils/indexes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

from urllib3.util import parse_url

from pipenv import environments
from pipenv.exceptions import PipenvUsageError
from pipenv.utils.constants import MYPY_RUNNING

from .internet import create_mirror_source, is_pypi_url

if environments.MYPY_RUNNING:
if MYPY_RUNNING:
from typing import List, Optional, Union # noqa

from pipenv.project import Project, TSource # noqa
Expand Down
4 changes: 2 additions & 2 deletions pipenv/utils/processes.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import os
import subprocess

from pipenv import environments
from pipenv.exceptions import PipenvCmdError
from pipenv.utils.constants import MYPY_RUNNING
from pipenv.vendor import click

if environments.MYPY_RUNNING:
if MYPY_RUNNING:
from typing import Tuple # noqa


Expand Down
4 changes: 2 additions & 2 deletions pipenv/utils/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from pipenv import environments
from pipenv.exceptions import RequirementError, ResolutionFailure
from pipenv.utils.constants import MYPY_RUNNING
from pipenv.vendor import click
from pipenv.vendor.requirementslib import Pipfile, Requirement
from pipenv.vendor.requirementslib.models.utils import DIRECT_URL_RE
Expand All @@ -31,11 +32,10 @@
from .shell import make_posix, subprocess_run, temp_environ
from .spinner import create_spinner

if environments.MYPY_RUNNING:
if MYPY_RUNNING:
from typing import Any, Dict, List, Optional, Set, Tuple, Union # noqa

from pipenv.project import Project # noqa
from pipenv.vendor.requirementslib import Pipfile, Requirement # noqa
from pipenv.vendor.requirementslib.models.requirements import Line # noqa


Expand Down
33 changes: 30 additions & 3 deletions pipenv/utils/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@
from functools import lru_cache
from pathlib import Path

from pipenv import environments
from pipenv.utils.constants import MYPY_RUNNING
from pipenv.vendor import click

from .constants import SCHEME_LIST
from .constants import FALSE_VALUES, SCHEME_LIST, TRUE_VALUES
from .processes import subprocess_run

if environments.MYPY_RUNNING:
if MYPY_RUNNING:
from typing import Text # noqa


Expand Down Expand Up @@ -424,3 +425,29 @@ def handle_remove_readonly(func, path, exc):
return

raise exc


def style_no_color(text, fg=None, bg=None, **kwargs) -> str:
"""Wrap click style to ignore colors."""
if hasattr(click, "original_style"):
return click.original_style(text, **kwargs)
return click.style(text, **kwargs)


def env_to_bool(val):
"""
Convert **val** to boolean, returning True if truthy or False if falsey

:param Any val: The value to convert
:return: False if Falsey, True if truthy
:rtype: bool
"""
if val is None:
return False
if isinstance(val, bool):
return val
if val.lower() in FALSE_VALUES:
return False
if val.lower() in TRUE_VALUES:
return True
raise ValueError(f"Value is not a valid boolean-like: {val}")
8 changes: 4 additions & 4 deletions tests/integration/test_dot_venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@

import pytest

from pipenv.environments import _true_values, _false_values
from pipenv.utils.constants import FALSE_VALUES, TRUE_VALUES
from pipenv.utils.shell import normalize_drive, temp_environ


@pytest.mark.dotvenv
@pytest.mark.parametrize("true_value", _true_values)
@pytest.mark.parametrize("true_value", TRUE_VALUES)
def test_venv_in_project(true_value, PipenvInstance):
with temp_environ():
os.environ['PIPENV_VENV_IN_PROJECT'] = true_value
Expand All @@ -23,7 +23,7 @@ def test_venv_in_project(true_value, PipenvInstance):


@pytest.mark.dotvenv
@pytest.mark.parametrize("false_value", _false_values)
@pytest.mark.parametrize("false_value", FALSE_VALUES)
def test_venv_in_project_disabled_ignores_venv(false_value, PipenvInstance):
venv_name = "my_project"
with temp_environ():
Expand All @@ -50,7 +50,7 @@ def test_venv_in_project_disabled_ignores_venv(false_value, PipenvInstance):


@pytest.mark.dotvenv
@pytest.mark.parametrize("true_value", _true_values)
@pytest.mark.parametrize("true_value", TRUE_VALUES)
def test_venv_at_project_root(true_value, PipenvInstance):
with temp_environ():
with PipenvInstance(chdir=True) as p:
Expand Down