From 6707b0c19bb68e267ef423391b244dafca1c288b Mon Sep 17 00:00:00 2001 From: Stefane Fermigier Date: Mon, 16 Apr 2018 12:36:40 +0200 Subject: [PATCH 1/8] I believe this tox config is more correct this way --- docs/advanced.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/advanced.rst b/docs/advanced.rst index 10ae790b..6a879e76 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -474,7 +474,6 @@ and external testing:: [testenv:flake8-py3] basepython = python3.4 commands= - {[testenv]deps} pipenv install --dev pipenv run flake8 --version pipenv run flake8 setup.py docs project test From 55121bd2aec64d7fdf776647419ab24a21b5160b Mon Sep 17 00:00:00 2001 From: Gasper Zejn Date: Fri, 20 Apr 2018 12:10:09 +0200 Subject: [PATCH 2/8] Fix progress.py to obey PIPENV_COLORBLIND and not use any colors. Crayon library is used in progress.py to set up BAR_FILLED_CHAR as module global, and since progress is imported in core before crayons.disable() call takes effect, the BAR_FILLED_CHARS is green even though PIPENV_COLORBLIND is set. To make this work as advertised, PIPENV_COLORBLIND is checked in progress.py too. --- pipenv/core.py | 2 +- pipenv/progress.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pipenv/core.py b/pipenv/core.py index 9e761ff7..57974de6 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -103,7 +103,7 @@ else: STARTING_LABEL = ' ' # Enable shell completion. click_completion.init() -# Disable colors, for the soulless. +# Disable colors, for the color blind and others who do not prefer colors. if PIPENV_COLORBLIND: crayons.disable() # Disable spinner, for cleaner build logs (the unworthy). diff --git a/pipenv/progress.py b/pipenv/progress.py index 7d6dc31e..14bb721f 100644 --- a/pipenv/progress.py +++ b/pipenv/progress.py @@ -13,11 +13,12 @@ import os import sys import time import crayons +from .environments import PIPENV_COLORBLIND STREAM = sys.stderr MILL_TEMPLATE = '%s %s %i/%i\r' DOTS_CHAR = '.' -if os.name != 'nt': +if os.name != 'nt' and not PIPENV_COLORBLIND: BAR_FILLED_CHAR = str(crayons.green('▉', bold=True)) BAR_EMPTY_CHAR = str(crayons.black('▉')) else: From 8bf64918e36044021b276e4aebf41aac1c76bee3 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Fri, 20 Apr 2018 21:19:38 +0800 Subject: [PATCH 3/8] Do not destroy lock early --- pipenv/core.py | 2 -- pipenv/project.py | 8 -------- 2 files changed, 10 deletions(-) diff --git a/pipenv/core.py b/pipenv/core.py index 9e761ff7..d8948b4c 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -1000,8 +1000,6 @@ def do_lock( ) sys.exit(1) cached_lockfile = project.lockfile_content - if write: - project.destroy_lockfile() if write: # Alert the user of progress. click.echo( diff --git a/pipenv/project.py b/pipenv/project.py index 70e05006..ade9cd87 100644 --- a/pipenv/project.py +++ b/pipenv/project.py @@ -668,14 +668,6 @@ class Project(object): return found_source raise SourceNotFound(name or url) - def destroy_lockfile(self): - """Deletes the lockfile.""" - try: - return os.remove(self.lockfile_location) - - except OSError: - pass - def get_package_name_in_pipfile(self, package_name, dev=False): """Get the equivalent package name in pipfile""" key = 'dev-packages' if dev else 'packages' From 33945de1eb597b0ffcd99d9d9f6d4d3a0d5e6404 Mon Sep 17 00:00:00 2001 From: Gasper Zejn Date: Fri, 20 Apr 2018 17:22:29 +0200 Subject: [PATCH 4/8] Obey both color-blind and hide-emoji flags. --- pipenv/progress.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/pipenv/progress.py b/pipenv/progress.py index 14bb721f..1a0593b1 100644 --- a/pipenv/progress.py +++ b/pipenv/progress.py @@ -13,17 +13,31 @@ import os import sys import time import crayons -from .environments import PIPENV_COLORBLIND +from .environments import (PIPENV_COLORBLIND, PIPENV_HIDE_EMOJIS) STREAM = sys.stderr MILL_TEMPLATE = '%s %s %i/%i\r' DOTS_CHAR = '.' -if os.name != 'nt' and not PIPENV_COLORBLIND: - BAR_FILLED_CHAR = str(crayons.green('▉', bold=True)) - BAR_EMPTY_CHAR = str(crayons.black('▉')) +if os.name != 'nt': + if PIPENV_HIDE_EMOJIS: + if PIPENV_COLORBLIND: + BAR_FILLED_CHAR = '=' + BAR_EMPTY_CHAR = '-' + else: + BAR_FILLED_CHAR = str(crayons.green('=', bold=True)) + BAR_EMPTY_CHAR = str(crayons.black('-')) + else: + if PIPENV_COLORBLIND: + BAR_FILLED_CHAR = str(crayons.white('▉', bold=True)) + BAR_EMPTY_CHAR = str(crayons.black('▉')) + else: + BAR_FILLED_CHAR = str(crayons.green('▉', bold=True)) + BAR_EMPTY_CHAR = str(crayons.black('▉')) + else: BAR_FILLED_CHAR = '=' BAR_EMPTY_CHAR = '-' + if (sys.version_info[0] >= 3) and (os.name != 'nt'): BAR_TEMPLATE = u' %s%s%s %i/%i — {0}\r'.format(crayons.black('%s')) else: From 7423d235c674a73c8dbb77be86af04d13273cd6f Mon Sep 17 00:00:00 2001 From: Gasper Zejn Date: Fri, 20 Apr 2018 20:29:21 +0200 Subject: [PATCH 5/8] Fix: don't use crayons for colorblind. --- pipenv/progress.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pipenv/progress.py b/pipenv/progress.py index 1a0593b1..256bec9d 100644 --- a/pipenv/progress.py +++ b/pipenv/progress.py @@ -28,8 +28,8 @@ if os.name != 'nt': BAR_EMPTY_CHAR = str(crayons.black('-')) else: if PIPENV_COLORBLIND: - BAR_FILLED_CHAR = str(crayons.white('▉', bold=True)) - BAR_EMPTY_CHAR = str(crayons.black('▉')) + BAR_FILLED_CHAR = '▉' + BAR_EMPTY_CHAR = ' ' else: BAR_FILLED_CHAR = str(crayons.green('▉', bold=True)) BAR_EMPTY_CHAR = str(crayons.black('▉')) From bd057000b2ce4a1af8466f6f9745bf3ad9d49249 Mon Sep 17 00:00:00 2001 From: Tzu-ping Chung Date: Fri, 27 Apr 2018 15:13:23 +0800 Subject: [PATCH 6/8] Implement utility to write to lockfile atomically --- pipenv/core.py | 3 ++- pipenv/utils.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/pipenv/core.py b/pipenv/core.py index 423176e1..f6bebbcb 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -25,6 +25,7 @@ import six from .cmdparse import ScriptEmptyError from .project import Project, SourceNotFound from .utils import ( + atomic_open_for_write, convert_deps_from_pip, convert_deps_to_pip, is_required_version, @@ -1165,7 +1166,7 @@ def do_lock( ] if write: # Write out the lockfile. - with open(project.lockfile_location, 'w') as f: + with atomic_open_for_write(project.lockfile_location) as f: simplejson.dump( lockfile, f, indent=4, separators=(',', ': '), sort_keys=True ) diff --git a/pipenv/utils.py b/pipenv/utils.py index 10e3ae75..4e7fc028 100644 --- a/pipenv/utils.py +++ b/pipenv/utils.py @@ -1323,3 +1323,41 @@ def split_argument(req, short=None, long_=None): index, more_req = remaining_line[0], ' '.join(remaining_line[1:]) req = '{0} {1}'.format(req, more_req) return req, index + + +@contextmanager +def atomic_open_for_write(target, binary=False): + """Atomically open `target` for writing. + + This is based on Lektor's `atomic_open()` utility, but simplified a lot + to handle only writing, and skip many multi-process/thread edge cases + handled by Werkzeug. + + How this works: + + * Create a temp file (in the same directory of the actual target), and + yield for surrounding code to write to it. + * If some thing goes wrong, try to remove the temp file. The actual target + is not touched whatsoever. + * If everything goes well, close the temp file, and replace the actual + target with this new file. + """ + fd, tmp = tempfile.mkstemp( + dir=os.path.dirname(target), + prefix='.__atomic-write', + ) + os.chmod(tmp, 0o644) + f = os.fdopen(fd, 'wb' if binary else 'w') + try: + yield f + except BaseException: + f.close() + try: + os.remove(tmp) + except OSError: + pass + raise + else: + f.close() + os.remove(target) # This is needed on Windows. + os.rename(tmp, target) # No os.replace() on Python 2. From 0944eb56d84ee1060a8e8b8292f36839d761c66c Mon Sep 17 00:00:00 2001 From: Tzu-ping Chung Date: Fri, 27 Apr 2018 15:31:46 +0800 Subject: [PATCH 7/8] Ignore errors when removing old file --- pipenv/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pipenv/utils.py b/pipenv/utils.py index 4e7fc028..2c815b6f 100644 --- a/pipenv/utils.py +++ b/pipenv/utils.py @@ -1359,5 +1359,8 @@ def atomic_open_for_write(target, binary=False): raise else: f.close() - os.remove(target) # This is needed on Windows. + try: + os.remove(target) # This is needed on Windows. + except OSError: + pass os.rename(tmp, target) # No os.replace() on Python 2. From 1a196c1c9fa71ea459c4de1a44d1b02d8577e838 Mon Sep 17 00:00:00 2001 From: Grey Baker Date: Fri, 27 Apr 2018 22:17:34 +0100 Subject: [PATCH 8/8] Allow pyenv installs in non-interactive sessions in PIPENV_YES is set --- pipenv/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pipenv/core.py b/pipenv/core.py index 8965668d..13898f22 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -461,7 +461,7 @@ def ensure_python(three=None, python=None): if not PYENV_INSTALLED: abort() else: - if (not PIPENV_DONT_USE_PYENV) and (SESSION_IS_INTERACTIVE): + if (not PIPENV_DONT_USE_PYENV) and (SESSION_IS_INTERACTIVE or PIPENV_YES): version_map = { # TODO: Keep this up to date! # These versions appear incompatible with pew: