Commits

Daniel Holth committed ffa2af1

avoid importing pkg_resources except when necessary.
include version in WHEEL's Generator: key

Comments (0)

Files changed (7)

 [console_scripts]
 wininst2wheel = wheel.wininst2wheel:main
 egg2wheel = wheel.egg2wheel:main
-wheel = wheel.__main__:main
+wheel = wheel.tool:main
 
 [distutils.commands]
 bdist_wheel = wheel.bdist_wheel:bdist_wheel"""

wheel/__main__.py

 Wheel command line tool (enable python -m wheel syntax)
 """
 
+import sys
+
 def main(): # needed for console script
     if __package__ == '':
         # To be able to run 'python wheel-0.9.whl/wheel':
-        import sys
         import os.path
         path = os.path.dirname(os.path.dirname(__file__))
         sys.path[0:0] = [path]
     import wheel.tool
-    wheel.tool.main()
+    sys.exit(wheel.tool.main())
 
 if __name__ == "__main__":
-    main()
+    sys.exit(main())

wheel/bdist_wheel.py

 import warnings
 import shutil
 import json
+import wheel
 
 try:
     import sysconfig
 
 from distutils import log as logger
 
-from .util import get_abbr_impl, get_impl_ver, native, open_for_csv
+from .pep425tags import get_abbr_impl, get_impl_ver
+from .util import native, open_for_csv
 from .archive import archive_wheelfile
 from .pkginfo import read_pkg_info, write_pkg_info
 from .metadata import pkginfo_to_dict
             else:
                 rmtree(self.bdist_dir)
 
-    def write_wheelfile(self, wheelfile_base, generator='bdist_wheel'):
+    def write_wheelfile(self, wheelfile_base, generator='bdist_wheel (' + wheel.__version__ + ')'):
         from email.message import Message
         msg = Message()
         msg['Wheel-Version'] = '1.0'  # of the spec
     _big_number = sys.maxint
 
 from wheel.decorator import reify
-from wheel.util import (urlsafe_b64encode, from_json,
-    urlsafe_b64decode, native, binary, HashingFile, parse_version)
+from wheel.util import (urlsafe_b64encode, from_json, urlsafe_b64decode,
+                        native, binary, HashingFile)
 from wheel import signatures
 from wheel.pkginfo import read_pkg_info_bytes
 from wheel.util import open_for_csv
     \.whl|\.dist-info)$""",
     re.VERBOSE).match
 
+def parse_version(version):
+    """Use parse_version from pkg_resources or distutils as available."""
+    global parse_version
+    try:
+        from pkg_resources import parse_version
+    except ImportError:
+        from distutils.version import LooseVersion as parse_version
+    return parse_version(version)
 
 class BadWheelFile(ValueError):
     pass
     
     WheelFile can be used to simply parse a wheel filename by avoiding the
     methods that require the actual file contents."""
-    
+
     WHEEL_INFO = "WHEEL"
     RECORD = "RECORD"
 
             for abi in tags['abi'].split('.'):
                 for plat in tags['plat'].split('.'):
                     yield (pyver, abi, plat)
-    
+
     compatibility_tags = tags
 
     @property
     def arity(self):
         """The number of compatibility tags the wheel declares."""
         return len(list(self.compatibility_tags))
-    
+
     @property
     def rank(self):
         """
 
     @property
     def compatible(self):
-        return self.rank[0] != _big_number # bad API!
+        return self.rank[0] != _big_number  # bad API!
 
     # deprecated:
     def compatibility_rank(self, supported):
         if len(preferences):
             return (min(preferences), self.arity)
         return (_big_number, 0)
-    
+
     # deprecated
     def supports_current_python(self, x):
         assert self.context == x, 'context mismatch'
     #   1. Name
     #   2. Version
     #   3. Compatibility rank
-    #   4. Filename (as a tiebreaker)        
+    #   4. Filename (as a tiebreaker)
     @property
     def _sort_key(self):
         return (self.parsed_filename.group('name'),
                 parse_version(self.parsed_filename.group('ver')),
                 tuple(-x for x in self.rank),
                 self.filename)
-    
+
     def __eq__(self, other):
         return self.filename == other.filename
-    
+
     def __ne__(self, other):
         return self.filename != other.filename
-    
+
     def __lt__(self, other):
         if self.context != other.context:
             raise TypeError("{}.context != {}.context".format(self, other))
 
         return self._sort_key < other._sort_key
-    
+
         # XXX prune
 
         sn = self.parsed_filename.group('name')
         elif sc == None and oc != None:
             return False
         return self.filename < other.filename
-    
+
     def __gt__(self, other):
         return other < self
-    
+
     def __le__(self, other):
         return self == other or self < other
-    
+
     def __ge__(self, other):
         return self == other or other < self
 
     #
     # Methods using the file's contents:
-    # 
-    
+    #
+
     @reify
     def zipfile(self):
         mode = "r"
         #   3. Actual install - put the files in their target locations.
         #   4. Update RECORD - write a suitably modified RECORD file to
         #      reflect the actual installed paths.
-        
+
         if not force:
             for info, v in name_trans.items():
                 k = info.filename
             source.close()
             # preserve attributes (especially +x bit for scripts)
             attrs = info.external_attr >> 16
-            if attrs: # tends to be 0 if Windows.
+            if attrs:  # tends to be 0 if Windows.
                 os.chmod(dest, info.external_attr >> 16)
 
         record_name = os.path.join(root, self.record_name)
         for reldest, digest, length in sorted(record_data):
             writer.writerow((reldest, digest, length))
         writer.writerow((self.record_name, '', ''))
-        
+
     def verify(self, zipfile=None):
         """Configure the VerifyingZipFile `zipfile` by verifying its signature 
         and setting expected hashes for every hash in RECORD.
         if zipfile is None:
             zipfile = self.zipfile
         zipfile.strict = True
-        
+
         record_name = '/'.join((self.distinfo_name, 'RECORD'))
         sig_name = '/'.join((self.distinfo_name, 'RECORD.jws'))
-        # tolerate s/mime signatures: 
+        # tolerate s/mime signatures:
         smime_sig_name = '/'.join((self.distinfo_name, 'RECORD.p7s'))
         zipfile.set_expected_hash(record_name, None)
         zipfile.set_expected_hash(sig_name, None)
         zipfile.set_expected_hash(smime_sig_name, None)
         record = zipfile.read(record_name)
-                
+
         record_digest = urlsafe_b64encode(hashlib.sha256(record).digest())
         try:
             sig = from_json(native(zipfile.read(sig_name)))
-        except KeyError: # no signature
+        except KeyError:  # no signature
             pass
         if sig:
             headers, payload = signatures.verify(sig)
                 msg = "RECORD.sig claimed RECORD hash {0} != computed hash {1}."
                 raise BadWheelFile(msg.format(payload['hash'],
                                               native(record_digest)))
-        
+
         reader = csv.reader((native(r) for r in record.splitlines()))
-        
+
         for row in reader:
             filename = row[0]
             hash = row[1]
             algo, data = row[1].split('=', 1)
             assert algo == "sha256", "Unsupported hash algorithm"
             zipfile.set_expected_hash(filename, urlsafe_b64decode(binary(data)))
-    
-    
+
+
 class VerifyingZipFile(zipfile.ZipFile):
     """ZipFile that can assert that each of its extracted contents matches
     an expected sha256 hash. Note that each file must be completly read in 
     order for its hash to be checked."""
-    
+
     def __init__(self, file, mode="r",
                  compression=zipfile.ZIP_STORED,
                  allowZip64=False):
         self.strict = False
         self._expected_hashes = {}
         self._hash_algorithm = hashlib.sha256
-        
+
     def set_expected_hash(self, name, hash):
         """
         :param name: name of zip entry
         :param hash: bytes of hash (or None for "don't care")
         """
         self._expected_hashes[name] = hash
-        
+
     def open(self, name_or_info, mode="r", pwd=None):
         """Return file-like object for 'name'."""
         # A non-monkey-patched version would contain most of zipfile.py
             name = name_or_info.filename
         else:
             name = name_or_info
-        if (name in self._expected_hashes 
+        if (name in self._expected_hashes
             and self._expected_hashes[name] != None):
             expected_hash = self._expected_hashes[name]
             try:
                               'file hash verification (in Python >= 2.7)')
                 return ef
             running_hash = self._hash_algorithm()
-            if hasattr(ef, '_eof'): # py33
+            if hasattr(ef, '_eof'):  # py33
                 def _update_crc(data):
                     _update_crc_orig(data)
                     running_hash.update(data)

wheel/test/test_ranking.py

 import unittest
 
-from wheel.util import generate_supported
+from wheel.pep425tags import get_supported
 from wheel.install import WheelFile
 
 WHEELPAT = "%(name)s-%(ver)s-%(pyver)s-%(abi)s-%(arch)s.whl"
 
 # This relies on the fact that generate_supported will always return the
 # exact pyver, abi, and architecture for its first (best) match.
-sup = generate_supported()
+sup = get_supported()
 pyver, abi, arch = sup[0]
 genver = 'py' + pyver[2:]
 majver = genver[:3]

wheel/tool/__init__.py

 
 from glob import iglob
 from .. import signatures
-from ..util import (urlsafe_b64decode, urlsafe_b64encode, native, binary,
-        have_pkgresources, matches_requirement)
+from ..util import (urlsafe_b64decode, urlsafe_b64encode, native, binary, 
+                    matches_requirement)
 from ..install import WheelFile
 
-if have_pkgresources:
-    # Only support wheel convert if pkg_resources is present
-    from ..wininst2wheel import bdist_wininst2wheel
-    from ..egg2wheel import egg2wheel
+def require_pkgresources(name):
+    try:
+        import pkg_resources
+    except ImportError:
+        raise RuntimeError("'{0}' needs pkg_resources (part of setuptools).".format(name))
 
 import argparse
 
         command.install_egg_scripts(pkg_resources_dist)
 
 def convert(installers, dest_dir, verbose):
-    if not have_pkgresources:
-        raise RuntimeError("'wheel convert' needs pkg_resources (part of setuptools).")
+    require_pkgresources('wheel convert')
+    
+    # Only support wheel convert if pkg_resources is present
+    from ..wininst2wheel import bdist_wininst2wheel
+    from ..egg2wheel import egg2wheel
 
     for pat in installers:
         for installer in iglob(pat):
         # XXX on Python 3.3 we get 'args has no func' rather than short help.
         try:
             args.func(args)
+            return 0
         except WheelError as e:
             sys.stderr.write(e.message + "\n")
-            sys.exit(1)
+    return 1
 import json
 import hashlib
 
-from .pep425tags import (get_abbr_impl, get_impl_ver, get_supported,
-                         get_supported as generate_supported) # b/c
-
-try:
-    from pkg_resources import Distribution, Requirement, parse_version
-    have_pkgresources = True
-except ImportError:
-    from distutils.version import LooseVersion as parse_version
-    have_pkgresources = False
-
-__all__ = ['urlsafe_b64encode', 'urlsafe_b64decode', 'utf8', 'to_json',
-           'from_json', 'generate_supported', 'get_abbr_impl', 'get_impl_ver',
-           'parse_version', 'matches_requirement',
-           'have_pkgresources']
-
+__all__ = ['urlsafe_b64encode', 'urlsafe_b64decode', 'utf8', 
+           'to_json', 'from_json', 'matches_requirement']
 
 def urlsafe_b64encode(data):
     """urlsafe_b64encode without padding"""
         import dirspec.basedir
         return dirspec.basedir.load_config_paths(*resource)
 
-if have_pkgresources:
-    def matches_requirement(req, wheels):
-        """List of wheels matching a requirement.
+def matches_requirement(req, wheels):
+    """List of wheels matching a requirement.
 
-        :param req: The requirement to satisfy
-        :param wheels: List of wheels to search.
-        """
-        # If we don't have pkg_resources, raise an error
-        req = Requirement.parse(req)
+    :param req: The requirement to satisfy
+    :param wheels: List of wheels to search.
+    """
+    try:
+        from pkg_resources import Distribution, Requirement
+    except ImportError:
+        raise RuntimeError("Cannot use requirements without pkg_resources")
 
-        selected =  []
-        for wf in wheels:
-            f = wf.parsed_filename
-            dist = Distribution(project_name=f.group("name"), version=f.group("ver"))
-            if dist in req:
-                selected.append(wf)
-        return selected
-else:
-    def matches_requirement(req, wheels):
-        raise RuntimeError("Cannot use requirements without pkg_resources")
+    req = Requirement.parse(req)
+
+    selected = []
+    for wf in wheels:
+        f = wf.parsed_filename
+        dist = Distribution(project_name=f.group("name"), version=f.group("ver"))
+        if dist in req:
+            selected.append(wf)
+    return selected