Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wheel binary package format #684

Closed
wants to merge 68 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
a659a9c
resolve symlinks in local files
ptone Apr 3, 2011
ea60bb7
Merge branch 'hotfix/1.0.2'
jezdez Jul 16, 2011
47f1b2c
Merge pull request #245 from ptone/find-links-symlinks
Oct 23, 2011
ee554dd
Merge branch 'release/1.1'
jezdez Feb 16, 2012
14b8321
add ability to uninstall based on PEP-376 .dist-info/RECORD
dholth Jul 15, 2012
f55a37a
install from wheel files
dholth Jul 15, 2012
f6ccc0b
install from wheel files
dholth Jul 15, 2012
983e244
py25 compat 'with statement'
dholth Jul 15, 2012
6da13f2
define 'data' path for py < 2.7
dholth Jul 19, 2012
0015360
open RECORD with proper newline mode (Paul Moore)
dholth Jul 28, 2012
dae276d
support multi-python-version wheels
dholth Aug 2, 2012
899844a
Merge branch 'develop' of https://github.com/pypa/pip into develop
dholth Aug 24, 2012
9d49cdf
add broken wheel unit test
dholth Aug 24, 2012
b8b0d62
debug unit test
dholth Aug 25, 2012
0129670
fix wheel logger
dholth Aug 25, 2012
78a1ac4
Merge remote-tracking branch 'dholth/master' into develop
rubik Aug 26, 2012
a060f96
Merge remote-tracking branch 'dholth/develop' into develop
rubik Aug 26, 2012
adef43b
coverage marker
dholth Aug 27, 2012
a764540
turn on debug logging in test
dholth Aug 27, 2012
2f6c140
more info in test
dholth Aug 27, 2012
636c12f
use str(Path)
dholth Aug 27, 2012
c8bfe6d
even more unit test tweaks
dholth Aug 27, 2012
32c78f5
further wheel tweaks
dholth Aug 27, 2012
b98abfd
passing wheel test
dholth Aug 27, 2012
0e519c4
minor refactor
dholth Aug 27, 2012
31a81c5
require markerlib>=0.5.1 in test; next version of distribute won't
dholth Aug 27, 2012
d7fdb44
don't ask for a specific markerlib
dholth Aug 28, 2012
9198366
Added first support for wheel cache
rubik Aug 29, 2012
5110355
Remove --only-wheels and rely upon --no-install
rubik Aug 29, 2012
f0a2177
Add whl to archive extensions so that local wheels can be installed (…
rubik Aug 29, 2012
2214d23
Merge branch 'develop' of https://github.com/pypa/pip into develop
dholth Aug 29, 2012
ef0da94
Merge branch 'master' of https://github.com/dholth/pip into develop
dholth Aug 29, 2012
de46189
Merge https://github.com/rubik/pip into develop
dholth Aug 29, 2012
46a0f14
Fix local wheels installation
rubik Aug 30, 2012
54884de
no longer try to upgrade distribute in unit test
dholth Sep 2, 2012
6545c84
modified wheel tests
dholth Sep 2, 2012
2f36414
remove obsolete comments
dholth Sep 2, 2012
213d7d6
use make_path_relative for python < 2.6
dholth Sep 2, 2012
cbc7478
support extras when installing from wheel
dholth Sep 3, 2012
630e938
Merge https://github.com/pypa/pip into HEAD
dholth Sep 3, 2012
f864932
account for argparse==1.2.1 in test_freeze
dholth Sep 3, 2012
c9cce27
... and account for "no argparse" in other Pythons
dholth Sep 3, 2012
8700b00
add "--use-wheel" option (disabling wheel by default)
dholth Sep 4, 2012
3d7a789
clarify help
dholth Sep 4, 2012
ddf1217
s/distribution/package/
dholth Sep 4, 2012
ba6e492
Merge branch 'develop' of https://github.com/pypa/pip into develop
dholth Sep 5, 2012
51689aa
Merge branch 'develop' of https://github.com/pypa/pip into develop
dholth Sep 12, 2012
0e5e4f3
wheel folds - to _ in package names
dholth Sep 12, 2012
75ca249
import SkipTest
dholth Sep 12, 2012
6870ab5
add wheel install scripts fixer
dholth Sep 20, 2012
c90b657
import with_statement
dholth Sep 20, 2012
48700b7
Merge branch 'develop' of https://github.com/pypa/pip into develop
dholth Sep 20, 2012
a0c4d83
import with_statement
dholth Sep 20, 2012
dc6cd29
add .pyc files when uninstalling from RECORD
dholth Sep 24, 2012
3089e26
test package with hyphenated name
dholth Sep 24, 2012
cedff7d
delete complex.dist test package
dholth Sep 24, 2012
07760d2
change --wheel-cache argument name to --build-wheel
dholth Sep 24, 2012
65634a1
Merge branch 'develop' of https://github.com/pypa/pip into wheel
dholth Sep 24, 2012
8878a40
add script to complex-dist, py3 compat script fixer
dholth Sep 24, 2012
bb5b8b3
basic docs and news on pip wheel support
qwcode Sep 25, 2012
34b2ad6
Merge pull request #1 from qwcode/wheel_pip_docs
dholth Sep 25, 2012
9cf6c8d
add broken 'pip build' command
dholth Sep 26, 2012
8011c49
Added a seemingly working build command
rubik Sep 26, 2012
f221306
Removed default upgrade value
rubik Sep 26, 2012
f7bbc23
Removed silly print
rubik Sep 26, 2012
a591bff
Encode sys.executable properly in script hashbang lines
pfmoore Oct 1, 2012
8ee22ff
Merge pull request #2 from pfmoore/dholth-develop
dholth Oct 1, 2012
f5dfea6
Merge branch 'develop' of https://github.com/dholth/pip into wheel
dholth Oct 1, 2012
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/news.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Beta and final releases planned for the end of 2012.
develop (unreleased)
--------------------

* Pip now has experimental "wheel" support. Thanks Daniel Holth.

* Added check in ``install --download`` to prevent re-downloading if the target
file already exists. Thanks Andrey Bulgakov.

Expand Down
18 changes: 18 additions & 0 deletions docs/usage.txt
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,24 @@ To get info about an installed package, including its location and included
files, run ``pip show ProjectName``.


Wheel support
-------------

Pip has experimental support for building and installing "wheel" archives.

To build wheel archives::

$ pip install --build-wheel=/tmp/wheel-cache --no-install SomePackage

To install from wheel archives::

$ pip install --use-wheel --no-index --find-links=file:///tmp/wheel-cache SomePackage

Pip currently only supports finding wheels based on the python version tag, not implementation, abi or platform tags.

For more information, see the `wheel documentation <http://wheel.readthedocs.org/en/latest/index.html>`_


Bundles
-------

Expand Down
22 changes: 22 additions & 0 deletions pip/backwardcompat.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,25 @@ def home_lib(home):
else:
lib = os.path.join('lib', 'python')
return os.path.join(home, lib)

try:
import sysconfig
except: # pragma nocover
from distutils import sysconfig

import pip.locations
get_path_locations = {'purelib':pip.locations.site_packages,
'platlib':pip.locations.site_packages,
'scripts':pip.locations.bin_py,
'data':sys.prefix}
try:
sysconfig.get_path
def get_path(path):
try:
return get_path_locations[path]
except KeyError:
return sysconfig.get_path(path)
except AttributeError: # Python < 2.7
from pip.locations import site_packages, bin_py
def get_path(path):
return get_path_locations[path]
25 changes: 25 additions & 0 deletions pip/commands/build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from pip.locations import build_prefix, src_prefix
from pip.util import display_path, backup_dir
from pip.log import logger
from pip.exceptions import CommandError
from pip.commands.install import InstallCommand


class BuildCommand(InstallCommand):
name = 'build'
usage = '%prog [OPTIONS] PACKAGE_NAMES...'
summary = 'Build packages'

def __init__(self):
super(BuildCommand, self).__init__()
self.parser.set_defaults(**{
'use_wheel': False,
'no_install': True,
})

def run(self, options, args):
if not options.wheel_cache:
raise CommandError('You must supply -w, --build-wheel option')
super(BuildCommand, self).run(options, args)

BuildCommand()
21 changes: 19 additions & 2 deletions pip/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ class InstallCommand(Command):

def __init__(self):
super(InstallCommand, self).__init__()
self.parser.add_option('--use-wheel',
dest='use_wheel',
action='store_true',
help='Find wheel archives when searching index and find-links')
self.parser.add_option(
'-w', '--build-wheel ',
dest='wheel_cache',
default=None,
metavar='DIR',
help="Run 'setup.py bdist_wheel -d DIR' prior to installing "
"distributions. The \"wheel\" package is required to build wheels. "
"Combine with --no-install to only build.")
self.parser.add_option(
'-e', '--editable',
dest='editables',
Expand Down Expand Up @@ -181,7 +193,8 @@ def _build_package_finder(self, options, index_urls):
return PackageFinder(find_links=options.find_links,
index_urls=index_urls,
use_mirrors=options.use_mirrors,
mirrors=options.mirrors)
mirrors=options.mirrors,
use_wheel=options.use_wheel)

def run(self, options, args):
if options.download_dir:
Expand All @@ -190,6 +203,7 @@ def run(self, options, args):
options.build_dir = os.path.abspath(options.build_dir)
options.src_dir = os.path.abspath(options.src_dir)
install_options = options.install_options or []
only_wheels = options.no_install and options.wheel_cache
if options.use_user_site:
if virtualenv_no_global():
raise InstallationError("Can not perform a '--user' install. User site-packages are not visible in this virtualenv.")
Expand All @@ -214,11 +228,14 @@ def run(self, options, args):
src_dir=options.src_dir,
download_dir=options.download_dir,
download_cache=options.download_cache,
use_wheel=options.use_wheel,
wheel_cache=options.wheel_cache,
upgrade=options.upgrade,
as_egg=options.as_egg,
ignore_installed=options.ignore_installed,
ignore_dependencies=options.ignore_dependencies,
force_reinstall=options.force_reinstall,
only_wheels=only_wheels,
use_user_site=options.use_user_site)
for name in args:
requirement_set.add_requirement(
Expand Down Expand Up @@ -257,7 +274,7 @@ def run(self, options, args):
else:
requirement_set.locate_files()

if not options.no_install and not self.bundle:
if (not options.no_install and not self.bundle) or only_wheels:
requirement_set.install(install_options, global_options)
installed = ' '.join([req.name for req in
requirement_set.successfully_installed])
Expand Down
3 changes: 2 additions & 1 deletion pip/commands/zip.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ def unzip_package(self, module_name, filename):
## FIXME: this should be undoable:
zip = zipfile.ZipFile(zip_filename)
to_save = []
for name in zip.namelist():
for info in zip.infolist():
name = info.filename
if name.startswith(module_name + os.path.sep):
content = zip.read(name)
dest = os.path.join(package_path, name)
Expand Down
3 changes: 2 additions & 1 deletion pip/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@ def geturl(urllib2_resp):

def is_archive_file(name):
"""Return True if `name` is a considered as an archive file."""
archives = ('.zip', '.tar.gz', '.tar.bz2', '.tgz', '.tar', '.pybundle')
archives = ('.zip', '.tar.gz', '.tar.bz2', '.tgz', '.tar', '.pybundle',
'.whl')
ext = splitext(name)[1].lower()
if ext in archives:
return True
Expand Down
38 changes: 34 additions & 4 deletions pip/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ class PackageFinder(object):
"""

def __init__(self, find_links, index_urls,
use_mirrors=False, mirrors=None, main_mirror_url=None):
use_mirrors=False, mirrors=None, main_mirror_url=None,
use_wheel=False):
self.find_links = find_links
self.index_urls = index_urls
self.dependency_links = []
Expand All @@ -52,6 +53,7 @@ def __init__(self, find_links, index_urls,
logger.info('Using PyPI mirrors: %s' % ', '.join(self.mirror_urls))
else:
self.mirror_urls = []
self.use_wheel = use_wheel

def add_dependency_links(self, links):
## FIXME: this shouldn't be global list this, it should only
Expand Down Expand Up @@ -258,9 +260,14 @@ def _get_queued_page(self, req, pending_queue, done, seen):
for link in page.rel_links():
pending_queue.put(link)

_egg_fragment_re = re.compile(r'#egg=([^&]*)')
_egg_fragment_re = re.compile(r'#egg=([^&]*)')
_egg_info_re = re.compile(r'([a-z0-9_.]+)-([a-z0-9_.-]+)', re.I)
_py_version_re = re.compile(r'-py([123]\.?[0-9]?)$')
_wheel_info_re = re.compile(
r"""^(?P<namever>(?P<name>.+?)(-(?P<ver>\d.+?))?)
((-(?P<build>\d.*?))?-(?P<pyver>.+?)-(?P<abi>.+?)-(?P<plat>.+?)
\.whl|\.dist-info)$""",
re.VERBOSE)

def _sort_links(self, links):
"Returns elements of links in order, non-egg links first, egg links second, while eliminating duplicates"
Expand All @@ -279,6 +286,12 @@ def _package_versions(self, links, search_name):
for link in self._sort_links(links):
for v in self._link_package_versions(link, search_name):
yield v

def _known_extensions(self):
extensions = ('.tar.gz', '.tar.bz2', '.tar', '.tgz', '.zip')
if self.use_wheel:
return extensions + ('.whl',)
return extensions

def _link_package_versions(self, link, search_name):
"""
Expand All @@ -288,6 +301,7 @@ def _link_package_versions(self, link, search_name):

Meant to be overridden by subclasses, not called by clients.
"""
version = None
if link.egg_fragment:
egg_info = link.egg_fragment
else:
Expand All @@ -301,7 +315,7 @@ def _link_package_versions(self, link, search_name):
# Special double-extension case:
egg_info = egg_info[:-4]
ext = '.tar' + ext
if ext not in ('.tar.gz', '.tar.bz2', '.tar', '.tgz', '.zip'):
if ext not in self._known_extensions():
if link not in self.logged_links:
logger.debug('Skipping link %s; unknown archive format: %s' % (link, ext))
self.logged_links.add(link)
Expand All @@ -311,7 +325,23 @@ def _link_package_versions(self, link, search_name):
logger.debug('Skipping link %s; macosx10 one' % (link))
self.logged_links.add(link)
return []
version = self._egg_info_matches(egg_info, search_name, link)
if ext == '.whl':
wheel_info = self._wheel_info_re.match(link.filename)
if wheel_info.group('name').replace('_', '-').lower() == search_name.lower():
version = wheel_info.group('ver')
nodot = sys.version[:3].replace('.', '')
pyversions = wheel_info.group('pyver').split('.')
ok = False
for pv in pyversions:
# TODO: Doesn't check Python implementation
if nodot.startswith(pv[2:]):
ok = True
break
if not ok:
logger.debug('Skipping %s because Python version is incorrect' % link)
return []
if not version:
version = self._egg_info_matches(egg_info, search_name, link)
if version is None:
logger.debug('Skipping link %s; wrong project name (not %s)' % (link, search_name))
return []
Expand Down
Loading