[Python-modules-commits] [python-pex] 04/10: Import python-pex_1.1.4.orig.tar.gz

Barry Warsaw barry at moszumanska.debian.org
Tue Mar 22 17:19:18 UTC 2016


This is an automated email from the git hooks/post-receive script.

barry pushed a commit to branch master
in repository python-pex.

commit 29506132c8cc219ccd6293ea037df31210fab499
Author: Barry Warsaw <barry at python.org>
Date:   Tue Mar 22 13:09:44 2016 -0400

    Import python-pex_1.1.4.orig.tar.gz
---
 CHANGES.rst                                        |  31 ++++++++++++++
 PKG-INFO                                           |  33 ++++++++++++++-
 pex.egg-info/PKG-INFO                              |  33 ++++++++++++++-
 pex.egg-info/SOURCES.txt                           |   3 +-
 pex/commands/bdist_pex.py                          |   2 +-
 pex/common.py                                      |  41 +++++++++---------
 pex/compatibility.py                               |  13 ++++++
 pex/compiler.py                                    |   4 +-
 pex/crawler.py                                     |   6 +--
 pex/http.py                                        |   2 +-
 pex/installer.py                                   |   6 ++-
 pex/link.py                                        |  21 ++++++---
 pex/package.py                                     |   4 +-
 pex/pep425.py                                      |  10 +++--
 pex/pex.py                                         |  15 +++++--
 pex/resolver.py                                    |   6 +--
 pex/testing.py                                     |  20 +++++++--
 pex/translator.py                                  |   4 +-
 pex/util.py                                        |  23 +++++++++-
 pex/version.py                                     |   2 +-
 tests/example_packages/setuptools-18.0.1-py2.7.egg | Bin 0 -> 356249 bytes
 tests/test_environment.py                          |  37 ++++++++--------
 tests/test_http.py                                 |   6 +--
 tests/test_integration.py                          |   8 +++-
 tests/test_link.py                                 |  12 +++++-
 tests/test_package.py                              |   9 ++++
 tests/test_pep425.py                               |  14 +++++-
 tests/test_pex.py                                  |  47 ++++++++++++++++++++-
 tests/test_pex_builder.py                          |   4 +-
 tests/test_pex_info.py                             |   7 ++-
 tests/test_util.py                                 |  45 +++++++++++++-------
 tests/test_variables.py                            |   9 ++--
 tox.ini                                            |   2 +-
 33 files changed, 371 insertions(+), 108 deletions(-)

diff --git a/CHANGES.rst b/CHANGES.rst
index fa242f8..b6af105 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -3,6 +3,37 @@ CHANGES
 =======
 
 -----
+1.1.4
+-----
+
+This release is a quick fix for a regression that inadvertently went out in 1.1.3 (Issue #216).
+
+* Add a test for the regression in `FixedEggMetadata._zipinfo_name` and revert the breaking commit.
+  `Fixes #216 <https://github.com/pantsbuild/pex/issues/216>`_
+
+-----
+1.1.3
+-----
+
+This release includes an initial body of work towards Windows support, ABI tag support for CPython 2.x and a fix for version number normalization.
+
+* Add python 2.x abi tag support.
+  `#214 <https://github.com/pantsbuild/pex/pull/214>`_
+  `Fixes #213 <https://github.com/pantsbuild/pex/issues/213>`_
+
+* Add .idea to .gitignore.
+  `#205 <https://github.com/pantsbuild/pex/pull/205>`_
+
+* Don't normalize version numbers as names.
+  `#204 <https://github.com/pantsbuild/pex/pull/204>`_
+
+* More fixes for windows.
+  `#202 <https://github.com/pantsbuild/pex/pull/202>`_
+
+* Fixes to get pex to work on windows.
+  `#198 <https://github.com/pantsbuild/pex/pull/198>`_
+
+-----
 1.1.2
 -----
 
diff --git a/PKG-INFO b/PKG-INFO
index c236fe2..49867c0 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pex
-Version: 1.1.2
+Version: 1.1.4
 Summary: The PEX packaging toolchain.
 Home-page: https://github.com/pantsbuild/pex
 Author: UNKNOWN
@@ -11,6 +11,37 @@ Description: =======
         =======
         
         -----
+        1.1.4
+        -----
+        
+        This release is a quick fix for a regression that inadvertently went out in 1.1.3 (Issue #216).
+        
+        * Add a test for the regression in `FixedEggMetadata._zipinfo_name` and revert the breaking commit.
+          `Fixes #216 <https://github.com/pantsbuild/pex/issues/216>`_
+        
+        -----
+        1.1.3
+        -----
+        
+        This release includes an initial body of work towards Windows support, ABI tag support for CPython 2.x and a fix for version number normalization.
+        
+        * Add python 2.x abi tag support.
+          `#214 <https://github.com/pantsbuild/pex/pull/214>`_
+          `Fixes #213 <https://github.com/pantsbuild/pex/issues/213>`_
+        
+        * Add .idea to .gitignore.
+          `#205 <https://github.com/pantsbuild/pex/pull/205>`_
+        
+        * Don't normalize version numbers as names.
+          `#204 <https://github.com/pantsbuild/pex/pull/204>`_
+        
+        * More fixes for windows.
+          `#202 <https://github.com/pantsbuild/pex/pull/202>`_
+        
+        * Fixes to get pex to work on windows.
+          `#198 <https://github.com/pantsbuild/pex/pull/198>`_
+        
+        -----
         1.1.2
         -----
         
diff --git a/pex.egg-info/PKG-INFO b/pex.egg-info/PKG-INFO
index c236fe2..49867c0 100644
--- a/pex.egg-info/PKG-INFO
+++ b/pex.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pex
-Version: 1.1.2
+Version: 1.1.4
 Summary: The PEX packaging toolchain.
 Home-page: https://github.com/pantsbuild/pex
 Author: UNKNOWN
@@ -11,6 +11,37 @@ Description: =======
         =======
         
         -----
+        1.1.4
+        -----
+        
+        This release is a quick fix for a regression that inadvertently went out in 1.1.3 (Issue #216).
+        
+        * Add a test for the regression in `FixedEggMetadata._zipinfo_name` and revert the breaking commit.
+          `Fixes #216 <https://github.com/pantsbuild/pex/issues/216>`_
+        
+        -----
+        1.1.3
+        -----
+        
+        This release includes an initial body of work towards Windows support, ABI tag support for CPython 2.x and a fix for version number normalization.
+        
+        * Add python 2.x abi tag support.
+          `#214 <https://github.com/pantsbuild/pex/pull/214>`_
+          `Fixes #213 <https://github.com/pantsbuild/pex/issues/213>`_
+        
+        * Add .idea to .gitignore.
+          `#205 <https://github.com/pantsbuild/pex/pull/205>`_
+        
+        * Don't normalize version numbers as names.
+          `#204 <https://github.com/pantsbuild/pex/pull/204>`_
+        
+        * More fixes for windows.
+          `#202 <https://github.com/pantsbuild/pex/pull/202>`_
+        
+        * Fixes to get pex to work on windows.
+          `#198 <https://github.com/pantsbuild/pex/pull/198>`_
+        
+        -----
         1.1.2
         -----
         
diff --git a/pex.egg-info/SOURCES.txt b/pex.egg-info/SOURCES.txt
index 60c4df3..6f48eac 100644
--- a/pex.egg-info/SOURCES.txt
+++ b/pex.egg-info/SOURCES.txt
@@ -86,4 +86,5 @@ tests/test_resolver.py
 tests/test_sorter.py
 tests/test_translator.py
 tests/test_util.py
-tests/test_variables.py
\ No newline at end of file
+tests/test_variables.py
+tests/example_packages/setuptools-18.0.1-py2.7.egg
\ No newline at end of file
diff --git a/pex/commands/bdist_pex.py b/pex/commands/bdist_pex.py
index 4f7950f..7a261f2 100644
--- a/pex/commands/bdist_pex.py
+++ b/pex/commands/bdist_pex.py
@@ -68,6 +68,7 @@ class bdist_pex(Command):  # noqa
     except ValueError:
       console_scripts = {}
 
+    target = os.path.join(self.bdist_dir, name + '-' + version + '.pex')
     if self.bdist_all:
       # Write all entry points into unversioned pex files.
       for script_name in console_scripts:
@@ -76,7 +77,6 @@ class bdist_pex(Command):  # noqa
         self._write(pex_builder, target, script=script_name)
     elif name in console_scripts:
       # The package has a namesake entry point, so use it.
-      target = os.path.join(self.bdist_dir, name + '-' + version + '.pex')
       log.info('Writing %s to %s' % (name, target))
       self._write(pex_builder, target, script=name)
     else:
diff --git a/pex/common.py b/pex/common.py
index 89f1941..acdad91 100644
--- a/pex/common.py
+++ b/pex/common.py
@@ -28,18 +28,26 @@ def safe_copy(source, dest, overwrite=False):
     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 the platform supports hard-linking, use that and fall back to copying.
+  # Windows does not support hard-linking.
+  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
@@ -264,17 +272,8 @@ class Chroot(object):
     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)
+    # TODO: Ensure the target and dest are the same if the file already exists.
 
   def write(self, data, dst, label=None, mode='wb'):
     """Write data to ``chroot/dst`` with optional label.
diff --git a/pex/compatibility.py b/pex/compatibility.py
index 1a86d5f..97139e1 100644
--- a/pex/compatibility.py
+++ b/pex/compatibility.py
@@ -4,6 +4,7 @@
 # This file contains several 2.x/3.x compatibility checkstyle violations for a reason
 # checkstyle: noqa
 
+import os
 from abc import ABCMeta
 from numbers import Integral, Real
 from sys import version_info as sys_version_info
@@ -81,15 +82,27 @@ else:
   from contextlib import nested
 
 
+if PY3:
+  from urllib.request import pathname2url, url2pathname
+else:
+  from urllib import pathname2url, url2pathname
+
+
+WINDOWS = os.name == 'nt'
+
+
 __all__ = (
   'AbstractClass',
   'BytesIO',
   'PY2',
   'PY3',
   'StringIO',
+  'WINDOWS',
   'bytes',
   'exec_function',
   'nested',
+  'pathname2url',
   'string',
   'to_bytes',
+  'url2pathname',
 )
diff --git a/pex/compiler.py b/pex/compiler.py
index 6346d0f..d301de8 100644
--- a/pex/compiler.py
+++ b/pex/compiler.py
@@ -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 = """
@@ -78,7 +78,7 @@ class Compiler(object):
     :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],
diff --git a/pex/crawler.py b/pex/crawler.py
index 00b70fb..ee89d7f 100644
--- a/pex/crawler.py
+++ b/pex/crawler.py
@@ -85,11 +85,11 @@ class Crawler(object):
   @classmethod
   def crawl_local(cls, link):
     try:
-      dirents = os.listdir(link.path)
+      dirents = os.listdir(link.local_path)
     except OSError as e:
-      TRACER.log('Failed to read %s: %s' % (link.path, e), V=1)
+      TRACER.log('Failed to read %s: %s' % (link.local_path, e), V=1)
       return set(), set()
-    files, dirs = partition([os.path.join(link.path, fn) for fn in dirents], os.path.isdir)
+    files, dirs = partition([os.path.join(link.local_path, fn) for fn in dirents], os.path.isdir)
     return set(map(Link.from_filename, files)), set(map(Link.from_filename, dirs))
 
   @classmethod
diff --git a/pex/http.py b/pex/http.py
index 1166615..5b91a5c 100644
--- a/pex/http.py
+++ b/pex/http.py
@@ -212,7 +212,7 @@ class RequestsContext(Context):
   def open(self, link):
     # requests does not support file:// -- so we must short-circuit manually
     if link.local:
-      return open(link.path, 'rb')  # noqa: T802
+      return open(link.local_path, 'rb')  # noqa: T802
     for attempt in range(self._max_retries + 1):
       try:
         return StreamFilelike(self._session.get(
diff --git a/pex/installer.py b/pex/installer.py
index acb0bf0..e2e955a 100644
--- a/pex/installer.py
+++ b/pex/installer.py
@@ -11,6 +11,7 @@ import tempfile
 from pkg_resources import Distribution, PathMetadata
 
 from .common import safe_mkdtemp, safe_rmtree
+from .compatibility import WINDOWS
 from .interpreter import PythonInterpreter
 from .tracer import TRACER
 from .version import SETUPTOOLS_REQUIREMENT, WHEEL_REQUIREMENT
@@ -209,7 +210,10 @@ class Packager(DistributionPackager):
   """
 
   def _setup_command(self):
-    return ['sdist', '--formats=gztar', '--dist-dir=%s' % self._install_tmp]
+    if WINDOWS:
+      return ['sdist', '--formats=zip', '--dist-dir=%s' % self._install_tmp]
+    else:
+      return ['sdist', '--formats=gztar', '--dist-dir=%s' % self._install_tmp]
 
   @after_installation
   def sdist(self):
diff --git a/pex/link.py b/pex/link.py
index 2146a7f..c8f8664 100644
--- a/pex/link.py
+++ b/pex/link.py
@@ -8,7 +8,7 @@ import posixpath
 from collections import Iterable
 
 from .compatibility import string as compatible_string
-from .compatibility import PY3
+from .compatibility import PY3, WINDOWS, pathname2url, url2pathname
 from .util import Memoizer
 
 if PY3:
@@ -52,7 +52,8 @@ class Link(object):
 
   @classmethod
   def _normalize(cls, filename):
-    return 'file://' + os.path.realpath(os.path.expanduser(filename))
+    return urlparse.urljoin('file:', pathname2url(
+        os.path.realpath(os.path.expanduser(filename))))
 
   # A cache for the result of from_filename
   _FROM_FILENAME_CACHE = Memoizer()
@@ -72,8 +73,10 @@ class Link(object):
     :param url: A string-like object representing a url.
     """
     purl = urlparse.urlparse(url)
-    if purl.scheme == '':
+    if purl.scheme == '' or (
+        WINDOWS and len(purl.scheme) == 1):  # This is likely a drive letter.
       purl = urlparse.urlparse(self._normalize(url))
+
     self._url = purl
 
   def __ne__(self, other):
@@ -95,12 +98,18 @@ class Link(object):
   @property
   def filename(self):
     """The basename of this url."""
-    return posixpath.basename(self._url.path)
+    return urlparse.unquote(posixpath.basename(self._url.path))
 
   @property
   def path(self):
     """The full path of this url with any hostname and scheme components removed."""
-    return self._url.path
+    return urlparse.unquote(self._url.path)
+
+  @property
+  def local_path(self):
+    """Returns the local filesystem path (only works for file:// urls)."""
+    assert self.local, 'local_path called on a non-file:// url %s' % (self.url,)
+    return url2pathname(self.path)
 
   @property
   def url(self):
@@ -110,7 +119,7 @@ class Link(object):
   @property
   def fragment(self):
     """The url fragment following '#' if any."""
-    return self._url.fragment
+    return urlparse.unquote(self._url.fragment)
 
   @property
   def scheme(self):
diff --git a/pex/package.py b/pex/package.py
index f50f2c7..a432aed 100644
--- a/pex/package.py
+++ b/pex/package.py
@@ -133,7 +133,7 @@ class SourcePackage(Package):
 
   @property
   def raw_version(self):
-    return safe_name(self._raw_version)
+    return self._raw_version
 
   # SourcePackages are always compatible as they can be translated to a distribution.
   def compatible(self, identity, platform=Platform.current()):
@@ -167,7 +167,7 @@ class EggPackage(Package):
 
   @property
   def raw_version(self):
-    return safe_name(self._raw_version)
+    return self._raw_version
 
   @property
   def py_version(self):
diff --git a/pex/pep425.py b/pex/pep425.py
index 6689f6e..a859b13 100644
--- a/pex/pep425.py
+++ b/pex/pep425.py
@@ -99,15 +99,19 @@ class PEP425(object):  # noqa
     """
     # Predict soabi for reasonable interpreters.  This is technically wrong but essentially right.
     abis = []
-    if impl == 'cp' and version.startswith('3'):
+    if impl == 'cp' and (version.startswith('2') or version.startswith('3')):
       abis.extend([
         'cp%s' % version,
         'cp%sdmu' % version, 'cp%sdm' % version, 'cp%sdu' % version, 'cp%sd' % version,
         'cp%smu' % version, 'cp%sm' % version,
-        'cp%su' % version,
-        'abi3'
+        'cp%su' % version
       ])
 
+      if version.startswith('3'):
+        abis.extend([
+          'abi3'
+        ])
+
     major_version = int(version[0])
     minor_versions = []
     for minor in range(int(version[1]), -1, -1):
diff --git a/pex/pex.py b/pex/pex.py
index 111ef0f..d338769 100644
--- a/pex/pex.py
+++ b/pex/pex.py
@@ -96,14 +96,21 @@ class PEX(object):  # noqa: T000
       yield os.path.join(standard_lib, path)
 
   @classmethod
-  def _site_libs(cls):
+  def _get_site_packages(cls):
     try:
       from site import getsitepackages
-      site_libs = set(getsitepackages())
+      return set(getsitepackages())
     except ImportError:
-      site_libs = set()
+      return set()
+
+  @classmethod
+  def site_libs(cls):
+    site_libs = cls._get_site_packages()
     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
 
@@ -190,7 +197,7 @@ class PEX(object):  # noqa: T000
     :returns: (sys.path, sys.path_importer_cache, sys.modules) tuple of a
       bare python installation.
     """
-    site_libs = set(cls._site_libs())
+    site_libs = set(cls.site_libs())
     for site_lib in site_libs:
       TRACER.log('Found site-library: %s' % site_lib)
     for extras_path in cls._extras_paths():
diff --git a/pex/resolver.py b/pex/resolver.py
index b65fcc1..e7669c4 100644
--- a/pex/resolver.py
+++ b/pex/resolver.py
@@ -162,7 +162,7 @@ class Resolver(object):
       local_package = Package.from_href(context.fetch(package))
     if local_package is None:
       raise Untranslateable('Could not fetch package %s' % package)
-    with TRACER.timed('Translating %s into distribution' % local_package.path, V=2):
+    with TRACER.timed('Translating %s into distribution' % local_package.local_path, V=2):
       dist = translator.translate(local_package)
     if dist is None:
       raise Untranslateable('Package %s is not translateable by %s' % (package, translator))
@@ -221,7 +221,7 @@ class CachingResolver(Resolver):
   def filter_packages_by_ttl(cls, packages, ttl, now=None):
     now = now if now is not None else time.time()
     return [package for package in packages
-        if package.remote or package.local and (now - os.path.getmtime(package.path)) < ttl]
+        if package.remote or package.local and (now - os.path.getmtime(package.local_path)) < ttl]
 
   def __init__(self, cache, cache_ttl, *args, **kw):
     self.__cache = cache
@@ -251,7 +251,7 @@ class CachingResolver(Resolver):
     # cache package locally
     if package.remote:
       package = Package.from_href(options.get_context().fetch(package, into=self.__cache))
-      os.utime(package.path, None)
+      os.utime(package.local_path, None)
 
     # build into distribution
     dist = super(CachingResolver, self).build(package, options)
diff --git a/pex/testing.py b/pex/testing.py
index cded62c..2726486 100644
--- a/pex/testing.py
+++ b/pex/testing.py
@@ -5,6 +5,7 @@ import contextlib
 import os
 import random
 import subprocess
+import sys
 import tempfile
 import zipfile
 from textwrap import dedent
@@ -13,7 +14,7 @@ from .common import safe_mkdir, safe_rmtree
 from .compatibility import nested
 from .installer import EggInstaller, Packager
 from .pex_builder import PEXBuilder
-from .util import DistributionHelper
+from .util import DistributionHelper, named_temporary_file
 
 
 @contextlib.contextmanager
@@ -25,6 +26,19 @@ def temporary_dir():
     safe_rmtree(td)
 
 
+ at contextlib.contextmanager
+def temporary_filename():
+  """Creates a temporary filename.
+
+  This is useful when you need to pass a filename to an API. Windows requires all
+  handles to a file be closed before deleting/renaming it, so this makes it a bit
+  simpler."""
+  with named_temporary_file() as fp:
+    fp.write(b'')
+    fp.close()
+    yield fp.name
+
+
 def random_bytes(length):
   return ''.join(
       map(chr, (random.randint(ord('a'), ord('z')) for _ in range(length)))).encode('utf-8')
@@ -162,12 +176,12 @@ def write_simple_pex(td, exe_contents, dists=None, coverage=False):
 # TODO(wickman) Why not PEX.run?
 def run_simple_pex(pex, args=(), env=None):
   po = subprocess.Popen(
-      [pex] + list(args),
+      [sys.executable, pex] + list(args),
       stdout=subprocess.PIPE,
       stderr=subprocess.STDOUT,
       env=env)
   po.wait()
-  return po.stdout.read(), po.returncode
+  return po.stdout.read().replace(b'\r', b''), po.returncode
 
 
 def run_simple_pex_test(body, args=(), env=None, dists=None, coverage=False):
diff --git a/pex/translator.py b/pex/translator.py
index 8800343..67bb84b 100644
--- a/pex/translator.py
+++ b/pex/translator.py
@@ -88,7 +88,7 @@ class SourceTranslator(TranslatorBase):
 
     installer = None
     version = self._interpreter.version
-    unpack_path = Archiver.unpack(package.path)
+    unpack_path = Archiver.unpack(package.local_path)
     into = into or safe_mkdtemp()
 
     try:
@@ -147,7 +147,7 @@ class BinaryTranslator(TranslatorBase):
       return None
     into = into or safe_mkdtemp()
     target_path = os.path.join(into, package.filename)
-    safe_copy(package.path, target_path)
+    safe_copy(package.local_path, target_path)
     return DistributionHelper.distribution_from_path(target_path)
 
 
diff --git a/pex/util.py b/pex/util.py
index d9dbe2e..00e4f63 100644
--- a/pex/util.py
+++ b/pex/util.py
@@ -7,6 +7,7 @@ import contextlib
 import errno
 import os
 import shutil
+import tempfile
 import uuid
 from hashlib import sha1
 from threading import Lock
@@ -118,7 +119,9 @@ class CacheHelper(object):
   @classmethod
   def _compute_hash(cls, names, stream_factory):
     digest = sha1()
-    digest.update(''.join(names).encode('utf-8'))
+    # Always use / as the path separator, since that's what zip uses.
+    hashed_names = [n.replace(os.sep, '/') for n in names]
+    digest.update(''.join(hashed_names).encode('utf-8'))
     for name in names:
       with contextlib.closing(stream_factory(name)) as fp:
         cls.update_hash(fp, digest)
@@ -136,7 +139,7 @@ class CacheHelper(object):
 
   @classmethod
   def _iter_files(cls, directory):
-    normpath = os.path.normpath(directory)
+    normpath = os.path.realpath(os.path.normpath(directory))
     for root, _, files in os.walk(normpath):
       for f in files:
         yield os.path.relpath(os.path.join(root, f), normpath)
@@ -200,3 +203,19 @@ class Memoizer(object):
   def store(self, key, value):
     with self._lock:
       self._data[key] = value
+
+
+ at contextlib.contextmanager
+def named_temporary_file(*args, **kwargs):
+  """
+  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.
+  """
+  assert 'delete' not in kwargs
+  kwargs['delete'] = False
+  fp = tempfile.NamedTemporaryFile(*args, **kwargs)
+  try:
+    with fp:
+      yield fp
+  finally:
+    os.remove(fp.name)
diff --git a/pex/version.py b/pex/version.py
index 51862fa..1366236 100644
--- a/pex/version.py
+++ b/pex/version.py
@@ -1,7 +1,7 @@
 # Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
 # Licensed under the Apache License, Version 2.0 (see LICENSE).
 
-__version__ = '1.1.2'
+__version__ = '1.1.4'
 
 SETUPTOOLS_REQUIREMENT = 'setuptools>=2.2,<20'
 WHEEL_REQUIREMENT = 'wheel>=0.24.0,<0.27.0'
diff --git a/tests/example_packages/setuptools-18.0.1-py2.7.egg b/tests/example_packages/setuptools-18.0.1-py2.7.egg
new file mode 100644
index 0000000..aba6783
Binary files /dev/null and b/tests/example_packages/setuptools-18.0.1-py2.7.egg differ
diff --git a/tests/test_environment.py b/tests/test_environment.py
index 0c3baaa..adf06e4 100644
--- a/tests/test_environment.py
+++ b/tests/test_environment.py
@@ -4,13 +4,13 @@
 import os
 from contextlib import contextmanager
 
-from twitter.common.contextutil import temporary_dir, temporary_file
+from twitter.common.contextutil import temporary_dir
 
 from pex.compatibility import nested
 from pex.environment import PEXEnvironment
 from pex.pex_builder import PEXBuilder
 from pex.pex_info import PexInfo
-from pex.testing import make_bdist
+from pex.testing import make_bdist, temporary_filename
 
 
 @contextmanager
@@ -22,64 +22,65 @@ def yield_pex_builder(zip_safe=True):
 
 
 def test_force_local():
-  with nested(yield_pex_builder(), temporary_dir(), temporary_file()) as (pb, pex_root, pex_file):
+  with nested(yield_pex_builder(), temporary_dir(), temporary_filename()) as (
+          pb, pex_root, pex_file):
     pb.info.pex_root = pex_root
-    pb.build(pex_file.name)
+    pb.build(pex_file)
 
-    code_cache = PEXEnvironment.force_local(pex_file.name, pb.info)
+    code_cache = PEXEnvironment.force_local(pex_file, pb.info)
     assert os.path.exists(pb.info.zip_unsafe_cache)
     assert len(os.listdir(pb.info.zip_unsafe_cache)) == 1
     assert [os.path.basename(code_cache)] == os.listdir(pb.info.zip_unsafe_cache)
     assert set(os.listdir(code_cache)) == set([PexInfo.PATH, '__main__.py', '__main__.pyc'])
 
     # idempotence
-    assert PEXEnvironment.force_local(pex_file.name, pb.info) == code_cache
+    assert PEXEnvironment.force_local(pex_file, pb.info) == code_cache
 
 
 def normalize(path):
-  return os.path.normpath(os.path.realpath(path))
+  return os.path.normpath(os.path.realpath(path)).lower()
 
 
 def test_write_zipped_internal_cache():
   # zip_safe pex will not be written to install cache unless always_write_cache
-  with nested(yield_pex_builder(zip_safe=True), temporary_dir(), temporary_file()) as (
+  with nested(yield_pex_builder(zip_safe=True), temporary_dir(), temporary_filename()) as (
       pb, pex_root, pex_file):
 
     pb.info.pex_root = pex_root
-    pb.build(pex_file.name)
+    pb.build(pex_file)
 
-    existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file.name, pb.info)
+    existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file, pb.info)
     assert len(zip_safe) == 1
     assert normalize(zip_safe[0].location).startswith(
-        normalize(os.path.join(pex_file.name, pb.info.internal_cache))), (
+        normalize(os.path.join(pex_file, pb.info.internal_cache))), (
             'loc: %s, cache: %s' % (
                 normalize(zip_safe[0].location),
-                normalize(os.path.join(pex_file.name, pb.info.internal_cache))))
+                normalize(os.path.join(pex_file, pb.info.internal_cache))))
 
     pb.info.always_write_cache = True
-    existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file.name, pb.info)
+    existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file, pb.info)
     assert len(new) == 1
     assert normalize(new[0].location).startswith(normalize(pb.info.install_cache))
 
     # Check that we can read from the cache
-    existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file.name, pb.info)
+    existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file, pb.info)
     assert len(existing) == 1
     assert normalize(existing[0].location).startswith(normalize(pb.info.install_cache))
 
   # non-zip_safe pex will be written to install cache
-  with nested(yield_pex_builder(zip_safe=False), temporary_dir(), temporary_file()) as (
+  with nested(yield_pex_builder(zip_safe=False), temporary_dir(), temporary_filename()) as (
       pb, pex_root, pex_file):
 
     pb.info.pex_root = pex_root
-    pb.build(pex_file.name)
+    pb.build(pex_file)
 
-    existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file.name, pb.info)
+    existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file, pb.info)
     assert len(new) == 1
     assert normalize(new[0].location).startswith(normalize(pb.info.install_cache))
     original_location = normalize(new[0].location)
 
     # do the second time to validate idempotence of caching
-    existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file.name, pb.info)
+    existing, new, zip_safe = PEXEnvironment.write_zipped_internal_cache(pex_file, pb.info)
     assert len(existing) == 1
     assert normalize(existing[0].location) == original_location
 
diff --git a/tests/test_http.py b/tests/test_http.py
index e5ab82b..b07f207 100644
--- a/tests/test_http.py
+++ b/tests/test_http.py
@@ -6,11 +6,11 @@ from contextlib import contextmanager
 from io import BytesIO
 
 import pytest
-from twitter.common.contextutil import temporary_file
 
 from pex.compatibility import PY2
 from pex.http import Context, RequestsContext, StreamFilelike, UrllibContext
 from pex.link import Link
+from pex.util import named_temporary_file
 from pex.variables import Variables
 
 try:
@@ -107,7 +107,7 @@ def test_requests_context():
       assert fp.read() == BLOB
 
   # test local reading
-  with temporary_file() as tf:
+  with named_temporary_file() as tf:
     tf.write(b'goop')
     tf.flush()
     assert context.read(Link.wrap(tf.name)) == b'goop'
@@ -225,7 +225,7 @@ def test_requests_context_retries_read_timeout_retries_exhausted():
 def test_urllib_context_utf8_encoding():
   BYTES = b'this is a decoded utf8 string'
 
-  with temporary_file() as tf:
+  with named_temporary_file() as tf:
     tf.write(BYTES)
     tf.flush()
     local_link = Link.wrap(tf.name)
diff --git a/tests/test_integration.py b/tests/test_integration.py
index f53b8f5..81386a7 100644
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -4,9 +4,12 @@
 import os
 import sys
 
-from twitter.common.contextutil import environment_as, temporary_dir, temporary_file
+import pytest
+from twitter.common.contextutil import environment_as, temporary_dir
 
+from pex.compatibility import WINDOWS
 from pex.testing import run_simple_pex_test
+from pex.util import named_temporary_file
 
 
 def test_pex_execute():
@@ -21,7 +24,7 @@ def test_pex_raise():
 
 
 def test_pex_interpreter():
-  with temporary_file() as fp:
+  with named_temporary_file() as fp:
     fp.write(b"print('Hello world')")
     fp.flush()
 
@@ -33,6 +36,7 @@ def test_pex_interpreter():
     assert rc == 0
 
 
+ at pytest.mark.skipif(WINDOWS, reason='No symlinks on windows')
 def test_pex_python_symlink():
   with temporary_dir() as td:
     with environment_as(HOME=td):
diff --git a/tests/test_link.py b/tests/test_link.py
index 586dada..e3bd981 100644
--- a/tests/test_link.py
+++ b/tests/test_link.py
@@ -51,7 +51,17 @@ def test_link_schemes():
   link = Link('/foo/bar')
   assert link.scheme == 'file'
   assert link.local
-  assert link.path == os.path.realpath('/foo/bar')
+  assert link.local_path == os.path.realpath('/foo/bar')
+
+
+def test_link_escaping():
+  link = Link('/foo/bar#baz.pex')
+  assert link.scheme == 'file'
+  assert link.local
+  assert link.local_path == os.path.realpath('/foo/bar#baz.pex')
+
+  link = Link('http://www.google.com/%20/%3Afile+%2B2.tar.gz')
+  assert link.filename == ':file++2.tar.gz'
 
 
 def test_link_equality():
diff --git a/tests/test_package.py b/tests/test_package.py
index d7748b7..87a7dbf 100644
--- a/tests/test_package.py
+++ b/tests/test_package.py
@@ -24,6 +24,15 @@ def test_source_packages():
   assert sl.raw_version == '1.5'
 
 
+def test_local_specifier():
+  for ext in ('.tar.gz', '.tar', '.tgz', '.zip', '.tar.bz2'):
+    sl = SourcePackage('a_p_r-3.1.3+pexed.1' + ext)
+    assert sl.name == 'a-p-r'
+    assert sl.raw_version == '3.1.3+pexed.1'
+    assert sl.version == parse_version(sl.raw_version)
+    assert sl.satisfies('a_p_r==3.1.3+pexed.1')
+
+
 def test_egg_packages():
   el = EggPackage('psutil-0.4.1-py2.6-macosx-10.7-intel.egg')
   assert el.name == 'psutil'
diff --git a/tests/test_pep425.py b/tests/test_pep425.py
index 87caedd..1b8ca36 100644
--- a/tests/test_pep425.py
+++ b/tests/test_pep425.py
@@ -46,6 +46,18 @@ def test_iter_supported_tags():
     for interp in ('cp', 'py'):
       for interp_suffix in ('2', '20', '21', '22', '23', '24', '25', '26'):
         for platform in ('linux_x86_64', 'any'):
-          yield (interp + interp_suffix, 'none', platform)
+          abis = ['none']
+
+          if interp == 'cp' and interp_suffix == '26' and platform == 'linux_x86_64':
+            abis.extend([
+              'cp%s' % interp_suffix,
+              'cp%sdmu' % interp_suffix, 'cp%sdm' % interp_suffix,
+              'cp%sdu' % interp_suffix, 'cp%sd' % interp_suffix,
+              'cp%smu' % interp_suffix, 'cp%sm' % interp_suffix,
+              'cp%su' % interp_suffix
+            ])
+
+          for abi in abis:
+            yield (interp + interp_suffix, abi, platform)
 
   assert set(PEP425.iter_supported_tags(identity, platform)) == set(iter_solutions())
diff --git a/tests/test_pex.py b/tests/test_pex.py
index a54abce..c923b49 100644
--- a/tests/test_pex.py
+++ b/tests/test_pex.py
@@ -2,17 +2,24 @@
 # Licensed under the Apache License, Version 2.0 (see LICENSE).
 
 import os
+import sys
 import textwrap
 from types import ModuleType
 
 import pytest
+from twitter.common.contextutil import temporary_dir
 
-from pex.compatibility import to_bytes
+from pex.compatibility import WINDOWS, nested, to_bytes
 from pex.installer import EggInstaller, WheelInstaller
 from pex.pex import PEX
 from pex.testing import make_installer, run_simple_pex_test
 from pex.util import DistributionHelper
 
+try:
+  from unittest import mock
+except ImportError:
+  import mock
+
 
 @pytest.mark.skipif('sys.version_info > (3,)')
 def test_pex_uncaught_exceptions():
@@ -120,6 +127,44 @@ def test_minimum_sys_modules():
   assert tainted_module.__path__ == ['good_path']
 
 
... 250 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-pex.git



More information about the Python-modules-commits mailing list