don't install empty dirs during wheel installs

This commit is contained in:
Marcus Smith 2014-04-21 23:07:25 -07:00
parent 627cb5591a
commit b274b85f96
11 changed files with 203 additions and 16 deletions

View File

@ -134,10 +134,11 @@ def get_entrypoints(filename):
def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None,
pycompile=True):
pycompile=True, scheme=None):
"""Install a wheel"""
scheme = distutils_scheme(name, user=user, home=home, root=root)
if not scheme:
scheme = distutils_scheme(name, user=user, home=home, root=root)
if root_is_purelib(name, wheeldir):
lib_dir = scheme['purelib']
@ -177,6 +178,7 @@ def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None,
for dir, subdirs, files in os.walk(source):
basedir = dir[len(source):].lstrip(os.path.sep)
destdir = os.path.join(dest, basedir)
if is_base and basedir.split(os.path.sep, 1)[0].endswith('.data'):
continue
for s in subdirs:
@ -190,8 +192,8 @@ def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None,
and s.lower().startswith(req.project_name.replace('-', '_').lower())):
assert not info_dir, 'Multiple .dist-info directories'
info_dir.append(destsubdir)
if not os.path.exists(destsubdir):
os.makedirs(destsubdir)
if files and not os.path.exists(destdir):
os.makedirs(destdir)
for f in files:
# Skip unwanted files
if filter and filter(f):

Binary file not shown.

View File

@ -0,0 +1,8 @@
include DESCRIPTION.rst
# Include the test suite (FIXME: does not work yet)
# recursive-include tests *
# If using Python 2.6 or less, then have to include package data, even though
# it's already declared in setup.py
include sample/*.dat

View File

@ -0,0 +1 @@
some data

View File

@ -0,0 +1,5 @@
__version__ = '1.2.0'
def main():
"""Entry point for the application script"""
print("Call your main application code here")

View File

@ -0,0 +1 @@
some data

View File

@ -0,0 +1,5 @@
[bdist_wheel]
# This flag says that the code is written to work on both Python 2 and Python
# 3. If at all possible, it is good practice to do this. If you cannot, you
# will need to generate wheels for each Python version that you support.
universal=1

View File

@ -0,0 +1,103 @@
from setuptools import setup, find_packages
import codecs
import os
import re
here = os.path.abspath(os.path.dirname(__file__))
# Read the version number from a source file.
# Why read it, and not import?
# see https://groups.google.com/d/topic/pypa-dev/0PkjVpcxTzQ/discussion
def find_version(*file_paths):
# Open in Latin-1 so that we avoid encoding errors.
# Use codecs.open for Python 2 compatibility
with codecs.open(os.path.join(here, *file_paths), 'r', 'latin1') as f:
version_file = f.read()
# The version line must have the form
# __version__ = 'ver'
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
version_file, re.M)
if version_match:
return version_match.group(1)
raise RuntimeError("Unable to find version string.")
# Get the long description from the relevant file
with codecs.open('DESCRIPTION.rst', encoding='utf-8') as f:
long_description = f.read()
setup(
name="sample",
version=find_version('sample', '__init__.py'),
description="A sample Python project",
long_description=long_description,
# The project URL.
url='https://github.com/pypa/sampleproject',
# Author details
author='The Python Packaging Authority',
author_email='pypa-dev@googlegroups.com',
# Choose your license
license='MIT',
classifiers=[
# How mature is this project? Common values are
# 3 - Alpha
# 4 - Beta
# 5 - Production/Stable
'Development Status :: 3 - Alpha',
# Indicate who your project is intended for
'Intended Audience :: Developers',
'Topic :: Software Development :: Build Tools',
# Pick your license as you wish (should match "license" above)
'License :: OSI Approved :: MIT License',
# Specify the Python versions you support here. In particular, ensure
# that you indicate whether you support Python 2, Python 3 or both.
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.1',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
],
# What does your project relate to?
keywords='sample setuptools development',
# You can just specify the packages manually here if your project is
# simple. Or you can use find_packages.
packages=find_packages(exclude=["contrib", "docs", "tests*"]),
# List run-time dependencies here. These will be installed by pip when your
# project is installed.
install_requires = ['peppercorn'],
# If there are data files included in your packages that need to be
# installed, specify them here. If using Python 2.6 or less, then these
# have to be included in MANIFEST.in as well.
package_data={
'sample': ['package_data.dat'],
},
# Although 'package_data' is the preferred approach, in some case you may
# need to place data files outside of your packages.
# see http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files
# In this case, 'data_file' will be installed into '<sys.prefix>/my_data'
data_files=[('my_data', ['data/data_file'])],
# To provide executable scripts, use entry points in preference to the
# "scripts" keyword. Entry points provide cross-platform support and allow
# pip to create the appropriate form of executable for the target platform.
entry_points={
'console_scripts': [
'sample=sample:main',
],
},
)

View File

@ -228,3 +228,14 @@ def test_uninstallpathset_non_local(mock_logger):
uninstall_set = UninstallPathSet(test_dist)
uninstall_set.remove() #with no files added to set; which is the case when trying to remove non-local dists
mock_logger.notify.assert_any_call("Not uninstalling pip at %s, outside environment %s" % (nonlocal_path, sys.prefix)), mock_logger.notify.mock_calls
def test_uninstall_wheel(script, data):
"""
Test uninstalling a wheel
"""
package = data.packages.join("simple.dist-0.1-py2.py3-none-any.whl")
result = script.pip('install', package, '--no-index')
dist_info_folder = script.site_packages / 'simple.dist-0.1.dist-info'
assert dist_info_folder in result.files_created
result2 = script.pip('uninstall', 'simple.dist', '-y')
assert_all_changes(result, result2, [])

View File

@ -9,7 +9,7 @@ from pip import wheel
from pip.download import path_to_url as path_to_url_d
from pip.locations import write_delete_marker_file
from pip.status_codes import PREVIOUS_BUILD_DIR_ERROR
from tests.lib import pyversion_nodot, path_to_url
from tests.lib import pyversion, path_to_url
def test_pip_wheel_fails_without_wheel(script, data):
@ -26,7 +26,7 @@ def test_pip_wheel_success(script, data):
"""
script.pip('install', 'wheel')
result = script.pip('wheel', '--no-index', '-f', data.find_links, 'simple==3.0')
wheel_file_name = 'simple-3.0-py%s-none-any.whl' % pyversion_nodot
wheel_file_name = 'simple-3.0-py%s-none-any.whl' % pyversion[0]
wheel_file_path = script.scratch/'wheelhouse'/wheel_file_name
assert wheel_file_path in result.files_created, result.stdout
assert "Successfully built simple" in result.stdout, result.stdout
@ -52,7 +52,7 @@ def test_pip_wheel_fail(script, data):
"""
script.pip('install', 'wheel')
result = script.pip('wheel', '--no-index', '-f', data.find_links, 'wheelbroken==0.1')
wheel_file_name = 'wheelbroken-0.1-py%s-none-any.whl' % pyversion_nodot
wheel_file_name = 'wheelbroken-0.1-py%s-none-any.whl' % pyversion[0]
wheel_file_path = script.scratch/'wheelhouse'/wheel_file_name
assert wheel_file_path not in result.files_created, (wheel_file_path, result.files_created)
assert "FakeError" in result.stdout, result.stdout
@ -73,7 +73,7 @@ def test_pip_wheel_ignore_wheels_editables(script, data):
simple
""" % (local_wheel, local_editable)))
result = script.pip('wheel', '--no-index', '-f', data.find_links, '-r', script.scratch_path / 'reqs.txt')
wheel_file_name = 'simple-3.0-py%s-none-any.whl' % pyversion_nodot
wheel_file_name = 'simple-3.0-py%s-none-any.whl' % pyversion[0]
wheel_file_path = script.scratch/'wheelhouse'/wheel_file_name
assert wheel_file_path in result.files_created, (wheel_file_path, result.files_created)
assert "Successfully built simple" in result.stdout, result.stdout
@ -102,9 +102,9 @@ def test_pip_wheel_source_deps(script, data):
# 'requires_source' is a wheel that depends on the 'source' project
script.pip('install', 'wheel')
result = script.pip('wheel', '--use-wheel', '--no-index', '-f', data.find_links, 'requires_source')
wheel_file_name = 'source-1.0-py%s-none-any.whl' % pyversion_nodot
wheel_file_name = 'source-1.0-py%s-none-any.whl' % pyversion[0]
wheel_file_path = script.scratch/'wheelhouse'/wheel_file_name
assert wheel_file_path in result.files_created, result.stdout
assert wheel_file_path in result.files_created, result.files_created
assert "Successfully built source" in result.stdout, result.stdout

View File

@ -2,14 +2,12 @@
import os
import pytest
from mock import patch
import pkg_resources
from mock import patch, Mock
from pip._vendor import pkg_resources
from pip import wheel
from pip.exceptions import (
InstallationError, InvalidWheelFilename, UnsupportedWheel,
)
from pip.index import PackageFinder
from pip.exceptions import InvalidWheelFilename, UnsupportedWheel
from pip.req import InstallRequirement
from pip.util import unpack_file
from tests.lib import assert_raises_regexp
@ -225,3 +223,56 @@ class TestPEP425Tags(object):
with patch('pip.pep425tags.sysconfig.get_config_var', raises_ioerror):
assert len(pip.pep425tags.get_supported())
class TestMoveWheelFiles(object):
"""
Tests for moving files from wheel src to scheme paths
"""
def prep(self, data, tmpdir):
self.name = 'sample'
self.wheelpath = data.packages.join('sample-1.2.0-py2.py3-none-any.whl')
self.req = pkg_resources.Requirement.parse('sample')
self.src = os.path.join(tmpdir, 'src')
self.dest = os.path.join(tmpdir, 'dest')
unpack_file(self.wheelpath, self.src, None, None)
self.scheme = {
'scripts': os.path.join(self.dest, 'bin'),
'purelib': os.path.join(self.dest, 'lib'),
'data': os.path.join(self.dest, 'data'),
}
self.src_dist_info = os.path.join(
self.src, 'sample-1.2.0.dist-info')
self.dest_dist_info = os.path.join(
self.scheme['purelib'], 'sample-1.2.0.dist-info')
def assert_installed(self):
# lib
assert os.path.isdir(
os.path.join(self.scheme['purelib'], 'sample'))
# dist-info
metadata = os.path.join(self.dest_dist_info, 'METADATA')
assert os.path.isfile(metadata)
# data files
data_file = os.path.join(self.scheme['data'], 'my_data', 'data_file')
assert os.path.isfile(data_file)
# package data
pkg_data = os.path.join(self.scheme['purelib'], 'sample', 'package_data.dat')
def test_std_install(self, data, tmpdir):
self.prep(data, tmpdir)
wheel.move_wheel_files(self.name, self.req, self.src, scheme=self.scheme)
self.assert_installed()
def test_dist_info_contains_empty_dir(self, data, tmpdir):
"""
Test that empty dirs are not installed
"""
# e.g. https://github.com/pypa/pip/issues/1632#issuecomment-38027275
self.prep(data, tmpdir)
src_empty_dir = os.path.join(self.src_dist_info, 'empty_dir', 'empty_dir')
os.makedirs(src_empty_dir)
assert os.path.isdir(src_empty_dir)
wheel.move_wheel_files(self.name, self.req, self.src, scheme=self.scheme)
self.assert_installed()
assert not os.path.isdir(os.path.join(self.dest_dist_info, 'empty_dir'))