Merge pull request #2077 from pypa/atomic-lockfile-overwrite

Atomic lockfile overwrite
This commit is contained in:
Dan Ryan
2018-04-29 15:44:19 -04:00
committed by GitHub
3 changed files with 43 additions and 11 deletions
+2 -3
View File
@@ -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,
@@ -1005,8 +1006,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(
@@ -1167,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
)
-8
View File
@@ -673,14 +673,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'
+41
View File
@@ -1323,3 +1323,44 @@ 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()
try:
os.remove(target) # This is needed on Windows.
except OSError:
pass
os.rename(tmp, target) # No os.replace() on Python 2.