From cb9d87ad05830449743cbc3ffa09ebbc7cca602d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 25 Oct 2022 08:27:31 +0000 Subject: [PATCH 01/14] Bumped version. Signed-off-by: github-actions[bot] --- pipenv/__version__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pipenv/__version__.py b/pipenv/__version__.py index 2987c860..33bf4c6f 100644 --- a/pipenv/__version__.py +++ b/pipenv/__version__.py @@ -2,4 +2,4 @@ # // ) ) / / // ) ) //___) ) // ) ) || / / # //___/ / / / //___/ / // // / / || / / # // / / // ((____ // / / ||/ / -__version__ = "2022.10.13.dev0" +__version__ = "2022.10.25.dev1" From 932e98637aec352b342a2fcbddd9f5c4803055fa Mon Sep 17 00:00:00 2001 From: Micah Smith Date: Tue, 25 Oct 2022 10:55:21 -0400 Subject: [PATCH 02/14] Fix rst in contributing guide --- docs/dev/contributing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/contributing.rst b/docs/dev/contributing.rst index b84c658c..640aa94b 100644 --- a/docs/dev/contributing.rst +++ b/docs/dev/contributing.rst @@ -110,7 +110,7 @@ Pipenv now uses pre-commit hooks similar to Pip in order to apply linting and code formatting automatically! The build now also checks that these linting rules have been applied to the code before running the tests. The build will fail when linting changes are detected so be sure to sync dev requirements -and install the pre-commit hooks locally: +and install the pre-commit hooks locally:: $ ``pipenv install --dev`` # This will configure running the pre-commit checks at start of each commit From d1adb504be6808e6b2bf1c795cf7ea6db027b153 Mon Sep 17 00:00:00 2001 From: Micah Smith Date: Tue, 25 Oct 2022 10:56:09 -0400 Subject: [PATCH 03/14] Add default kwarg to get_from_env --- pipenv/environments.py | 6 ++++-- tests/unit/test_environments.py | 34 ++++++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/pipenv/environments.py b/pipenv/environments.py index 9338c2eb..b562eec5 100644 --- a/pipenv/environments.py +++ b/pipenv/environments.py @@ -25,7 +25,7 @@ def _is_env_truthy(name): return os.environ.get(name).lower() not in FALSE_VALUES -def get_from_env(arg, prefix="PIPENV", check_for_negation=True): +def get_from_env(arg, prefix="PIPENV", check_for_negation=True, default=None): """ Check the environment for a variable, returning its truthy or stringified value @@ -36,6 +36,8 @@ def get_from_env(arg, prefix="PIPENV", check_for_negation=True): :param str prefix: The prefix to attach to the variable, defaults to "PIPENV" :param bool check_for_negation: Whether to check for ``_NO_``, defaults to True + :param Optional[Union[str, bool]] default: The value to return if the environment variable does + not exist, defaults to None :return: The value from the environment if available :rtype: Optional[Union[str, bool]] """ @@ -56,7 +58,7 @@ def get_from_env(arg, prefix="PIPENV", check_for_negation=True): return not env_to_bool(value) except ValueError: return value - return None + return default def normalize_pipfile_path(p): diff --git a/tests/unit/test_environments.py b/tests/unit/test_environments.py index c818840d..eee669cc 100644 --- a/tests/unit/test_environments.py +++ b/tests/unit/test_environments.py @@ -31,19 +31,17 @@ def test_get_from_env(arg, prefix, use_negation): main_expected_value = False # use negation means if the normal variable isnt set we will check # for the negated version - negative_expected_value = ( - True if is_negative else None - ) + negative_expected_value = True if is_negative else None if is_positive: assert ( environments.get_from_env( - var_to_set, prefix, check_for_negation=use_negation + var_to_set, prefix=prefix, check_for_negation=use_negation ) is main_expected_value ) assert ( environments.get_from_env( - opposite_var, prefix, check_for_negation=use_negation + opposite_var, prefix=prefix, check_for_negation=use_negation ) is negative_expected_value ) @@ -54,7 +52,7 @@ def test_get_from_env(arg, prefix, use_negation): # get NO_BLAH -- expecting this to be True assert ( environments.get_from_env( - var_to_set, prefix, check_for_negation=use_negation + var_to_set, prefix=prefix, check_for_negation=use_negation ) is negative_expected_value ) @@ -62,7 +60,29 @@ def test_get_from_env(arg, prefix, use_negation): # but otherwise should be None assert ( environments.get_from_env( - opposite_var, prefix, check_for_negation=use_negation + opposite_var, prefix=prefix, check_for_negation=use_negation ) is main_expected_value ) + + +@pytest.mark.environments +@pytest.mark.parametrize( + "check_for_negation, default", + list(itertools.product((True, False), (None, "default", 1))), +) +def test_get_from_env_default(check_for_negation, default): + """When the desired env var does""" + arg = "ENABLE_SOMETHING" + prefix = "FAKEPREFIX" + envvar = f"{prefix}_{arg}" + negated_envvar = f"{prefix}_NO_{arg}" + with temp_environ(): + os.environ.pop(envvar, None) + os.environ.pop(negated_envvar, None) + assert ( + environments.get_from_env( + arg, prefix=prefix, check_for_negation=check_for_negation, default=default + ) + == default + ) From e61a8a0b1d8dc640956a6a818c1902e57fc57256 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Tue, 25 Oct 2022 22:17:01 -0400 Subject: [PATCH 04/14] Release v2022.10.25 --- CHANGELOG.rst | 22 ++++++++++++++++++++++ news/5419.removal.rst | 1 - news/5420.vendor.rst | 1 - news/5431.feature.rst | 1 - pipenv/__version__.py | 2 +- pipenv/pipenv.1 | 2 +- 6 files changed, 24 insertions(+), 5 deletions(-) delete mode 100644 news/5419.removal.rst delete mode 100644 news/5420.vendor.rst delete mode 100644 news/5431.feature.rst diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d2429860..23f01f73 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,25 @@ +2022.10.25 (2022-10-25) +======================= +Pipenv 2022.10.25 (2022-10-25) +============================== + + +Features & Improvements +----------------------- + +- Add support to export requirements file for a specified set of categories. `#5431 `_ + +Vendored Libraries +------------------ + +- Remove appdirs.py in favor of platformdirs. `#5420 `_ + +Removals and Deprecations +------------------------- + +- Remove usage of vistir.cmdparse in favor of pipenv.cmdparse `#5419 `_ + + 2022.10.12 (2022-10-12) ======================= Pipenv 2022.10.12 (2022-10-12) diff --git a/news/5419.removal.rst b/news/5419.removal.rst deleted file mode 100644 index 97a67c5a..00000000 --- a/news/5419.removal.rst +++ /dev/null @@ -1 +0,0 @@ -Remove usage of vistir.cmdparse in favor of pipenv.cmdparse diff --git a/news/5420.vendor.rst b/news/5420.vendor.rst deleted file mode 100644 index 71670b09..00000000 --- a/news/5420.vendor.rst +++ /dev/null @@ -1 +0,0 @@ -Remove appdirs.py in favor of platformdirs. diff --git a/news/5431.feature.rst b/news/5431.feature.rst deleted file mode 100644 index 627693cd..00000000 --- a/news/5431.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Add support to export requirements file for a specified set of categories. diff --git a/pipenv/__version__.py b/pipenv/__version__.py index 33bf4c6f..339f0876 100644 --- a/pipenv/__version__.py +++ b/pipenv/__version__.py @@ -2,4 +2,4 @@ # // ) ) / / // ) ) //___) ) // ) ) || / / # //___/ / / / //___/ / // // / / || / / # // / / // ((____ // / / ||/ / -__version__ = "2022.10.25.dev1" +__version__ = "2022.10.25" diff --git a/pipenv/pipenv.1 b/pipenv/pipenv.1 index 740f610f..55bbe2f5 100644 --- a/pipenv/pipenv.1 +++ b/pipenv/pipenv.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "PIPENV" "1" "Oct 24, 2022" "2022.10.13.dev0" "pipenv" +.TH "PIPENV" "1" "Oct 25, 2022" "2022.10.25" "pipenv" .sp Pipenv uses a set of commands to manage your Project\(aqs dependencies and custom scripts. It replaces the use of \fBMakefile\fP, direct calls to \fBpip\fP and \fBpython \-m venv\fP or \fBvirtualenv\fP\&. From 09e60097b3cb93b67014657d8dbd41e618fe1152 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 26 Oct 2022 02:20:35 +0000 Subject: [PATCH 05/14] Bumped version. Signed-off-by: github-actions[bot] --- pipenv/__version__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pipenv/__version__.py b/pipenv/__version__.py index 339f0876..039f0176 100644 --- a/pipenv/__version__.py +++ b/pipenv/__version__.py @@ -2,4 +2,4 @@ # // ) ) / / // ) ) //___) ) // ) ) || / / # //___/ / / / //___/ / // // / / || / / # // / / // ((____ // / / ||/ / -__version__ = "2022.10.25" +__version__ = "2022.10.26.dev0" From cd15007e4d080510bb44a1996b9699bbb02c0902 Mon Sep 17 00:00:00 2001 From: Micah Smith Date: Thu, 27 Oct 2022 17:50:01 -0400 Subject: [PATCH 06/14] Refactor all relevant settings to use get_from_env --- pipenv/environments.py | 80 ++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/pipenv/environments.py b/pipenv/environments.py index b562eec5..bc1d5888 100644 --- a/pipenv/environments.py +++ b/pipenv/environments.py @@ -120,14 +120,12 @@ class Setting: #: Location for Pipenv to store it's package cache. #: Default is to use appdir's user cache directory. - self.PIPENV_CACHE_DIR = os.environ.get( - "PIPENV_CACHE_DIR", user_cache_dir("pipenv") + self.PIPENV_CACHE_DIR = get_from_env( + "CACHE_DIR", default=user_cache_dir("pipenv") ) # Tells Pipenv which Python to default to, when none is provided. - self.PIPENV_DEFAULT_PYTHON_VERSION = os.environ.get( - "PIPENV_DEFAULT_PYTHON_VERSION" - ) + self.PIPENV_DEFAULT_PYTHON_VERSION = get_from_env("DEFAULT_PYTHON_VERSION") """Use this Python version when creating new virtual environments by default. This can be set to a version string, e.g. ``3.9``, or a path. Default is to use @@ -136,38 +134,38 @@ class Setting: this configuration. """ - self.PIPENV_DONT_LOAD_ENV = bool(os.environ.get("PIPENV_DONT_LOAD_ENV")) + self.PIPENV_DONT_LOAD_ENV = bool(get_from_env("DONT_LOAD_ENV")) """If set, Pipenv does not load the ``.env`` file. Default is to load ``.env`` for ``run`` and ``shell`` commands. """ - self.PIPENV_DONT_USE_PYENV = bool(os.environ.get("PIPENV_DONT_USE_PYENV")) + self.PIPENV_DONT_USE_PYENV = bool(get_from_env("DONT_USE_PYENV")) """If set, Pipenv does not attempt to install Python with pyenv. Default is to install Python automatically via pyenv when needed, if possible. """ - self.PIPENV_DONT_USE_ASDF = bool(os.environ.get("PIPENV_DONT_USE_ASDF")) + self.PIPENV_DONT_USE_ASDF = bool(get_from_env("DONT_USE_ASDF")) """If set, Pipenv does not attempt to install Python with asdf. Default is to install Python automatically via asdf when needed, if possible. """ - self.PIPENV_DOTENV_LOCATION = os.environ.get("PIPENV_DOTENV_LOCATION") + self.PIPENV_DOTENV_LOCATION = get_from_env("DOTENV_LOCATION") """If set, Pipenv loads the ``.env`` file at the specified location. Default is to load ``.env`` from the project root, if found. """ - self.PIPENV_EMULATOR = os.environ.get("PIPENV_EMULATOR", "") + self.PIPENV_EMULATOR = get_from_env("EMULATOR", default="") """If set, the terminal emulator's name for ``pipenv shell`` to use. Default is to detect emulators automatically. This should be set if your emulator, e.g. Cmder, cannot be detected correctly. """ - self.PIPENV_IGNORE_VIRTUALENVS = bool(os.environ.get("PIPENV_IGNORE_VIRTUALENVS")) + self.PIPENV_IGNORE_VIRTUALENVS = bool(get_from_env("IGNORE_VIRTUALENVS")) """If set, Pipenv will always assign a virtual environment for this project. By default, Pipenv tries to detect whether it is run inside a virtual @@ -176,7 +174,7 @@ class Setting: """ self.PIPENV_INSTALL_TIMEOUT = int( - os.environ.get("PIPENV_INSTALL_TIMEOUT", 60 * 15) + get_from_env("INSTALL_TIMEOUT", default=60 * 15) ) """Max number of seconds to wait for package installation. @@ -184,14 +182,14 @@ class Setting: """ # NOTE: +1 because of a temporary bug in Pipenv. - self.PIPENV_MAX_DEPTH = int(os.environ.get("PIPENV_MAX_DEPTH", "3")) + 1 + self.PIPENV_MAX_DEPTH = int(get_from_env("PIPENV_MAX_DEPTH", default=3)) + 1 """Maximum number of directories to recursively search for a Pipfile. Default is 3. See also ``PIPENV_NO_INHERIT``. """ - self.PIPENV_MAX_RETRIES = int( - os.environ.get("PIPENV_MAX_RETRIES", "1" if PIPENV_IS_CI else "0") + self.PIPENV_MAX_RETRIES = ( + int(get_from_env("MAX_RETRIES", default=1)) if PIPENV_IS_CI else 0 ) """Specify how many retries Pipenv should attempt for network requests. @@ -207,7 +205,7 @@ class Setting: if self.PIPENV_NO_INHERIT: self.PIPENV_MAX_DEPTH = 2 - self.PIPENV_NOSPIN = bool(os.environ.get("PIPENV_NOSPIN")) + self.PIPENV_NOSPIN = bool(get_from_env("NOSPIN", check_for_negation=False)) """If set, disable terminal spinner. This can make the logs cleaner. Automatically set on Windows, and in CI @@ -217,14 +215,16 @@ class Setting: self.PIPENV_NOSPIN = True pipenv_spinner = "dots" if not os.name == "nt" else "bouncingBar" - self.PIPENV_SPINNER = os.environ.get("PIPENV_SPINNER", pipenv_spinner) + 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 """ - pipenv_pipfile = os.environ.get("PIPENV_PIPFILE") + pipenv_pipfile = get_from_env("PIPFILE", check_for_negation=False) if pipenv_pipfile: if not os.path.isfile(pipenv_pipfile): raise RuntimeError("Given PIPENV_PIPFILE is not found!") @@ -245,20 +245,20 @@ class Setting: See also ``PIPENV_MAX_DEPTH``. """ - self.PIPENV_PYPI_MIRROR = os.environ.get("PIPENV_PYPI_MIRROR") + self.PIPENV_PYPI_MIRROR = get_from_env("PYPI_MIRROR", check_for_negation=False) """If set, tells pipenv to override PyPI index urls with a mirror. Default is to not mirror PyPI, i.e. use the real one, pypi.org. The ``--pypi-mirror`` command line flag overwrites this. """ - self.PIPENV_QUIET = bool(os.environ.get("PIPENV_QUIET")) + self.PIPENV_QUIET = bool(get_from_env("QUIET", check_for_negation=False)) """If set, makes Pipenv quieter. Default is unset, for normal verbosity. ``PIPENV_VERBOSE`` overrides this. """ - self.PIPENV_SHELL_EXPLICIT = os.environ.get("PIPENV_SHELL") + self.PIPENV_SHELL_EXPLICIT = get_from_env("SHELL", check_for_negation=False) """An absolute path to the preferred shell for ``pipenv shell``. Default is to detect automatically what shell is currently in use. @@ -266,19 +266,19 @@ class Setting: # Hack because PIPENV_SHELL is actually something else. Internally this # variable is called PIPENV_SHELL_EXPLICIT instead. - self.PIPENV_SHELL_FANCY = bool(os.environ.get("PIPENV_SHELL_FANCY")) + self.PIPENV_SHELL_FANCY = bool(get_from_env("SHELL_FANCY")) """If set, always use fancy mode when invoking ``pipenv shell``. Default is to use the compatibility shell if possible. """ - self.PIPENV_TIMEOUT = int(os.environ.get("PIPENV_TIMEOUT", 120)) + self.PIPENV_TIMEOUT = int(get_from_env("TIMEOUT", default=120)) """Max number of seconds Pipenv will wait for virtualenv creation to complete. Default is 120 seconds, an arbitrary number that seems to work. """ - self.PIPENV_VENV_IN_PROJECT = os.environ.get("PIPENV_VENV_IN_PROJECT") + self.PIPENV_VENV_IN_PROJECT = get_from_env("VENV_IN_PROJECT") if self.PIPENV_VENV_IN_PROJECT is not None: if self.PIPENV_VENV_IN_PROJECT.lower() in TRUE_VALUES: self.PIPENV_VENV_IN_PROJECT = True @@ -288,25 +288,25 @@ class Setting: self.PIPENV_VENV_IN_PROJECT = None """ When set True, will create or use the ``.venv`` in your project directory. When Set False, will ignore the .venv in your project directory even if it exists. - Default is None will use the .venv of project directory should it exist, otherwise + If unset (default), will use the .venv of project directory should it exist, otherwise will create new virtual environments in a global location. """ - self.PIPENV_VERBOSE = bool(os.environ.get("PIPENV_VERBOSE")) + self.PIPENV_VERBOSE = bool(get_from_env("VERBOSE", check_for_negation=False)) """If set, makes Pipenv more wordy. Default is unset, for normal verbosity. This takes precedence over ``PIPENV_QUIET``. """ - self.PIPENV_YES = bool(os.environ.get("PIPENV_YES")) + self.PIPENV_YES = bool(get_from_env("YES")) """If set, Pipenv automatically assumes "yes" at all prompts. Default is to prompt the user for an answer if the current command line session if interactive. """ - self.PIPENV_SKIP_LOCK = bool(os.environ.get("PIPENV_SKIP_LOCK", False)) + self.PIPENV_SKIP_LOCK = bool(get_from_env("SKIP_LOCK")) """If set, Pipenv won't lock dependencies automatically. This might be desirable if a project has large number of dependencies, @@ -319,33 +319,32 @@ class Setting: NOTE: This only affects the ``install`` and ``uninstall`` commands. """ - self.PIP_EXISTS_ACTION = os.environ.get("PIP_EXISTS_ACTION", "w") + self.PIP_EXISTS_ACTION = get_from_env( + "EXISTS_ACTION", prefix="PIP", check_for_negation=False, default="w" + ) """Specifies the value for pip's --exists-action option Defaults to ``(w)ipe`` """ - self.PIPENV_RESOLVE_VCS = os.environ.get( - "PIPENV_RESOLVE_VCS" - ) is None or _is_env_truthy("PIPENV_RESOLVE_VCS") - + self.PIPENV_RESOLVE_VCS = bool(get_from_env("RESOLVE_VCS", default=True)) """Tells Pipenv whether to resolve all VCS dependencies in full. As of Pipenv 2018.11.26, only editable VCS dependencies were resolved in full. To retain this behavior and avoid handling any conflicts that arise from the new - approach, you may set this to '0', 'off', or 'false'. + approach, you may disable this. """ - self.PIPENV_CUSTOM_VENV_NAME = os.getenv("PIPENV_CUSTOM_VENV_NAME", None) + self.PIPENV_CUSTOM_VENV_NAME = get_from_env("CUSTOM_VENV_NAME") """Tells Pipenv whether to name the venv something other than the default dir name.""" - self.PIPENV_PYUP_API_KEY = os.environ.get("PIPENV_PYUP_API_KEY", None) + self.PIPENV_PYUP_API_KEY = get_from_env("PYUP_API_KEY") # Internal, support running in a different Python from sys.executable. - self.PIPENV_PYTHON = os.environ.get("PIPENV_PYTHON") + self.PIPENV_PYTHON = get_from_env("PYTHON") # Internal, overwrite all index funcitonality. - self.PIPENV_TEST_INDEX = os.environ.get("PIPENV_TEST_INDEX") + self.PIPENV_TEST_INDEX = get_from_env("TEST_INDEX") # Internal, tells Pipenv about the surrounding environment. self.PIPENV_USE_SYSTEM = False @@ -366,9 +365,8 @@ class Setting: # Internal, consolidated verbosity representation as an integer. The default # level is 0, increased for wordiness and decreased for terseness. - verbosity = os.environ.get("PIPENV_VERBOSITY", "") try: - self.PIPENV_VERBOSITY = int(verbosity) + self.PIPENV_VERBOSITY = int(get_from_env("VERBOSITY")) except (ValueError, TypeError): if self.PIPENV_VERBOSE: self.PIPENV_VERBOSITY = 1 @@ -407,7 +405,7 @@ def is_in_virtualenv(): pipenv_active = os.environ.get("PIPENV_ACTIVE", False) virtual_env = bool(os.environ.get("VIRTUAL_ENV")) - ignore_virtualenvs = bool(os.environ.get("PIPENV_IGNORE_VIRTUALENVS", False)) + ignore_virtualenvs = bool(get_from_env("IGNORE_VIRTUALENVS")) return virtual_env and not (pipenv_active or ignore_virtualenvs) From 93ca10759918bc1c31756f198ed53c31dd7236d0 Mon Sep 17 00:00:00 2001 From: Micah Smith Date: Tue, 1 Nov 2022 21:15:58 -0400 Subject: [PATCH 07/14] Fix behavior of VENV_IN_PROJECT setting --- pipenv/environments.py | 10 ++-------- tests/unit/test_environments.py | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pipenv/environments.py b/pipenv/environments.py index bc1d5888..e5e0cd91 100644 --- a/pipenv/environments.py +++ b/pipenv/environments.py @@ -8,7 +8,7 @@ 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.constants import FALSE_VALUES, TRUE_VALUES +from pipenv.utils.constants import FALSE_VALUES from pipenv.utils.shell import env_to_bool from pipenv.vendor.vistir.misc import _isatty @@ -18,6 +18,7 @@ from pipenv.vendor.vistir.misc import _isatty os.environ["PYTHONDONTWRITEBYTECODE"] = "1" +# TODO(micahjsmith) refactor to use env2bool 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: @@ -279,13 +280,6 @@ class Setting: """ self.PIPENV_VENV_IN_PROJECT = get_from_env("VENV_IN_PROJECT") - if self.PIPENV_VENV_IN_PROJECT is not None: - 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: - self.PIPENV_VENV_IN_PROJECT = False - else: - self.PIPENV_VENV_IN_PROJECT = None """ When set True, will create or use the ``.venv`` in your project directory. When Set False, will ignore the .venv in your project directory even if it exists. If unset (default), will use the .venv of project directory should it exist, otherwise diff --git a/tests/unit/test_environments.py b/tests/unit/test_environments.py index eee669cc..b5bfbef4 100644 --- a/tests/unit/test_environments.py +++ b/tests/unit/test_environments.py @@ -86,3 +86,18 @@ def test_get_from_env_default(check_for_negation, default): ) == default ) + + +def test_pipenv_venv_in_project_set_true(monkeypatch): + monkeypatch.setenv("PIPENV_VENV_IN_PROJECT", "1") + assert environments.Setting().PIPENV_VENV_IN_PROJECT is True + + +def test_pipenv_venv_in_project_set_false(monkeypatch): + monkeypatch.setenv("PIPENV_VENV_IN_PROJECT", "0") + assert environments.Setting().PIPENV_VENV_IN_PROJECT is False + + +def test_pipenv_venv_in_project_unset(monkeypatch): + monkeypatch.delenv("PIPENV_VENV_IN_PROJECT", raising=False) + assert environments.Setting().PIPENV_VENV_IN_PROJECT is None From 38813b96b8a978356628fc33a6779ea7f653926c Mon Sep 17 00:00:00 2001 From: Micah Smith Date: Tue, 1 Nov 2022 21:57:12 -0400 Subject: [PATCH 08/14] Refactor out is_env_truthy --- pipenv/environments.py | 15 +++------------ pipenv/utils/shell.py | 5 +++++ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/pipenv/environments.py b/pipenv/environments.py index e5e0cd91..5b9a1499 100644 --- a/pipenv/environments.py +++ b/pipenv/environments.py @@ -8,8 +8,7 @@ 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.constants import FALSE_VALUES -from pipenv.utils.shell import env_to_bool +from pipenv.utils.shell import env_to_bool, is_env_truthy from pipenv.vendor.vistir.misc import _isatty # HACK: avoid resolver.py uses the wrong byte code files. @@ -18,14 +17,6 @@ from pipenv.vendor.vistir.misc import _isatty os.environ["PYTHONDONTWRITEBYTECODE"] = "1" -# TODO(micahjsmith) refactor to use env2bool -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 - - def get_from_env(arg, prefix="PIPENV", check_for_negation=True, default=None): """ Check the environment for a variable, returning its truthy or stringified value @@ -86,7 +77,7 @@ os.environ.pop("__PYVENV_LAUNCHER__", None) SESSION_IS_INTERACTIVE = _isatty(sys.stdout) # TF_BUILD indicates to Azure pipelines it is a build step -PIPENV_IS_CI = _is_env_truthy("CI") or _is_env_truthy("TF_BUILD") +PIPENV_IS_CI = is_env_truthy("CI") or is_env_truthy("TF_BUILD") NO_COLOR = False @@ -101,7 +92,7 @@ if os.getenv("NO_COLOR") or os.getenv("PIPENV_COLORBLIND"): PIPENV_HIDE_EMOJIS = ( os.environ.get("PIPENV_HIDE_EMOJIS") is None and (os.name == "nt" or PIPENV_IS_CI) - or _is_env_truthy("PIPENV_HIDE_EMOJIS") + or is_env_truthy("PIPENV_HIDE_EMOJIS") ) """Disable emojis in output. diff --git a/pipenv/utils/shell.py b/pipenv/utils/shell.py index a8765e29..1668d757 100644 --- a/pipenv/utils/shell.py +++ b/pipenv/utils/shell.py @@ -407,6 +407,11 @@ def env_to_bool(val): 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)""" + return env_to_bool(os.getenv(name)) + + def project_python(project, system=False): if not system: python = project._which("python") From 6463bcd7020a6c59a1d25e79bc62b2ecf8105d42 Mon Sep 17 00:00:00 2001 From: Micah Smith Date: Tue, 1 Nov 2022 22:34:55 -0400 Subject: [PATCH 09/14] Update docs --- docs/advanced.rst | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/advanced.rst b/docs/advanced.rst index a6cf001c..2714c60c 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -628,11 +628,17 @@ You can display the names and commands of your shortcuts by running ``pipenv scr ☤ Configuration With Environment Variables ------------------------------------------ -Pipenv comes with a handful of options that can be enabled via shell environment -variables. To activate them, simply create the variable in your shell and pipenv -will detect it. +Pipenv comes with a handful of options that can be set via shell environment +variables. -.. automodule:: pipenv.environments +To enable boolean options, create the variable in your shell and assign to it a +truthy value (i.e. ``"1"``):: + + $ PIPENV_IGNORE_VIRTUALENVS=1 + +To explicitly disable a boolean option, assign to it a falsy value (i.e. ``"0"``). + +.. autoclass:: pipenv.environments.Setting :members: If you'd like to set these environment variables on a per-project basis, I recommend utilizing the fantastic `direnv `_ project, in order to do so. From 1a714b05de9544f6ff627ce2f72204829daa2ffd Mon Sep 17 00:00:00 2001 From: Micah Smith Date: Tue, 1 Nov 2022 22:40:36 -0400 Subject: [PATCH 10/14] Add news item --- news/5451.feature.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/5451.feature.rst diff --git a/news/5451.feature.rst b/news/5451.feature.rst new file mode 100644 index 00000000..30786d74 --- /dev/null +++ b/news/5451.feature.rst @@ -0,0 +1 @@ +Allow pipenv settings to be explicitly disabled more easily by assigning to the environment variable a falsy value. From 258d82f21d82201c5fac58865ba6871d5bd4b043 Mon Sep 17 00:00:00 2001 From: Micah Smith Date: Wed, 2 Nov 2022 11:58:29 -0400 Subject: [PATCH 11/14] Fix is_env_truthy --- docs/advanced.rst | 2 +- pipenv/utils/shell.py | 19 +++++++++++++------ tests/unit/test_utils.py | 39 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/docs/advanced.rst b/docs/advanced.rst index 2714c60c..28af1352 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -636,7 +636,7 @@ truthy value (i.e. ``"1"``):: $ PIPENV_IGNORE_VIRTUALENVS=1 -To explicitly disable a boolean option, assign to it a falsy value (i.e. ``"0"``). +To explicitly disable a boolean option, assign to it a falsey value (i.e. ``"0"``). .. autoclass:: pipenv.environments.Setting :members: diff --git a/pipenv/utils/shell.py b/pipenv/utils/shell.py index 1668d757..2739021c 100644 --- a/pipenv/utils/shell.py +++ b/pipenv/utils/shell.py @@ -395,21 +395,28 @@ 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 + :return: False if falsey, True if truthy :rtype: bool + :raises: + ValueError: if val is not a valid boolean-like """ if isinstance(val, bool): return val - if val.lower() in FALSE_VALUES: - return False - if val.lower() in TRUE_VALUES: - return True + + try: + if val.lower() in FALSE_VALUES: + return False + if val.lower() in TRUE_VALUES: + return True + except AttributeError: + pass + 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)""" - return env_to_bool(os.getenv(name)) + return env_to_bool(os.getenv(name, False)) def project_python(project, system=False): diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 703bfff6..2972b087 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -3,7 +3,6 @@ from unittest import mock import pytest -import pipenv.utils.shell from pipenv.utils import dependencies from pipenv.utils import indexes from pipenv.utils import internet @@ -338,7 +337,7 @@ twine = "*" ) @pytest.mark.skipif(os.name != "nt", reason="Windows file paths tested") def test_win_normalize_drive(self, input_path, expected): - assert pipenv.utils.shell.normalize_drive(input_path) == expected + assert shell.normalize_drive(input_path) == expected @pytest.mark.utils @pytest.mark.parametrize( @@ -352,7 +351,7 @@ twine = "*" ) @pytest.mark.skipif(os.name == "nt", reason="*nix file paths tested") def test_nix_normalize_drive(self, input_path, expected): - assert pipenv.utils.shell.normalize_drive(input_path) == expected + assert shell.normalize_drive(input_path) == expected @pytest.mark.utils @pytest.mark.parametrize( @@ -490,3 +489,37 @@ twine = "*" python = shell.project_python(project, system=True) assert python == "/usr/local/bin/python3" + + @pytest.mark.utils + @pytest.mark.parametrize( + "val, expected", + ( + (True, True), + (False, False), + ("true", True), + ("1", True), + ("off", False), + ("0", False), + ) + ) + def test_env_to_bool(self, val, expected): + actual = shell.env_to_bool(val) + assert actual == expected + + @pytest.mark.utils + def test_is_env_truthy_exists_true(self, monkeypatch): + name = "ZZZ" + monkeypatch.setenv(name, "1") + assert shell.is_env_truthy(name) is True + + @pytest.mark.utils + def test_is_env_truthy_exists_false(self, monkeypatch): + name = "ZZZ" + monkeypatch.setenv(name, "0") + assert shell.is_env_truthy(name) is False + + @pytest.mark.utils + def test_is_env_truthy_does_not_exisxt(self, monkeypatch): + name = "ZZZ" + monkeypatch.delenv(name, raising=False) + assert shell.is_env_truthy(name) is False From 135463653ff534b89e41bd385b53ba72960c4f49 Mon Sep 17 00:00:00 2001 From: Micah Smith Date: Wed, 2 Nov 2022 15:09:37 -0400 Subject: [PATCH 12/14] Update more settings --- pipenv/environments.py | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/pipenv/environments.py b/pipenv/environments.py index 5b9a1499..f0c4667e 100644 --- a/pipenv/environments.py +++ b/pipenv/environments.py @@ -113,11 +113,13 @@ class Setting: #: Location for Pipenv to store it's package cache. #: Default is to use appdir's user cache directory. self.PIPENV_CACHE_DIR = get_from_env( - "CACHE_DIR", default=user_cache_dir("pipenv") + "CACHE_DIR", check_for_negation=False, default=user_cache_dir("pipenv") ) # Tells Pipenv which Python to default to, when none is provided. - self.PIPENV_DEFAULT_PYTHON_VERSION = get_from_env("DEFAULT_PYTHON_VERSION") + self.PIPENV_DEFAULT_PYTHON_VERSION = get_from_env( + "DEFAULT_PYTHON_VERSION", check_for_negation=False + ) """Use this Python version when creating new virtual environments by default. This can be set to a version string, e.g. ``3.9``, or a path. Default is to use @@ -126,25 +128,33 @@ class Setting: this configuration. """ - self.PIPENV_DONT_LOAD_ENV = bool(get_from_env("DONT_LOAD_ENV")) + self.PIPENV_DONT_LOAD_ENV = bool( + get_from_env("DONT_LOAD_ENV", check_for_negation=False) + ) """If set, Pipenv does not load the ``.env`` file. Default is to load ``.env`` for ``run`` and ``shell`` commands. """ - self.PIPENV_DONT_USE_PYENV = bool(get_from_env("DONT_USE_PYENV")) + self.PIPENV_DONT_USE_PYENV = bool( + get_from_env("DONT_USE_PYENV", check_for_negation=False) + ) """If set, Pipenv does not attempt to install Python with pyenv. Default is to install Python automatically via pyenv when needed, if possible. """ - self.PIPENV_DONT_USE_ASDF = bool(get_from_env("DONT_USE_ASDF")) + self.PIPENV_DONT_USE_ASDF = bool( + get_from_env("DONT_USE_ASDF", check_for_negation=False) + ) """If set, Pipenv does not attempt to install Python with asdf. Default is to install Python automatically via asdf when needed, if possible. """ - self.PIPENV_DOTENV_LOCATION = get_from_env("DOTENV_LOCATION") + self.PIPENV_DOTENV_LOCATION = get_from_env( + "DOTENV_LOCATION", check_for_negation=False + ) """If set, Pipenv loads the ``.env`` file at the specified location. Default is to load ``.env`` from the project root, if found. @@ -188,7 +198,9 @@ class Setting: Default is 0. Automatically set to 1 on CI environments for robust testing. """ - self.PIPENV_NO_INHERIT = "PIPENV_NO_INHERIT" in os.environ + self.PIPENV_NO_INHERIT = bool( + get_from_env("NO_INHERIT", check_for_negation=False) + ) """Tell Pipenv not to inherit parent directories. This is useful for deployment to avoid using the wrong current directory. @@ -264,7 +276,9 @@ class Setting: Default is to use the compatibility shell if possible. """ - self.PIPENV_TIMEOUT = int(get_from_env("TIMEOUT", default=120)) + self.PIPENV_TIMEOUT = int( + get_from_env("TIMEOUT", check_for_negation=False, default=120) + ) """Max number of seconds Pipenv will wait for virtualenv creation to complete. Default is 120 seconds, an arbitrary number that seems to work. @@ -320,16 +334,18 @@ class Setting: approach, you may disable this. """ - self.PIPENV_CUSTOM_VENV_NAME = get_from_env("CUSTOM_VENV_NAME") + self.PIPENV_CUSTOM_VENV_NAME = get_from_env( + "CUSTOM_VENV_NAME", check_for_negation=False + ) """Tells Pipenv whether to name the venv something other than the default dir name.""" - self.PIPENV_PYUP_API_KEY = get_from_env("PYUP_API_KEY") + self.PIPENV_PYUP_API_KEY = get_from_env("PYUP_API_KEY", check_for_negation=False) # Internal, support running in a different Python from sys.executable. - self.PIPENV_PYTHON = get_from_env("PYTHON") + self.PIPENV_PYTHON = get_from_env("PYTHON", check_for_negation=False) # Internal, overwrite all index funcitonality. - self.PIPENV_TEST_INDEX = get_from_env("TEST_INDEX") + self.PIPENV_TEST_INDEX = get_from_env("TEST_INDEX", check_for_negation=False) # Internal, tells Pipenv about the surrounding environment. self.PIPENV_USE_SYSTEM = False From 93e97b75104f7b72017f7bf8e1cfd61b518f1a01 Mon Sep 17 00:00:00 2001 From: Micah Smith Date: Wed, 2 Nov 2022 15:22:08 -0400 Subject: [PATCH 13/14] Update test_pipenv_check to ignore new vulnerability with wheel<0.38 --- tests/integration/test_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_cli.py b/tests/integration/test_cli.py index 7a6badb3..75acb744 100644 --- a/tests/integration/test_cli.py +++ b/tests/integration/test_cli.py @@ -152,7 +152,7 @@ def test_pipenv_check(pipenv_instance_private_pypi): assert c.returncode == 0 c = p.pipenv('install six') assert c.returncode == 0 - c = p.pipenv('check --ignore 35015') + c = p.pipenv('check --ignore 35015 --ignore 51499') assert c.returncode == 0 assert 'Ignoring' in c.stderr From 2049648772a3fded373865c21489fa990eaaf418 Mon Sep 17 00:00:00 2001 From: Oz N Tiram Date: Fri, 4 Nov 2022 10:49:06 +0100 Subject: [PATCH 14/14] Pin vendoring Python version to 3.10 PyInvoke still does not support Python3.11 --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 92dd3fe1..076b87f6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -65,7 +65,7 @@ jobs: - uses: actions/setup-python@v4 with: - python-version: 3.x + python-version: "3.10" - name: Collect Workflow Telemetry uses: runforesight/foresight-workflow-kit-action@v1 if: ${{ always() }}