Skip to content

Commit

Permalink
Fixes to get pex to work on windows.
Browse files Browse the repository at this point in the history
The fixes are:
 - os.link doesn't exist on windows. Always copy instead.
 - NamedTemporaryFile doesn't work correctly on windows (see https://bugs.python.org/issue14243)
 - sys.prefix is part of site.getsitepackages() on windows. Don't remove it.
  • Loading branch information
mikekap committed Jan 12, 2016
1 parent 8283de1 commit 43a106a
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 23 deletions.
39 changes: 18 additions & 21 deletions pex/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,24 @@ def do_copy():
shutil.copyfile(source, temp_dest)
os.rename(temp_dest, dest)

try:
os.link(source, dest)
except OSError as e:
if e.errno == errno.EEXIST:
# File already exists. If overwrite=True, write otherwise skip.
if overwrite:
if hasattr(os, 'link'):
try:
os.link(source, dest)
except OSError as e:
if e.errno == errno.EEXIST:
# File already exists. If overwrite=True, write otherwise skip.
if overwrite:
do_copy()
elif e.errno == errno.EXDEV:
# Hard link across devices, fall back on copying
do_copy()
elif e.errno == errno.EXDEV:
# Hard link across devices, fall back on copying
else:
raise
elif os.path.exists(dest):
if overwrite:
do_copy()
else:
raise
else:
do_copy()


# See http://stackoverflow.com/questions/2572172/referencing-other-modules-in-atexit
Expand Down Expand Up @@ -264,17 +270,8 @@ def link(self, src, dst, label=None):
self._ensure_parent(dst)
abs_src = src
abs_dst = os.path.join(self.chroot, dst)
try:
os.link(abs_src, abs_dst)
except OSError as e:
if e.errno == errno.EEXIST:
# File already exists, skip XXX -- ensure target and dest are same?
pass
elif e.errno == errno.EXDEV:
# Hard link across devices, fall back on copying
shutil.copyfile(abs_src, abs_dst)
else:
raise
safe_copy(abs_src, abs_dst, overwrite=False)
# If the file already exists, skip XXX -- ensure target and dest are same?

def write(self, data, dst, label=None, mode='wb'):
"""Write data to ``chroot/dst`` with optional label.
Expand Down
4 changes: 2 additions & 2 deletions pex/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
from __future__ import absolute_import

import subprocess
import tempfile

from .compatibility import to_bytes
from .util import named_temporary_file


_COMPILER_MAIN = """
Expand Down Expand Up @@ -78,7 +78,7 @@ def compile(self, root, relpaths):
:returns: A list of relative paths of the compiled bytecode files.
:raises: A :class:`Compiler.Error` if there was a problem bytecode compiling any of the files.
"""
with tempfile.NamedTemporaryFile() as fp:
with named_temporary_file() as fp:
fp.write(to_bytes(_COMPILER_MAIN % {'root': root, 'relpaths': relpaths}, encoding='utf-8'))
fp.flush()
process = subprocess.Popen([self._interpreter.binary, fp.name],
Expand Down
3 changes: 3 additions & 0 deletions pex/pex.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ def _site_libs(cls):
site_libs = set()
site_libs.update([sysconfig.get_python_lib(plat_specific=False),
sysconfig.get_python_lib(plat_specific=True)])
# On windows getsitepackages() returns the python stdlib too.
if sys.prefix in site_libs:
site_libs.remove(sys.prefix)
real_site_libs = set(os.path.realpath(path) for path in site_libs)
return site_libs | real_site_libs

Expand Down
15 changes: 15 additions & 0 deletions pex/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import errno
import os
import shutil
import tempfile
import uuid
from hashlib import sha1
from threading import Lock
Expand Down Expand Up @@ -200,3 +201,17 @@ def get(self, key, default=None):
def store(self, key, value):
with self._lock:
self._data[key] = value


@contextlib.contextmanager
def named_temporary_file():
"""
Due to a bug in python (https://bugs.python.org/issue14243), we need
this to be able to use the temporary file without deleting it.
"""
fp = tempfile.NamedTemporaryFile(delete=False)
try:
with fp:
yield fp
finally:
os.remove(fp.name)

0 comments on commit 43a106a

Please sign in to comment.