summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormalthe <devnull@localhost>2014-07-08 10:48:29 +0200
committermalthe <devnull@localhost>2014-07-08 10:48:29 +0200
commit1c580ae5ae23e64bfffd7597a8336d26017765cb (patch)
tree72197e0fbf30449fb11eeb8a71b23fd8dc830fd0
parent159e2abd981ea16b3594a476a44f25358a9a96d9 (diff)
downloadwheel-use-temporary-file-and-rename-with-force.tar.gz
Write to temporary file and then rename when using --force.use-temporary-file-and-rename-with-force
-rw-r--r--CHANGES.txt6
-rw-r--r--wheel/install.py37
-rw-r--r--wheel/test/test_install.py21
3 files changed, 41 insertions, 23 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 26754c9..e6beb32 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,9 @@
+In next release ...
+
+- When force is given, write to temporary file and rename. This is
+ necessary because when a file exists, it may already be in use by a
+ running process.
+
0.24.0
======
- The python tag used for pure-python packages is now .pyN (major version
diff --git a/wheel/install.py b/wheel/install.py
index 3af6d0c..768fec4 100644
--- a/wheel/install.py
+++ b/wheel/install.py
@@ -335,25 +335,36 @@ class WheelFile(object):
record_name = self.distinfo_name + '/RECORD'
for info, (key, target, filename, dest) in name_trans.items():
name = info.filename
- source = self.zipfile.open(info)
- # Skip the RECORD file
- if name == record_name:
- continue
ddir = os.path.dirname(dest)
if not os.path.isdir(ddir):
os.makedirs(ddir)
- destination = HashingFile(open(dest, 'wb'))
- if key == 'scripts':
- hashbang = source.readline()
- if hashbang.startswith(b'#!python'):
- hashbang = b'#!' + exename + binary(os.linesep)
- destination.write(hashbang)
- shutil.copyfileobj(source, destination)
+
+ if force:
+ dest = "{0}.tmp".format(dest)
+
+ with self.zipfile.open(info) as source, open(dest, 'wb') as f:
+ destination = HashingFile(f)
+
+ # Skip the RECORD file
+ if name == record_name:
+ continue
+
+ if key == 'scripts':
+ hashbang = source.readline()
+ if hashbang.startswith(b'#!python'):
+ hashbang = b'#!' + exename + binary(os.linesep)
+ destination.write(hashbang)
+ shutil.copyfileobj(source, destination)
+
+ if force:
+ tmp = dest
+ dest = dest[:-4]
+ os.rename(tmp, dest)
+
reldest = os.path.relpath(dest, root)
reldest.replace(os.sep, '/')
record_data.append((reldest, destination.digest(), destination.length))
- destination.close()
- source.close()
+
# preserve attributes (especially +x bit for scripts)
attrs = info.external_attr >> 16
if attrs: # tends to be 0 if Windows.
diff --git a/wheel/test/test_install.py b/wheel/test/test_install.py
index ddcddf5..ff76c18 100644
--- a/wheel/test/test_install.py
+++ b/wheel/test/test_install.py
@@ -36,15 +36,16 @@ def test_install():
for key in ('purelib', 'platlib', 'scripts', 'headers', 'data'):
locs[key] = os.path.join(tempdir, key)
os.mkdir(locs[key])
- whl.install(overrides=locs)
- assert len(os.listdir(locs['purelib'])) == 0
- assert check(locs['platlib'], 'hello.pyd')
- assert check(locs['platlib'], 'hello', 'hello.py')
- assert check(locs['platlib'], 'hello', '__init__.py')
- assert check(locs['data'], 'hello.dat')
- assert check(locs['headers'], 'hello.dat')
- assert check(locs['scripts'], 'hello.sh')
- assert check(locs['platlib'], 'test-1.0.dist-info', 'RECORD')
+ for force in (False, True):
+ whl.install(force=force, overrides=locs)
+ assert len(os.listdir(locs['purelib'])) == 0
+ assert check(locs['platlib'], 'hello.pyd')
+ assert check(locs['platlib'], 'hello', 'hello.py')
+ assert check(locs['platlib'], 'hello', '__init__.py')
+ assert check(locs['data'], 'hello.dat')
+ assert check(locs['headers'], 'hello.dat')
+ assert check(locs['scripts'], 'hello.sh')
+ assert check(locs['platlib'], 'test-1.0.dist-info', 'RECORD')
finally:
shutil.rmtree(tempdir)
@@ -52,4 +53,4 @@ def test_install_tool():
"""Slightly improve coverage of wheel.install"""
wheel.tool.install([TESTWHEEL], force=True, dry_run=True)
- \ No newline at end of file
+