diff options
| author | Alex Grönholm <alex.gronholm@nextday.fi> | 2017-07-26 02:51:59 +0300 |
|---|---|---|
| committer | Alex Grönholm <alex.gronholm@nextday.fi> | 2017-07-26 03:20:11 +0300 |
| commit | 88fff78c73e300592ecba4d9a0550d1dac761eb7 (patch) | |
| tree | 2e056f5ed661cfd73e87b5779dccce8149cc3f46 | |
| parent | 0eea55fe5b9f6d690a3069b9e1e69f7764384e0b (diff) | |
| download | wheel-git-88fff78c73e300592ecba4d9a0550d1dac761eb7.tar.gz | |
Fixed errors reported by flake8
33 files changed, 567 insertions, 422 deletions
@@ -4,9 +4,13 @@ # and then run "tox" from this directory. [tox] -envlist = py27, pypy, py33, py34, py35, py36 +envlist = py27, pypy, py33, py34, py35, py36, flake8 minversion = 2.5.0 +[travis] +python = + 2.7: py27, flake8 + [testenv] commands = python -m pytest extras = diff --git a/wheel/__init__.py b/wheel/__init__.py index 7b3d278..0857063 100644 --- a/wheel/__init__.py +++ b/wheel/__init__.py @@ -1,2 +1,2 @@ # __variables__ with double-quoted values will be available in setup.py: -__version__ = "0.30.0.a0" +__version__ = "0.30.0a0" diff --git a/wheel/__main__.py b/wheel/__main__.py index 889359c..8f0c4fe 100644 --- a/wheel/__main__.py +++ b/wheel/__main__.py @@ -4,7 +4,8 @@ Wheel command line tool (enable python -m wheel syntax) import sys -def main(): # needed for console script + +def main(): # needed for console script if __package__ == '': # To be able to run 'python wheel-0.9.whl/wheel': import os.path @@ -13,5 +14,6 @@ def main(): # needed for console script import wheel.tool sys.exit(wheel.tool.main()) + if __name__ == "__main__": sys.exit(main()) diff --git a/wheel/archive.py b/wheel/archive.py index 403d45b..ce747c9 100644 --- a/wheel/archive.py +++ b/wheel/archive.py @@ -3,18 +3,16 @@ Archive tools for wheel. """ import os -import time -import logging import os.path +import time import zipfile - from distutils import log def archive_wheelfile(base_name, base_dir): - '''Archive all files under `base_dir` in a whl file and name it like + """Archive all files under `base_dir` in a whl file and name it like `base_name`. - ''' + """ olddir = os.path.abspath(os.curdir) base_name = os.path.abspath(base_name) try: diff --git a/wheel/bdist_wheel.py b/wheel/bdist_wheel.py index dfcc910..dd67a8a 100644 --- a/wheel/bdist_wheel.py +++ b/wheel/bdist_wheel.py @@ -13,19 +13,13 @@ import shutil import json import sys import re - -import pkg_resources - -safe_name = pkg_resources.safe_name -safe_version = pkg_resources.safe_version - -from shutil import rmtree from email.generator import Generator - from distutils.core import Command from distutils.sysconfig import get_python_version - from distutils import log as logger +from shutil import rmtree + +import pkg_resources from .pep425tags import get_abbr_impl, get_impl_ver, get_abi_tag, get_platform from .util import native, open_for_csv @@ -35,14 +29,21 @@ from .metadata import pkginfo_to_dict from . import pep425tags, metadata from . import __version__ as wheel_version + +safe_name = pkg_resources.safe_name +safe_version = pkg_resources.safe_version + PY_LIMITED_API_PATTERN = r'cp3\d' + def safer_name(name): return safe_name(name).replace('-', '_') + def safer_version(version): return safe_version(version).replace('-', '_') + class bdist_wheel(Command): description = 'create a wheel distribution' @@ -147,7 +148,6 @@ class bdist_wheel(Command): plat_name = 'linux_i686' plat_name = plat_name.replace('-', '_').replace('.', '_') - if self.root_is_pure: if self.universal: impl = 'py2.py3' @@ -300,7 +300,7 @@ class bdist_wheel(Command): def license_file(self): """Return license filename from a license-file key in setup.cfg, or None.""" metadata = self.distribution.get_option_dict('metadata') - if not 'license_file' in metadata: + if 'license_file' not in metadata: return None return metadata['license_file'][1] @@ -329,7 +329,7 @@ class bdist_wheel(Command): # our .ini parser folds - to _ in key names: for key, title in (('provides_extra', 'Provides-Extra'), ('requires_dist', 'Requires-Dist')): - if not key in metadata: + if key not in metadata: continue field = metadata[key] for line in field[1].splitlines(): @@ -341,7 +341,9 @@ class bdist_wheel(Command): def add_requirements(self, metadata_path): """Add additional requirements from setup.cfg to file metadata_path""" additional = list(self.setupcfg_requirements()) - if not additional: return + if not additional: + return + pkg_info = read_pkg_info(metadata_path) if 'Provides-Extra' in pkg_info or 'Requires-Dist' in pkg_info: warnings.warn('setup.cfg requirements overwrite values from setup.py') @@ -389,10 +391,9 @@ class bdist_wheel(Command): # ignore common egg metadata that is useless to wheel shutil.copytree(egginfo_path, distinfo_path, - ignore=lambda x, y: set(('PKG-INFO', - 'requires.txt', - 'SOURCES.txt', - 'not-zip-safe',))) + ignore=lambda x, y: {'PKG-INFO', 'requires.txt', 'SOURCES.txt', + 'not-zip-safe'} + ) # delete dependency_links if it is only whitespace dependency_links_path = os.path.join(distinfo_path, 'dependency_links.txt') @@ -419,7 +420,8 @@ class bdist_wheel(Command): description_filename) with open(description_path, "wb") as description_file: description_file.write(description_text.encode('utf-8')) - pymeta['extensions']['python.details']['document_names']['description'] = description_filename + pymeta['extensions']['python.details']['document_names']['description'] = \ + description_filename # XXX heuristically copy any LICENSE/LICENSE.txt? license = self.license_file() diff --git a/wheel/egg2wheel.py b/wheel/egg2wheel.py index e8d3153..3799909 100755 --- a/wheel/egg2wheel.py +++ b/wheel/egg2wheel.py @@ -1,20 +1,22 @@ #!/usr/bin/env python +import distutils.dist import os.path import re +import shutil import sys import tempfile import zipfile -import wheel.bdist_wheel -import shutil -import distutils.dist -from distutils.archive_util import make_archive from argparse import ArgumentParser +from distutils.archive_util import make_archive from glob import iglob + +import wheel.bdist_wheel from wheel.wininst2wheel import _bdist_wheel_tag egg_info_re = re.compile(r'''(?P<name>.+?)-(?P<ver>.+?) (-(?P<pyver>.+?))?(-(?P<arch>.+?))?.egg''', re.VERBOSE) + def egg2wheel(egg_path, dest_dir): egg_info = egg_info_re.match(os.path.basename(egg_path)).groupdict() dir = tempfile.mkdtemp(suffix="_e2w") @@ -67,11 +69,12 @@ def egg2wheel(egg_path, dest_dir): os.rename(filename, filename[:-3] + 'whl') shutil.rmtree(dir) + def main(): parser = ArgumentParser() parser.add_argument('eggs', nargs='*', help="Eggs to convert") parser.add_argument('--dest-dir', '-d', default=os.path.curdir, - help="Directory to store wheels (default %(default)s)") + help="Directory to store wheels (default %(default)s)") parser.add_argument('--verbose', '-v', action='store_true') args = parser.parse_args() for pat in args.eggs: @@ -82,5 +85,6 @@ def main(): if args.verbose: sys.stdout.write("OK\n") + if __name__ == "__main__": main() diff --git a/wheel/install.py b/wheel/install.py index a422b0e..a4fd8aa 100644 --- a/wheel/install.py +++ b/wheel/install.py @@ -1,33 +1,32 @@ """ -Operations on existing wheel files, including basic installation. +Operations on existing wheel files, including basic installation. """ # XXX see patched pip to install -import sys -import warnings +import csv +import hashlib import os.path import re -import zipfile -import hashlib -import csv - import shutil +import sys +import warnings +import zipfile -try: - _big_number = sys.maxsize -except NameError: - _big_number = sys.maxint - +from . import signatures from .decorator import reify from .util import (urlsafe_b64encode, from_json, urlsafe_b64decode, native, binary, HashingFile) -from . import signatures from .pkginfo import read_pkg_info_bytes from .util import open_for_csv from .pep425tags import get_supported from .paths import get_install_paths +try: + _big_number = sys.maxsize +except NameError: + _big_number = sys.maxint + # The next major version after this version of the 'wheel' tool: VERSION_TOO_HIGH = (1, 0) @@ -39,6 +38,7 @@ WHEEL_INFO_RE = re.compile( \.whl|\.dist-info)$""", re.VERBOSE).match + def parse_version(version): """Use parse_version from pkg_resources or distutils as available.""" global parse_version @@ -48,6 +48,7 @@ def parse_version(version): from distutils.version import LooseVersion as parse_version return parse_version(version) + class BadWheelFile(ValueError): pass @@ -55,7 +56,7 @@ class BadWheelFile(ValueError): class WheelFile(object): """Parse wheel-specific attributes from a wheel (.whl) file and offer basic installation and verification support. - + WheelFile can be used to simply parse a wheel filename by avoiding the methods that require the actual file contents.""" @@ -201,11 +202,11 @@ class WheelFile(object): raise TypeError("{0}.context != {1}.context".format(self, other)) sc = self.rank oc = other.rank - if sc != None and oc != None and sc != oc: + if sc is not None and oc is not None and sc != oc: # Smaller compatibility ranks are "better" than larger ones, # so we have to reverse the sense of the comparison here! return sc > oc - elif sc == None and oc != None: + elif sc is None and oc is not None: return False return self.filename < other.filename @@ -247,10 +248,10 @@ class WheelFile(object): """ Consult distutils to get the install paths for our dist. A dict with ('purelib', 'platlib', 'headers', 'scripts', 'data'). - + We use the name from our filename as the dist name, which means headers could be installed in the wrong place if the filesystem-escaped name - is different than the Name. Who cares? + is different than the Name. Who cares? """ name = self.parsed_filename.group('name') return get_install_paths(name) @@ -323,7 +324,9 @@ class WheelFile(object): k = info.filename key, target, filename, dest = v if os.path.exists(dest): - raise ValueError("Wheel file {0} would overwrite {1}. Use force if this is intended".format(k, dest)) + raise ValueError( + "Wheel file {0} would overwrite {1}. Use force if this is intended".format( + k, dest)) # Get the name of our executable, for use when replacing script # wrapper hashbang lines. @@ -367,9 +370,9 @@ class WheelFile(object): writer.writerow((self.record_name, '', '')) def verify(self, zipfile=None): - """Configure the VerifyingZipFile `zipfile` by verifying its signature + """Configure the VerifyingZipFile `zipfile` by verifying its signature and setting expected hashes for every hash in RECORD. - Caller must complete the verification process by completely reading + Caller must complete the verification process by completely reading every file in the archive (e.g. with extractall).""" sig = None if zipfile is None: @@ -413,7 +416,7 @@ class WheelFile(object): 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 + 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", @@ -440,8 +443,8 @@ class VerifyingZipFile(zipfile.ZipFile): name = name_or_info.filename else: name = name_or_info - if (name in self._expected_hashes - and self._expected_hashes[name] != None): + + if name in self._expected_hashes and self._expected_hashes[name] is not None: expected_hash = self._expected_hashes[name] try: _update_crc_orig = ef._update_crc diff --git a/wheel/metadata.py b/wheel/metadata.py index 2b123f9..a779188 100644 --- a/wheel/metadata.py +++ b/wheel/metadata.py @@ -2,35 +2,33 @@ Tools for converting old- to new-style metadata. """ -from collections import namedtuple -from .pkginfo import read_pkg_info -from .util import OrderedDefaultDict -from collections import OrderedDict - -import re +import email.parser import os.path +import re import textwrap +from collections import namedtuple, OrderedDict + import pkg_resources -import email.parser from . import __version__ as wheel_version +from .pkginfo import read_pkg_info +from .util import OrderedDefaultDict METADATA_VERSION = "2.0" -PLURAL_FIELDS = { "classifier" : "classifiers", - "provides_dist" : "provides", - "provides_extra" : "extras" } +PLURAL_FIELDS = {"classifier": "classifiers", + "provides_dist": "provides", + "provides_extra": "extras"} SKIP_FIELDS = set() -CONTACT_FIELDS = (({"email":"author_email", "name": "author"}, - "author"), - ({"email":"maintainer_email", "name": "maintainer"}, - "maintainer")) +CONTACT_FIELDS = (({"email": "author_email", "name": "author"}, + "author"), + ({"email": "maintainer_email", "name": "maintainer"}, + "maintainer")) # commonly filled out as "UNKNOWN" by distutils: -UNKNOWN_FIELDS = set(("author", "author_email", "platform", "home_page", - "license")) +UNKNOWN_FIELDS = {"author", "author_email", "platform", "home_page", "license"} # Wheel itself is probably the only program that uses non-extras markers # in METADATA/PKG-INFO. Support its syntax with the extra at the end only. @@ -39,13 +37,14 @@ KEYWORDS_RE = re.compile("[\0-,]+") MayRequiresKey = namedtuple('MayRequiresKey', ('condition', 'extra')) + def unique(iterable): """ Yield unique values in iterable, preserving order. """ seen = set() for value in iterable: - if not value in seen: + if value not in seen: seen.add(value) yield value @@ -72,6 +71,7 @@ def handle_requires(metadata, pkg_info, key): if may_requires: metadata['run_requires'] = [] + def sort_key(item): # Both condition and extra could be None, which can't be compared # against strings in Python 3. @@ -79,6 +79,7 @@ def handle_requires(metadata, pkg_info, key): if key.condition is None: return '' return key.condition + for key, value in sorted(may_requires.items(), key=sort_key): may_requirement = OrderedDict((('requires', value),)) if key.extra: @@ -87,7 +88,7 @@ def handle_requires(metadata, pkg_info, key): may_requirement['environment'] = key.condition metadata['run_requires'].append(may_requirement) - if not 'extras' in metadata: + if 'extras' not in metadata: metadata['extras'] = [] metadata['extras'].extend([key.extra for key in may_requires.keys() if key.extra]) @@ -103,7 +104,8 @@ def pkginfo_to_dict(path, distribution=None): distribution: optional distutils Distribution() """ - metadata = OrderedDefaultDict(lambda: OrderedDefaultDict(lambda: OrderedDefaultDict(OrderedDict))) + metadata = OrderedDefaultDict( + lambda: OrderedDefaultDict(lambda: OrderedDefaultDict(OrderedDict))) metadata["generator"] = "bdist_wheel (" + wheel_version + ")" try: unicode @@ -148,12 +150,12 @@ def pkginfo_to_dict(path, distribution=None): handle_requires(metadata, pkg_info, key) elif low_key == 'provides_extra': - if not 'extras' in metadata: + if 'extras' not in metadata: metadata['extras'] = [] metadata['extras'].extend(pkg_info.get_all(key)) elif low_key == 'home_page': - metadata['extensions']['python.details']['project_urls'] = {'Home':pkg_info[key]} + metadata['extensions']['python.details']['project_urls'] = {'Home': pkg_info[key]} elif low_key == 'keywords': metadata['keywords'] = KEYWORDS_RE.split(pkg_info[key]) @@ -173,7 +175,7 @@ def pkginfo_to_dict(path, distribution=None): requirements = getattr(distribution, attr) if isinstance(requirements, list): new_requirements = sorted(convert_requirements(requirements)) - metadata[requires] = [{'requires':new_requirements}] + metadata[requires] = [{'requires': new_requirements}] except AttributeError: pass @@ -215,6 +217,7 @@ def pkginfo_to_dict(path, distribution=None): return metadata + def requires_to_requires_dist(requirement): """Compose the version predicates for requirement in PEP 345 fashion.""" requires_dist = [] @@ -224,6 +227,7 @@ def requires_to_requires_dist(requirement): return '' return " (%s)" % ','.join(requires_dist) + def convert_requirements(requirements): """Yield Requires-Dist: strings for parsed requirements strings.""" for req in requirements: @@ -234,6 +238,7 @@ def convert_requirements(requirements): extras = "[%s]" % extras yield (parsed_requirement.project_name + extras + spec) + def generate_requirements(extras_require): """ Convert requirements from a setup()-style dictionary to ('Requires-Dist', 'requirement') @@ -257,6 +262,7 @@ def generate_requirements(extras_require): for new_req in convert_requirements(depends): yield ('Requires-Dist', new_req + condition) + def pkginfo_to_metadata(egg_info_path, pkginfo_path): """ Convert .egg-info directory with PKG-INFO to the Metadata 1.3 aka @@ -290,8 +296,8 @@ def pkginfo_unicode(pkg_info, field): return str(text) for item in pkg_info.raw_items(): if item[0].lower() == field: - text = item[1].encode('ascii', 'surrogateescape')\ - .decode('utf-8') + text = item[1].encode('ascii', 'surrogateescape') \ + .decode('utf-8') break return text @@ -311,20 +317,22 @@ def dedent_description(pkg_info): description_lines = description.splitlines() description_dedent = '\n'.join( - # if the first line of long_description is blank, - # the first line here will be indented. - (description_lines[0].lstrip(), - textwrap.dedent('\n'.join(description_lines[1:])), - '\n')) + # if the first line of long_description is blank, + # the first line here will be indented. + (description_lines[0].lstrip(), + textwrap.dedent('\n'.join(description_lines[1:])), + '\n')) if surrogates: - description_dedent = description_dedent\ - .encode("utf8")\ - .decode("ascii", "surrogateescape") + description_dedent = description_dedent \ + .encode("utf8") \ + .decode("ascii", "surrogateescape") return description_dedent if __name__ == "__main__": - import sys, pprint + import sys + import pprint + pprint.pprint(pkginfo_to_dict(sys.argv[1])) diff --git a/wheel/paths.py b/wheel/paths.py index fe3dfd6..afb3cae 100644 --- a/wheel/paths.py +++ b/wheel/paths.py @@ -4,22 +4,24 @@ Installation paths. Map the .data/ subdirectory names to install paths. """ +import distutils.command.install as install +import distutils.dist as dist import os.path import sys -import distutils.dist as dist -import distutils.command.install as install + def get_install_command(name): # late binding due to potential monkeypatching - d = dist.Distribution({'name':name}) + d = dist.Distribution({'name': name}) i = install.install(d) i.finalize_options() return i + def get_install_paths(name): """ Return the (distutils) install paths for the named dist. - + A dict with ('purelib', 'platlib', 'headers', 'scripts', 'data') keys. """ paths = {} diff --git a/wheel/pep425tags.py b/wheel/pep425tags.py index a7bd4a9..47b959e 100644 --- a/wheel/pep425tags.py +++ b/wheel/pep425tags.py @@ -1,10 +1,9 @@ """Generate and work with PEP 425 Compatibility Tags.""" +import distutils.util import sys -import warnings - import sysconfig -import distutils.util +import warnings def get_config_var(var): @@ -107,11 +106,11 @@ def get_supported(versions=None, supplied_platform=None): """Return a list of supported tags for each version specified in `versions`. - :param versions: a list of string versions, of the form ["33", "32"], + :param versions: a list of string versions, of the form ["33", "32"], or None. The first version will be assumed to support our ABI. """ supported = [] - + # Versions must be given with respect to the preference if versions is None: versions = [] @@ -120,15 +119,15 @@ def get_supported(versions=None, supplied_platform=None): # Support all previous minor Python versions. for minor in range(version_info[-1], -1, -1): versions.append(''.join(map(str, major + (minor,)))) - + impl = get_abbr_impl() - + abis = [] abi = get_abi_tag() if abi: abis[0:0] = [abi] - + abi3s = set() import imp for suffix in imp.get_suffixes(): @@ -143,7 +142,7 @@ def get_supported(versions=None, supplied_platform=None): if supplied_platform: platforms.append(supplied_platform) platforms.append(get_platform()) - + # Current version, current API (built specifically for our Python): for abi in abis: for arch in platforms: @@ -162,10 +161,10 @@ def get_supported(versions=None, supplied_platform=None): for i, version in enumerate(versions): supported.append(('%s%s' % (impl, version), 'none', 'any')) if i == 0: - # Tagged specifically as being cross-version compatible + # Tagged specifically as being cross-version compatible # (with just the major version specified) - supported.append(('%s%s' % (impl, versions[0][0]), 'none', 'any')) - + supported.append(('%s%s' % (impl, versions[0][0]), 'none', 'any')) + # Major Python version + platform; e.g. binaries not using the Python API supported.append(('py%s' % (versions[0][0]), 'none', arch)) @@ -174,5 +173,5 @@ def get_supported(versions=None, supplied_platform=None): supported.append(('py%s' % (version,), 'none', 'any')) if i == 0: supported.append(('py%s' % (version[0]), 'none', 'any')) - + return supported diff --git a/wheel/pkginfo.py b/wheel/pkginfo.py index 8a4aca3..e153db0 100644 --- a/wheel/pkginfo.py +++ b/wheel/pkginfo.py @@ -11,7 +11,7 @@ except NameError: if not _PY3: from email.generator import Generator - + def read_pkg_info_bytes(bytestr): return Parser().parsestr(bytestr) @@ -22,18 +22,18 @@ if not _PY3: def write_pkg_info(path, message): with open(path, 'w') as metadata: - Generator(metadata, maxheaderlen=0).flatten(message) - + Generator(metadata, maxheaderlen=0).flatten(message) else: from email.generator import BytesGenerator + def read_pkg_info_bytes(bytestr): headers = bytestr.decode(encoding="ascii", errors="surrogateescape") message = Parser().parsestr(headers) return message def read_pkg_info(path): - with open(path, "r", - encoding="ascii", + with open(path, "r", + encoding="ascii", errors="surrogateescape") as headers: message = Parser().parse(headers) return message @@ -41,4 +41,3 @@ else: def write_pkg_info(path, message): with open(path, "wb") as out: BytesGenerator(out, maxheaderlen=0).flatten(message) - diff --git a/wheel/signatures/__init__.py b/wheel/signatures/__init__.py index 3f21b50..e7a5331 100644 --- a/wheel/signatures/__init__.py +++ b/wheel/signatures/__init__.py @@ -2,63 +2,67 @@ Create and verify jws-js format Ed25519 signatures. """ -__all__ = [ 'sign', 'verify' ] - import json from ..util import urlsafe_b64decode, urlsafe_b64encode, native, binary +__all__ = ['sign', 'verify'] + ed25519ll = None ALG = "Ed25519" + def get_ed25519ll(): """Lazy import-and-test of ed25519 module""" global ed25519ll - + if not ed25519ll: try: - import ed25519ll # fast (thousands / s) - except (ImportError, OSError): # pragma nocover - from . import ed25519py as ed25519ll # pure Python (hundreds / s) + import ed25519ll # fast (thousands / s) + except (ImportError, OSError): # pragma nocover + from . import ed25519py as ed25519ll # pure Python (hundreds / s) test() - + return ed25519ll + def sign(payload, keypair): - """Return a JWS-JS format signature given a JSON-serializable payload and + """Return a JWS-JS format signature given a JSON-serializable payload and an Ed25519 keypair.""" get_ed25519ll() # header = { "alg": ALG, "jwk": { - "kty": ALG, # alg -> kty in jwk-08. + "kty": ALG, # alg -> kty in jwk-08. "vk": native(urlsafe_b64encode(keypair.vk)) } } - + encoded_header = urlsafe_b64encode(binary(json.dumps(header, sort_keys=True))) encoded_payload = urlsafe_b64encode(binary(json.dumps(payload, sort_keys=True))) secured_input = b".".join((encoded_header, encoded_payload)) sig_msg = ed25519ll.crypto_sign(secured_input, keypair.sk) signature = sig_msg[:ed25519ll.SIGNATUREBYTES] encoded_signature = urlsafe_b64encode(signature) - - return {"recipients": - [{"header":native(encoded_header), - "signature":native(encoded_signature)}], + + return {"recipients": + [{"header": native(encoded_header), + "signature": native(encoded_signature)}], "payload": native(encoded_payload)} + def assertTrue(condition, message=""): if not condition: raise ValueError(message) - + + def verify(jwsjs): """Return (decoded headers, payload) if all signatures in jwsjs are consistent, else raise ValueError. - + Caller must decide whether the keys are actually trusted.""" - get_ed25519ll() + get_ed25519ll() # XXX forbid duplicate keys in JSON input using object_pairs_hook (2.7+) recipients = jwsjs["recipients"] encoded_payload = binary(jwsjs["payload"]) @@ -68,12 +72,12 @@ def verify(jwsjs): h = binary(recipient["header"]) s = binary(recipient["signature"]) header = json.loads(native(urlsafe_b64decode(h))) - assertTrue(header["alg"] == ALG, - "Unexpected algorithm {0}".format(header["alg"])) - if "alg" in header["jwk"] and not "kty" in header["jwk"]: - header["jwk"]["kty"] = header["jwk"]["alg"] # b/w for JWK < -08 - assertTrue(header["jwk"]["kty"] == ALG, # true for Ed25519 - "Unexpected key type {0}".format(header["jwk"]["kty"])) + assertTrue(header["alg"] == ALG, + "Unexpected algorithm {0}".format(header["alg"])) + if "alg" in header["jwk"] and "kty" not in header["jwk"]: + header["jwk"]["kty"] = header["jwk"]["alg"] # b/w for JWK < -08 + assertTrue(header["jwk"]["kty"] == ALG, # true for Ed25519 + "Unexpected key type {0}".format(header["jwk"]["kty"])) vk = urlsafe_b64decode(binary(header["jwk"]["vk"])) secured_input = b".".join((h, encoded_payload)) sig = urlsafe_b64decode(s) @@ -91,6 +95,7 @@ def verify(jwsjs): return headers, payload + def test(): kp = ed25519ll.crypto_sign_keypair() payload = {'test': 'onstartup'} @@ -101,6 +106,5 @@ def test(): verify(jwsjs) except ValueError: pass - else: # pragma no cover + else: # pragma no cover raise RuntimeError("No error from bad wheel.signatures payload.") - diff --git a/wheel/signatures/djbec.py b/wheel/signatures/djbec.py index 56efe44..87f72d4 100644 --- a/wheel/signatures/djbec.py +++ b/wheel/signatures/djbec.py @@ -6,61 +6,80 @@ # http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html # Specifically add-2008-hwcd-4 and dbl-2008-hwcd -try: # pragma nocover +import hashlib +import random + +try: # pragma nocover unicode PY3 = False + def asbytes(b): """Convert array of integers to byte string""" return ''.join(chr(x) for x in b) + def joinbytes(b): """Convert array of bytes to byte string""" return ''.join(b) + def bit(h, i): """Return i'th bit of bytestring h""" - return (ord(h[i//8]) >> (i%8)) & 1 - -except NameError: # pragma nocover + return (ord(h[i // 8]) >> (i % 8)) & 1 +except NameError: # pragma nocover PY3 = True asbytes = bytes joinbytes = bytes - def bit(h, i): - return (h[i//8] >> (i%8)) & 1 -import hashlib + def bit(h, i): + return (h[i // 8] >> (i % 8)) & 1 b = 256 -q = 2**255 - 19 -l = 2**252 + 27742317777372353535851937790883648493 +q = 2 ** 255 - 19 +l = 2 ** 252 + 27742317777372353535851937790883648493 + def H(m): return hashlib.sha512(m).digest() + def expmod(b, e, m): - if e == 0: return 1 + if e == 0: + return 1 + t = expmod(b, e // 2, m) ** 2 % m - if e & 1: t = (t * b) % m + if e & 1: + t = (t * b) % m + return t + # Can probably get some extra speedup here by replacing this with # an extended-euclidean, but performance seems OK without that def inv(x): - return expmod(x, q-2, q) + return expmod(x, q - 2, q) + d = -121665 * inv(121666) -I = expmod(2,(q-1)//4,q) +I = expmod(2, (q - 1) // 4, q) + def xrecover(y): - xx = (y*y-1) * inv(d*y*y+1) - x = expmod(xx,(q+3)//8,q) - if (x*x - xx) % q != 0: x = (x*I) % q - if x % 2 != 0: x = q-x + xx = (y * y - 1) * inv(d * y * y + 1) + x = expmod(xx, (q + 3) // 8, q) + if (x * x - xx) % q != 0: + x = (x * I) % q + + if x % 2 != 0: + x = q - x + return x + By = 4 * inv(5) Bx = xrecover(By) -B = [Bx % q,By % q] +B = [Bx % q, By % q] + -#def edwards(P,Q): +# def edwards(P,Q): # x1 = P[0] # y1 = P[1] # x2 = Q[0] @@ -69,7 +88,7 @@ B = [Bx % q,By % q] # y3 = (y1*y2+x1*x2) * inv(1-d*x1*x2*y1*y2) # return (x3 % q,y3 % q) -#def scalarmult(P,e): +# def scalarmult(P,e): # if e == 0: return [0,1] # Q = scalarmult(P,e/2) # Q = edwards(Q,Q) @@ -82,113 +101,137 @@ B = [Bx % q,By % q] def xpt_add(pt1, pt2): (X1, Y1, Z1, T1) = pt1 (X2, Y2, Z2, T2) = pt2 - A = ((Y1-X1)*(Y2+X2)) % q - B = ((Y1+X1)*(Y2-X2)) % q - C = (Z1*2*T2) % q - D = (T1*2*Z2) % q - E = (D+C) % q - F = (B-A) % q - G = (B+A) % q - H = (D-C) % q - X3 = (E*F) % q - Y3 = (G*H) % q - Z3 = (F*G) % q - T3 = (E*H) % q + A = ((Y1 - X1) * (Y2 + X2)) % q + B = ((Y1 + X1) * (Y2 - X2)) % q + C = (Z1 * 2 * T2) % q + D = (T1 * 2 * Z2) % q + E = (D + C) % q + F = (B - A) % q + G = (B + A) % q + H = (D - C) % q + X3 = (E * F) % q + Y3 = (G * H) % q + Z3 = (F * G) % q + T3 = (E * H) % q return (X3, Y3, Z3, T3) -def xpt_double (pt): + +def xpt_double(pt): (X1, Y1, Z1, _) = pt - A = (X1*X1) - B = (Y1*Y1) - C = (2*Z1*Z1) + A = (X1 * X1) + B = (Y1 * Y1) + C = (2 * Z1 * Z1) D = (-A) % q - J = (X1+Y1) % q - E = (J*J-A-B) % q - G = (D+B) % q - F = (G-C) % q - H = (D-B) % q - X3 = (E*F) % q - Y3 = (G*H) % q - Z3 = (F*G) % q - T3 = (E*H) % q - return (X3, Y3, Z3, T3) - -def pt_xform (pt): + J = (X1 + Y1) % q + E = (J * J - A - B) % q + G = (D + B) % q + F = (G - C) % q + H = (D - B) % q + X3 = (E * F) % q + Y3 = (G * H) % q + Z3 = (F * G) % q + T3 = (E * H) % q + return X3, Y3, Z3, T3 + + +def pt_xform(pt): (x, y) = pt - return (x, y, 1, (x*y)%q) + return x, y, 1, (x * y) % q + -def pt_unxform (pt): +def pt_unxform(pt): (x, y, z, _) = pt - return ((x*inv(z))%q, (y*inv(z))%q) + return (x * inv(z)) % q, (y * inv(z)) % q + + +def xpt_mult(pt, n): + if n == 0: + return pt_xform((0, 1)) + + _ = xpt_double(xpt_mult(pt, n >> 1)) + return xpt_add(_, pt) if n & 1 else _ -def xpt_mult (pt, n): - if n==0: return pt_xform((0,1)) - _ = xpt_double(xpt_mult(pt, n>>1)) - return xpt_add(_, pt) if n&1 else _ def scalarmult(pt, e): return pt_unxform(xpt_mult(pt_xform(pt), e)) + def encodeint(y): bits = [(y >> i) & 1 for i in range(b)] e = [(sum([bits[i * 8 + j] << j for j in range(8)])) - for i in range(b//8)] + for i in range(b // 8)] return asbytes(e) + def encodepoint(P): x = P[0] y = P[1] bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1] e = [(sum([bits[i * 8 + j] << j for j in range(8)])) - for i in range(b//8)] + for i in range(b // 8)] return asbytes(e) - + + def publickey(sk): h = H(sk) - a = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2)) - A = scalarmult(B,a) + a = 2 ** (b - 2) + sum(2 ** i * bit(h, i) for i in range(3, b - 2)) + A = scalarmult(B, a) return encodepoint(A) + def Hint(m): h = H(m) - return sum(2**i * bit(h,i) for i in range(2*b)) + return sum(2 ** i * bit(h, i) for i in range(2 * b)) + -def signature(m,sk,pk): +def signature(m, sk, pk): h = H(sk) - a = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2)) - inter = joinbytes([h[i] for i in range(b//8,b//4)]) + a = 2 ** (b - 2) + sum(2 ** i * bit(h, i) for i in range(3, b - 2)) + inter = joinbytes([h[i] for i in range(b // 8, b // 4)]) r = Hint(inter + m) - R = scalarmult(B,r) + R = scalarmult(B, r) S = (r + Hint(encodepoint(R) + pk + m) * a) % l return encodepoint(R) + encodeint(S) + def isoncurve(P): x = P[0] y = P[1] - return (-x*x + y*y - 1 - d*x*x*y*y) % q == 0 + return (-x * x + y * y - 1 - d * x * x * y * y) % q == 0 + def decodeint(s): - return sum(2**i * bit(s,i) for i in range(0,b)) + return sum(2 ** i * bit(s, i) for i in range(0, b)) + def decodepoint(s): - y = sum(2**i * bit(s,i) for i in range(0,b-1)) + y = sum(2 ** i * bit(s, i) for i in range(0, b - 1)) x = xrecover(y) - if x & 1 != bit(s,b-1): x = q-x - P = [x,y] - if not isoncurve(P): raise Exception("decoding point that is not on curve") + if x & 1 != bit(s, b - 1): + x = q - x + + P = [x, y] + if not isoncurve(P): + raise Exception("decoding point that is not on curve") + return P + def checkvalid(s, m, pk): - if len(s) != b//4: raise Exception("signature length is wrong") - if len(pk) != b//8: raise Exception("public-key length is wrong") - R = decodepoint(s[0:b//8]) + if len(s) != b // 4: + raise Exception("signature length is wrong") + if len(pk) != b // 8: + raise Exception("public-key length is wrong") + + R = decodepoint(s[0:b // 8]) A = decodepoint(pk) - S = decodeint(s[b//8:b//4]) + S = decodeint(s[b // 8:b // 4]) h = Hint(encodepoint(R) + pk + m) - v1 = scalarmult(B,S) -# v2 = edwards(R,scalarmult(A,h)) + v1 = scalarmult(B, S) + # v2 = edwards(R,scalarmult(A,h)) v2 = pt_unxform(xpt_add(pt_xform(R), pt_xform(scalarmult(A, h)))) - return v1==v2 + return v1 == v2 + ########################################################## # @@ -199,7 +242,8 @@ def checkvalid(s, m, pk): P = q A = 486662 -#def expmod(b, e, m): + +# def expmod(b, e, m): # if e == 0: return 1 # t = expmod(b, e / 2, m) ** 2 % m # if e & 1: t = (t * b) % m @@ -207,64 +251,73 @@ A = 486662 # def inv(x): return expmod(x, P - 2, P) + def add(n, m, d): (xn, zn) = n - (xm, zm) = m + (xm, zm) = m (xd, zd) = d x = 4 * (xm * xn - zm * zn) ** 2 * zd z = 4 * (xm * zn - zm * xn) ** 2 * xd return (x % P, z % P) + def double(n): (xn, zn) = n x = (xn ** 2 - zn ** 2) ** 2 z = 4 * xn * zn * (xn ** 2 + A * xn * zn + zn ** 2) return (x % P, z % P) + def curve25519(n, base=9): - one = (base,1) + one = (base, 1) two = double(one) + # f(m) evaluates to a tuple # containing the mth multiple and the # (m+1)th multiple of base. def f(m): - if m == 1: return (one, two) + if m == 1: + return (one, two) + (pm, pm1) = f(m // 2) - if (m & 1): + if m & 1: return (add(pm, pm1, one), double(pm1)) + return (double(pm), add(pm, pm1, one)) - ((x,z), _) = f(n) + + ((x, z), _) = f(n) return (x * inv(z)) % P -import random def genkey(n=0): - n = n or random.randint(0,P) + n = n or random.randint(0, P) n &= ~7 n &= ~(128 << 8 * 31) n |= 64 << 8 * 31 return n -#def str2int(s): + +# def str2int(s): # return int(hexlify(s), 16) # # return sum(ord(s[i]) << (8 * i) for i in range(32)) # -#def int2str(n): +# def int2str(n): # return unhexlify("%x" % n) # # return ''.join([chr((n >> (8 * i)) & 255) for i in range(32)]) ################################################# + def dsa_test(): import os - msg = str(random.randint(q,q+q)).encode('utf-8') + msg = str(random.randint(q, q + q)).encode('utf-8') sk = os.urandom(32) pk = publickey(sk) sig = signature(msg, sk, pk) return checkvalid(sig, msg, pk) + def dh_test(): sk1 = genkey() sk2 = genkey() return curve25519(sk1, curve25519(sk2)) == curve25519(sk2, curve25519(sk1)) - diff --git a/wheel/signatures/ed25519py.py b/wheel/signatures/ed25519py.py index 55eba2e..0c4ab8f 100644 --- a/wheel/signatures/ed25519py.py +++ b/wheel/signatures/ed25519py.py @@ -1,22 +1,21 @@ -# -*- coding: utf-8 -*- - -import warnings import os - +import warnings from collections import namedtuple + from . import djbec __all__ = ['crypto_sign', 'crypto_sign_open', 'crypto_sign_keypair', 'Keypair', 'PUBLICKEYBYTES', 'SECRETKEYBYTES', 'SIGNATUREBYTES'] -PUBLICKEYBYTES=32 -SECRETKEYBYTES=64 -SIGNATUREBYTES=64 +PUBLICKEYBYTES = 32 +SECRETKEYBYTES = 64 +SIGNATUREBYTES = 64 + +Keypair = namedtuple('Keypair', ('vk', 'sk')) # verifying key, secret key -Keypair = namedtuple('Keypair', ('vk', 'sk')) # verifying key, secret key def crypto_sign_keypair(seed=None): - """Return (verifying, secret) key from a given seed, or os.urandom(32)""" + """Return (verifying, secret) key from a given seed, or os.urandom(32)""" if seed is None: seed = os.urandom(PUBLICKEYBYTES) else: @@ -47,6 +46,5 @@ def crypto_sign_open(signed, vk): raise ValueError("Bad verifying key length %d" % len(vk)) rc = djbec.checkvalid(signed[:SIGNATUREBYTES], signed[SIGNATUREBYTES:], vk) if not rc: - raise ValueError("rc != True", rc) + raise ValueError("rc != True", rc) return signed[SIGNATUREBYTES:] - diff --git a/wheel/signatures/keys.py b/wheel/signatures/keys.py index 57d7feb..eb5d4ac 100644 --- a/wheel/signatures/keys.py +++ b/wheel/signatures/keys.py @@ -1,7 +1,7 @@ """Store and retrieve wheel signing / verifying keys. -Given a scope (a package name, + meaning "all packages", or - meaning -"no packages"), return a list of verifying keys that are trusted for that +Given a scope (a package name, + meaning "all packages", or - meaning +"no packages"), return a list of verifying keys that are trusted for that scope. Given a package name, return a list of (scope, key) suggested keys to sign @@ -33,15 +33,17 @@ wheel export key import json import os.path + from ..util import native, load_config_paths, save_config_path + class WheelKeys(object): SCHEMA = 1 CONFIG_NAME = 'wheel.json' - + def __init__(self): - self.data = {'signers':[], 'verifiers':[]} - + self.data = {'signers': [], 'verifiers': []} + def load(self): # XXX JSON is not a great database for path in load_config_paths('wheel'): @@ -50,7 +52,7 @@ class WheelKeys(object): with open(conf, 'r') as infile: self.data = json.load(infile) for x in ('signers', 'verifiers'): - if not x in self.data: + if x not in self.data: self.data[x] = [] if 'schema' not in self.data: self.data['schema'] = self.SCHEMA @@ -62,38 +64,38 @@ class WheelKeys(object): return self def save(self): - # Try not to call this a very long time after load() + # Try not to call this a very long time after load() path = save_config_path('wheel') conf = os.path.join(native(path), self.CONFIG_NAME) with open(conf, 'w+') as out: json.dump(self.data, out, indent=2) return self - + def trust(self, scope, vk): """Start trusting a particular key for given scope.""" - self.data['verifiers'].append({'scope':scope, 'vk':vk}) + self.data['verifiers'].append({'scope': scope, 'vk': vk}) return self - + def untrust(self, scope, vk): """Stop trusting a particular key for given scope.""" - self.data['verifiers'].remove({'scope':scope, 'vk':vk}) + self.data['verifiers'].remove({'scope': scope, 'vk': vk}) return self - + def trusted(self, scope=None): """Return list of [(scope, trusted key), ...] for given scope.""" - trust = [(x['scope'], x['vk']) for x in self.data['verifiers'] if x['scope'] in (scope, '+')] + trust = [(x['scope'], x['vk']) for x in self.data['verifiers'] + if x['scope'] in (scope, '+')] trust.sort(key=lambda x: x[0]) trust.reverse() return trust - + def signers(self, scope): """Return list of signing key(s).""" sign = [(x['scope'], x['vk']) for x in self.data['signers'] if x['scope'] in (scope, '+')] sign.sort(key=lambda x: x[0]) sign.reverse() return sign - + def add_signer(self, scope, vk): """Remember verifying key vk as being valid for signing in scope.""" - self.data['signers'].append({'scope':scope, 'vk':vk}) - + self.data['signers'].append({'scope': scope, 'vk': vk}) diff --git a/wheel/test/__init__.py b/wheel/test/__init__.py index 4287ca8..e69de29 100644 --- a/wheel/test/__init__.py +++ b/wheel/test/__init__.py @@ -1 +0,0 @@ -#
\ No newline at end of file diff --git a/wheel/test/complex-dist/setup.py b/wheel/test/complex-dist/setup.py index 615d5dc..0b3b76a 100644 --- a/wheel/test/complex-dist/setup.py +++ b/wheel/test/complex-dist/setup.py @@ -2,6 +2,7 @@ from setuptools import setup try: unicode + def u8(s): return s.decode('unicode-escape') except NameError: @@ -18,7 +19,7 @@ setup(name='complex-dist', packages=['complexdist'], setup_requires=["wheel", "setuptools"], install_requires=["quux", "splort"], - extras_require={'simple':['simple.dist']}, + extras_require={'simple': ['simple.dist']}, tests_require=["foo", "bar>=10.0.0"], entry_points={ 'console_scripts': [ @@ -27,4 +28,3 @@ setup(name='complex-dist', ], }, ) - diff --git a/wheel/test/conftest.py b/wheel/test/conftest.py index ad87cb3..8d5b073 100644 --- a/wheel/test/conftest.py +++ b/wheel/test/conftest.py @@ -12,7 +12,7 @@ import pytest def error_on_ResourceWarning():
"""This fixture captures ResourceWarning's and reports an "error"
describing the file handles left open.
-
+
This is shown regardless of how successful the test was, if a test fails
and leaves files open then those files will be reported. Ideally, even
those files should be closed properly after a test failure or exception.
@@ -35,11 +35,11 @@ def error_on_ResourceWarning(): # Python 3, PyPy3
with warnings.catch_warnings(record=True) as caught:
- warnings.resetwarnings() # clear all filters
- warnings.simplefilter('ignore') # ignore all
- warnings.simplefilter('always', ResourceWarning) # add filter
- yield # run tests in this context
- gc.collect() # run garbage collection (for pypy3)
+ warnings.resetwarnings() # clear all filters
+ warnings.simplefilter('ignore') # ignore all
+ warnings.simplefilter('always', ResourceWarning) # add filter
+ yield # run tests in this context
+ gc.collect() # run garbage collection (for pypy3)
if not caught:
return
pytest.fail('The following file descriptors were not closed properly:\n' +
diff --git a/wheel/test/extension.dist/setup.py b/wheel/test/extension.dist/setup.py index 7a66845..9e78469 100644 --- a/wheel/test/extension.dist/setup.py +++ b/wheel/test/extension.dist/setup.py @@ -2,6 +2,7 @@ from setuptools import setup, Extension try: unicode + def u8(s): return s.decode('unicode-escape').encode('utf-8') except NameError: @@ -12,9 +13,8 @@ setup(name='extension.dist', version='0.1', description=u8('A testing distribution \N{SNOWMAN}'), ext_modules=[ - Extension(name='extension', - sources=['extension.c'], - py_limited_api=True) + Extension(name='extension', + sources=['extension.c'], + py_limited_api=True) ], ) - diff --git a/wheel/test/headers.dist/setup.py b/wheel/test/headers.dist/setup.py index 2704f01..8e5273f 100644 --- a/wheel/test/headers.dist/setup.py +++ b/wheel/test/headers.dist/setup.py @@ -2,6 +2,7 @@ from setuptools import setup try: unicode + def u8(s): return s.decode('unicode-escape').encode('utf-8') except NameError: @@ -13,4 +14,3 @@ setup(name='headers.dist', description=u8('A distribution with headers'), headers=['header.h'] ) - diff --git a/wheel/test/simple.dist/setup.py b/wheel/test/simple.dist/setup.py index 50c909f..ef90683 100644 --- a/wheel/test/simple.dist/setup.py +++ b/wheel/test/simple.dist/setup.py @@ -2,6 +2,7 @@ from setuptools import setup try: unicode + def u8(s): return s.decode('unicode-escape').encode('utf-8') except NameError: @@ -14,4 +15,3 @@ setup(name='simple.dist', packages=['simpledist'], extras_require={'voting': ['beaglevote']}, ) - diff --git a/wheel/test/test_basic.py b/wheel/test/test_basic.py index 6bd46b1..e36c05a 100644 --- a/wheel/test/test_basic.py +++ b/wheel/test/test_basic.py @@ -2,23 +2,23 @@ Basic wheel tests. """ -import os -import pkg_resources import json +import os import sys +from shutil import rmtree +from zipfile import ZipFile +import pkg_resources from pkg_resources import resource_filename -import wheel.util import wheel.tool - +import wheel.util from wheel import egg2wheel from wheel.install import WheelFile -from zipfile import ZipFile -from shutil import rmtree test_distributions = ("complex-dist", "simple.dist", "headers.dist") + def teardown_module(): """Delete eggs/wheels created by tests.""" base = pkg_resources.resource_filename('wheel.test', '') @@ -29,10 +29,12 @@ def teardown_module(): except OSError: pass + def setup_module(): build_wheel() build_egg() + def build_wheel(): """Build wheels from test distributions.""" for dist in test_distributions: @@ -45,6 +47,7 @@ def build_wheel(): finally: os.chdir(pwd) + def build_egg(): """Build eggs from test distributions.""" for dist in test_distributions: @@ -57,10 +60,12 @@ def build_egg(): finally: os.chdir(pwd) + def test_findable(): """Make sure pkg_resources can find us.""" assert pkg_resources.working_set.by_key['wheel'].version + def test_egg_re(): """Make sure egg_info_re matches.""" egg_names_path = pkg_resources.resource_filename('wheel', 'eggnames.txt') @@ -71,17 +76,19 @@ def test_egg_re(): continue assert egg2wheel.egg_info_re.match(line), line + def test_compatibility_tags(): """Test compatibilty tags are working.""" wf = WheelFile("package-1.0.0-cp32.cp33-noabi-noarch.whl") assert (list(wf.compatibility_tags) == - [('cp32', 'noabi', 'noarch'), ('cp33', 'noabi', 'noarch')]) - assert (wf.arity == 2) + [('cp32', 'noabi', 'noarch'), ('cp33', 'noabi', 'noarch')]) + assert wf.arity == 2 wf2 = WheelFile("package-1.0.0-1st-cp33-noabi-noarch.whl") wf2_info = wf2.parsed_filename.groupdict() assert wf2_info['build'] == '1st', wf2_info + def test_convert_egg(): base = pkg_resources.resource_filename('wheel.test', '') for dist in test_distributions: @@ -89,6 +96,7 @@ def test_convert_egg(): eggs = [e for e in os.listdir(distdir) if e.endswith('.egg')] wheel.tool.convert(eggs, distdir, verbose=False) + def test_unpack(): """ Make sure 'wheel unpack' works. @@ -100,6 +108,7 @@ def test_unpack(): for wheelfile in (w for w in os.listdir(distdir) if w.endswith('.whl')): wheel.tool.unpack(os.path.join(distdir, wheelfile), distdir) + def test_no_scripts(): """Make sure entry point scripts are not generated.""" dist = "complex-dist" @@ -109,7 +118,8 @@ def test_no_scripts(): if filename.endswith('.whl'): whl = ZipFile(os.path.join(dirname, filename)) for entry in whl.infolist(): - assert not '.data/scripts/' in entry.filename + assert '.data/scripts/' not in entry.filename + def test_pydist(): """Make sure pydist.json exists and validates against our schema.""" @@ -137,6 +147,7 @@ def test_pydist(): valid += 1 assert valid > 0, "No metadata.json found" + def test_util(): """Test functions in util.py.""" for i in range(10): @@ -168,9 +179,11 @@ def test_pick_best(): ('cp27', 'noabi', 'linux_i686'), ('py27', 'noabi', 'noarch')] for supp in (supported, supported2, supported3): - context = lambda: list(supp) - for wheel in cand_wheels: - wheel.context = context + def context(): + return list(supp) + + for wheel_ in cand_wheels: + wheel_.context = context best = max(cand_wheels) assert list(best.tags)[0] == supp[0] diff --git a/wheel/test/test_install.py b/wheel/test/test_install.py index ddcddf5..46ea02e 100644 --- a/wheel/test/test_install.py +++ b/wheel/test/test_install.py @@ -12,23 +12,27 @@ # The root is PLATLIB
# So, some in PLATLIB, and one in each of DATA, HEADERS and SCRIPTS.
-import wheel.tool
+import os
+import shutil
+from tempfile import mkdtemp
+
import wheel.pep425tags
+import wheel.tool
from wheel.install import WheelFile
-from tempfile import mkdtemp
-import shutil
-import os
THISDIR = os.path.dirname(__file__)
TESTWHEEL = os.path.join(THISDIR, 'test-1.0-py2.py3-none-win32.whl')
+
def check(*path):
return os.path.exists(os.path.join(*path))
+
def test_install():
- tempdir = mkdtemp()
def get_supported():
return list(wheel.pep425tags.get_supported()) + [('py3', 'none', 'win32')]
+
+ tempdir = mkdtemp()
whl = WheelFile(TESTWHEEL, context=get_supported)
assert whl.supports_current_python(get_supported)
try:
@@ -48,8 +52,7 @@ def test_install(): finally:
shutil.rmtree(tempdir)
+
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 diff --git a/wheel/test/test_keys.py b/wheel/test/test_keys.py index f96166b..9a74e29 100644 --- a/wheel/test/test_keys.py +++ b/wheel/test/test_keys.py @@ -9,76 +9,78 @@ wheel_json = """ { "verifiers": [ { - "scope": "+", + "scope": "+", "vk": "bp-bjK2fFgtA-8DhKKAAPm9-eAZcX_u03oBv2RlKOBc" - }, + }, { - "scope": "+", + "scope": "+", "vk": "KAHZBfyqFW3OcFDbLSG4nPCjXxUPy72phP9I4Rn9MAo" }, { - "scope": "+", + "scope": "+", "vk": "tmAYCrSfj8gtJ10v3VkvW7jOndKmQIYE12hgnFu3cvk" - } - ], + } + ], "signers": [ { - "scope": "+", + "scope": "+", "vk": "tmAYCrSfj8gtJ10v3VkvW7jOndKmQIYE12hgnFu3cvk" - }, + }, { - "scope": "+", + "scope": "+", "vk": "KAHZBfyqFW3OcFDbLSG4nPCjXxUPy72phP9I4Rn9MAo" } - ], + ], "schema": 1 } """ + class TestWheelKeys(unittest.TestCase): def setUp(self): - self.config = tempfile.NamedTemporaryFile(suffix='.json') - self.config.close() - - self.config_path, self.config_filename = os.path.split(self.config.name) def load(*args): return [self.config_path] + def save(*args): return self.config_path + + self.config = tempfile.NamedTemporaryFile(suffix='.json') + self.config.close() + self.config_path, self.config_filename = os.path.split(self.config.name) keys.load_config_paths = load keys.save_config_path = save self.wk = keys.WheelKeys() self.wk.CONFIG_NAME = self.config_filename - + def tearDown(self): os.unlink(self.config.name) - + def test_load_save(self): self.wk.data = json.loads(wheel_json) - + self.wk.add_signer('+', '67890') self.wk.add_signer('scope', 'abcdefg') - + self.wk.trust('epocs', 'gfedcba') self.wk.trust('+', '12345') - + self.wk.save() - + del self.wk.data self.wk.load() - + signers = self.wk.signers('scope') self.assertTrue(signers[0] == ('scope', 'abcdefg'), self.wk.data['signers']) self.assertTrue(signers[1][0] == '+', self.wk.data['signers']) - + trusted = self.wk.trusted('epocs') self.assertTrue(trusted[0] == ('epocs', 'gfedcba')) self.assertTrue(trusted[1][0] == '+') - + self.wk.untrust('epocs', 'gfedcba') trusted = self.wk.trusted('epocs') self.assertTrue(('epocs', 'gfedcba') not in trusted) - + def test_load_save_incomplete(self): self.wk.data = json.loads(wheel_json) del self.wk.data['signers'] @@ -90,9 +92,7 @@ class TestWheelKeys(unittest.TestCase): pass else: raise Exception("Expected ValueError") - + del self.wk.data['schema'] self.wk.save() self.wk.load() - - diff --git a/wheel/test/test_paths.py b/wheel/test/test_paths.py index a23d506..7aed099 100644 --- a/wheel/test/test_paths.py +++ b/wheel/test/test_paths.py @@ -1,6 +1,8 @@ -import wheel.paths from distutils.command.install import SCHEME_KEYS +import wheel.paths + + def test_path(): d = wheel.paths.get_install_paths('wheel') assert len(d) == len(SCHEME_KEYS) diff --git a/wheel/test/test_ranking.py b/wheel/test/test_ranking.py index 1632a13..64423f5 100644 --- a/wheel/test/test_ranking.py +++ b/wheel/test/test_ranking.py @@ -4,11 +4,13 @@ from wheel.pep425tags import get_supported from wheel.install import WheelFile WHEELPAT = "%(name)s-%(ver)s-%(pyver)s-%(abi)s-%(arch)s.whl" + + def make_wheel(name, ver, pyver, abi, arch): - name = WHEELPAT % dict(name=name, ver=ver, pyver=pyver, abi=abi, - arch=arch) + name = WHEELPAT % dict(name=name, ver=ver, pyver=pyver, abi=abi, arch=arch) return WheelFile(name) + # This relies on the fact that generate_supported will always return the # exact pyver, abi, and architecture for its first (best) match. sup = get_supported() @@ -30,14 +32,15 @@ COMBINATIONS = ( # This will not be compatible for Python x.0. Beware when we hit Python # 4.0, and don't test with 3.0!!! ('foo', '2.1', majver + '1', 'none', 'any'), - ('foo', '2.1', pyver , 'none', 'any'), - ('foo', '2.1', pyver , abi, arch), + ('foo', '2.1', pyver, 'none', 'any'), + ('foo', '2.1', pyver, abi, arch), ) -WHEELS = [ make_wheel(*args) for args in COMBINATIONS ] +WHEELS = [make_wheel(*args) for args in COMBINATIONS] + class TestRanking(unittest.TestCase): def test_comparison(self): for i in range(len(WHEELS)-1): for j in range(i): - self.assertTrue(WHEELS[j]<WHEELS[i]) + self.assertTrue(WHEELS[j] < WHEELS[i]) diff --git a/wheel/test/test_signatures.py b/wheel/test/test_signatures.py index 0af19a7..aa0eb23 100644 --- a/wheel/test/test_signatures.py +++ b/wheel/test/test_signatures.py @@ -2,46 +2,48 @@ from wheel import signatures from wheel.signatures import djbec, ed25519py from wheel.util import binary + def test_getlib(): signatures.get_ed25519ll() + def test_djbec(): - djbec.dsa_test() + djbec.dsa_test() djbec.dh_test() - + + def test_ed25519py(): kp0 = ed25519py.crypto_sign_keypair(binary(' '*32)) kp = ed25519py.crypto_sign_keypair() - + signed = ed25519py.crypto_sign(binary('test'), kp.sk) - + ed25519py.crypto_sign_open(signed, kp.vk) - + try: ed25519py.crypto_sign_open(signed, kp0.vk) except ValueError: pass else: raise Exception("Expected ValueError") - + try: ed25519py.crypto_sign_keypair(binary(' '*33)) except ValueError: pass else: raise Exception("Expected ValueError") - + try: ed25519py.crypto_sign(binary(''), binary(' ')*31) except ValueError: pass else: raise Exception("Expected ValueError") - + try: ed25519py.crypto_sign_open(binary(''), binary(' ')*31) except ValueError: pass else: raise Exception("Expected ValueError") -
\ No newline at end of file diff --git a/wheel/test/test_tagopt.py b/wheel/test/test_tagopt.py index b0d083e..03a5f0c 100644 --- a/wheel/test/test_tagopt.py +++ b/wheel/test/test_tagopt.py @@ -24,11 +24,13 @@ setup( EXT_MODULES = "ext_modules=[Extension('_test', sources=['test.c'])]," + @pytest.fixture def temp_pkg(request, ext=False): - tempdir = tempfile.mkdtemp() def fin(): shutil.rmtree(tempdir) + + tempdir = tempfile.mkdtemp() request.addfinalizer(fin) temppath = py.path.local(tempdir) temppath.join('test.py').write('print("Hello, world")') @@ -40,13 +42,14 @@ def temp_pkg(request, ext=False): temppath.join('setup.py').write(setup_py) return temppath + @pytest.fixture def temp_ext_pkg(request): return temp_pkg(request, ext=True) + def test_default_tag(temp_pkg): - subprocess.check_call([sys.executable, 'setup.py', 'bdist_wheel'], - cwd=str(temp_pkg)) + subprocess.check_call([sys.executable, 'setup.py', 'bdist_wheel'], cwd=str(temp_pkg)) dist_dir = temp_pkg.join('dist') assert dist_dir.check(dir=1) wheels = dist_dir.listdir() @@ -54,6 +57,7 @@ def test_default_tag(temp_pkg): assert wheels[0].basename == 'Test-1.0-py%s-none-any.whl' % (sys.version[0],) assert wheels[0].ext == '.whl' + def test_explicit_tag(temp_pkg): subprocess.check_call( [sys.executable, 'setup.py', 'bdist_wheel', '--python-tag=py32'], @@ -65,6 +69,7 @@ def test_explicit_tag(temp_pkg): assert wheels[0].basename.startswith('Test-1.0-py32-') assert wheels[0].ext == '.whl' + def test_universal_tag(temp_pkg): subprocess.check_call( [sys.executable, 'setup.py', 'bdist_wheel', '--universal'], @@ -76,6 +81,7 @@ def test_universal_tag(temp_pkg): assert wheels[0].basename.startswith('Test-1.0-py2.py3-') assert wheels[0].ext == '.whl' + def test_universal_beats_explicit_tag(temp_pkg): subprocess.check_call( [sys.executable, 'setup.py', 'bdist_wheel', '--universal', '--python-tag=py32'], @@ -87,6 +93,7 @@ def test_universal_beats_explicit_tag(temp_pkg): assert wheels[0].basename.startswith('Test-1.0-py2.py3-') assert wheels[0].ext == '.whl' + def test_universal_in_setup_cfg(temp_pkg): temp_pkg.join('setup.cfg').write('[bdist_wheel]\nuniversal=1') subprocess.check_call( @@ -99,6 +106,7 @@ def test_universal_in_setup_cfg(temp_pkg): assert wheels[0].basename.startswith('Test-1.0-py2.py3-') assert wheels[0].ext == '.whl' + def test_pythontag_in_setup_cfg(temp_pkg): temp_pkg.join('setup.cfg').write('[bdist_wheel]\npython_tag=py32') subprocess.check_call( @@ -111,6 +119,7 @@ def test_pythontag_in_setup_cfg(temp_pkg): assert wheels[0].basename.startswith('Test-1.0-py32-') assert wheels[0].ext == '.whl' + def test_legacy_wheel_section_in_setup_cfg(temp_pkg): temp_pkg.join('setup.cfg').write('[wheel]\nuniversal=1') subprocess.check_call( @@ -123,6 +132,7 @@ def test_legacy_wheel_section_in_setup_cfg(temp_pkg): assert wheels[0].basename.startswith('Test-1.0-py2.py3-') assert wheels[0].ext == '.whl' + def test_plat_name_purepy(temp_pkg): subprocess.check_call( [sys.executable, 'setup.py', 'bdist_wheel', '--plat-name=testplat.pure'], @@ -134,6 +144,7 @@ def test_plat_name_purepy(temp_pkg): assert wheels[0].basename.endswith('-testplat_pure.whl') assert wheels[0].ext == '.whl' + def test_plat_name_ext(temp_ext_pkg): try: subprocess.check_call( @@ -148,6 +159,7 @@ def test_plat_name_ext(temp_ext_pkg): assert wheels[0].basename.endswith('-testplat_arch.whl') assert wheels[0].ext == '.whl' + def test_plat_name_purepy_in_setupcfg(temp_pkg): temp_pkg.join('setup.cfg').write('[bdist_wheel]\nplat_name=testplat.pure') subprocess.check_call( @@ -160,6 +172,7 @@ def test_plat_name_purepy_in_setupcfg(temp_pkg): assert wheels[0].basename.endswith('-testplat_pure.whl') assert wheels[0].ext == '.whl' + def test_plat_name_ext_in_setupcfg(temp_ext_pkg): temp_ext_pkg.join('setup.cfg').write('[bdist_wheel]\nplat_name=testplat.arch') try: diff --git a/wheel/test/test_tool.py b/wheel/test/test_tool.py index 1f3b11b..f37c836 100644 --- a/wheel/test/test_tool.py +++ b/wheel/test/test_tool.py @@ -1,5 +1,6 @@ from wheel import tool + def test_keygen(): def get_keyring(): WheelKeys, keyring = tool.get_keyring() @@ -13,8 +14,10 @@ def test_keygen(): def get_keyring(cls): class keyringTest2: pw = None + def set_password(self, a, b, c): self.pw = c + def get_password(self, a, b): return self.pw diff --git a/wheel/test/test_wheelfile.py b/wheel/test/test_wheelfile.py index 181668f..1d559a3 100644 --- a/wheel/test/test_wheelfile.py +++ b/wheel/test/test_wheelfile.py @@ -1,17 +1,21 @@ +import codecs +import hashlib import os -import wheel.install +import shutil +import tempfile +import zipfile +from contextlib import contextmanager + +import pytest + import wheel.archive -import hashlib +import wheel.install + try: from StringIO import StringIO except ImportError: from io import BytesIO as StringIO -import codecs -import zipfile -import pytest -import shutil -import tempfile -from contextlib import contextmanager + @contextmanager def environ(key, value): @@ -25,6 +29,7 @@ def environ(key, value): else: os.environ[key] = old_value + @contextmanager def temporary_directory(): # tempfile.TemporaryDirectory doesn't exist in Python 2. @@ -34,6 +39,7 @@ def temporary_directory(): finally: shutil.rmtree(tempdir) + @contextmanager def readable_zipfile(path): # zipfile.ZipFile() isn't a context manager under Python 2. @@ -47,14 +53,14 @@ def readable_zipfile(path): def test_verifying_zipfile(): if not hasattr(zipfile.ZipExtFile, '_update_crc'): pytest.skip('No ZIP verification. Missing ZipExtFile._update_crc.') - + sio = StringIO() zf = zipfile.ZipFile(sio, 'w') zf.writestr("one", b"first file") zf.writestr("two", b"second file") zf.writestr("three", b"third file") zf.close() - + # In default mode, VerifyingZipFile checks the hash of any read file # mentioned with set_expected_hash(). Files not mentioned with # set_expected_hash() are not checked. @@ -69,7 +75,7 @@ def test_verifying_zipfile(): pass else: raise Exception("expected exception 'BadWheelFile()'") - + # In strict mode, VerifyingZipFile requires every read file to be # mentioned with set_expected_hash(). vzf.strict = True @@ -79,31 +85,33 @@ def test_verifying_zipfile(): pass else: raise Exception("expected exception 'BadWheelFile()'") - + vzf.set_expected_hash("two", None) vzf.open("two").read() - + + def test_pop_zipfile(): sio = StringIO() zf = wheel.install.VerifyingZipFile(sio, 'w') zf.writestr("one", b"first file") zf.writestr("two", b"second file") zf.close() - + try: zf.pop() except RuntimeError: - pass # already closed + pass # already closed else: raise Exception("expected RuntimeError") - + zf = wheel.install.VerifyingZipFile(sio, 'a') zf.pop() zf.close() - + zf = wheel.install.VerifyingZipFile(sio, 'r') assert len(zf.infolist()) == 1 + def test_zipfile_timestamp(): # An environment variable can be used to influence the timestamp on # TarInfo objects inside the zip. See issue #143. TemporaryDirectory is @@ -122,6 +130,7 @@ def test_zipfile_timestamp(): for info in zf.infolist(): assert info.date_time[:3] == (1980, 1, 1) + def test_zipfile_attributes(): # With the change from ZipFile.write() to .writestr(), we need to manually # set member attributes. diff --git a/wheel/tool/__init__.py b/wheel/tool/__init__.py index 4c0187b..a55752d 100644 --- a/wheel/tool/__init__.py +++ b/wheel/tool/__init__.py @@ -2,27 +2,29 @@ Wheel command-line utility. """ -import os +import argparse import hashlib -import sys import json - +import os +import sys from glob import iglob + from .. import signatures -from ..util import (urlsafe_b64decode, urlsafe_b64encode, native, binary, - matches_requirement) from ..install import WheelFile, VerifyingZipFile from ..paths import get_install_command +from ..util import urlsafe_b64decode, urlsafe_b64encode, native, binary, matches_requirement + def require_pkgresources(name): try: - import pkg_resources + import pkg_resources # noqa: F401 except ImportError: raise RuntimeError("'{0}' needs pkg_resources (part of setuptools).".format(name)) -import argparse -class WheelError(Exception): pass +class WheelError(Exception): + pass + # For testability def get_keyring(): @@ -31,9 +33,12 @@ def get_keyring(): import keyring assert keyring.get_keyring().priority except (ImportError, AssertionError): - raise WheelError("Install wheel[signatures] (requires keyring, keyrings.alt, pyxdg) for signatures.") + raise WheelError( + "Install wheel[signatures] (requires keyring, keyrings.alt, pyxdg) for signatures.") + return keys.WheelKeys, keyring + def keygen(get_keyring=get_keyring): """Generate a public/private key pair.""" WheelKeys, keyring = get_keyring() @@ -59,6 +64,7 @@ def keygen(get_keyring=get_keyring): wk.trust('+', vk) wk.save() + def sign(wheelfile, replace=False, get_keyring=get_keyring): """Sign a wheel""" WheelKeys, keyring = get_keyring() @@ -78,17 +84,17 @@ def sign(wheelfile, replace=False, get_keyring=get_keyring): keypair = ed25519ll.Keypair(urlsafe_b64decode(binary(vk)), urlsafe_b64decode(binary(sk))) - record_name = wf.distinfo_name + '/RECORD' sig_name = wf.distinfo_name + '/RECORD.jws' if sig_name in wf.zipfile.namelist(): raise WheelError("Wheel is already signed.") record_data = wf.zipfile.read(record_name) - payload = {"hash":"sha256=" + native(urlsafe_b64encode(hashlib.sha256(record_data).digest()))} + payload = {"hash": "sha256=" + native(urlsafe_b64encode(hashlib.sha256(record_data).digest()))} sig = signatures.sign(payload, keypair) wf.zipfile.writestr(sig_name, json.dumps(sig, sort_keys=True)) wf.zipfile.close() + def unsign(wheelfile): """ Remove RECORD.jws from a wheel by truncating the zip file. @@ -104,6 +110,7 @@ def unsign(wheelfile): vzf.pop() vzf.close() + def verify(wheelfile): """Verify a wheel. @@ -119,6 +126,7 @@ def verify(wheelfile): sys.stdout.write(json.dumps(verified, indent=2)) sys.stdout.write('\n') + def unpack(wheelfile, dest='.'): """Unpack a wheel. @@ -135,6 +143,7 @@ def unpack(wheelfile, dest='.'): wf.zipfile.extractall(destination) wf.zipfile.close() + def install(requirements, requirements_file=None, wheel_dirs=None, force=False, list_files=False, dry_run=False): @@ -155,7 +164,7 @@ def install(requirements, requirements_file=None, if wheelpath: wheel_dirs = wheelpath.split(os.pathsep) else: - wheel_dirs = [ os.path.curdir ] + wheel_dirs = [os.path.curdir] # Get a list of all valid wheels in wheel_dirs all_wheels = [] @@ -220,6 +229,7 @@ def install(requirements, requirements_file=None, wf.install(force=force) wf.zipfile.close() + def install_scripts(distributions): """ Regenerate the entry_points console_scripts for the named distribution. @@ -234,10 +244,11 @@ def install_scripts(distributions): pkg_resources_dist = pkg_resources.get_distribution(dist) install = get_install_command(dist) command = easy_install.easy_install(install.distribution) - command.args = ['wheel'] # dummy argument + command.args = ['wheel'] # dummy argument command.finalize_options() command.install_egg_scripts(pkg_resources_dist) + def convert(installers, dest_dir, verbose): require_pkgresources('wheel convert') @@ -258,6 +269,7 @@ def convert(installers, dest_dir, verbose): if verbose: sys.stdout.write("OK\n") + def parser(): p = argparse.ArgumentParser() s = p.add_subparsers(help="commands") @@ -327,7 +339,7 @@ def parser(): convert_parser = s.add_parser('convert', help='Convert egg or wininst to wheel') convert_parser.add_argument('installers', nargs='*', help='Installers to convert') convert_parser.add_argument('--dest-dir', '-d', default=os.path.curdir, - help="Directory to store wheels (default %(default)s)") + help="Directory to store wheels (default %(default)s)") convert_parser.add_argument('--verbose', '-v', action='store_true') convert_parser.set_defaults(func=convert_f) @@ -344,6 +356,7 @@ def parser(): return p + def main(): p = parser() args = p.parse_args() diff --git a/wheel/util.py b/wheel/util.py index 20f386f..1878fb2 100644 --- a/wheel/util.py +++ b/wheel/util.py @@ -1,15 +1,32 @@ """Utility functions.""" -import sys -import os import base64 -import json import hashlib +import json +import os +import sys from collections import OrderedDict __all__ = ['urlsafe_b64encode', 'urlsafe_b64decode', 'utf8', 'to_json', 'from_json', 'matches_requirement'] + +# For encoding ascii back and forth between bytestrings, as is repeatedly +# necessary in JSON-based crypto under Python 3 +if sys.version_info[0] < 3: + text_type = unicode # noqa: F821 + + def native(s): + return s +else: + text_type = str + + def native(s): + if isinstance(s, bytes): + return s.decode('ascii') + return s + + def urlsafe_b64encode(data): """urlsafe_b64encode without padding""" return base64.urlsafe_b64encode(data).rstrip(binary('=')) @@ -22,57 +39,38 @@ def urlsafe_b64decode(data): def to_json(o): - '''Convert given data to JSON.''' + """Convert given data to JSON.""" return json.dumps(o, sort_keys=True) def from_json(j): - '''Decode a JSON payload.''' + """Decode a JSON payload.""" return json.loads(j) + def open_for_csv(name, mode): if sys.version_info[0] < 3: nl = {} bin = 'b' else: - nl = { 'newline': '' } + nl = {'newline': ''} bin = '' + return open(name, mode + bin, **nl) -try: - unicode - - def utf8(data): - '''Utf-8 encode data.''' - if isinstance(data, unicode): - return data.encode('utf-8') - return data -except NameError: - def utf8(data): - '''Utf-8 encode data.''' - if isinstance(data, str): - return data.encode('utf-8') - return data - - -try: - # For encoding ascii back and forth between bytestrings, as is repeatedly - # necessary in JSON-based crypto under Python 3 - unicode - def native(s): - return s - def binary(s): - if isinstance(s, unicode): - return s.encode('ascii') - return s -except NameError: - def native(s): - if isinstance(s, bytes): - return s.decode('ascii') - return s - def binary(s): - if isinstance(s, str): - return s.encode('ascii') + +def utf8(data): + """Utf-8 encode data.""" + if isinstance(data, text_type): + return data.encode('utf-8') + return data + + +def binary(s): + if isinstance(s, text_type): + return s.encode('ascii') + return s + class HashingFile(object): def __init__(self, fd, hashtype='sha256'): @@ -80,18 +78,22 @@ class HashingFile(object): self.hashtype = hashtype self.hash = hashlib.new(hashtype) self.length = 0 + def write(self, data): self.hash.update(data) self.length += len(data) self.fd.write(data) + def close(self): self.fd.close() + def digest(self): if self.hashtype == 'md5': return self.hash.hexdigest() digest = self.hash.digest() return self.hashtype + '=' + native(urlsafe_b64encode(digest)) + class OrderedDefaultDict(OrderedDict): def __init__(self, *args, **kwargs): if not args: @@ -103,18 +105,19 @@ class OrderedDefaultDict(OrderedDict): args = args[1:] super(OrderedDefaultDict, self).__init__(*args, **kwargs) - def __missing__ (self, key): + def __missing__(self, key): if self.default_factory is None: raise KeyError(key) self[key] = default = self.default_factory() return default + if sys.platform == 'win32': import ctypes.wintypes # CSIDL_APPDATA for reference - not used here for compatibility with # dirspec, which uses LOCAL_APPDATA and COMMON_APPDATA in that order - csidl = dict(CSIDL_APPDATA=26, CSIDL_LOCAL_APPDATA=28, - CSIDL_COMMON_APPDATA=35) + csidl = dict(CSIDL_APPDATA=26, CSIDL_LOCAL_APPDATA=28, CSIDL_COMMON_APPDATA=35) + def get_path(name): SHGFP_TYPE_CURRENT = 0 buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH) @@ -127,6 +130,7 @@ if sys.platform == 'win32': if not os.path.isdir(path): os.makedirs(path) return path + def load_config_paths(*resource): ids = ["CSIDL_LOCAL_APPDATA", "CSIDL_COMMON_APPDATA"] for id in ids: @@ -138,10 +142,12 @@ else: def save_config_path(*resource): import xdg.BaseDirectory return xdg.BaseDirectory.save_config_path(*resource) + def load_config_paths(*resource): import xdg.BaseDirectory return xdg.BaseDirectory.load_config_paths(*resource) + def matches_requirement(req, wheels): """List of wheels matching a requirement. diff --git a/wheel/wininst2wheel.py b/wheel/wininst2wheel.py index 15f0cdf..b8a3469 100755 --- a/wheel/wininst2wheel.py +++ b/wheel/wininst2wheel.py @@ -1,23 +1,24 @@ #!/usr/bin/env python +import distutils.dist import os.path import re import sys import tempfile import zipfile -import wheel.bdist_wheel -import distutils.dist -from distutils.archive_util import make_archive -from shutil import rmtree -from wheel.archive import archive_wheelfile from argparse import ArgumentParser from glob import iglob +from shutil import rmtree + +import wheel.bdist_wheel +from wheel.archive import archive_wheelfile egg_info_re = re.compile(r'''(^|/)(?P<name>[^/]+?)-(?P<ver>.+?) (-(?P<pyver>.+?))?(-(?P<arch>.+?))?.egg-info(/|$)''', re.VERBOSE) + def parse_info(wininfo_name, egginfo_name): """Extract metadata from filenames. - + Extracts the 4 metadataitems needed (name, version, pyversion, arch) from the installer filename and the name of the egg-info directory embedded in the zipfile (if any). @@ -52,15 +53,14 @@ def parse_info(wininfo_name, egginfo_name): if egginfo_name: egginfo = egg_info_re.search(egginfo_name) if not egginfo: - raise ValueError("Egg info filename %s is not valid" % - (egginfo_name,)) + raise ValueError("Egg info filename %s is not valid" % (egginfo_name,)) # Parse the wininst filename # 1. Distribution name (up to the first '-') w_name, sep, rest = wininfo_name.partition('-') if not sep: - raise ValueError("Installer filename %s is not valid" % - (wininfo_name,)) + raise ValueError("Installer filename %s is not valid" % (wininfo_name,)) + # Strip '.exe' rest = rest[:-4] # 2. Python version (from the last '-', must start with 'py') @@ -78,8 +78,7 @@ def parse_info(wininfo_name, egginfo_name): # 3. Version and architecture w_ver, sep, w_arch = rest.rpartition('.') if not sep: - raise ValueError("Installer filename %s is not valid" % - (wininfo_name,)) + raise ValueError("Installer filename %s is not valid" % (wininfo_name,)) if egginfo: w_name = egginfo.group('name') @@ -87,6 +86,7 @@ def parse_info(wininfo_name, egginfo_name): return dict(name=w_name, ver=w_ver, arch=w_arch, pyver=w_pyver) + def bdist_wininst2wheel(path, dest_dir=os.path.curdir): bdw = zipfile.ZipFile(path) @@ -176,7 +176,7 @@ def bdist_wininst2wheel(path, dest_dir=os.path.curdir): bw.egg2dist(os.path.join(dir, egginfo_name), dist_info_dir) bw.write_wheelfile(dist_info_dir, generator='wininst2wheel') bw.write_record(dir, dist_info_dir) - + archive_wheelfile(os.path.join(dest_dir, wheel_name), dir) rmtree(dir) @@ -201,7 +201,7 @@ def main(): parser = ArgumentParser() parser.add_argument('installers', nargs='*', help="Installers to convert") parser.add_argument('--dest-dir', '-d', default=os.path.curdir, - help="Directory to store wheels (default %(default)s)") + help="Directory to store wheels (default %(default)s)") parser.add_argument('--verbose', '-v', action='store_true') args = parser.parse_args() for pat in args.installers: @@ -212,5 +212,6 @@ def main(): if args.verbose: sys.stdout.write("OK\n") + if __name__ == "__main__": main() |
