From ff497abc7d922edf8a0221946dd41445be7f968c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 12 Nov 2017 13:40:27 -0500 Subject: Removing everything --- CHANGES.txt | 281 -------------- LICENSE.txt | 22 -- MANIFEST.in | 7 - METADATA.in | 34 -- README.txt | 50 --- appveyor.yml | 5 - bento.info | 11 - docs/Makefile | 153 -------- docs/api.rst | 30 -- docs/conf.py | 242 ------------ docs/index.rst | 225 ----------- docs/make.bat | 190 ---------- docs/story.rst | 62 --- entry_points.txt | 5 - pylintrc | 249 ------------ setup.cfg | 9 - setup.py | 66 ---- tox.ini | 17 - vendorize.sh | 3 - wheel/__init__.py | 2 - wheel/__main__.py | 17 - wheel/archive.py | 79 ---- wheel/bdist_wheel.py | 467 ----------------------- wheel/decorator.py | 19 - wheel/egg2wheel.py | 86 ----- wheel/eggnames.txt | 87 ----- wheel/install.py | 481 ------------------------ wheel/metadata.py | 330 ---------------- wheel/paths.py | 41 -- wheel/pep425tags.py | 178 --------- wheel/pkginfo.py | 44 --- wheel/signatures/__init__.py | 106 ------ wheel/signatures/djbec.py | 270 ------------- wheel/signatures/ed25519py.py | 52 --- wheel/signatures/keys.py | 99 ----- wheel/test/__init__.py | 1 - wheel/test/complex-dist/complexdist/__init__.py | 2 - wheel/test/complex-dist/setup.py | 30 -- wheel/test/conftest.py | 45 --- wheel/test/extension.dist/extension.c | 2 - wheel/test/extension.dist/setup.cfg | 2 - wheel/test/extension.dist/setup.py | 20 - wheel/test/headers.dist/header.h | 0 wheel/test/headers.dist/headersdist.py | 0 wheel/test/headers.dist/setup.cfg | 2 - wheel/test/headers.dist/setup.py | 16 - wheel/test/pydist-schema.json | 362 ------------------ wheel/test/simple.dist/setup.py | 17 - wheel/test/simple.dist/simpledist/__init__.py | 0 wheel/test/test-1.0-py2.py3-none-win32.whl | Bin 5226 -> 0 bytes wheel/test/test_basic.py | 178 --------- wheel/test/test_install.py | 55 --- wheel/test/test_keys.py | 98 ----- wheel/test/test_paths.py | 6 - wheel/test/test_ranking.py | 43 --- wheel/test/test_signatures.py | 47 --- wheel/test/test_tagopt.py | 176 --------- wheel/test/test_tool.py | 25 -- wheel/test/test_wheelfile.py | 142 ------- wheel/tool/__init__.py | 359 ------------------ wheel/util.py | 164 -------- wheel/wininst2wheel.py | 216 ----------- wscript | 133 ------- 63 files changed, 6160 deletions(-) delete mode 100644 CHANGES.txt delete mode 100644 LICENSE.txt delete mode 100644 MANIFEST.in delete mode 100644 METADATA.in delete mode 100644 README.txt delete mode 100644 appveyor.yml delete mode 100644 bento.info delete mode 100644 docs/Makefile delete mode 100644 docs/api.rst delete mode 100644 docs/conf.py delete mode 100644 docs/index.rst delete mode 100644 docs/make.bat delete mode 100644 docs/story.rst delete mode 100644 entry_points.txt delete mode 100644 pylintrc delete mode 100644 setup.cfg delete mode 100644 setup.py delete mode 100644 tox.ini delete mode 100755 vendorize.sh delete mode 100644 wheel/__init__.py delete mode 100644 wheel/__main__.py delete mode 100644 wheel/archive.py delete mode 100644 wheel/bdist_wheel.py delete mode 100644 wheel/decorator.py delete mode 100755 wheel/egg2wheel.py delete mode 100644 wheel/eggnames.txt delete mode 100644 wheel/install.py delete mode 100644 wheel/metadata.py delete mode 100644 wheel/paths.py delete mode 100644 wheel/pep425tags.py delete mode 100644 wheel/pkginfo.py delete mode 100644 wheel/signatures/__init__.py delete mode 100644 wheel/signatures/djbec.py delete mode 100644 wheel/signatures/ed25519py.py delete mode 100644 wheel/signatures/keys.py delete mode 100644 wheel/test/__init__.py delete mode 100644 wheel/test/complex-dist/complexdist/__init__.py delete mode 100644 wheel/test/complex-dist/setup.py delete mode 100644 wheel/test/conftest.py delete mode 100644 wheel/test/extension.dist/extension.c delete mode 100644 wheel/test/extension.dist/setup.cfg delete mode 100644 wheel/test/extension.dist/setup.py delete mode 100644 wheel/test/headers.dist/header.h delete mode 100644 wheel/test/headers.dist/headersdist.py delete mode 100644 wheel/test/headers.dist/setup.cfg delete mode 100644 wheel/test/headers.dist/setup.py delete mode 100644 wheel/test/pydist-schema.json delete mode 100644 wheel/test/simple.dist/setup.py delete mode 100644 wheel/test/simple.dist/simpledist/__init__.py delete mode 100644 wheel/test/test-1.0-py2.py3-none-win32.whl delete mode 100644 wheel/test/test_basic.py delete mode 100644 wheel/test/test_install.py delete mode 100644 wheel/test/test_keys.py delete mode 100644 wheel/test/test_paths.py delete mode 100644 wheel/test/test_ranking.py delete mode 100644 wheel/test/test_signatures.py delete mode 100644 wheel/test/test_tagopt.py delete mode 100644 wheel/test/test_tool.py delete mode 100644 wheel/test/test_wheelfile.py delete mode 100644 wheel/tool/__init__.py delete mode 100644 wheel/util.py delete mode 100755 wheel/wininst2wheel.py delete mode 100644 wscript diff --git a/CHANGES.txt b/CHANGES.txt deleted file mode 100644 index dfbb12b..0000000 --- a/CHANGES.txt +++ /dev/null @@ -1,281 +0,0 @@ -0.30.0 -====== -- Add py-limited-api {cp32|cp33|cp34|...} flag to produce cpNN.abi3.{arch} - tags on CPython 3. -- Improve Python, abi tagging for `wheel convert`. Thanks Ales Erjavec. -- Much improved use of context managers for file handling. Thanks Kyle - Stewart. -- Convert absolute imports to relative. Thanks Ashish Bhate. -- Remove support for Python 2.6. - -0.29.0 -====== -- Fix compression type of files in archive (Issue #155, Pull Request #62, - thanks Xavier Fernandez) - -0.28.0 -====== -- Fix file modes in archive (Issue #154) - -0.27.0 -====== -- Support forcing a platform tag using `--plat-name` on pure-Python wheels, as - well as nonstandard platform tags on non-pure wheels (Pull Request #60, Issue - #144, thanks Andrés Díaz) -- Add SOABI tags to platform-specific wheels built for Python 2.X (Pull Request - #55, Issue #63, Issue #101) -- Support reproducible wheel files, wheels that can be rebuilt and will hash to - the same values as previous builds (Pull Request #52, Issue #143, thanks - Barry Warsaw) -- Support for changes in keyring >= 8.0 (Pull Request #61, thanks Jason R. - Coombs) -- Use the file context manager when checking if dependency_links.txt is empty, - fixes problems building wheels under PyPy on Windows (Issue #150, thanks - Cosimo Lupo) -- Don't attempt to (recursively) create a build directory ending with `..` - (invalid on all platforms, but code was only executed on Windows) (Issue #91) -- Added the PyPA Code of Conduct (Pull Request #56) - -0.26.0 -====== -- Fix multiple entrypoint comparison failure on Python 3 (Issue #148) - -0.25.0 -====== -- Add Python 3.5 to tox configuration -- Deterministic (sorted) metadata -- Fix tagging for Python 3.5 compatibility -- Support py2-none-'arch' and py3-none-'arch' tags -- Treat data-only wheels as pure -- Write to temporary file and rename when using wheel install --force - -0.24.0 -====== -- The python tag used for pure-python packages is now .pyN (major version - only). This change actually occurred in 0.23.0 when the --python-tag - option was added, but was not explicitly mentioned in the changelog then. -- wininst2wheel and egg2wheel removed. Use "wheel convert [archive]" - instead. -- Wheel now supports setuptools style conditional requirements via the - extras_require={} syntax. Separate 'extra' names from conditions using - the : character. Wheel's own setup.py does this. (The empty-string - extra is the same as install_requires.) These conditional requirements - should work the same whether the package is installed by wheel or - by setup.py. - -0.23.0 -====== -- Compatibility tag flags added to the bdist_wheel command -- sdist should include files necessary for tests -- 'wheel convert' can now also convert unpacked eggs to wheel -- Rename pydist.json to metadata.json to avoid stepping on the PEP -- The --skip-scripts option has been removed, and not generating scripts is now - the default. The option was a temporary approach until installers could - generate scripts themselves. That is now the case with pip 1.5 and later. - Note that using pip 1.4 to install a wheel without scripts will leave the - installation without entry-point wrappers. The "wheel install-scripts" - command can be used to generate the scripts in such cases. -- Thank you contributors - -0.22.0 -====== -- Include entry_points.txt, scripts a.k.a. commands, in experimental - pydist.json -- Improved test_requires parsing -- Python 2.6 fixes, "wheel version" command courtesy pombredanne - -0.21.0 -====== -- Pregenerated scripts are the default again. -- "setup.py bdist_wheel --skip-scripts" turns them off. -- setuptools is no longer a listed requirement for the 'wheel' - package. It is of course still required in order for bdist_wheel - to work. -- "python -m wheel" avoids importing pkg_resources until it's necessary. - -0.20.0 -====== -- No longer include console_scripts in wheels. Ordinary scripts (shell files, - standalone Python files) are included as usual. -- Include new command "python -m wheel install-scripts [distribution - [distribution ...]]" to install the console_scripts (setuptools-style - scripts using pkg_resources) for a distribution. - -0.19.0 -====== -- pymeta.json becomes pydist.json - -0.18.0 -====== -- Python 3 Unicode improvements - -0.17.0 -====== -- Support latest PEP-426 "pymeta.json" (json-format metadata) - -0.16.0 -====== -- Python 2.6 compatibility bugfix (thanks John McFarlane) -- Non-prerelease version number - -1.0.0a2 -======= -- Bugfix for C-extension tags for CPython 3.3 (using SOABI) - -1.0.0a1 -======= -- Bugfix for bdist_wininst converter "wheel convert" -- Bugfix for dists where "is pure" is None instead of True or False - -1.0.0a0 -======= -- Update for version 1.0 of Wheel (PEP accepted). -- Python 3 fix for moving Unicode Description to metadata body -- Include rudimentary API documentation in Sphinx (thanks Kevin Horn) - -0.15.0 -====== -- Various improvements - -0.14.0 -====== -- Changed the signature format to better comply with the current JWS spec. - Breaks all existing signatures. -- Include ``wheel unsign`` command to remove RECORD.jws from an archive. -- Put the description in the newly allowed payload section of PKG-INFO - (METADATA) files. - -0.13.0 -====== -- Use distutils instead of sysconfig to get installation paths; can install - headers. -- Improve WheelFile() sort. -- Allow bootstrap installs without any pkg_resources. - -0.12.0 -====== -- Unit test for wheel.tool.install - -0.11.0 -====== -- API cleanup - -0.10.3 -====== -- Scripts fixer fix - -0.10.2 -====== -- Fix keygen - -0.10.1 -====== -- Preserve attributes on install. - -0.10.0 -====== -- Include a copy of pkg_resources. Wheel can now install into a virtualenv - that does not have distribute (though most packages still require - pkg_resources to actually work; wheel install distribute) -- Define a new setup.cfg section [wheel]. universal=1 will - apply the py2.py3-none-any tag for pure python wheels. - -0.9.7 -===== -- Only import dirspec when needed. dirspec is only needed to find the - configuration for keygen/signing operations. - -0.9.6 -===== -- requires-dist from setup.cfg overwrites any requirements from setup.py - Care must be taken that the requirements are the same in both cases, - or just always install from wheel. -- drop dirspec requirement on win32 -- improved command line utility, adds 'wheel convert [egg or wininst]' to - convert legacy binary formats to wheel - -0.9.5 -===== -- Wheel's own wheel file can be executed by Python, and can install itself: - ``python wheel-0.9.5-py27-none-any/wheel install ...`` -- Use argparse; basic ``wheel install`` command should run with only stdlib - dependencies. -- Allow requires_dist in setup.cfg's [metadata] section. In addition to - dependencies in setup.py, but will only be interpreted when installing - from wheel, not from sdist. Can be qualified with environment markers. - -0.9.4 -===== -- Fix wheel.signatures in sdist - -0.9.3 -===== -- Integrated digital signatures support without C extensions. -- Integrated "wheel install" command (single package, no dependency - resolution) including compatibility check. -- Support Python 3.3 -- Use Metadata 1.3 (PEP 426) - -0.9.2 -===== -- Automatic signing if WHEEL_TOOL points to the wheel binary -- Even more Python 3 fixes - -0.9.1 -===== -- 'wheel sign' uses the keys generated by 'wheel keygen' (instead of generating - a new key at random each time) -- Python 2/3 encoding/decoding fixes -- Run tests on Python 2.6 (without signature verification) - -0.9 -=== -- Updated digital signatures scheme -- Python 3 support for digital signatures -- Always verify RECORD hashes on extract -- "wheel" command line tool to sign, verify, unpack wheel files - -0.8 -=== -- none/any draft pep tags update -- improved wininst2wheel script -- doc changes and other improvements - -0.7 -=== -- sort .dist-info at end of wheel archive -- Windows & Python 3 fixes from Paul Moore -- pep8 -- scripts to convert wininst & egg to wheel - -0.6 -=== -- require distribute >= 0.6.28 -- stop using verlib - -0.5 -=== -- working pretty well - -0.4.2 -===== -- hyphenated name fix - -0.4 -=== -- improve test coverage -- improve Windows compatibility -- include tox.ini courtesy of Marc Abramowitz -- draft hmac sha-256 signing function - -0.3 -=== -- prototype egg2wheel conversion script - -0.2 -=== -- Python 3 compatibility - -0.1 -=== -- Initial version diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index c3441e6..0000000 --- a/LICENSE.txt +++ /dev/null @@ -1,22 +0,0 @@ -"wheel" copyright (c) 2012-2014 Daniel Holth and -contributors. - -The MIT License - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 990e55d..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,7 +0,0 @@ -include wheel/*.txt *.txt *.sh -recursive-include wheel/test *.py -include wheel/test/test-1.0-py2.py3-none-win32.whl -include wheel/test/headers.dist/header.h -include wheel/test/pydist-schema.json -prune wheel/test/*/dist -prune wheel/test/*/build diff --git a/METADATA.in b/METADATA.in deleted file mode 100644 index 18ebf32..0000000 --- a/METADATA.in +++ /dev/null @@ -1,34 +0,0 @@ -Metadata-Version: 2.0 -Name: wheel -Version: ${VERSION} -Summary: A built-package format for Python. -Home-page: http://bitbucket.org/pypa/wheel/ -Author: Daniel Holth -Author-email: dholth@fastmail.fm -License: MIT -Keywords: wheel,packaging -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.2 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Provides-Extra: tool -Provides-Extra: signatures -Requires-Dist: keyring; extra == 'signatures' -Provides-Extra: faster-signatures -Requires-Dist: ed25519ll; extra == 'faster-signatures' -Requires-Dist: argparse; python_version=="2.6" -Provides-Extra: signatures -Requires-Dist: pyxdg; sys_platform!="win32" and extra == 'signatures' - -${DESCRIPTION} diff --git a/README.txt b/README.txt deleted file mode 100644 index 7b37ad9..0000000 --- a/README.txt +++ /dev/null @@ -1,50 +0,0 @@ -Wheel -===== - -A built-package format for Python. - -A wheel is a ZIP-format archive with a specially formatted filename -and the .whl extension. It is designed to contain all the files for a -PEP 376 compatible install in a way that is very close to the on-disk -format. Many packages will be properly installed with only the "Unpack" -step (simply extracting the file onto sys.path), and the unpacked archive -preserves enough information to "Spread" (copy data and scripts to their -final locations) at any later time. - -The wheel project provides a `bdist_wheel` command for setuptools -(requires setuptools >= 0.8.0). Wheel files can be installed with a -newer `pip` from https://github.com/pypa/pip or with wheel's own command -line utility. - -The wheel documentation is at http://wheel.rtfd.org/. The file format -is documented in PEP 427 (http://www.python.org/dev/peps/pep-0427/). - -The reference implementation is at https://bitbucket.org/pypa/wheel - -Why not egg? ------------- - -Python's egg format predates the packaging related standards we have -today, the most important being PEP 376 "Database of Installed Python -Distributions" which specifies the .dist-info directory (instead of -.egg-info) and PEP 426 "Metadata for Python Software Packages 2.0" -which specifies how to express dependencies (instead of requires.txt -in .egg-info). - -Wheel implements these things. It also provides a richer file naming -convention that communicates the Python implementation and ABI as well -as simply the language version used in a particular package. - -Unlike .egg, wheel will be a fully-documented standard at the binary -level that is truly easy to install even if you do not want to use the -reference implementation. - - -Code of Conduct ---------------- - -Everyone interacting in the wheel project's codebases, issue trackers, chat -rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_. - -.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/ - diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 37ffa8e..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,5 +0,0 @@ -version: "{build}" -install: -- cmd: python -m pip install tox -build_script: -- cmd: python -m tox diff --git a/bento.info b/bento.info deleted file mode 100644 index cd2e496..0000000 --- a/bento.info +++ /dev/null @@ -1,11 +0,0 @@ -# Experimental bento.info for wheel -Name: wheel -Version: 0.12.0 -Library: - Packages: - wheel, wheel.tool, wheel.test - -ExtraSourceFiles: - wheel/test/simple.dist/*.py, - wheel/test/simple.dist/simpledist/*.py - diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 54b44f9..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,153 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/wheel.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/wheel.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/wheel" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/wheel" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/docs/api.rst b/docs/api.rst deleted file mode 100644 index 152b2f5..0000000 --- a/docs/api.rst +++ /dev/null @@ -1,30 +0,0 @@ -API Documentation -================= - -wheel.archive -------------- - -.. automodule:: wheel.archive - :members: - - -wheel.install -------------- - -.. automodule:: wheel.install - :members: - - -wheel.pkginfo -------------- - -.. automodule:: wheel.pkginfo - :members: - - -wheel.util ----------- - -.. automodule:: wheel.util - :members: - diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 62b1001..0000000 --- a/docs/conf.py +++ /dev/null @@ -1,242 +0,0 @@ -# -*- coding: utf-8 -*- -# -# wheel documentation build configuration file, created by -# sphinx-quickstart on Thu Jul 12 00:14:09 2012. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys, os, pkg_resources - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ----------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'wheel' -copyright = u'2012, Daniel Holth' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = pkg_resources.working_set.by_key['wheel'].version -# The full version, including alpha/beta/rc tags. -release = version - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'wheeldoc' - - -# -- Options for LaTeX output -------------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ - ('index', 'wheel.tex', u'wheel Documentation', - u'Daniel Holth', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output -------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'wheel', u'wheel Documentation', - [u'Daniel Holth'], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------------ - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'wheel', u'wheel Documentation', - u'Daniel Holth', 'wheel', 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index f1dbce6..0000000 --- a/docs/index.rst +++ /dev/null @@ -1,225 +0,0 @@ -.. wheel documentation master file, created by - sphinx-quickstart on Thu Jul 12 00:14:09 2012. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -.. include:: ../README.txt - -Usage ------ - -The current version of wheel can be used to speed up repeated -installations by reducing the number of times you have to compile your -software. When you are creating a virtualenv for each revision of your -software the savings can be dramatic. This example packages pyramid -and all its dependencies as wheels, and then installs pyramid from the -built packages:: - - # Make sure you have the latest pip that supports wheel - pip install --upgrade pip - - # Install wheel - pip install wheel - - # Build a directory of wheels for pyramid and all its dependencies - pip wheel --wheel-dir=/tmp/wheelhouse pyramid - - # Install from cached wheels - pip install --use-wheel --no-index --find-links=/tmp/wheelhouse pyramid - - # Install from cached wheels remotely - pip install --use-wheel --no-index --find-links=https://wheelhouse.example.com/ pyramid - - -For lxml, an up to 3-minute "search for the newest version and compile" -can become a less-than-1 second "unpack from wheel". - -As a side effect the wheel directory, "/tmp/wheelhouse" in the example, -contains installable copies of the exact versions of your application's -dependencies. By installing from those cached wheels -you can recreate that environment quickly and with no surprises. - -To build an individual wheel, run ``python setup.py bdist_wheel``. Note that -``bdist_wheel`` only works with distribute (``import setuptools``); plain -``distutils`` does not support pluggable commands like ``bdist_wheel``. On -the other hand ``pip`` always runs ``setup.py`` with setuptools enabled. - -Wheel also includes its own installer that can only install wheels (not -sdists) from a local file or folder, but has the advantage of working -even when distribute or pip has not been installed. - -Wheel's builtin utility can be invoked directly from wheel's own wheel:: - - $ python wheel-0.21.0-py2.py3-none-any.whl/wheel -h - usage: wheel [-h] - - {keygen,sign,unsign,verify,unpack,install,install-scripts,convert,help} - ... - - positional arguments: - {keygen,sign,unsign,verify,unpack,install,install-scripts,convert,help} - commands - keygen Generate signing key - sign Sign wheel - unsign Remove RECORD.jws from a wheel by truncating the zip - file. RECORD.jws must be at the end of the archive. - The zip file must be an ordinary archive, with the - compressed files and the directory in the same order, - and without any non-zip content after the truncation - point. - verify Verify a wheel. The signature will be verified for - internal consistency ONLY and printed. Wheel's own - unpack/install commands verify the manifest against - the signature and file contents. - unpack Unpack wheel - install Install wheels - install-scripts Install console_scripts - convert Convert egg or wininst to wheel - help Show this help - - optional arguments: - -h, --help show this help message and exit - -Setuptools scripts handling ---------------------------- - -Setuptools' popular `console_scripts` and `gui_scripts` entry points can -be used to generate platform-specific scripts wrappers. Most usefully -these wrappers include `.exe` launchers if they are generated on a -Windows machine. - -As of 0.23.0, `bdist_wheel` no longer places pre-generated versions of these -wrappers into the `*.data/scripts/` directory of the archive (non-setuptools -scripts are still present, of course). - -If the scripts are needed, use a real installer like `pip`. The wheel tool -`python -m wheel install-scripts package [package ...]` can also be used at -any time to call setuptools to write the appropriate scripts wrappers. - -Defining the Python version ---------------------------- - -The `bdist_wheel` command automatically determines the correct tags to use for -the generated wheel. These are based on the Python interpreter used to -generate the wheel and whether the project contains C extension code or not. -While this is usually correct for C code, it can be too conservative for pure -Python code. The bdist_wheel command therefore supports two flags that can be -used to specify the Python version tag to use more precisely:: - - --universal Specifies that a pure-python wheel is "universal" - (i.e., it works on any version of Python). This - equates to the tag "py2.py3". - --python-tag XXX Specifies the precise python version tag to use for - a pure-python wheel. - --py-limited-api {cp32|cp33|cp34|...} - Specifies Python Py_LIMITED_API compatibility with - the version of CPython passed and later versions. - The wheel will be tagged cpNN.abi3.{arch} on CPython 3. - This flag does not affect Python 2 builds or alternate - Python implementations. - - To conform to the limited API, all your C - extensions must use only functions from the limited - API, pass Extension(py_limited_api=True) and e.g. - #define Py_LIMITED_API=0x03020000 depending on - the exact minimun Python you wish to support. - -The --universal and --python-tag flags have no effect when used on a -project that includes C extension code. - -The default for a pure Python project (if no explicit flags are given) is "pyN" -where N is the major version of the Python interpreter used to build the wheel. -This is generally the correct choice, as projects would not typically ship -different wheels for different minor versions of Python. - -A reasonable use of the `--python-tag` argument would be for a project that -uses Python syntax only introduced in a particular Python version. There are -no current examples of this, but if wheels had been available when Python 2.5 -was released (the first version containing the `with` statement), wheels for a -project that used the `with` statement would typically use `--python-tag py25`. -However, unless a separate version of the wheel was shipped which avoided the -use of the new syntax, there is little benefit in explicitly marking the tag in -this manner. - -Typically, projects would not specify Python tags on the command line, but -would use `setup.cfg` to set them as a project default:: - - [bdist_wheel] - universal=1 - -or:: - - [bdist_wheel] - python-tag = py32 - -Defining conditional dependencies ---------------------------------- - -In wheel, the only way to have conditional dependencies (that might only be -needed on certain platforms) is to use environment markers as defined by -PEP 426. - -As of wheel 0.24.0, the recommended way to do this is in the setuptools -`extras_require` parameter. A `:` separates the extra name from the marker. -Wheel's own setup.py has an example:: - extras_require={ - ':python_version=="2.6"': ['argparse'], - 'signatures': ['keyring'], - 'signatures:sys_platform!="win32"': ['pyxdg'], - 'faster-signatures': ['ed25519ll'], - 'tool': [] - }, - -The extra named '' signifies a default requirement, as if it was passed to -`install_requires`. - -Older versions of bdist_wheel supported passing requirements in a -now-deprecated [metadata] section in setup.cfg. - -Automatically sign wheel files ------------------------------- - -Wheel contains an experimental digital signatures scheme based on Ed25519 -signatures; these signatures are unrelated to pgp/gpg signatures and do not -include a trust model. - -`python setup.py bdist_wheel` will automatically sign wheel files if -the environment variable `WHEEL_TOOL` is set to the path of the `wheel` -command line tool.:: - - # Install the wheel tool and its dependencies - $ pip install wheel[tool] - # Generate a signing key (only once) - $ wheel keygen - - $ export WHEEL_TOOL=/path/to/wheel - $ python setup.py bdist_wheel - -Signing is done in a subprocess because it is not convenient for the -build environment to contain bindings to the keyring and cryptography -libraries. The keyring library may not be able to find your keys (choosing -a different key storage back end based on available dependencies) unless -you run it from the same environment used for keygen. - -Format ------- - -The wheel format is documented as PEP 427 "The Wheel Binary Package -Format..." (http://www.python.org/dev/peps/pep-0427/). - -Slogans -------- - -Wheel - -* Because ‘newegg’ was taken. -* Python packaging - reinvented. -* A container for cheese. -* It makes it easier to roll out software. - -.. toctree:: - :maxdepth: 2 - - story - api - diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 8083236..0000000 --- a/docs/make.bat +++ /dev/null @@ -1,190 +0,0 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=_build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . -set I18NSPHINXOPTS=%SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% - set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. texinfo to make Texinfo files - echo. gettext to make PO message catalogs - echo. changes to make an overview over all changed/added/deprecated items - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\wheel.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\wheel.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "texinfo" ( - %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. - goto end -) - -if "%1" == "gettext" ( - %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The message catalogs are in %BUILDDIR%/locale. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - if errorlevel 1 exit /b 1 - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -:end diff --git a/docs/story.rst b/docs/story.rst deleted file mode 100644 index 2c150c2..0000000 --- a/docs/story.rst +++ /dev/null @@ -1,62 +0,0 @@ -The Story of Wheel -================== - -I was impressed with Tarek’s packaging talk at PyCon 2010, and I -admire PEP 345 (Metadata for Python Software Packages 1.2) and PEP 376 -(Database of Installed Python Distributions) which standardize a richer -metadata format and show how distributions should be installed on disk. So -naturally with all the hubbub about `packaging` in Python 3.3, I decided -to try it to reap the benefits of a more standardized and predictable -Python packaging experience. - -I began by converting `cryptacular`, a password hashing package which -has a simple C extension, to use setup.cfg. I downloaded the Python 3.3 -source, struggled with the difference between setup.py and setup.cfg -syntax, fixed the `define_macros` feature, stopped using the missing -`extras` functionality, and several hours later I was able to generate my -`METADATA` from `setup.cfg`. I rejoiced at my newfound freedom from the -tyranny of arbitrary code execution during the build and install process. - -It was a lot of work. The package is worse off than before, and it can’t -be built or installed without patching the Python source code itself. - -It was about that time that distutils-sig had a discussion about the -need to include a generated setup.cfg from setup.cfg because setup.cfg -wasn’t static enough. Wait, what? - -Of course there is a different way to massively simplify the install -process. It’s called built or binary packages. You never have to run -`setup.py` because there is no `setup.py`. There is only METADATA aka -PKG-INFO. Installation has two steps: ‘build package’; ‘install -package’, and you can skip the first step, have someone else do it -for you, do it on another machine, or install the build system from a -binary package and let the build system handle the building. The build -is still complicated, but installation is simple. - -With the binary package strategy people who want to install use a simple, -compatible installer, and people who want to package use whatever is -convenient for them for as long as it meets their needs. No one has -to rewrite `setup.py` for their own or the 20k+ other packages on PyPi -unless a different build system does a better job. - -Wheel is my attempt to benefit from the excellent distutils-sig work -without having to fix the intractable `distutils` software itself. Like -METADATA and .dist-info directories but unlike Extension(), it’s -simple enough that there really could be alternate implementations; the -simplest (but less than ideal) installer is nothing more than “unzip -archive.whl” somewhere on sys.path. - -If you’ve made it this far you probably wonder whether I’ve heard -of eggs. Some comparisons: - -* Wheel is an installation format; egg is importable. Wheel archives do not need to include .pyc and are less tied to a specific Python version or implementation. Wheel can install (pure Python) packages built with previous versions of Python so you don’t always have to wait for the packager to catch up. - -* Wheel uses .dist-info directories; egg uses .egg-info. Wheel is compatible with the new world of Python `packaging` and the new concepts it brings. - -* Wheel has a richer file naming convention for today’s multi-implementation world. A single wheel archive can indicate its compatibility with a number of Python language versions and implementations, ABIs, and system architectures. Historically the ABI has been specific to a CPython release, but when we get a longer-term ABI, wheel will be ready. - -* Wheel is lossless. The first wheel implementation `bdist_wheel` always generates `egg-info`, and then converts it to a `.whl`. Later tools will allow for the conversion of existing eggs and bdist_wininst distributions. - -* Wheel is versioned. Every wheel file contains the version of the wheel specification and the implementation that packaged it. Hopefully the next migration can simply be to Wheel 2.0. - -I hope you will benefit from wheel. diff --git a/entry_points.txt b/entry_points.txt deleted file mode 100644 index f57b8c0..0000000 --- a/entry_points.txt +++ /dev/null @@ -1,5 +0,0 @@ -[console_scripts] -wheel = wheel.tool:main - -[distutils.commands] -bdist_wheel = wheel.bdist_wheel:bdist_wheel \ No newline at end of file diff --git a/pylintrc b/pylintrc deleted file mode 100644 index ebd9658..0000000 --- a/pylintrc +++ /dev/null @@ -1,249 +0,0 @@ -[MASTER] - -# Specify a configuration file. -#rcfile= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Profiled execution. -profile=no - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS - -# Pickle collected data for later comparisons. -persistent=yes - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - - -[MESSAGES CONTROL] - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time. -#enable= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). -disable=F0401,E0611,E1101,C0103 - - -[REPORTS] - -# Set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html -output-format=colorized - -# Include message's id in output -include-ids=yes - -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". -files-output=no - -# Tells whether to display a full report or only the messages -reports=yes - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Add a comment according to your evaluation note. This is used by the global -# evaluation report (RP0004). -comment=yes - - -[BASIC] - -# Required attributes for module, separated by a comma -required-attributes= - -# List of builtins function names that should not be used, separated by a comma -bad-functions=map,filter,apply,input - -# Regular expression which should only match correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression which should only match correct module level names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Regular expression which should only match correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ - -# Regular expression which should only match correct function names -function-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct method names -method-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct instance attribute names -attr-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct argument names -argument-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct variable names -variable-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct list comprehension / -# generator expression variable names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Good variable names which should always be accepted, separated by a comma -good-names=i,j,k,ex,Run,_ - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# Regular expression which should only match functions or classes name which do -# not require a docstring -no-docstring-rgx=__.*__ - - -[TYPECHECK] - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# List of classes names for which member attributes should not be checked -# (useful for classes with attributes dynamically set). -ignored-classes=SQLObject - -# When zope mode is activated, add a predefined set of Zope acquired attributes -# to generated-members. -zope=no - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E0201 when accessed. Python regular -# expressions are accepted. -generated-members=REQUEST,acl_users,aq_parent - - -[FORMAT] - -# Maximum number of characters on a single line. -max-line-length=80 - -# Maximum number of lines in a module -max-module-lines=1000 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - - -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=2 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO - - -[VARIABLES] - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching the beginning of the name of dummy variables -# (i.e. not used). -dummy-variables-rgx=_|dummy - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - - -[CLASSES] - -# List of interface methods to ignore, separated by a comma. This is used for -# instance to not check methods defines in Zope's Interface base class. -ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - - -[DESIGN] - -# Maximum number of arguments for function / method -max-args=5 - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.* - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of branch for function / method body -max-branchs=12 - -# Maximum number of statements in function / method body -max-statements=50 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of attributes for a class (see R0902). -max-attributes=7 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - - -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub,string,TERMIOS,Bastion,rexec - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=Exception diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 99207b9..0000000 --- a/setup.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[pytest] -addopts=--ignore=dist --ignore=build --cov=wheel - -[metadata] -license-file = LICENSE.txt - -[bdist_wheel] -# use py2.py3 tag for pure-python dist: -universal=1 diff --git a/setup.py b/setup.py deleted file mode 100644 index e187cba..0000000 --- a/setup.py +++ /dev/null @@ -1,66 +0,0 @@ -import os.path, codecs, re - -from setuptools import setup - -here = os.path.abspath(os.path.dirname(__file__)) -README = codecs.open(os.path.join(here, 'README.txt'), encoding='utf8').read() -CHANGES = codecs.open(os.path.join(here, 'CHANGES.txt'), encoding='utf8').read() - -with codecs.open(os.path.join(os.path.dirname(__file__), 'wheel', '__init__.py'), - encoding='utf8') as version_file: - metadata = dict(re.findall(r"""__([a-z]+)__ = "([^"]+)""", version_file.read())) - -setup(name='wheel', - version=metadata['version'], - description='A built-package format for Python.', - long_description=README + '\n\n' + CHANGES, - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.2", - "Programming Language :: Python :: 3.3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - ], - author='Daniel Holth', - author_email='dholth@fastmail.fm', - url='https://bitbucket.org/pypa/wheel/', - keywords=['wheel', 'packaging'], - license='MIT', - packages=[ - 'wheel', - 'wheel.test', - 'wheel.tool', - 'wheel.signatures' - ], - extras_require={ - 'signatures': ['keyring', 'keyrings.alt'], - 'signatures:sys_platform!="win32"': ['pyxdg'], - 'faster-signatures': ['ed25519ll'], - 'tool': [] - }, - tests_require=[ - 'jsonschema', - 'pytest', - 'coverage', - 'pytest-cov', - ], - include_package_data=True, - zip_safe=False, - entry_points = { - 'console_scripts': [ - 'wheel=wheel.tool:main' - ], - 'distutils.commands': [ - 'bdist_wheel=wheel.bdist_wheel:bdist_wheel' - ] - } - ) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 309d58d..0000000 --- a/tox.ini +++ /dev/null @@ -1,17 +0,0 @@ -# Tox (http://tox.testrun.org/) is a tool for running tests -# in multiple virtualenvs. This configuration file will run the -# test suite on all supported python versions. To use it, "pip install tox" -# and then run "tox" from this directory. - -[tox] -envlist = py27, pypy, py33, py34, py35, py36 - -[testenv] -commands = - py.test -deps = - .[tool,signatures] - jsonschema - pytest - pytest-cov - setuptools>3.0 diff --git a/vendorize.sh b/vendorize.sh deleted file mode 100755 index 3402172..0000000 --- a/vendorize.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -# Vendorize pep 425 tagging scheme -cp ../pep425/pep425tags.py wheel diff --git a/wheel/__init__.py b/wheel/__init__.py deleted file mode 100644 index 7b3d278..0000000 --- a/wheel/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# __variables__ with double-quoted values will be available in setup.py: -__version__ = "0.30.0.a0" diff --git a/wheel/__main__.py b/wheel/__main__.py deleted file mode 100644 index 889359c..0000000 --- a/wheel/__main__.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -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 os.path - path = os.path.dirname(os.path.dirname(__file__)) - sys.path[0:0] = [path] - import wheel.tool - sys.exit(wheel.tool.main()) - -if __name__ == "__main__": - sys.exit(main()) diff --git a/wheel/archive.py b/wheel/archive.py deleted file mode 100644 index 403d45b..0000000 --- a/wheel/archive.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -Archive tools for wheel. -""" - -import os -import time -import logging -import os.path -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 - `base_name`. - ''' - olddir = os.path.abspath(os.curdir) - base_name = os.path.abspath(base_name) - try: - os.chdir(base_dir) - return make_wheelfile_inner(base_name) - finally: - os.chdir(olddir) - - -def make_wheelfile_inner(base_name, base_dir='.'): - """Create a whl file from all the files under 'base_dir'. - - Places .dist-info at the end of the archive.""" - - zip_filename = base_name + ".whl" - - log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir) - - # Some applications need reproducible .whl files, but they can't do this - # without forcing the timestamp of the individual ZipInfo objects. See - # issue #143. - timestamp = os.environ.get('SOURCE_DATE_EPOCH') - if timestamp is None: - date_time = None - else: - date_time = time.gmtime(int(timestamp))[0:6] - - # XXX support bz2, xz when available - zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_DEFLATED) - - score = {'WHEEL': 1, 'METADATA': 2, 'RECORD': 3} - deferred = [] - - def writefile(path, date_time): - st = os.stat(path) - if date_time is None: - mtime = time.gmtime(st.st_mtime) - date_time = mtime[0:6] - zinfo = zipfile.ZipInfo(path, date_time) - zinfo.external_attr = st.st_mode << 16 - zinfo.compress_type = zipfile.ZIP_DEFLATED - with open(path, 'rb') as fp: - zip.writestr(zinfo, fp.read()) - log.info("adding '%s'" % path) - - for dirpath, dirnames, filenames in os.walk(base_dir): - for name in filenames: - path = os.path.normpath(os.path.join(dirpath, name)) - - if os.path.isfile(path): - if dirpath.endswith('.dist-info'): - deferred.append((score.get(name, 0), path)) - else: - writefile(path, date_time) - - deferred.sort() - for score, path in deferred: - writefile(path, date_time) - - zip.close() - - return zip_filename diff --git a/wheel/bdist_wheel.py b/wheel/bdist_wheel.py deleted file mode 100644 index dfcc910..0000000 --- a/wheel/bdist_wheel.py +++ /dev/null @@ -1,467 +0,0 @@ -""" -Create a wheel (.whl) distribution. - -A wheel is a built archive format. -""" - -import csv -import hashlib -import os -import subprocess -import warnings -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 .pep425tags import get_abbr_impl, get_impl_ver, get_abi_tag, get_platform -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 -from . import pep425tags, metadata -from . import __version__ as wheel_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' - - user_options = [('bdist-dir=', 'b', - "temporary directory for creating the distribution"), - ('plat-name=', 'p', - "platform name to embed in generated filenames " - "(default: %s)" % get_platform()), - ('keep-temp', 'k', - "keep the pseudo-installation tree around after " + - "creating the distribution archive"), - ('dist-dir=', 'd', - "directory to put final built distributions in"), - ('skip-build', None, - "skip rebuilding everything (for testing/debugging)"), - ('relative', None, - "build the archive using relative paths" - "(default: false)"), - ('owner=', 'u', - "Owner name used when creating a tar file" - " [default: current user]"), - ('group=', 'g', - "Group name used when creating a tar file" - " [default: current group]"), - ('universal', None, - "make a universal wheel" - " (default: false)"), - ('python-tag=', None, - "Python implementation compatibility tag" - " (default: py%s)" % get_impl_ver()[0]), - ('py-limited-api=', None, - "Python tag (cp32|cp33|cpNN) for abi3 wheel tag" - " (default: false)"), - ] - - boolean_options = ['keep-temp', 'skip-build', 'relative', 'universal'] - - def initialize_options(self): - self.bdist_dir = None - self.data_dir = None - self.plat_name = None - self.plat_tag = None - self.format = 'zip' - self.keep_temp = False - self.dist_dir = None - self.distinfo_dir = None - self.egginfo_dir = None - self.root_is_pure = None - self.skip_build = None - self.relative = False - self.owner = None - self.group = None - self.universal = False - self.python_tag = 'py' + get_impl_ver()[0] - self.py_limited_api = False - self.plat_name_supplied = False - - def finalize_options(self): - if self.bdist_dir is None: - bdist_base = self.get_finalized_command('bdist').bdist_base - self.bdist_dir = os.path.join(bdist_base, 'wheel') - - self.data_dir = self.wheel_dist_name + '.data' - self.plat_name_supplied = self.plat_name is not None - - need_options = ('dist_dir', 'plat_name', 'skip_build') - - self.set_undefined_options('bdist', - *zip(need_options, need_options)) - - self.root_is_pure = not (self.distribution.has_ext_modules() - or self.distribution.has_c_libraries()) - - if self.py_limited_api and not re.match(PY_LIMITED_API_PATTERN, self.py_limited_api): - raise ValueError("py-limited-api must match '%s'" % PY_LIMITED_API_PATTERN) - - # Support legacy [wheel] section for setting universal - wheel = self.distribution.get_option_dict('wheel') - if 'universal' in wheel: - # please don't define this in your global configs - val = wheel['universal'][1].strip() - if val.lower() in ('1', 'true', 'yes'): - self.universal = True - - @property - def wheel_dist_name(self): - """Return distribution full name with - replaced with _""" - return '-'.join((safer_name(self.distribution.get_name()), - safer_version(self.distribution.get_version()))) - - def get_tag(self): - # bdist sets self.plat_name if unset, we should only use it for purepy - # wheels if the user supplied it. - if self.plat_name_supplied: - plat_name = self.plat_name - elif self.root_is_pure: - plat_name = 'any' - else: - plat_name = self.plat_name or get_platform() - if plat_name in ('linux-x86_64', 'linux_x86_64') and sys.maxsize == 2147483647: - plat_name = 'linux_i686' - plat_name = plat_name.replace('-', '_').replace('.', '_') - - - if self.root_is_pure: - if self.universal: - impl = 'py2.py3' - else: - impl = self.python_tag - tag = (impl, 'none', plat_name) - else: - impl_name = get_abbr_impl() - impl_ver = get_impl_ver() - impl = impl_name + impl_ver - # We don't work on CPython 3.1, 3.0. - if self.py_limited_api and (impl_name + impl_ver).startswith('cp3'): - impl = self.py_limited_api - abi_tag = 'abi3' - else: - abi_tag = str(get_abi_tag()).lower() - tag = (impl, abi_tag, plat_name) - supported_tags = pep425tags.get_supported( - supplied_platform=plat_name if self.plat_name_supplied else None) - # XXX switch to this alternate implementation for non-pure: - if not self.py_limited_api: - assert tag == supported_tags[0], "%s != %s" % (tag, supported_tags[0]) - assert tag in supported_tags, "would build wheel with unsupported tag %s" % tag - return tag - - def get_archive_basename(self): - """Return archive name without extension""" - - impl_tag, abi_tag, plat_tag = self.get_tag() - - archive_basename = "%s-%s-%s-%s" % ( - self.wheel_dist_name, - impl_tag, - abi_tag, - plat_tag) - return archive_basename - - def run(self): - build_scripts = self.reinitialize_command('build_scripts') - build_scripts.executable = 'python' - - if not self.skip_build: - self.run_command('build') - - install = self.reinitialize_command('install', - reinit_subcommands=True) - install.root = self.bdist_dir - install.compile = False - install.skip_build = self.skip_build - install.warn_dir = False - - # A wheel without setuptools scripts is more cross-platform. - # Use the (undocumented) `no_ep` option to setuptools' - # install_scripts command to avoid creating entry point scripts. - install_scripts = self.reinitialize_command('install_scripts') - install_scripts.no_ep = True - - # Use a custom scheme for the archive, because we have to decide - # at installation time which scheme to use. - for key in ('headers', 'scripts', 'data', 'purelib', 'platlib'): - setattr(install, - 'install_' + key, - os.path.join(self.data_dir, key)) - - basedir_observed = '' - - if os.name == 'nt': - # win32 barfs if any of these are ''; could be '.'? - # (distutils.command.install:change_roots bug) - basedir_observed = os.path.normpath(os.path.join(self.data_dir, '..')) - self.install_libbase = self.install_lib = basedir_observed - - setattr(install, - 'install_purelib' if self.root_is_pure else 'install_platlib', - basedir_observed) - - logger.info("installing to %s", self.bdist_dir) - - self.run_command('install') - - archive_basename = self.get_archive_basename() - - pseudoinstall_root = os.path.join(self.dist_dir, archive_basename) - if not self.relative: - archive_root = self.bdist_dir - else: - archive_root = os.path.join( - self.bdist_dir, - self._ensure_relative(install.install_base)) - - self.set_undefined_options( - 'install_egg_info', ('target', 'egginfo_dir')) - self.distinfo_dir = os.path.join(self.bdist_dir, - '%s.dist-info' % self.wheel_dist_name) - self.egg2dist(self.egginfo_dir, - self.distinfo_dir) - - self.write_wheelfile(self.distinfo_dir) - - self.write_record(self.bdist_dir, self.distinfo_dir) - - # Make the archive - if not os.path.exists(self.dist_dir): - os.makedirs(self.dist_dir) - wheel_name = archive_wheelfile(pseudoinstall_root, archive_root) - - # Sign the archive - if 'WHEEL_TOOL' in os.environ: - subprocess.call([os.environ['WHEEL_TOOL'], 'sign', wheel_name]) - - # Add to 'Distribution.dist_files' so that the "upload" command works - getattr(self.distribution, 'dist_files', []).append( - ('bdist_wheel', get_python_version(), wheel_name)) - - if not self.keep_temp: - if self.dry_run: - logger.info('removing %s', self.bdist_dir) - else: - rmtree(self.bdist_dir) - - 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 - msg['Generator'] = generator - msg['Root-Is-Purelib'] = str(self.root_is_pure).lower() - - # Doesn't work for bdist_wininst - impl_tag, abi_tag, plat_tag = self.get_tag() - for impl in impl_tag.split('.'): - for abi in abi_tag.split('.'): - for plat in plat_tag.split('.'): - msg['Tag'] = '-'.join((impl, abi, plat)) - - wheelfile_path = os.path.join(wheelfile_base, 'WHEEL') - logger.info('creating %s', wheelfile_path) - with open(wheelfile_path, 'w') as f: - Generator(f, maxheaderlen=0).flatten(msg) - - def _ensure_relative(self, path): - # copied from dir_util, deleted - drive, path = os.path.splitdrive(path) - if path[0:1] == os.sep: - path = drive + path[1:] - return path - - def _pkginfo_to_metadata(self, egg_info_path, pkginfo_path): - return metadata.pkginfo_to_metadata(egg_info_path, pkginfo_path) - - 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: - return None - return metadata['license_file'][1] - - def setupcfg_requirements(self): - """Generate requirements from setup.cfg as - ('Requires-Dist', 'requirement; qualifier') tuples. From a metadata - section in setup.cfg: - - [metadata] - provides-extra = extra1 - extra2 - requires-dist = requirement; qualifier - another; qualifier2 - unqualified - - Yields - - ('Provides-Extra', 'extra1'), - ('Provides-Extra', 'extra2'), - ('Requires-Dist', 'requirement; qualifier'), - ('Requires-Dist', 'another; qualifier2'), - ('Requires-Dist', 'unqualified') - """ - metadata = self.distribution.get_option_dict('metadata') - - # 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: - continue - field = metadata[key] - for line in field[1].splitlines(): - line = line.strip() - if not line: - continue - yield (title, line) - - 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 - 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') - del pkg_info['Provides-Extra'] - del pkg_info['Requires-Dist'] - for k, v in additional: - pkg_info[k] = v - write_pkg_info(metadata_path, pkg_info) - - def egg2dist(self, egginfo_path, distinfo_path): - """Convert an .egg-info directory into a .dist-info directory""" - def adios(p): - """Appropriately delete directory, file or link.""" - if os.path.exists(p) and not os.path.islink(p) and os.path.isdir(p): - shutil.rmtree(p) - elif os.path.exists(p): - os.unlink(p) - - adios(distinfo_path) - - if not os.path.exists(egginfo_path): - # There is no egg-info. This is probably because the egg-info - # file/directory is not named matching the distribution name used - # to name the archive file. Check for this case and report - # accordingly. - import glob - pat = os.path.join(os.path.dirname(egginfo_path), '*.egg-info') - possible = glob.glob(pat) - err = "Egg metadata expected at %s but not found" % (egginfo_path,) - if possible: - alt = os.path.basename(possible[0]) - err += " (%s found - possible misnamed archive file?)" % (alt,) - - raise ValueError(err) - - if os.path.isfile(egginfo_path): - # .egg-info is a single file - pkginfo_path = egginfo_path - pkg_info = self._pkginfo_to_metadata(egginfo_path, egginfo_path) - os.mkdir(distinfo_path) - else: - # .egg-info is a directory - pkginfo_path = os.path.join(egginfo_path, 'PKG-INFO') - pkg_info = self._pkginfo_to_metadata(egginfo_path, pkginfo_path) - - # 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',))) - - # delete dependency_links if it is only whitespace - dependency_links_path = os.path.join(distinfo_path, 'dependency_links.txt') - with open(dependency_links_path, 'r') as dependency_links_file: - dependency_links = dependency_links_file.read().strip() - if not dependency_links: - adios(dependency_links_path) - - write_pkg_info(os.path.join(distinfo_path, 'METADATA'), pkg_info) - - # XXX deprecated. Still useful for current distribute/setuptools. - metadata_path = os.path.join(distinfo_path, 'METADATA') - self.add_requirements(metadata_path) - - # XXX intentionally a different path than the PEP. - metadata_json_path = os.path.join(distinfo_path, 'metadata.json') - pymeta = pkginfo_to_dict(metadata_path, - distribution=self.distribution) - - if 'description' in pymeta: - description_filename = 'DESCRIPTION.rst' - description_text = pymeta.pop('description') - description_path = os.path.join(distinfo_path, - 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 - - # XXX heuristically copy any LICENSE/LICENSE.txt? - license = self.license_file() - if license: - license_filename = 'LICENSE.txt' - shutil.copy(license, os.path.join(self.distinfo_dir, license_filename)) - pymeta['extensions']['python.details']['document_names']['license'] = license_filename - - with open(metadata_json_path, "w") as metadata_json: - json.dump(pymeta, metadata_json, sort_keys=True) - - adios(egginfo_path) - - def write_record(self, bdist_dir, distinfo_dir): - from .util import urlsafe_b64encode - - record_path = os.path.join(distinfo_dir, 'RECORD') - record_relpath = os.path.relpath(record_path, bdist_dir) - - def walk(): - for dir, dirs, files in os.walk(bdist_dir): - dirs.sort() - for f in sorted(files): - yield os.path.join(dir, f) - - def skip(path): - """Wheel hashes every possible file.""" - return (path == record_relpath) - - with open_for_csv(record_path, 'w+') as record_file: - writer = csv.writer(record_file) - for path in walk(): - relpath = os.path.relpath(path, bdist_dir) - if skip(relpath): - hash = '' - size = '' - else: - with open(path, 'rb') as f: - data = f.read() - digest = hashlib.sha256(data).digest() - hash = 'sha256=' + native(urlsafe_b64encode(digest)) - size = len(data) - record_path = os.path.relpath( - path, bdist_dir).replace(os.path.sep, '/') - writer.writerow((record_path, hash, size)) diff --git a/wheel/decorator.py b/wheel/decorator.py deleted file mode 100644 index e4b56d1..0000000 --- a/wheel/decorator.py +++ /dev/null @@ -1,19 +0,0 @@ -# from Pyramid - - -class reify(object): - """Put the result of a method which uses this (non-data) - descriptor decorator in the instance dict after the first call, - effectively replacing the decorator with an instance variable. - """ - - def __init__(self, wrapped): - self.wrapped = wrapped - self.__doc__ = wrapped.__doc__ - - def __get__(self, inst, objtype=None): - if inst is None: - return self - val = self.wrapped(inst) - setattr(inst, self.wrapped.__name__, val) - return val diff --git a/wheel/egg2wheel.py b/wheel/egg2wheel.py deleted file mode 100755 index e8d3153..0000000 --- a/wheel/egg2wheel.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python -import os.path -import re -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 glob import iglob -from wheel.wininst2wheel import _bdist_wheel_tag - -egg_info_re = re.compile(r'''(?P.+?)-(?P.+?) - (-(?P.+?))?(-(?P.+?))?.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") - if os.path.isfile(egg_path): - # assume we have a bdist_egg otherwise - egg = zipfile.ZipFile(egg_path) - egg.extractall(dir) - else: - # support buildout-style installed eggs directories - for pth in os.listdir(egg_path): - src = os.path.join(egg_path, pth) - if os.path.isfile(src): - shutil.copy2(src, dir) - else: - shutil.copytree(src, os.path.join(dir, pth)) - - dist_info = "%s-%s" % (egg_info['name'], egg_info['ver']) - abi = 'none' - pyver = egg_info['pyver'].replace('.', '') - arch = (egg_info['arch'] or 'any').replace('.', '_').replace('-', '_') - if arch != 'any': - # assume all binary eggs are for CPython - pyver = 'cp' + pyver[2:] - wheel_name = '-'.join(( - dist_info, - pyver, - abi, - arch - )) - root_is_purelib = egg_info['arch'] is None - if root_is_purelib: - bw = wheel.bdist_wheel.bdist_wheel(distutils.dist.Distribution()) - else: - bw = _bdist_wheel_tag(distutils.dist.Distribution()) - - bw.root_is_pure = root_is_purelib - bw.python_tag = pyver - bw.plat_name_supplied = True - bw.plat_name = egg_info['arch'] or 'any' - if not root_is_purelib: - bw.full_tag_supplied = True - bw.full_tag = (pyver, abi, arch) - - dist_info_dir = os.path.join(dir, '%s.dist-info' % dist_info) - bw.egg2dist(os.path.join(dir, 'EGG-INFO'), - dist_info_dir) - bw.write_wheelfile(dist_info_dir, generator='egg2wheel') - bw.write_record(dir, dist_info_dir) - filename = make_archive(os.path.join(dest_dir, wheel_name), 'zip', root_dir=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)") - parser.add_argument('--verbose', '-v', action='store_true') - args = parser.parse_args() - for pat in args.eggs: - for egg in iglob(pat): - if args.verbose: - sys.stdout.write("{0}... ".format(egg)) - egg2wheel(egg, args.dest_dir) - if args.verbose: - sys.stdout.write("OK\n") - -if __name__ == "__main__": - main() diff --git a/wheel/eggnames.txt b/wheel/eggnames.txt deleted file mode 100644 index d422120..0000000 --- a/wheel/eggnames.txt +++ /dev/null @@ -1,87 +0,0 @@ -vcard-0.7.8-py2.7.egg -qtalchemy-0.7.1-py2.7.egg -AMQPDeliver-0.1-py2.7.egg -infi.registry-0.1.1-py2.7.egg -infi.instruct-0.5.5-py2.7.egg -infi.devicemanager-0.1.2-py2.7.egg -TracTixSummary-1.0-py2.7.egg -ToscaWidgets-0.9.12-py2.7.egg -archipel_agent_iphone_notification-0.5.0beta-py2.7.egg -archipel_agent_action_scheduler-0.5.0beta-py2.7.egg -ao.social-1.0.2-py2.7.egg -apgl-0.7-py2.7.egg -satchmo_payment_payworld-0.1.1-py2.7.egg -snmpsim-0.1.3-py2.7.egg -sshim-0.2-py2.7.egg -shove-0.3.4-py2.7.egg -simpleavro-0.3.0-py2.7.egg -wkhtmltopdf-0.2-py2.7.egg -wokkel-0.7.0-py2.7.egg -jmbo_social-0.0.6-py2.7.egg -jmbo_post-0.0.6-py2.7.egg -jcrack-0.0.2-py2.7.egg -riak-1.4.0-py2.7.egg -restclient-0.10.2-py2.7.egg -Sutekh-0.8.1-py2.7.egg -trayify-0.0.1-py2.7.egg -tweepy-1.9-py2.7.egg -topzootools-0.2.1-py2.7.egg -haystack-0.16-py2.7.egg -zope.interface-4.0.1-py2.7-win32.egg -neuroshare-0.8.5-py2.7-macosx-10.7-intel.egg -ndg_httpsclient-0.2.0-py2.7.egg -libtele-0.3-py2.7.egg -litex.cxpool-1.0.2-py2.7.egg -obspy.iris-0.5.1-py2.7.egg -obspy.mseed-0.6.1-py2.7-win32.egg -obspy.core-0.6.2-py2.7.egg -CorePost-0.0.3-py2.7.egg -fnordstalk-0.0.3-py2.7.egg -Persistence-2.13.2-py2.7-win32.egg -Pydap-3.1.RC1-py2.7.egg -PyExecJS-1.0.4-py2.7.egg -Wally-0.7.2-py2.7.egg -ExtensionClass-4.0a1-py2.7-win32.egg -Feedjack-0.9.16-py2.7.egg -Mars24-0.3.9-py2.7.egg -HalWeb-0.6.0-py2.7.egg -DARE-0.7.140-py2.7.egg -macholib-1.3-py2.7.egg -marrow.wsgi.egress.compression-1.1-py2.7.egg -mcs-0.3.7-py2.7.egg -Kook-0.6.0-py2.7.egg -er-0.1-py2.7.egg -evasion_director-1.1.4-py2.7.egg -djquery-0.1a-py2.7.egg -django_factory-0.7-py2.7.egg -django_gizmo-0.0.3-py2.7.egg -django_category-0.1-py2.7.egg -dbwrap-0.3.2-py2.7.egg -django_supergeneric-1.0-py2.7.egg -django_dynamo-0.25-py2.7.egg -django_acollabauth-0.1-py2.7.egg -django_qrlink-0.1.0-py2.7.egg -django_addons-0.6.6-py2.7.egg -cover_grabber-1.1.2-py2.7.egg -chem-1.1-py2.7.egg -crud-0.1-py2.7.egg -bongo-0.1-py2.7.egg -bytecodehacks-April2000-py2.7.egg -greenlet-0.3.4-py2.7-win32.egg -ginvoke-0.3.1-py2.7.egg -pyobjc_framework_ScriptingBridge-2.3-py2.7.egg -pecan-0.2.0a-py2.7.egg -pyress-0.2.0-py2.7.egg -pyobjc_framework_PubSub-2.3-py2.7.egg -pyobjc_framework_ExceptionHandling-2.3-py2.7.egg -pywps-trunk-py2.7.egg -pyobjc_framework_CFNetwork-2.3-py2.7-macosx-10.6-fat.egg -py.saunter-0.40-py2.7.egg -pyfnordmetric-0.0.1-py2.7.egg -pyws-1.1.1-py2.7.egg -prestapyt-0.4.0-py2.7.egg -passlib-1.5.3-py2.7.egg -pyga-2.1-py2.7.egg -pygithub3-0.3-py2.7.egg -pyobjc_framework_OpenDirectory-2.3-py2.7.egg -yaposib-0.2.75-py2.7-linux-x86_64.egg diff --git a/wheel/install.py b/wheel/install.py deleted file mode 100644 index a422b0e..0000000 --- a/wheel/install.py +++ /dev/null @@ -1,481 +0,0 @@ -""" -Operations on existing wheel files, including basic installation. -""" -# XXX see patched pip to install - -import sys -import warnings -import os.path -import re -import zipfile -import hashlib -import csv - -import shutil - -try: - _big_number = sys.maxsize -except NameError: - _big_number = sys.maxint - -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 - -# The next major version after this version of the 'wheel' tool: -VERSION_TOO_HIGH = (1, 0) - -# Non-greedy matching of an optional build number may be too clever (more -# invalid wheel filenames will match). Separate regex for .dist-info? -WHEEL_INFO_RE = re.compile( - r"""^(?P(?P.+?)(-(?P\d.+?))?) - ((-(?P\d.*?))?-(?P.+?)-(?P.+?)-(?P.+?) - \.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 - - -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.""" - - WHEEL_INFO = "WHEEL" - RECORD = "RECORD" - - def __init__(self, - filename, - fp=None, - append=False, - context=get_supported): - """ - :param fp: A seekable file-like object or None to open(filename). - :param append: Open archive in append mode. - :param context: Function returning list of supported tags. Wheels - must have the same context to be sortable. - """ - self.filename = filename - self.fp = fp - self.append = append - self.context = context - basename = os.path.basename(filename) - self.parsed_filename = WHEEL_INFO_RE(basename) - if not basename.endswith('.whl') or self.parsed_filename is None: - raise BadWheelFile("Bad filename '%s'" % filename) - - def __repr__(self): - return self.filename - - @property - def distinfo_name(self): - return "%s.dist-info" % self.parsed_filename.group('namever') - - @property - def datadir_name(self): - return "%s.data" % self.parsed_filename.group('namever') - - @property - def record_name(self): - return "%s/%s" % (self.distinfo_name, self.RECORD) - - @property - def wheelinfo_name(self): - return "%s/%s" % (self.distinfo_name, self.WHEEL_INFO) - - @property - def tags(self): - """A wheel file is compatible with the Cartesian product of the - period-delimited tags in its filename. - To choose a wheel file among several candidates having the same - distribution version 'ver', an installer ranks each triple of - (pyver, abi, plat) that its Python installation can run, sorting - the wheels by the best-ranked tag it supports and then by their - arity which is just len(list(compatibility_tags)). - """ - tags = self.parsed_filename.groupdict() - for pyver in tags['pyver'].split('.'): - 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): - """ - Lowest index of any of this wheel's tags in self.context(), and the - arity e.g. (0, 1) - """ - return self.compatibility_rank(self.context()) - - @property - def compatible(self): - return self.rank[0] != _big_number # bad API! - - # deprecated: - def compatibility_rank(self, supported): - """Rank the wheel against the supported tags. Smaller ranks are more - compatible! - - :param supported: A list of compatibility tags that the current - Python implemenation can run. - """ - preferences = [] - for tag in self.compatibility_tags: - try: - preferences.append(supported.index(tag)) - # Tag not present - except ValueError: - pass - 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' - return self.compatible - - # Comparability. - # Wheels are equal if they refer to the same file. - # If two wheels are not equal, compare based on (in this order): - # 1. Name - # 2. Version - # 3. Compatibility rank - # 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("{0}.context != {1}.context".format(self, other)) - - return self._sort_key < other._sort_key - - # XXX prune - - sn = self.parsed_filename.group('name') - on = other.parsed_filename.group('name') - if sn != on: - return sn < on - sv = parse_version(self.parsed_filename.group('ver')) - ov = parse_version(other.parsed_filename.group('ver')) - if sv != ov: - return sv < ov - # Compatibility - if self.context != other.context: - raise TypeError("{0}.context != {1}.context".format(self, other)) - sc = self.rank - oc = other.rank - if sc != None and oc != 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: - 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" - if self.append: - mode = "a" - vzf = VerifyingZipFile(self.fp if self.fp else self.filename, mode) - if not self.append: - self.verify(vzf) - return vzf - - @reify - def parsed_wheel_info(self): - """Parse wheel metadata (the .data/WHEEL file)""" - return read_pkg_info_bytes(self.zipfile.read(self.wheelinfo_name)) - - def check_version(self): - version = self.parsed_wheel_info['Wheel-Version'] - if tuple(map(int, version.split('.'))) >= VERSION_TOO_HIGH: - raise ValueError("Wheel version is too high") - - @reify - def install_paths(self): - """ - 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? - """ - name = self.parsed_filename.group('name') - return get_install_paths(name) - - def install(self, force=False, overrides={}): - """ - Install the wheel into site-packages. - """ - - # Utility to get the target directory for a particular key - def get_path(key): - return overrides.get(key) or self.install_paths[key] - - # The base target location is either purelib or platlib - if self.parsed_wheel_info['Root-Is-Purelib'] == 'true': - root = get_path('purelib') - else: - root = get_path('platlib') - - # Parse all the names in the archive - name_trans = {} - for info in self.zipfile.infolist(): - name = info.filename - # Zip files can contain entries representing directories. - # These end in a '/'. - # We ignore these, as we create directories on demand. - if name.endswith('/'): - continue - - # Pathnames in a zipfile namelist are always /-separated. - # In theory, paths could start with ./ or have other oddities - # but this won't happen in practical cases of well-formed wheels. - # We'll cover the simple case of an initial './' as it's both easy - # to do and more common than most other oddities. - if name.startswith('./'): - name = name[2:] - - # Split off the base directory to identify files that are to be - # installed in non-root locations - basedir, sep, filename = name.partition('/') - if sep and basedir == self.datadir_name: - # Data file. Target destination is elsewhere - key, sep, filename = filename.partition('/') - if not sep: - raise ValueError("Invalid filename in wheel: {0}".format(name)) - target = get_path(key) - else: - # Normal file. Target destination is root - key = '' - target = root - filename = name - - # Map the actual filename from the zipfile to its intended target - # directory and the pathname relative to that directory. - dest = os.path.normpath(os.path.join(target, filename)) - name_trans[info] = (key, target, filename, dest) - - # We're now ready to start processing the actual install. The process - # is as follows: - # 1. Prechecks - is the wheel valid, is its declared architecture - # OK, etc. [[Responsibility of the caller]] - # 2. Overwrite check - do any of the files to be installed already - # exist? - # 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 - 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)) - - # Get the name of our executable, for use when replacing script - # wrapper hashbang lines. - # We encode it using getfilesystemencoding, as that is "the name of - # the encoding used to convert Unicode filenames into system file - # names". - exename = sys.executable.encode(sys.getfilesystemencoding()) - record_data = [] - record_name = self.distinfo_name + '/RECORD' - for info, (key, target, filename, dest) in name_trans.items(): - name = info.filename - source = self.zipfile.open(info) - # Skip the RECORD file - if name == record_name: - continue - ddir = os.path.dirname(dest) - if not os.path.isdir(ddir): - os.makedirs(ddir) - destination = HashingFile(open(dest, 'wb')) - if key == 'scripts': - hashbang = source.readline() - if hashbang.startswith(b'#!python'): - hashbang = b'#!' + exename + binary(os.linesep) - destination.write(hashbang) - shutil.copyfileobj(source, destination) - reldest = os.path.relpath(dest, root) - reldest.replace(os.sep, '/') - record_data.append((reldest, destination.digest(), destination.length)) - destination.close() - source.close() - # preserve attributes (especially +x bit for scripts) - attrs = info.external_attr >> 16 - if attrs: # tends to be 0 if Windows. - os.chmod(dest, info.external_attr >> 16) - - record_name = os.path.join(root, self.record_name) - with open_for_csv(record_name, 'w+') as record_file: - writer = csv.writer(record_file) - 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. - Caller must complete the verification process by completely reading - every file in the archive (e.g. with extractall).""" - sig = None - 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: - 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 - pass - if sig: - headers, payload = signatures.verify(sig) - if payload['hash'] != "sha256=" + native(record_digest): - 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] - if not hash: - if filename not in (record_name, sig_name): - sys.stderr.write("%s has no hash!\n" % filename) - continue - 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): - zipfile.ZipFile.__init__(self, file, mode, compression, allowZip64) - - 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 - ef = zipfile.ZipFile.open(self, name_or_info, mode, pwd) - if isinstance(name_or_info, zipfile.ZipInfo): - name = name_or_info.filename - else: - name = name_or_info - if (name in self._expected_hashes - and self._expected_hashes[name] != None): - expected_hash = self._expected_hashes[name] - try: - _update_crc_orig = ef._update_crc - except AttributeError: - warnings.warn('Need ZipExtFile._update_crc to implement ' - 'file hash verification (in Python >= 2.7)') - return ef - running_hash = self._hash_algorithm() - if hasattr(ef, '_eof'): # py33 - def _update_crc(data): - _update_crc_orig(data) - running_hash.update(data) - if ef._eof and running_hash.digest() != expected_hash: - raise BadWheelFile("Bad hash for file %r" % ef.name) - else: - def _update_crc(data, eof=None): - _update_crc_orig(data, eof=eof) - running_hash.update(data) - if eof and running_hash.digest() != expected_hash: - raise BadWheelFile("Bad hash for file %r" % ef.name) - ef._update_crc = _update_crc - elif self.strict and name not in self._expected_hashes: - raise BadWheelFile("No expected hash for file %r" % ef.name) - return ef - - def pop(self): - """Truncate the last file off this zipfile. - Assumes infolist() is in the same order as the files (true for - ordinary zip files created by Python)""" - if not self.fp: - raise RuntimeError( - "Attempt to pop from ZIP archive that was already closed") - last = self.infolist().pop() - del self.NameToInfo[last.filename] - self.fp.seek(last.header_offset, os.SEEK_SET) - self.fp.truncate() - self._didModify = True diff --git a/wheel/metadata.py b/wheel/metadata.py deleted file mode 100644 index 2b123f9..0000000 --- a/wheel/metadata.py +++ /dev/null @@ -1,330 +0,0 @@ -""" -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 os.path -import textwrap -import pkg_resources -import email.parser - -from . import __version__ as wheel_version - -METADATA_VERSION = "2.0" - -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")) - -# commonly filled out as "UNKNOWN" by distutils: -UNKNOWN_FIELDS = set(("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. -EXTRA_RE = re.compile("""^(?P.*?)(;\s*(?P.*?)(extra == '(?P.*?)')?)$""") -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: - seen.add(value) - yield value - - -def handle_requires(metadata, pkg_info, key): - """ - Place the runtime requirements from pkg_info into metadata. - """ - may_requires = OrderedDefaultDict(list) - for value in sorted(pkg_info.get_all(key)): - extra_match = EXTRA_RE.search(value) - if extra_match: - groupdict = extra_match.groupdict() - condition = groupdict['condition'] - extra = groupdict['extra'] - package = groupdict['package'] - if condition.endswith(' and '): - condition = condition[:-5] - else: - condition, extra = None, None - package = value - key = MayRequiresKey(condition, extra) - may_requires[key].append(package) - - 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. - key, value = item - 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: - may_requirement['extra'] = key.extra - if key.condition: - may_requirement['environment'] = key.condition - metadata['run_requires'].append(may_requirement) - - if not 'extras' in metadata: - metadata['extras'] = [] - metadata['extras'].extend([key.extra for key in may_requires.keys() if key.extra]) - - -def pkginfo_to_dict(path, distribution=None): - """ - Convert PKG-INFO to a prototype Metadata 2.0 (PEP 426) dict. - - The description is included under the key ['description'] rather than - being written to a separate file. - - path: path to PKG-INFO file - distribution: optional distutils Distribution() - """ - - metadata = OrderedDefaultDict(lambda: OrderedDefaultDict(lambda: OrderedDefaultDict(OrderedDict))) - metadata["generator"] = "bdist_wheel (" + wheel_version + ")" - try: - unicode - pkg_info = read_pkg_info(path) - except NameError: - with open(path, 'rb') as pkg_info_file: - pkg_info = email.parser.Parser().parsestr(pkg_info_file.read().decode('utf-8')) - description = None - - if pkg_info['Summary']: - metadata['summary'] = pkginfo_unicode(pkg_info, 'Summary') - del pkg_info['Summary'] - - if pkg_info['Description']: - description = dedent_description(pkg_info) - del pkg_info['Description'] - else: - payload = pkg_info.get_payload() - if isinstance(payload, bytes): - # Avoid a Python 2 Unicode error. - # We still suffer ? glyphs on Python 3. - payload = payload.decode('utf-8') - if payload: - description = payload - - if description: - pkg_info['description'] = description - - for key in sorted(unique(k.lower() for k in pkg_info.keys())): - low_key = key.replace('-', '_') - - if low_key in SKIP_FIELDS: - continue - - if low_key in UNKNOWN_FIELDS and pkg_info.get(key) == 'UNKNOWN': - continue - - if low_key in sorted(PLURAL_FIELDS): - metadata[PLURAL_FIELDS[low_key]] = pkg_info.get_all(key) - - elif low_key == "requires_dist": - handle_requires(metadata, pkg_info, key) - - elif low_key == 'provides_extra': - if not 'extras' 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]} - - elif low_key == 'keywords': - metadata['keywords'] = KEYWORDS_RE.split(pkg_info[key]) - - else: - metadata[low_key] = pkg_info[key] - - metadata['metadata_version'] = METADATA_VERSION - - if 'extras' in metadata: - metadata['extras'] = sorted(set(metadata['extras'])) - - # include more information if distribution is available - if distribution: - for requires, attr in (('test_requires', 'tests_require'),): - try: - requirements = getattr(distribution, attr) - if isinstance(requirements, list): - new_requirements = sorted(convert_requirements(requirements)) - metadata[requires] = [{'requires':new_requirements}] - except AttributeError: - pass - - # handle contacts - contacts = [] - for contact_type, role in CONTACT_FIELDS: - contact = OrderedDict() - for key in sorted(contact_type): - if contact_type[key] in metadata: - contact[key] = metadata.pop(contact_type[key]) - if contact: - contact['role'] = role - contacts.append(contact) - if contacts: - metadata['extensions']['python.details']['contacts'] = contacts - - # convert entry points to exports - try: - with open(os.path.join(os.path.dirname(path), "entry_points.txt"), "r") as ep_file: - ep_map = pkg_resources.EntryPoint.parse_map(ep_file.read()) - exports = OrderedDict() - for group, items in sorted(ep_map.items()): - exports[group] = OrderedDict() - for item in sorted(map(str, items.values())): - name, export = item.split(' = ', 1) - exports[group][name] = export - if exports: - metadata['extensions']['python.exports'] = exports - except IOError: - pass - - # copy console_scripts entry points to commands - if 'python.exports' in metadata['extensions']: - for (ep_script, wrap_script) in (('console_scripts', 'wrap_console'), - ('gui_scripts', 'wrap_gui')): - if ep_script in metadata['extensions']['python.exports']: - metadata['extensions']['python.commands'][wrap_script] = \ - metadata['extensions']['python.exports'][ep_script] - - return metadata - -def requires_to_requires_dist(requirement): - """Compose the version predicates for requirement in PEP 345 fashion.""" - requires_dist = [] - for op, ver in requirement.specs: - requires_dist.append(op + ver) - if not requires_dist: - return '' - return " (%s)" % ','.join(requires_dist) - -def convert_requirements(requirements): - """Yield Requires-Dist: strings for parsed requirements strings.""" - for req in requirements: - parsed_requirement = pkg_resources.Requirement.parse(req) - spec = requires_to_requires_dist(parsed_requirement) - extras = ",".join(parsed_requirement.extras) - if extras: - 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') - and ('Provides-Extra', 'extra') tuples. - - extras_require is a dictionary of {extra: [requirements]} as passed to setup(), - using the empty extra {'': [requirements]} to hold install_requires. - """ - for extra, depends in extras_require.items(): - condition = '' - if extra and ':' in extra: # setuptools extra:condition syntax - extra, condition = extra.split(':', 1) - extra = pkg_resources.safe_extra(extra) - if extra: - yield ('Provides-Extra', extra) - if condition: - condition += " and " - condition += "extra == '%s'" % extra - if condition: - condition = '; ' + condition - 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 - old-draft Metadata 2.0 format. - """ - pkg_info = read_pkg_info(pkginfo_path) - pkg_info.replace_header('Metadata-Version', '2.0') - requires_path = os.path.join(egg_info_path, 'requires.txt') - if os.path.exists(requires_path): - with open(requires_path) as requires_file: - requires = requires_file.read() - for extra, reqs in sorted(pkg_resources.split_sections(requires), - key=lambda x: x[0] or ''): - for item in generate_requirements({extra: reqs}): - pkg_info[item[0]] = item[1] - - description = pkg_info['Description'] - if description: - pkg_info.set_payload(dedent_description(pkg_info)) - del pkg_info['Description'] - - return pkg_info - - -def pkginfo_unicode(pkg_info, field): - """Hack to coax Unicode out of an email Message() - Python 3.3+""" - text = pkg_info[field] - field = field.lower() - if not isinstance(text, str): - if not hasattr(pkg_info, 'raw_items'): # Python 3.2 - return str(text) - for item in pkg_info.raw_items(): - if item[0].lower() == field: - text = item[1].encode('ascii', 'surrogateescape')\ - .decode('utf-8') - break - - return text - - -def dedent_description(pkg_info): - """ - Dedent and convert pkg_info['Description'] to Unicode. - """ - description = pkg_info['Description'] - - # Python 3 Unicode handling, sorta. - surrogates = False - if not isinstance(description, str): - surrogates = True - description = pkginfo_unicode(pkg_info, 'Description') - - 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 surrogates: - description_dedent = description_dedent\ - .encode("utf8")\ - .decode("ascii", "surrogateescape") - - return description_dedent - - -if __name__ == "__main__": - import sys, pprint - pprint.pprint(pkginfo_to_dict(sys.argv[1])) diff --git a/wheel/paths.py b/wheel/paths.py deleted file mode 100644 index fe3dfd6..0000000 --- a/wheel/paths.py +++ /dev/null @@ -1,41 +0,0 @@ -""" -Installation paths. - -Map the .data/ subdirectory names to install paths. -""" - -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}) - 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 = {} - - i = get_install_command(name) - - for key in install.SCHEME_KEYS: - paths[key] = getattr(i, 'install_' + key) - - # pip uses a similar path as an alternative to the system's (read-only) - # include directory: - if hasattr(sys, 'real_prefix'): # virtualenv - paths['headers'] = os.path.join(sys.prefix, - 'include', - 'site', - 'python' + sys.version[:3], - name) - - return paths diff --git a/wheel/pep425tags.py b/wheel/pep425tags.py deleted file mode 100644 index a7bd4a9..0000000 --- a/wheel/pep425tags.py +++ /dev/null @@ -1,178 +0,0 @@ -"""Generate and work with PEP 425 Compatibility Tags.""" - -import sys -import warnings - -import sysconfig -import distutils.util - - -def get_config_var(var): - try: - return sysconfig.get_config_var(var) - except IOError as e: # pip Issue #1074 - warnings.warn("{0}".format(e), RuntimeWarning) - return None - - -def get_abbr_impl(): - """Return abbreviated implementation name.""" - if hasattr(sys, 'pypy_version_info'): - pyimpl = 'pp' - elif sys.platform.startswith('java'): - pyimpl = 'jy' - elif sys.platform == 'cli': - pyimpl = 'ip' - else: - pyimpl = 'cp' - return pyimpl - - -def get_impl_ver(): - """Return implementation version.""" - impl_ver = get_config_var("py_version_nodot") - if not impl_ver or get_abbr_impl() == 'pp': - impl_ver = ''.join(map(str, get_impl_version_info())) - return impl_ver - - -def get_impl_version_info(): - """Return sys.version_info-like tuple for use in decrementing the minor - version.""" - if get_abbr_impl() == 'pp': - # as per https://github.com/pypa/pip/issues/2882 - return (sys.version_info[0], sys.pypy_version_info.major, - sys.pypy_version_info.minor) - else: - return sys.version_info[0], sys.version_info[1] - - -def get_flag(var, fallback, expected=True, warn=True): - """Use a fallback method for determining SOABI flags if the needed config - var is unset or unavailable.""" - val = get_config_var(var) - if val is None: - if warn: - warnings.warn("Config variable '{0}' is unset, Python ABI tag may " - "be incorrect".format(var), RuntimeWarning, 2) - return fallback() - return val == expected - - -def get_abi_tag(): - """Return the ABI tag based on SOABI (if available) or emulate SOABI - (CPython 2, PyPy).""" - soabi = get_config_var('SOABI') - impl = get_abbr_impl() - if not soabi and impl in ('cp', 'pp') and hasattr(sys, 'maxunicode'): - d = '' - m = '' - u = '' - if get_flag('Py_DEBUG', - lambda: hasattr(sys, 'gettotalrefcount'), - warn=(impl == 'cp')): - d = 'd' - if get_flag('WITH_PYMALLOC', - lambda: impl == 'cp', - warn=(impl == 'cp')): - m = 'm' - if get_flag('Py_UNICODE_SIZE', - lambda: sys.maxunicode == 0x10ffff, - expected=4, - warn=(impl == 'cp' and - sys.version_info < (3, 3))) \ - and sys.version_info < (3, 3): - u = 'u' - abi = '%s%s%s%s%s' % (impl, get_impl_ver(), d, m, u) - elif soabi and soabi.startswith('cpython-'): - abi = 'cp' + soabi.split('-')[1] - elif soabi: - abi = soabi.replace('.', '_').replace('-', '_') - else: - abi = None - return abi - - -def get_platform(): - """Return our platform name 'win32', 'linux_x86_64'""" - # XXX remove distutils dependency - result = distutils.util.get_platform().replace('.', '_').replace('-', '_') - if result == "linux_x86_64" and sys.maxsize == 2147483647: - # pip pull request #3497 - result = "linux_i686" - return result - - -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"], - 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 = [] - version_info = get_impl_version_info() - major = version_info[:-1] - # 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(): - if suffix[0].startswith('.abi'): - abi3s.add(suffix[0].split('.', 2)[1]) - - abis.extend(sorted(list(abi3s))) - - abis.append('none') - - platforms = [] - 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: - supported.append(('%s%s' % (impl, versions[0]), abi, arch)) - - # abi3 modules compatible with older version of Python - for version in versions[1:]: - # abi3 was introduced in Python 3.2 - if version in ('31', '30'): - break - for abi in abi3s: # empty set if not Python 3 - for arch in platforms: - supported.append(("%s%s" % (impl, version), abi, arch)) - - # No abi / arch, but requires our implementation: - for i, version in enumerate(versions): - supported.append(('%s%s' % (impl, version), 'none', 'any')) - if i == 0: - # Tagged specifically as being cross-version compatible - # (with just the major version specified) - 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)) - - # No abi / arch, generic Python - for i, version in enumerate(versions): - 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 deleted file mode 100644 index 8a4aca3..0000000 --- a/wheel/pkginfo.py +++ /dev/null @@ -1,44 +0,0 @@ -"""Tools for reading and writing PKG-INFO / METADATA without caring -about the encoding.""" - -from email.parser import Parser - -try: - unicode - _PY3 = False -except NameError: - _PY3 = True - -if not _PY3: - from email.generator import Generator - - def read_pkg_info_bytes(bytestr): - return Parser().parsestr(bytestr) - - def read_pkg_info(path): - with open(path, "r") as headers: - message = Parser().parse(headers) - return message - - def write_pkg_info(path, message): - with open(path, 'w') as metadata: - 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", - errors="surrogateescape") as headers: - message = Parser().parse(headers) - return message - - 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 deleted file mode 100644 index 3f21b50..0000000 --- a/wheel/signatures/__init__.py +++ /dev/null @@ -1,106 +0,0 @@ -""" -Create and verify jws-js format Ed25519 signatures. -""" - -__all__ = [ 'sign', 'verify' ] - -import json -from ..util import urlsafe_b64decode, urlsafe_b64encode, native, binary - -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) - test() - - return ed25519ll - -def sign(payload, keypair): - """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. - "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)}], - "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() - # XXX forbid duplicate keys in JSON input using object_pairs_hook (2.7+) - recipients = jwsjs["recipients"] - encoded_payload = binary(jwsjs["payload"]) - headers = [] - for recipient in recipients: - assertTrue(len(recipient) == 2, "Unknown recipient key {0}".format(recipient)) - 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"])) - vk = urlsafe_b64decode(binary(header["jwk"]["vk"])) - secured_input = b".".join((h, encoded_payload)) - sig = urlsafe_b64decode(s) - sig_msg = sig+secured_input - verified_input = native(ed25519ll.crypto_sign_open(sig_msg, vk)) - verified_header, verified_payload = verified_input.split('.') - verified_header = binary(verified_header) - decoded_header = native(urlsafe_b64decode(verified_header)) - headers.append(json.loads(decoded_header)) - - verified_payload = binary(verified_payload) - - # only return header, payload that have passed through the crypto library. - payload = json.loads(native(urlsafe_b64decode(verified_payload))) - - return headers, payload - -def test(): - kp = ed25519ll.crypto_sign_keypair() - payload = {'test': 'onstartup'} - jwsjs = json.loads(json.dumps(sign(payload, kp))) - verify(jwsjs) - jwsjs['payload'] += 'x' - try: - verify(jwsjs) - except ValueError: - pass - 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 deleted file mode 100644 index 56efe44..0000000 --- a/wheel/signatures/djbec.py +++ /dev/null @@ -1,270 +0,0 @@ -# Ed25519 digital signatures -# Based on http://ed25519.cr.yp.to/python/ed25519.py -# See also http://ed25519.cr.yp.to/software.html -# Adapted by Ron Garret -# Sped up considerably using coordinate transforms found on: -# http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html -# Specifically add-2008-hwcd-4 and dbl-2008-hwcd - -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 - PY3 = True - asbytes = bytes - joinbytes = bytes - def bit(h, i): - return (h[i//8] >> (i%8)) & 1 - -import hashlib - -b = 256 -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 - t = expmod(b, e // 2, m) ** 2 % 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) - -d = -121665 * inv(121666) -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 - return x - -By = 4 * inv(5) -Bx = xrecover(By) -B = [Bx % q,By % q] - -#def edwards(P,Q): -# x1 = P[0] -# y1 = P[1] -# x2 = Q[0] -# y2 = Q[1] -# x3 = (x1*y2+x2*y1) * inv(1+d*x1*x2*y1*y2) -# y3 = (y1*y2+x1*x2) * inv(1-d*x1*x2*y1*y2) -# return (x3 % q,y3 % q) - -#def scalarmult(P,e): -# if e == 0: return [0,1] -# Q = scalarmult(P,e/2) -# Q = edwards(Q,Q) -# if e & 1: Q = edwards(Q,P) -# return Q - -# Faster (!) version based on: -# http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html - -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 - return (X3, Y3, Z3, T3) - -def xpt_double (pt): - (X1, Y1, Z1, _) = pt - 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): - (x, y) = pt - return (x, y, 1, (x*y)%q) - -def pt_unxform (pt): - (x, y, z, _) = pt - 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 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)] - 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)] - 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) - return encodepoint(A) - -def Hint(m): - h = H(m) - return sum(2**i * bit(h,i) for i in range(2*b)) - -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)]) - r = Hint(inter + m) - 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 - -def decodeint(s): - 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)) - 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") - 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]) - A = decodepoint(pk) - S = decodeint(s[b//8:b//4]) - h = Hint(encodepoint(R) + pk + m) - 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 - -########################################################## -# -# Curve25519 reference implementation by Matthew Dempsky, from: -# http://cr.yp.to/highspeed/naclcrypto-20090310.pdf - -# P = 2 ** 255 - 19 -P = q -A = 486662 - -#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 -# return t - -# def inv(x): return expmod(x, P - 2, P) - -def add(n, m, d): - (xn, zn) = n - (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) - 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) - (pm, pm1) = f(m // 2) - if (m & 1): - return (add(pm, pm1, one), double(pm1)) - return (double(pm), add(pm, pm1, one)) - ((x,z), _) = f(n) - return (x * inv(z)) % P - -import random - -def genkey(n=0): - n = n or random.randint(0,P) - n &= ~7 - n &= ~(128 << 8 * 31) - n |= 64 << 8 * 31 - return n - -#def str2int(s): -# return int(hexlify(s), 16) -# # return sum(ord(s[i]) << (8 * i) for i in range(32)) -# -#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') - 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 deleted file mode 100644 index 55eba2e..0000000 --- a/wheel/signatures/ed25519py.py +++ /dev/null @@ -1,52 +0,0 @@ -# -*- coding: utf-8 -*- - -import warnings -import os - -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 - -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)""" - if seed is None: - seed = os.urandom(PUBLICKEYBYTES) - else: - warnings.warn("ed25519ll should choose random seed.", - RuntimeWarning) - if len(seed) != 32: - raise ValueError("seed must be 32 random bytes or None.") - skbytes = seed - vkbytes = djbec.publickey(skbytes) - return Keypair(vkbytes, skbytes+vkbytes) - - -def crypto_sign(msg, sk): - """Return signature+message given message and secret key. - The signature is the first SIGNATUREBYTES bytes of the return value. - A copy of msg is in the remainder.""" - if len(sk) != SECRETKEYBYTES: - raise ValueError("Bad signing key length %d" % len(sk)) - vkbytes = sk[PUBLICKEYBYTES:] - skbytes = sk[:PUBLICKEYBYTES] - sig = djbec.signature(msg, skbytes, vkbytes) - return sig + msg - - -def crypto_sign_open(signed, vk): - """Return message given signature+message and the verifying key.""" - if len(vk) != PUBLICKEYBYTES: - 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) - return signed[SIGNATUREBYTES:] - diff --git a/wheel/signatures/keys.py b/wheel/signatures/keys.py deleted file mode 100644 index 57d7feb..0000000 --- a/wheel/signatures/keys.py +++ /dev/null @@ -1,99 +0,0 @@ -"""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 -scope. - -Given a package name, return a list of (scope, key) suggested keys to sign -that package (only the verifying keys; the private signing key is stored -elsewhere). - -Keys here are represented as urlsafe_b64encoded strings with no padding. - -Tentative command line interface: - -# list trusts -wheel trust -# trust a particular key for all -wheel trust + key -# trust key for beaglevote -wheel trust beaglevote key -# stop trusting a key for all -wheel untrust + key - -# generate a key pair -wheel keygen - -# import a signing key from a file -wheel import keyfile - -# export a signing key -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':[]} - - def load(self): - # XXX JSON is not a great database - for path in load_config_paths('wheel'): - conf = os.path.join(native(path), self.CONFIG_NAME) - if os.path.exists(conf): - with open(conf, 'r') as infile: - self.data = json.load(infile) - for x in ('signers', 'verifiers'): - if not x in self.data: - self.data[x] = [] - if 'schema' not in self.data: - self.data['schema'] = self.SCHEMA - elif self.data['schema'] != self.SCHEMA: - raise ValueError( - "Bad wheel.json version {0}, expected {1}".format( - self.data['schema'], self.SCHEMA)) - break - return self - - def save(self): - # 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}) - return self - - def untrust(self, scope, vk): - """Stop trusting a particular key for given scope.""" - 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.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}) - diff --git a/wheel/test/__init__.py b/wheel/test/__init__.py deleted file mode 100644 index 4287ca8..0000000 --- a/wheel/test/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# \ No newline at end of file diff --git a/wheel/test/complex-dist/complexdist/__init__.py b/wheel/test/complex-dist/complexdist/__init__.py deleted file mode 100644 index 559fbb7..0000000 --- a/wheel/test/complex-dist/complexdist/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -def main(): - return diff --git a/wheel/test/complex-dist/setup.py b/wheel/test/complex-dist/setup.py deleted file mode 100644 index 615d5dc..0000000 --- a/wheel/test/complex-dist/setup.py +++ /dev/null @@ -1,30 +0,0 @@ -from setuptools import setup - -try: - unicode - def u8(s): - return s.decode('unicode-escape') -except NameError: - def u8(s): - return s - -setup(name='complex-dist', - version='0.1', - description=u8('Another testing distribution \N{SNOWMAN}'), - long_description=u8('Another testing distribution \N{SNOWMAN}'), - author="Illustrious Author", - author_email="illustrious@example.org", - url="http://example.org/exemplary", - packages=['complexdist'], - setup_requires=["wheel", "setuptools"], - install_requires=["quux", "splort"], - extras_require={'simple':['simple.dist']}, - tests_require=["foo", "bar>=10.0.0"], - entry_points={ - 'console_scripts': [ - 'complex-dist=complexdist:main', - 'complex-dist2=complexdist:main', - ], - }, - ) - diff --git a/wheel/test/conftest.py b/wheel/test/conftest.py deleted file mode 100644 index d14cc47..0000000 --- a/wheel/test/conftest.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -pytest local configuration plug-in -""" - -import gc -import warnings - -import pytest - -@pytest.yield_fixture(scope='function', autouse=True) -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. - - Since only Python 3 and PyPy3 have ResourceWarning's, this context will - have no effect when running tests on Python 2 or PyPy. - - Because of autouse=True, this function will be automatically enabled for - all test_* functions in this module. - - This code is primarily based on the examples found here: - https://stackoverflow.com/questions/24717027/convert-python-3-resourcewarnings-into-exception - """ - try: - ResourceWarning - except NameError: - # Python 2, PyPy - yield - return - # 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) - if not caught: - return - pytest.fail('The following file descriptors were not closed properly:\n' + - '\n'.join((str(warning.message) for warning in caught)), - pytrace=False) diff --git a/wheel/test/extension.dist/extension.c b/wheel/test/extension.dist/extension.c deleted file mode 100644 index a37c3fa..0000000 --- a/wheel/test/extension.dist/extension.c +++ /dev/null @@ -1,2 +0,0 @@ -#define Py_LIMITED_API 0x03020000 -#include diff --git a/wheel/test/extension.dist/setup.cfg b/wheel/test/extension.dist/setup.cfg deleted file mode 100644 index 9f6ff39..0000000 --- a/wheel/test/extension.dist/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[bdist_wheel] -py_limited_api=cp32 diff --git a/wheel/test/extension.dist/setup.py b/wheel/test/extension.dist/setup.py deleted file mode 100644 index 7a66845..0000000 --- a/wheel/test/extension.dist/setup.py +++ /dev/null @@ -1,20 +0,0 @@ -from setuptools import setup, Extension - -try: - unicode - def u8(s): - return s.decode('unicode-escape').encode('utf-8') -except NameError: - def u8(s): - return s.encode('utf-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) - ], - ) - diff --git a/wheel/test/headers.dist/header.h b/wheel/test/headers.dist/header.h deleted file mode 100644 index e69de29..0000000 diff --git a/wheel/test/headers.dist/headersdist.py b/wheel/test/headers.dist/headersdist.py deleted file mode 100644 index e69de29..0000000 diff --git a/wheel/test/headers.dist/setup.cfg b/wheel/test/headers.dist/setup.cfg deleted file mode 100644 index 7c964b4..0000000 --- a/wheel/test/headers.dist/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[wheel] -universal=1 diff --git a/wheel/test/headers.dist/setup.py b/wheel/test/headers.dist/setup.py deleted file mode 100644 index 2704f01..0000000 --- a/wheel/test/headers.dist/setup.py +++ /dev/null @@ -1,16 +0,0 @@ -from setuptools import setup - -try: - unicode - def u8(s): - return s.decode('unicode-escape').encode('utf-8') -except NameError: - def u8(s): - return s.encode('utf-8') - -setup(name='headers.dist', - version='0.1', - description=u8('A distribution with headers'), - headers=['header.h'] - ) - diff --git a/wheel/test/pydist-schema.json b/wheel/test/pydist-schema.json deleted file mode 100644 index 566f3a4..0000000 --- a/wheel/test/pydist-schema.json +++ /dev/null @@ -1,362 +0,0 @@ -{ - "id": "http://www.python.org/dev/peps/pep-0426/", - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Metadata for Python Software Packages 2.0", - "type": "object", - "properties": { - "metadata_version": { - "description": "Version of the file format", - "type": "string", - "pattern": "^(\\d+(\\.\\d+)*)$" - }, - "generator": { - "description": "Name and version of the program that produced this file.", - "type": "string", - "pattern": "^[0-9A-Za-z]([0-9A-Za-z_.-]*[0-9A-Za-z])( \\(.*\\))?$" - }, - "name": { - "description": "The name of the distribution.", - "type": "string", - "$ref": "#/definitions/distribution_name" - }, - "version": { - "description": "The distribution's public version identifier", - "type": "string", - "pattern": "^(\\d+(\\.\\d+)*)((a|b|c|rc)(\\d+))?(\\.(post)(\\d+))?(\\.(dev)(\\d+))?$" - }, - "source_label": { - "description": "A constrained identifying text string", - "type": "string", - "pattern": "^[0-9a-z_.-+]+$" - }, - "source_url": { - "description": "A string containing a full URL where the source for this specific version of the distribution can be downloaded.", - "type": "string", - "format": "uri" - }, - "summary": { - "description": "A one-line summary of what the distribution does.", - "type": "string" - }, - "extras": { - "description": "A list of optional sets of dependencies that may be used to define conditional dependencies in \"may_require\" and similar fields.", - "type": "array", - "items": { - "type": "string", - "$ref": "#/definitions/extra_name" - } - }, - "meta_requires": { - "description": "A list of subdistributions made available through this metadistribution.", - "type": "array", - "$ref": "#/definitions/dependencies" - }, - "run_requires": { - "description": "A list of other distributions needed to run this distribution.", - "type": "array", - "$ref": "#/definitions/dependencies" - }, - "test_requires": { - "description": "A list of other distributions needed when this distribution is tested.", - "type": "array", - "$ref": "#/definitions/dependencies" - }, - "build_requires": { - "description": "A list of other distributions needed when this distribution is built.", - "type": "array", - "$ref": "#/definitions/dependencies" - }, - "dev_requires": { - "description": "A list of other distributions needed when this distribution is developed.", - "type": "array", - "$ref": "#/definitions/dependencies" - }, - "provides": { - "description": "A list of strings naming additional dependency requirements that are satisfied by installing this distribution. These strings must be of the form Name or Name (Version)", - "type": "array", - "items": { - "type": "string", - "$ref": "#/definitions/provides_declaration" - } - }, - "modules": { - "description": "A list of modules and/or packages available for import after installing this distribution.", - "type": "array", - "items": { - "type": "string", - "$ref": "#/definitions/qualified_name" - } - }, - "namespaces": { - "description": "A list of namespace packages this distribution contributes to", - "type": "array", - "items": { - "type": "string", - "$ref": "#/definitions/qualified_name" - } - }, - "obsoleted_by": { - "description": "A string that indicates that this project is no longer being developed. The named project provides a substitute or replacement.", - "type": "string", - "$ref": "#/definitions/requirement" - }, - "supports_environments": { - "description": "A list of strings specifying the environments that the distribution explicitly supports.", - "type": "array", - "items": { - "type": "string", - "$ref": "#/definitions/environment_marker" - } - }, - "install_hooks": { - "description": "The install_hooks field is used to define various operations that may be invoked on a distribution in a platform independent manner.", - "type": "object", - "properties": { - "postinstall": { - "type": "string", - "$ref": "#/definitions/export_specifier" - }, - "preuninstall": { - "type": "string", - "$ref": "#/definitions/export_specifier" - } - } - }, - "extensions": { - "description": "Extensions to the metadata may be present in a mapping under the 'extensions' key.", - "type": "object", - "$ref": "#/definitions/extensions" - } - }, - - "required": ["metadata_version", "name", "version", "summary"], - "additionalProperties": false, - - "definitions": { - "contact": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "email": { - "type": "string" - }, - "url": { - "type": "string" - }, - "role": { - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false - }, - "dependencies": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/dependency" - } - }, - "dependency": { - "type": "object", - "properties": { - "extra": { - "type": "string", - "$ref": "#/definitions/extra_name" - }, - "environment": { - "type": "string", - "$ref": "#/definitions/environment_marker" - }, - "requires": { - "type": "array", - "items": { - "type": "string", - "$ref": "#/definitions/requirement" - } - } - }, - "required": ["requires"], - "additionalProperties": false - }, - "extensions": { - "type": "object", - "patternProperties": { - "^[A-Za-z][0-9A-Za-z_]*([.][0-9A-Za-z_]*)*$": {} - }, - "properties": { - "python.details" : { - "description": "More information regarding the distribution.", - "type": "object", - "properties": { - "document_names": { - "description": "Names of supporting metadata documents", - "type": "object", - "properties": { - "description": { - "type": "string", - "$ref": "#/definitions/document_name" - }, - "changelog": { - "type": "string", - "$ref": "#/definitions/document_name" - }, - "license": { - "type": "string", - "$ref": "#/definitions/document_name" - } - }, - "additionalProperties": false - }, - "keywords": { - "description": "A list of additional keywords to be used to assist searching for the distribution in a larger catalog.", - "type": "array", - "items": { - "type": "string" - } - }, - "license": { - "description": "A string indicating the license covering the distribution.", - "type": "string" - }, - "classifiers": { - "description": "A list of strings, with each giving a single classification value for the distribution.", - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "python.project" : { - "description": "More information regarding the creation and maintenance of the distribution.", - "$ref": "#/definitions/project_or_integrator" - }, - "python.integrator" : { - "description": "More information regarding the downstream redistributor of the distribution.", - "$ref": "#/definitions/project_or_integrator" - }, - "python.commands" : { - "description": "Command line interfaces provided by this distribution", - "type": "object", - "$ref": "#/definitions/commands" - }, - "python.exports" : { - "description": "Other exported interfaces provided by this distribution", - "type": "object", - "$ref": "#/definitions/exports" - } - }, - "additionalProperties": false - }, - "commands": { - "type": "object", - "properties": { - "wrap_console": { - "type": "object", - "$ref": "#/definitions/command_map" - }, - "wrap_gui": { - "type": "object", - "$ref": "#/definitions/command_map" - }, - "prebuilt": { - "type": "array", - "items": { - "type": "string", - "$ref": "#/definitions/relative_path" - } - } - }, - "additionalProperties": false - }, - "exports": { - "type": "object", - "patternProperties": { - "^[A-Za-z][0-9A-Za-z_]*([.][0-9A-Za-z_]*)*$": { - "type": "object", - "patternProperties": { - ".": { - "type": "string", - "$ref": "#/definitions/export_specifier" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - "command_map": { - "type": "object", - "patternProperties": { - "^[0-9A-Za-z]([0-9A-Za-z_.-]*[0-9A-Za-z])?$": { - "type": "string", - "$ref": "#/definitions/export_specifier" - } - }, - "additionalProperties": false - }, - "project_or_integrator" : { - "type": "object", - "properties" : { - "contacts": { - "description": "A list of contributor entries giving the recommended contact points for getting more information about the project.", - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/contact" - } - }, - "contributors": { - "description": "A list of contributor entries for other contributors not already listed as current project points of contact.", - "type": "array", - "items": { - "type": "object", - "$ref": "#/definitions/contact" - } - }, - "project_urls": { - "description": "A mapping of arbitrary text labels to additional URLs relevant to the project.", - "type": "object" - } - } - }, - "distribution_name": { - "type": "string", - "pattern": "^[0-9A-Za-z]([0-9A-Za-z_.-]*[0-9A-Za-z])?$" - }, - "requirement": { - "type": "string" - }, - "provides_declaration": { - "type": "string" - }, - "environment_marker": { - "type": "string" - }, - "document_name": { - "type": "string" - }, - "extra_name" : { - "type": "string", - "pattern": "^[0-9A-Za-z]([0-9A-Za-z_.-]*[0-9A-Za-z])?$" - }, - "relative_path" : { - "type": "string" - }, - "export_specifier": { - "type": "string", - "pattern": "^([A-Za-z_][A-Za-z_0-9]*([.][A-Za-z_][A-Za-z_0-9]*)*)(:[A-Za-z_][A-Za-z_0-9]*([.][A-Za-z_][A-Za-z_0-9]*)*)?(\\[[0-9A-Za-z]([0-9A-Za-z_.-]*[0-9A-Za-z])?\\])?$" - }, - "qualified_name" : { - "type": "string", - "pattern": "^[A-Za-z_][A-Za-z_0-9]*([.][A-Za-z_][A-Za-z_0-9]*)*$" - }, - "prefixed_name" : { - "type": "string", - "pattern": "^[A-Za-z_][A-Za-z_0-9]*([.][A-Za-z_0-9]*)*$" - } - } -} diff --git a/wheel/test/simple.dist/setup.py b/wheel/test/simple.dist/setup.py deleted file mode 100644 index 50c909f..0000000 --- a/wheel/test/simple.dist/setup.py +++ /dev/null @@ -1,17 +0,0 @@ -from setuptools import setup - -try: - unicode - def u8(s): - return s.decode('unicode-escape').encode('utf-8') -except NameError: - def u8(s): - return s.encode('utf-8') - -setup(name='simple.dist', - version='0.1', - description=u8('A testing distribution \N{SNOWMAN}'), - packages=['simpledist'], - extras_require={'voting': ['beaglevote']}, - ) - diff --git a/wheel/test/simple.dist/simpledist/__init__.py b/wheel/test/simple.dist/simpledist/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/wheel/test/test-1.0-py2.py3-none-win32.whl b/wheel/test/test-1.0-py2.py3-none-win32.whl deleted file mode 100644 index dfd3070..0000000 Binary files a/wheel/test/test-1.0-py2.py3-none-win32.whl and /dev/null differ diff --git a/wheel/test/test_basic.py b/wheel/test/test_basic.py deleted file mode 100644 index 6bd46b1..0000000 --- a/wheel/test/test_basic.py +++ /dev/null @@ -1,178 +0,0 @@ -""" -Basic wheel tests. -""" - -import os -import pkg_resources -import json -import sys - -from pkg_resources import resource_filename - -import wheel.util -import wheel.tool - -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', '') - for dist in test_distributions: - for subdir in ('build', 'dist'): - try: - rmtree(os.path.join(base, dist, subdir)) - except OSError: - pass - -def setup_module(): - build_wheel() - build_egg() - -def build_wheel(): - """Build wheels from test distributions.""" - for dist in test_distributions: - pwd = os.path.abspath(os.curdir) - distdir = pkg_resources.resource_filename('wheel.test', dist) - os.chdir(distdir) - try: - sys.argv = ['', 'bdist_wheel'] - exec(compile(open('setup.py').read(), 'setup.py', 'exec')) - finally: - os.chdir(pwd) - -def build_egg(): - """Build eggs from test distributions.""" - for dist in test_distributions: - pwd = os.path.abspath(os.curdir) - distdir = pkg_resources.resource_filename('wheel.test', dist) - os.chdir(distdir) - try: - sys.argv = ['', 'bdist_egg'] - exec(compile(open('setup.py').read(), 'setup.py', 'exec')) - 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') - with open(egg_names_path) as egg_names: - for line in egg_names: - line = line.strip() - if not line: - 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) - - 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: - distdir = os.path.join(base, dist, 'dist') - 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. - This also verifies the integrity of our testing wheel files. - """ - for dist in test_distributions: - distdir = pkg_resources.resource_filename('wheel.test', - os.path.join(dist, 'dist')) - 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" - basedir = pkg_resources.resource_filename('wheel.test', dist) - for (dirname, subdirs, filenames) in os.walk(basedir): - for filename in filenames: - if filename.endswith('.whl'): - whl = ZipFile(os.path.join(dirname, filename)) - for entry in whl.infolist(): - assert not '.data/scripts/' in entry.filename - -def test_pydist(): - """Make sure pydist.json exists and validates against our schema.""" - # XXX this test may need manual cleanup of older wheels - - import jsonschema - - def open_json(filename): - with open(filename, 'rb') as json_file: - return json.loads(json_file.read().decode('utf-8')) - - pymeta_schema = open_json(resource_filename('wheel.test', - 'pydist-schema.json')) - valid = 0 - for dist in ("simple.dist", "complex-dist"): - basedir = pkg_resources.resource_filename('wheel.test', dist) - for (dirname, subdirs, filenames) in os.walk(basedir): - for filename in filenames: - if filename.endswith('.whl'): - whl = ZipFile(os.path.join(dirname, filename)) - for entry in whl.infolist(): - if entry.filename.endswith('/metadata.json'): - pymeta = json.loads(whl.read(entry).decode('utf-8')) - jsonschema.validate(pymeta, pymeta_schema) - valid += 1 - assert valid > 0, "No metadata.json found" - -def test_util(): - """Test functions in util.py.""" - for i in range(10): - before = b'*' * i - encoded = wheel.util.urlsafe_b64encode(before) - assert not encoded.endswith(b'=') - after = wheel.util.urlsafe_b64decode(encoded) - assert before == after - - -def test_pick_best(): - """Test the wheel ranking algorithm.""" - def get_tags(res): - info = res[-1].parsed_filename.groupdict() - return info['pyver'], info['abi'], info['plat'] - - cand_tags = [('py27', 'noabi', 'noarch'), ('py26', 'noabi', 'noarch'), - ('cp27', 'noabi', 'linux_i686'), - ('cp26', 'noabi', 'linux_i686'), - ('cp27', 'noabi', 'linux_x86_64'), - ('cp26', 'noabi', 'linux_x86_64')] - cand_wheels = [WheelFile('testpkg-1.0-%s-%s-%s.whl' % t) - for t in cand_tags] - - supported = [('cp27', 'noabi', 'linux_i686'), ('py27', 'noabi', 'noarch')] - supported2 = [('cp27', 'noabi', 'linux_i686'), ('py27', 'noabi', 'noarch'), - ('cp26', 'noabi', 'linux_i686'), ('py26', 'noabi', 'noarch')] - supported3 = [('cp26', 'noabi', 'linux_i686'), ('py26', 'noabi', 'noarch'), - ('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 - best = max(cand_wheels) - assert list(best.tags)[0] == supp[0] - - # assert_equal( - # list(map(get_tags, pick_best(cand_wheels, supp, top=False))), supp) diff --git a/wheel/test/test_install.py b/wheel/test/test_install.py deleted file mode 100644 index ddcddf5..0000000 --- a/wheel/test/test_install.py +++ /dev/null @@ -1,55 +0,0 @@ -# Test wheel. -# The file has the following contents: -# hello.pyd -# hello/hello.py -# hello/__init__.py -# test-1.0.data/data/hello.dat -# test-1.0.data/headers/hello.dat -# test-1.0.data/scripts/hello.sh -# test-1.0.dist-info/WHEEL -# test-1.0.dist-info/METADATA -# test-1.0.dist-info/RECORD -# The root is PLATLIB -# So, some in PLATLIB, and one in each of DATA, HEADERS and SCRIPTS. - -import wheel.tool -import wheel.pep425tags -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')] - whl = WheelFile(TESTWHEEL, context=get_supported) - assert whl.supports_current_python(get_supported) - try: - locs = {} - for key in ('purelib', 'platlib', 'scripts', 'headers', 'data'): - locs[key] = os.path.join(tempdir, key) - os.mkdir(locs[key]) - whl.install(overrides=locs) - assert len(os.listdir(locs['purelib'])) == 0 - assert check(locs['platlib'], 'hello.pyd') - assert check(locs['platlib'], 'hello', 'hello.py') - assert check(locs['platlib'], 'hello', '__init__.py') - assert check(locs['data'], 'hello.dat') - assert check(locs['headers'], 'hello.dat') - assert check(locs['scripts'], 'hello.sh') - assert check(locs['platlib'], 'test-1.0.dist-info', 'RECORD') - 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 deleted file mode 100644 index f96166b..0000000 --- a/wheel/test/test_keys.py +++ /dev/null @@ -1,98 +0,0 @@ -import tempfile -import os.path -import unittest -import json - -from wheel.signatures import keys - -wheel_json = """ -{ - "verifiers": [ - { - "scope": "+", - "vk": "bp-bjK2fFgtA-8DhKKAAPm9-eAZcX_u03oBv2RlKOBc" - }, - { - "scope": "+", - "vk": "KAHZBfyqFW3OcFDbLSG4nPCjXxUPy72phP9I4Rn9MAo" - }, - { - "scope": "+", - "vk": "tmAYCrSfj8gtJ10v3VkvW7jOndKmQIYE12hgnFu3cvk" - } - ], - "signers": [ - { - "scope": "+", - "vk": "tmAYCrSfj8gtJ10v3VkvW7jOndKmQIYE12hgnFu3cvk" - }, - { - "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 - 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'] - self.wk.data['schema'] = self.wk.SCHEMA+1 - self.wk.save() - try: - self.wk.load() - except ValueError: - 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 deleted file mode 100644 index a23d506..0000000 --- a/wheel/test/test_paths.py +++ /dev/null @@ -1,6 +0,0 @@ -import wheel.paths -from distutils.command.install import SCHEME_KEYS - -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 deleted file mode 100644 index 1632a13..0000000 --- a/wheel/test/test_ranking.py +++ /dev/null @@ -1,43 +0,0 @@ -import unittest - -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) - 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() -pyver, abi, arch = sup[0] -genver = 'py' + pyver[2:] -majver = genver[:3] - -COMBINATIONS = ( - ('bar', '0.9', 'py2.py3', 'none', 'any'), - ('bar', '0.9', majver, 'none', 'any'), - ('bar', '0.9', genver, 'none', 'any'), - ('bar', '0.9', pyver, abi, arch), - ('bar', '1.3.2', majver, 'none', 'any'), - ('bar', '3.1', genver, 'none', 'any'), - ('bar', '3.1', pyver, abi, arch), - ('foo', '1.0', majver, 'none', 'any'), - ('foo', '1.1', pyver, abi, arch), - ('foo', '2.1', majver + '0', 'none', 'any'), - # 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), -) - -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]') - setup_py = SETUP_PY.format(ext_modules=EXT_MODULES) - else: - setup_py = SETUP_PY.format(ext_modules='') - 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)) - dist_dir = temp_pkg.join('dist') - assert dist_dir.check(dir=1) - wheels = dist_dir.listdir() - assert len(wheels) == 1 - 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'], - cwd=str(temp_pkg)) - dist_dir = temp_pkg.join('dist') - assert dist_dir.check(dir=1) - wheels = dist_dir.listdir() - assert len(wheels) == 1 - 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'], - cwd=str(temp_pkg)) - dist_dir = temp_pkg.join('dist') - assert dist_dir.check(dir=1) - wheels = dist_dir.listdir() - assert len(wheels) == 1 - 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'], - cwd=str(temp_pkg)) - dist_dir = temp_pkg.join('dist') - assert dist_dir.check(dir=1) - wheels = dist_dir.listdir() - assert len(wheels) == 1 - 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( - [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() - assert len(wheels) == 1 - 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( - [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() - assert len(wheels) == 1 - 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( - [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() - assert len(wheels) == 1 - 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'], - cwd=str(temp_pkg)) - dist_dir = temp_pkg.join('dist') - assert dist_dir.check(dir=1) - wheels = dist_dir.listdir() - assert len(wheels) == 1 - 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( - [sys.executable, 'setup.py', 'bdist_wheel', '--plat-name=testplat.arch'], - cwd=str(temp_ext_pkg)) - except subprocess.CalledProcessError: - pytest.skip("Cannot compile C Extensions") - dist_dir = temp_ext_pkg.join('dist') - assert dist_dir.check(dir=1) - wheels = dist_dir.listdir() - assert len(wheels) == 1 - 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( - [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() - assert len(wheels) == 1 - 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: - subprocess.check_call( - [sys.executable, 'setup.py', 'bdist_wheel'], - cwd=str(temp_ext_pkg)) - except subprocess.CalledProcessError: - pytest.skip("Cannot compile C Extensions") - dist_dir = temp_ext_pkg.join('dist') - assert dist_dir.check(dir=1) - wheels = dist_dir.listdir() - assert len(wheels) == 1 - assert wheels[0].basename.endswith('-testplat_arch.whl') - assert wheels[0].ext == '.whl' diff --git a/wheel/test/test_tool.py b/wheel/test/test_tool.py deleted file mode 100644 index 078f1ed..0000000 --- a/wheel/test/test_tool.py +++ /dev/null @@ -1,25 +0,0 @@ -from .. import tool - -def test_keygen(): - def get_keyring(): - WheelKeys, keyring = tool.get_keyring() - - class WheelKeysTest(WheelKeys): - def save(self): - pass - - class keyringTest: - @classmethod - 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 - - return keyringTest2() - - return WheelKeysTest, keyringTest - - tool.keygen(get_keyring=get_keyring) diff --git a/wheel/test/test_wheelfile.py b/wheel/test/test_wheelfile.py deleted file mode 100644 index 181668f..0000000 --- a/wheel/test/test_wheelfile.py +++ /dev/null @@ -1,142 +0,0 @@ -import os -import wheel.install -import wheel.archive -import hashlib -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): - old_value = os.environ.get(key) - try: - os.environ[key] = value - yield - finally: - if old_value is None: - del os.environ[key] - else: - os.environ[key] = old_value - -@contextmanager -def temporary_directory(): - # tempfile.TemporaryDirectory doesn't exist in Python 2. - tempdir = tempfile.mkdtemp() - try: - yield tempdir - finally: - shutil.rmtree(tempdir) - -@contextmanager -def readable_zipfile(path): - # zipfile.ZipFile() isn't a context manager under Python 2. - zf = zipfile.ZipFile(path, 'r') - try: - yield zf - finally: - zf.close() - - -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. - vzf = wheel.install.VerifyingZipFile(sio, 'r') - vzf.set_expected_hash("one", hashlib.sha256(b"first file").digest()) - vzf.set_expected_hash("three", "blurble") - vzf.open("one").read() - vzf.open("two").read() - try: - vzf.open("three").read() - except wheel.install.BadWheelFile: - 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 - try: - vzf.open("two").read() - except wheel.install.BadWheelFile: - 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 - 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 - # not a context manager under Python 3. - with temporary_directory() as tempdir: - for filename in ('one', 'two', 'three'): - path = os.path.join(tempdir, filename) - with codecs.open(path, 'w', encoding='utf-8') as fp: - fp.write(filename + '\n') - zip_base_name = os.path.join(tempdir, 'dummy') - # The earliest date representable in TarInfos, 1980-01-01 - with environ('SOURCE_DATE_EPOCH', '315576060'): - zip_filename = wheel.archive.make_wheelfile_inner( - zip_base_name, tempdir) - with readable_zipfile(zip_filename) as zf: - 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. - with temporary_directory() as tempdir: - files = (('foo', 0o644), ('bar', 0o755)) - for filename, mode in files: - path = os.path.join(tempdir, filename) - with codecs.open(path, 'w', encoding='utf-8') as fp: - fp.write(filename + '\n') - os.chmod(path, mode) - zip_base_name = os.path.join(tempdir, 'dummy') - zip_filename = wheel.archive.make_wheelfile_inner( - zip_base_name, tempdir) - with readable_zipfile(zip_filename) as zf: - for filename, mode in files: - info = zf.getinfo(os.path.join(tempdir, filename)) - assert info.external_attr == (mode | 0o100000) << 16 - assert info.compress_type == zipfile.ZIP_DEFLATED diff --git a/wheel/tool/__init__.py b/wheel/tool/__init__.py deleted file mode 100644 index 4c0187b..0000000 --- a/wheel/tool/__init__.py +++ /dev/null @@ -1,359 +0,0 @@ -""" -Wheel command-line utility. -""" - -import os -import hashlib -import sys -import json - -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 - -def require_pkgresources(name): - try: - import pkg_resources - except ImportError: - raise RuntimeError("'{0}' needs pkg_resources (part of setuptools).".format(name)) - -import argparse - -class WheelError(Exception): pass - -# For testability -def get_keyring(): - try: - from ..signatures import keys - import keyring - assert keyring.get_keyring().priority - except (ImportError, AssertionError): - 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() - - ed25519ll = signatures.get_ed25519ll() - - wk = WheelKeys().load() - - keypair = ed25519ll.crypto_sign_keypair() - vk = native(urlsafe_b64encode(keypair.vk)) - sk = native(urlsafe_b64encode(keypair.sk)) - kr = keyring.get_keyring() - kr.set_password("wheel", vk, sk) - sys.stdout.write("Created Ed25519 keypair with vk={0}\n".format(vk)) - sys.stdout.write("in {0!r}\n".format(kr)) - - sk2 = kr.get_password('wheel', vk) - if sk2 != sk: - raise WheelError("Keyring is broken. Could not retrieve secret key.") - - sys.stdout.write("Trusting {0} to sign and verify all packages.\n".format(vk)) - wk.add_signer('+', vk) - wk.trust('+', vk) - wk.save() - -def sign(wheelfile, replace=False, get_keyring=get_keyring): - """Sign a wheel""" - WheelKeys, keyring = get_keyring() - - ed25519ll = signatures.get_ed25519ll() - - wf = WheelFile(wheelfile, append=True) - wk = WheelKeys().load() - - name = wf.parsed_filename.group('name') - sign_with = wk.signers(name)[0] - sys.stdout.write("Signing {0} with {1}\n".format(name, sign_with[1])) - - vk = sign_with[1] - kr = keyring.get_keyring() - sk = kr.get_password('wheel', vk) - 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()))} - 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. - - RECORD.jws must be at the end of the archive. The zip file must be an - ordinary archive, with the compressed files and the directory in the same - order, and without any non-zip content after the truncation point. - """ - vzf = VerifyingZipFile(wheelfile, "a") - info = vzf.infolist() - if not (len(info) and info[-1].filename.endswith('/RECORD.jws')): - raise WheelError("RECORD.jws not found at end of archive.") - vzf.pop() - vzf.close() - -def verify(wheelfile): - """Verify a wheel. - - The signature will be verified for internal consistency ONLY and printed. - Wheel's own unpack/install commands verify the manifest against the - signature and file contents. - """ - wf = WheelFile(wheelfile) - sig_name = wf.distinfo_name + '/RECORD.jws' - sig = json.loads(native(wf.zipfile.open(sig_name).read())) - verified = signatures.verify(sig) - sys.stderr.write("Signatures are internally consistent.\n") - sys.stdout.write(json.dumps(verified, indent=2)) - sys.stdout.write('\n') - -def unpack(wheelfile, dest='.'): - """Unpack a wheel. - - Wheel content will be unpacked to {dest}/{name}-{ver}, where {name} - is the package name and {ver} its version. - - :param wheelfile: The path to the wheel. - :param dest: Destination directory (default to current directory). - """ - wf = WheelFile(wheelfile) - namever = wf.parsed_filename.group('namever') - destination = os.path.join(dest, namever) - sys.stderr.write("Unpacking to: %s\n" % (destination)) - wf.zipfile.extractall(destination) - wf.zipfile.close() - -def install(requirements, requirements_file=None, - wheel_dirs=None, force=False, list_files=False, - dry_run=False): - """Install wheels. - - :param requirements: A list of requirements or wheel files to install. - :param requirements_file: A file containing requirements to install. - :param wheel_dirs: A list of directories to search for wheels. - :param force: Install a wheel file even if it is not compatible. - :param list_files: Only list the files to install, don't install them. - :param dry_run: Do everything but the actual install. - """ - - # If no wheel directories specified, use the WHEELPATH environment - # variable, or the current directory if that is not set. - if not wheel_dirs: - wheelpath = os.getenv("WHEELPATH") - if wheelpath: - wheel_dirs = wheelpath.split(os.pathsep) - else: - wheel_dirs = [ os.path.curdir ] - - # Get a list of all valid wheels in wheel_dirs - all_wheels = [] - for d in wheel_dirs: - for w in os.listdir(d): - if w.endswith('.whl'): - wf = WheelFile(os.path.join(d, w)) - if wf.compatible: - all_wheels.append(wf) - - # If there is a requirements file, add it to the list of requirements - if requirements_file: - # If the file doesn't exist, search for it in wheel_dirs - # This allows standard requirements files to be stored with the - # wheels. - if not os.path.exists(requirements_file): - for d in wheel_dirs: - name = os.path.join(d, requirements_file) - if os.path.exists(name): - requirements_file = name - break - - with open(requirements_file) as fd: - requirements.extend(fd) - - to_install = [] - for req in requirements: - if req.endswith('.whl'): - # Explicitly specified wheel filename - if os.path.exists(req): - wf = WheelFile(req) - if wf.compatible or force: - to_install.append(wf) - else: - msg = ("{0} is not compatible with this Python. " - "--force to install anyway.".format(req)) - raise WheelError(msg) - else: - # We could search on wheel_dirs, but it's probably OK to - # assume the user has made an error. - raise WheelError("No such wheel file: {}".format(req)) - continue - - # We have a requirement spec - # If we don't have pkg_resources, this will raise an exception - matches = matches_requirement(req, all_wheels) - if not matches: - raise WheelError("No match for requirement {}".format(req)) - to_install.append(max(matches)) - - # We now have a list of wheels to install - if list_files: - sys.stdout.write("Installing:\n") - - if dry_run: - return - - for wf in to_install: - if list_files: - sys.stdout.write(" {0}\n".format(wf.filename)) - continue - wf.install(force=force) - wf.zipfile.close() - -def install_scripts(distributions): - """ - Regenerate the entry_points console_scripts for the named distribution. - """ - try: - from setuptools.command import easy_install - import pkg_resources - except ImportError: - raise RuntimeError("'wheel install_scripts' needs setuptools.") - - for dist in 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.finalize_options() - command.install_egg_scripts(pkg_resources_dist) - -def convert(installers, dest_dir, verbose): - 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): - if os.path.splitext(installer)[1] == '.egg': - conv = egg2wheel - else: - conv = bdist_wininst2wheel - if verbose: - sys.stdout.write("{0}... ".format(installer)) - sys.stdout.flush() - conv(installer, dest_dir) - if verbose: - sys.stdout.write("OK\n") - -def parser(): - p = argparse.ArgumentParser() - s = p.add_subparsers(help="commands") - - def keygen_f(args): - keygen() - keygen_parser = s.add_parser('keygen', help='Generate signing key') - keygen_parser.set_defaults(func=keygen_f) - - def sign_f(args): - sign(args.wheelfile) - sign_parser = s.add_parser('sign', help='Sign wheel') - sign_parser.add_argument('wheelfile', help='Wheel file') - sign_parser.set_defaults(func=sign_f) - - def unsign_f(args): - unsign(args.wheelfile) - unsign_parser = s.add_parser('unsign', help=unsign.__doc__) - unsign_parser.add_argument('wheelfile', help='Wheel file') - unsign_parser.set_defaults(func=unsign_f) - - def verify_f(args): - verify(args.wheelfile) - verify_parser = s.add_parser('verify', help=verify.__doc__) - verify_parser.add_argument('wheelfile', help='Wheel file') - verify_parser.set_defaults(func=verify_f) - - def unpack_f(args): - unpack(args.wheelfile, args.dest) - unpack_parser = s.add_parser('unpack', help='Unpack wheel') - unpack_parser.add_argument('--dest', '-d', help='Destination directory', - default='.') - unpack_parser.add_argument('wheelfile', help='Wheel file') - unpack_parser.set_defaults(func=unpack_f) - - def install_f(args): - install(args.requirements, args.requirements_file, - args.wheel_dirs, args.force, args.list_files) - install_parser = s.add_parser('install', help='Install wheels') - install_parser.add_argument('requirements', nargs='*', - help='Requirements to install.') - install_parser.add_argument('--force', default=False, - action='store_true', - help='Install incompatible wheel files.') - install_parser.add_argument('--wheel-dir', '-d', action='append', - dest='wheel_dirs', - help='Directories containing wheels.') - install_parser.add_argument('--requirements-file', '-r', - help="A file containing requirements to " - "install.") - install_parser.add_argument('--list', '-l', default=False, - dest='list_files', - action='store_true', - help="List wheels which would be installed, " - "but don't actually install anything.") - install_parser.set_defaults(func=install_f) - - def install_scripts_f(args): - install_scripts(args.distributions) - install_scripts_parser = s.add_parser('install-scripts', help='Install console_scripts') - install_scripts_parser.add_argument('distributions', nargs='*', - help='Regenerate console_scripts for these distributions') - install_scripts_parser.set_defaults(func=install_scripts_f) - - def convert_f(args): - convert(args.installers, args.dest_dir, args.verbose) - 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)") - convert_parser.add_argument('--verbose', '-v', action='store_true') - convert_parser.set_defaults(func=convert_f) - - def version_f(args): - from .. import __version__ - sys.stdout.write("wheel %s\n" % __version__) - version_parser = s.add_parser('version', help='Print version and exit') - version_parser.set_defaults(func=version_f) - - def help_f(args): - p.print_help() - help_parser = s.add_parser('help', help='Show this help') - help_parser.set_defaults(func=help_f) - - return p - -def main(): - p = parser() - args = p.parse_args() - if not hasattr(args, 'func'): - p.print_help() - else: - # 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") - return 1 diff --git a/wheel/util.py b/wheel/util.py deleted file mode 100644 index 20f386f..0000000 --- a/wheel/util.py +++ /dev/null @@ -1,164 +0,0 @@ -"""Utility functions.""" - -import sys -import os -import base64 -import json -import hashlib -from collections import OrderedDict - -__all__ = ['urlsafe_b64encode', 'urlsafe_b64decode', 'utf8', - 'to_json', 'from_json', 'matches_requirement'] - -def urlsafe_b64encode(data): - """urlsafe_b64encode without padding""" - return base64.urlsafe_b64encode(data).rstrip(binary('=')) - - -def urlsafe_b64decode(data): - """urlsafe_b64decode without padding""" - pad = b'=' * (4 - (len(data) & 3)) - return base64.urlsafe_b64decode(data + pad) - - -def to_json(o): - '''Convert given data to JSON.''' - return json.dumps(o, sort_keys=True) - - -def from_json(j): - '''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': '' } - 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') - -class HashingFile(object): - def __init__(self, fd, hashtype='sha256'): - self.fd = fd - 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: - self.default_factory = None - else: - if not (args[0] is None or callable(args[0])): - raise TypeError('first argument must be callable or None') - self.default_factory = args[0] - args = args[1:] - super(OrderedDefaultDict, self).__init__(*args, **kwargs) - - 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) - def get_path(name): - SHGFP_TYPE_CURRENT = 0 - buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH) - ctypes.windll.shell32.SHGetFolderPathW(0, csidl[name], 0, SHGFP_TYPE_CURRENT, buf) - return buf.value - - def save_config_path(*resource): - appdata = get_path("CSIDL_LOCAL_APPDATA") - path = os.path.join(appdata, *resource) - 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: - base = get_path(id) - path = os.path.join(base, *resource) - if os.path.exists(path): - yield path -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. - - :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") - - 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 diff --git a/wheel/wininst2wheel.py b/wheel/wininst2wheel.py deleted file mode 100755 index 15f0cdf..0000000 --- a/wheel/wininst2wheel.py +++ /dev/null @@ -1,216 +0,0 @@ -#!/usr/bin/env python -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 - -egg_info_re = re.compile(r'''(^|/)(?P[^/]+?)-(?P.+?) - (-(?P.+?))?(-(?P.+?))?.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). - - The egginfo filename has the format:: - - name-ver(-pyver)(-arch).egg-info - - The installer filename has the format:: - - name-ver.arch(-pyver).exe - - Some things to note: - - 1. The installer filename is not definitive. An installer can be renamed - and work perfectly well as an installer. So more reliable data should - be used whenever possible. - 2. The egg-info data should be preferred for the name and version, because - these come straight from the distutils metadata, and are mandatory. - 3. The pyver from the egg-info data should be ignored, as it is - constructed from the version of Python used to build the installer, - which is irrelevant - the installer filename is correct here (even to - the point that when it's not there, any version is implied). - 4. The architecture must be taken from the installer filename, as it is - not included in the egg-info data. - 5. Architecture-neutral installers still have an architecture because the - installer format itself (being executable) is architecture-specific. We - should therefore ignore the architecture if the content is pure-python. - """ - - egginfo = None - if egginfo_name: - egginfo = egg_info_re.search(egginfo_name) - if not egginfo: - 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,)) - # Strip '.exe' - rest = rest[:-4] - # 2. Python version (from the last '-', must start with 'py') - rest2, sep, w_pyver = rest.rpartition('-') - if sep and w_pyver.startswith('py'): - rest = rest2 - w_pyver = w_pyver.replace('.', '') - else: - # Not version specific - use py2.py3. While it is possible that - # pure-Python code is not compatible with both Python 2 and 3, there - # is no way of knowing from the wininst format, so we assume the best - # here (the user can always manually rename the wheel to be more - # restrictive if needed). - w_pyver = 'py2.py3' - # 3. Version and architecture - w_ver, sep, w_arch = rest.rpartition('.') - if not sep: - raise ValueError("Installer filename %s is not valid" % - (wininfo_name,)) - - if egginfo: - w_name = egginfo.group('name') - w_ver = egginfo.group('ver') - - 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) - - # Search for egg-info in the archive - egginfo_name = None - for filename in bdw.namelist(): - if '.egg-info' in filename: - egginfo_name = filename - break - - info = parse_info(os.path.basename(path), egginfo_name) - - root_is_purelib = True - for zipinfo in bdw.infolist(): - if zipinfo.filename.startswith('PLATLIB'): - root_is_purelib = False - break - if root_is_purelib: - paths = {'purelib': ''} - else: - paths = {'platlib': ''} - - dist_info = "%(name)s-%(ver)s" % info - datadir = "%s.data/" % dist_info - - # rewrite paths to trick ZipFile into extracting an egg - # XXX grab wininst .ini - between .exe, padding, and first zip file. - members = [] - egginfo_name = '' - for zipinfo in bdw.infolist(): - key, basename = zipinfo.filename.split('/', 1) - key = key.lower() - basepath = paths.get(key, None) - if basepath is None: - basepath = datadir + key.lower() + '/' - oldname = zipinfo.filename - newname = basepath + basename - zipinfo.filename = newname - del bdw.NameToInfo[oldname] - bdw.NameToInfo[newname] = zipinfo - # Collect member names, but omit '' (from an entry like "PLATLIB/" - if newname: - members.append(newname) - # Remember egg-info name for the egg2dist call below - if not egginfo_name: - if newname.endswith('.egg-info'): - egginfo_name = newname - elif '.egg-info/' in newname: - egginfo_name, sep, _ = newname.rpartition('/') - dir = tempfile.mkdtemp(suffix="_b2w") - bdw.extractall(dir, members) - - # egg2wheel - abi = 'none' - pyver = info['pyver'] - arch = (info['arch'] or 'any').replace('.', '_').replace('-', '_') - # Wininst installers always have arch even if they are not - # architecture-specific (because the format itself is). - # So, assume the content is architecture-neutral if root is purelib. - if root_is_purelib: - arch = 'any' - # If the installer is architecture-specific, it's almost certainly also - # CPython-specific. - if arch != 'any': - pyver = pyver.replace('py', 'cp') - wheel_name = '-'.join(( - dist_info, - pyver, - abi, - arch - )) - if root_is_purelib: - bw = wheel.bdist_wheel.bdist_wheel(distutils.dist.Distribution()) - else: - bw = _bdist_wheel_tag(distutils.dist.Distribution()) - - bw.root_is_pure = root_is_purelib - bw.python_tag = pyver - bw.plat_name_supplied = True - bw.plat_name = info['arch'] or 'any' - - if not root_is_purelib: - bw.full_tag_supplied = True - bw.full_tag = (pyver, abi, arch) - - dist_info_dir = os.path.join(dir, '%s.dist-info' % dist_info) - 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) - - -class _bdist_wheel_tag(wheel.bdist_wheel.bdist_wheel): - # allow the client to override the default generated wheel tag - # The default bdist_wheel implementation uses python and abi tags - # of the running python process. This is not suitable for - # generating/repackaging prebuild binaries. - - full_tag_supplied = False - full_tag = None # None or a (pytag, soabitag, plattag) triple - - def get_tag(self): - if self.full_tag_supplied and self.full_tag is not None: - return self.full_tag - else: - return super(_bdist_wheel_tag, self).get_tag() - - -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)") - parser.add_argument('--verbose', '-v', action='store_true') - args = parser.parse_args() - for pat in args.installers: - for installer in iglob(pat): - if args.verbose: - sys.stdout.write("{0}... ".format(installer)) - bdist_wininst2wheel(installer, args.dest_dir) - if args.verbose: - sys.stdout.write("OK\n") - -if __name__ == "__main__": - main() diff --git a/wscript b/wscript deleted file mode 100644 index c1ee262..0000000 --- a/wscript +++ /dev/null @@ -1,133 +0,0 @@ -APPNAME = 'wheel' -WHEEL_TAG = 'py2.py3-none-any' -VERSION = '0.24.0' - -top = '.' -out = 'build' - -from waflib import Utils - -def options(opt): - opt.load('python') - -def configure(ctx): - ctx.load('python') - ctx.check_python_version() - -def build(bld): - bld(features='py', - source=bld.path.ant_glob('wheel/**/*.py', excl="wheel/test"), - install_from='.') - - # build the wheel: - - DIST_INFO = '%s-%s.dist-info' % (APPNAME, VERSION) - - node = bld.path.get_bld().make_node(DIST_INFO) - if not os.path.exists(node.abspath()): - os.mkdir(node.abspath()) - - metadata = node.make_node('METADATA') - - import codecs, string - README = codecs.open('README.txt', encoding='utf8').read() - CHANGES = codecs.open('CHANGES.txt', encoding='utf8').read() - METADATA = codecs.open('METADATA.in', encoding='utf8').read() - METADATA = string.Template(METADATA).substitute( - VERSION=VERSION, - DESCRIPTION='\n\n'.join((README, CHANGES)) - ) - - bld(source='METADATA.in', - target=metadata, - rule=lambda tsk: Utils.writef(tsk.outputs[0].abspath(), METADATA)) - - wheel = node.make_node('WHEEL') - - WHEEL="""Wheel-Version: 1.0 -Generator: waf (0.0.1) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any -""" - bld(target=wheel, - rule=lambda tsk: Utils.writef(tsk.outputs[0].abspath(), WHEEL)) - - # globs don't work, since here the files may not exist: - bld.install_files('${PYTHONDIR}/'+DIST_INFO, [DIST_INFO+'/WHEEL', DIST_INFO+'/METADATA']) - - # only if entry_points.txt exists: - bld.install_files('${PYTHONDIR}/'+DIST_INFO, ['entry_points.txt']) - -import shutil, os, base64 - -def urlsafe_b64encode(data): - """urlsafe_b64encode without padding""" - return base64.urlsafe_b64encode(data).rstrip(b'=') - -from waflib import Scripting -class WheelDist(Scripting.Dist): - def manifest(self): - """ - Add the wheel manifest. - """ - import hashlib - files = self.get_files() - lines = [] - for f in files: - print("File: %s" % f.relpath()) - size = os.stat(f.abspath()).st_size - digest = hashlib.sha256(open(f.abspath(), 'rb').read()).digest() - digest = "sha256="+(urlsafe_b64encode(digest).decode('ascii')) - lines.append("%s,%s,%s" % (f.path_from(self.base_path).replace(',', ',,'), digest, size)) - - record_path = '%s-%s.dist-info/RECORD' % (APPNAME, VERSION) - lines.append(record_path+',,') - RECORD = '\n'.join(lines) - - import zipfile - zip = zipfile.ZipFile(self.get_arch_name(), 'a') - zip.writestr(record_path, RECORD, zipfile.ZIP_DEFLATED) - zip.close() - -from waflib import Build -class package_cls(Build.InstallContext): - cmd = 'package' - fun = 'build' - - def init_dirs(self, *k, **kw): - super(package_cls, self).init_dirs(*k, **kw) - self.tmp = self.bldnode.make_node('package_tmp_dir') - try: - shutil.rmtree(self.tmp.abspath()) - except: - pass - if os.path.exists(self.tmp.abspath()): - self.fatal('Could not remove the temporary directory %r' % self.tmp) - self.tmp.mkdir() - self.options.destdir = self.tmp.abspath() - - def execute(self, *k, **kw): - back = self.options.destdir - try: - super(package_cls, self).execute(*k, **kw) - finally: - self.options.destdir = back - - files = self.tmp.ant_glob('**', excl=" **/*.pyc **/*.pyo") - - # we could mess with multiple inheritance but this is probably unnecessary - ctx = WheelDist() - ctx.algo = 'zip' - ctx.arch_name = '%s-%s-%s.whl' % (APPNAME, VERSION, WHEEL_TAG) - ctx.files = files - ctx.tar_prefix = '' - ctx.base_path = self.tmp - ctx.base_name = '' - ctx.archive() - - # add manifest... - ctx.manifest() - - shutil.rmtree(self.tmp.abspath()) - -- cgit v1.2.1