Skip to content

Commit be12f79

Browse files
committed
merge pypa/develop
2 parents 8d68f7f + 32f0be7 commit be12f79

12 files changed

+170
-39
lines changed

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ branches:
1818
- wheel_build
1919
matrix:
2020
allow_failures:
21+
- python: 3.1
2122
env:
2223
- PIP_USE_MIRRORS=true

AUTHORS.txt

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Alexandre Conrad
44
Andrey Bulgakov
55
Antti Kaihola
66
Armin Ronacher
7+
Aziz Köksal
8+
Ben Rosser
79
Brian Rosner
810
Carl Meyer
911
Chris McDonough

docs/configuration.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ Location
8484
The names and locations of the configuration files vary slightly across
8585
platforms.
8686

87-
On Unix and Mac OS X the configuration file is: :file:`$HOME/.pip/pip.conf`
87+
On Unix and Mac OS X the configuration file is:
88+
:file:`$HOME/.config/pip/pip.conf`
8889

8990
And on Windows, the configuration file is: :file:`%HOME%\\pip\\pip.ini`
9091

docs/news.txt

+6-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ develop (unreleased)
1717
* Pip now has experimental "wheel" support. Thanks Daniel Holth, Paul Moore,
1818
and Michele Lacchia.
1919

20+
* --user/--upgrade install options now work together; thanks eevee.
21+
2022
* Added check in ``install --download`` to prevent re-downloading if the target
2123
file already exists. Thanks Andrey Bulgakov.
2224

@@ -57,7 +59,10 @@ develop (unreleased)
5759
Hsiaoming Yang and Markus Hametner.
5860

5961
* Use a temporary directory as the default build location outside of a
60-
virtualenv. Fixes issues #339 and #381. Thanks TC01.
62+
virtualenv. Fixes issues #339 and #381. Thanks Ben Rosser.
63+
64+
* Moved pip configuration data to ~/.config/pip, the XDG standard for config
65+
files on Unix/Linux systems. Thanks Ben Rosser.
6166

6267
* Added support for specifying extras with local editables. Thanks Nick
6368
Stenning.

pip/index.py

+24-20
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,23 @@ def sort_path(path):
104104
return files, urls
105105

106106
def find_requirement(self, req, upgrade):
107+
108+
def mkurl_pypi_url(url):
109+
loc = posixpath.join(url, url_name)
110+
# For maximum compatibility with easy_install, ensure the path
111+
# ends in a trailing slash. Although this isn't in the spec
112+
# (and PyPI can handle it without the slash) some other index
113+
# implementations might break if they relied on easy_install's behavior.
114+
if not loc.endswith('/'):
115+
loc = loc + '/'
116+
return loc
117+
107118
url_name = req.url_name
108119
# Only check main index if index URL is given:
109120
main_index_url = None
110121
if self.index_urls:
111122
# Check that we have the url_name correctly spelled:
112-
main_index_url = Link(posixpath.join(self.index_urls[0], url_name))
123+
main_index_url = Link(mkurl_pypi_url(self.index_urls[0]))
113124
# This will also cache the page, so it's okay that we get it again later:
114125
page = self._get_page(main_index_url, req)
115126
if page is None:
@@ -119,15 +130,6 @@ def find_requirement(self, req, upgrade):
119130
# adding more index URLs from requirements files
120131
all_index_urls = self.index_urls + self.mirror_urls
121132

122-
def mkurl_pypi_url(url):
123-
loc = posixpath.join(url, url_name)
124-
# For maximum compatibility with easy_install, ensure the path
125-
# ends in a trailing slash. Although this isn't in the spec
126-
# (and PyPI can handle it without the slash) some other index
127-
# implementations might break if they relied on easy_install's behavior.
128-
if not loc.endswith('/'):
129-
loc = loc + '/'
130-
return loc
131133
if url_name is not None:
132134
locations = [
133135
mkurl_pypi_url(url)
@@ -167,21 +169,23 @@ def mkurl_pypi_url(url):
167169
if not found_versions and not page_versions and not dependency_versions and not file_versions:
168170
logger.fatal('Could not find any downloads that satisfy the requirement %s' % req)
169171
raise DistributionNotFound('No distributions at all found for %s' % req)
172+
installed_version = []
170173
if req.satisfied_by is not None:
171-
found_versions.append((req.satisfied_by.parsed_version, InfLink, req.satisfied_by.version))
174+
installed_version = [(req.satisfied_by.parsed_version, InfLink, req.satisfied_by.version)]
172175
if file_versions:
173176
file_versions.sort(reverse=True)
174177
logger.info('Local files found: %s' % ', '.join([url_to_path(link.url) for parsed, link, version in file_versions]))
175-
found_versions = file_versions + found_versions
176-
all_versions = found_versions + page_versions + dependency_versions
178+
#this is an intentional priority ordering
179+
all_versions = installed_version + file_versions + found_versions + page_versions + dependency_versions
177180
applicable_versions = []
178181
for (parsed_version, link, version) in all_versions:
179182
if version not in req.req:
180183
logger.info("Ignoring link %s, version %s doesn't match %s"
181184
% (link, version, ','.join([''.join(s) for s in req.req.specs])))
182185
continue
183186
applicable_versions.append((parsed_version, link, version))
184-
applicable_versions = sorted(applicable_versions, reverse=True)
187+
#bring the latest version to the front, but maintains the priority ordering as secondary
188+
applicable_versions = sorted(applicable_versions, key=lambda v: v[0], reverse=True)
185189
existing_applicable = bool([link for parsed_version, link, version in applicable_versions if link is InfLink])
186190
if not upgrade and existing_applicable:
187191
if applicable_versions[0][1] is InfLink:
@@ -193,7 +197,7 @@ def mkurl_pypi_url(url):
193197
return None
194198
if not applicable_versions:
195199
logger.fatal('Could not find a version that satisfies the requirement %s (from versions: %s)'
196-
% (req, ', '.join([version for parsed_version, link, version in found_versions])))
200+
% (req, ', '.join([version for parsed_version, link, version in all_versions])))
197201
raise DistributionNotFound('No distributions matching the version for %s' % req)
198202
if applicable_versions[0][1] is InfLink:
199203
# We have an existing version, and its the best version
@@ -227,7 +231,7 @@ def _find_url_name(self, index_url, url_name, req):
227231

228232
def _get_pages(self, locations, req):
229233
"""Yields (page, page_url) from the given locations, skipping
230-
locations that have errors, and adding download/homepage links"""
234+
locations that have errors, and adding homepage links"""
231235
pending_queue = Queue()
232236
for location in locations:
233237
pending_queue.put(location)
@@ -258,7 +262,7 @@ def _get_queued_page(self, req, pending_queue, done, seen):
258262
if page is None:
259263
continue
260264
done.append(page)
261-
for link in page.rel_links():
265+
for link in page.rel_links(rels=('homepage',)):
262266
pending_queue.put(link)
263267

264268
_egg_fragment_re = re.compile(r'#egg=([^&]*)')
@@ -568,8 +572,8 @@ def links(self):
568572
url = self.clean_link(urlparse.urljoin(self.base_url, url))
569573
yield Link(url, self)
570574

571-
def rel_links(self):
572-
for url in self.explicit_rel_links():
575+
def rel_links(self, rels=('homepage', 'download')):
576+
for url in self.explicit_rel_links(rels):
573577
yield url
574578
for url in self.scraped_rel_links():
575579
yield url
@@ -703,7 +707,7 @@ def show_url(self):
703707
return posixpath.basename(self.url.split('#', 1)[0].split('?', 1)[0])
704708

705709
#An "Infinite Link" that compares greater than other links
706-
InfLink = Link(Inf)
710+
InfLink = Link(Inf) #this object is not currently used as a sortable
707711

708712

709713
def get_requirement_from_url(url):

pip/locations.py

+22-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import sys
44
import site
55
import os
6+
import shutil
67
import tempfile
78
from pip.backwardcompat import get_python_lib
89

@@ -60,9 +61,29 @@ def virtualenv_no_global():
6061
default_log_file = os.path.join(default_storage_dir, 'pip.log')
6162
else:
6263
bin_py = os.path.join(sys.prefix, 'bin')
63-
default_storage_dir = os.path.join(user_dir, '.pip')
64+
65+
# Use XDG_CONFIG_HOME instead of the ~/.pip
66+
# On some systems, we may have to create this, on others it probably exists
67+
xdg_dir = os.path.join(user_dir, '.config')
68+
xdg_dir = os.environ.get('XDG_CONFIG_HOME', xdg_dir)
69+
if not os.path.exists(xdg_dir):
70+
os.mkdir(xdg_dir)
71+
default_storage_dir = os.path.join(xdg_dir, 'pip')
6472
default_config_file = os.path.join(default_storage_dir, 'pip.conf')
6573
default_log_file = os.path.join(default_storage_dir, 'pip.log')
74+
75+
# Migration path for users- move things from the old dir if it exists
76+
# If the new dir exists and has no pip.conf and the old dir does, move it
77+
# When these checks are finished, delete the old directory
78+
old_storage_dir = os.path.join(user_dir, '.pip')
79+
old_config_file = os.path.join(old_storage_dir, 'pip.conf')
80+
if os.path.exists(old_storage_dir):
81+
if not os.path.exists(default_storage_dir):
82+
shutil.copytree(old_storage_dir, default_storage_dir)
83+
elif os.path.exists(old_config_file) and not os.path.exists(default_config_file):
84+
shutil.copy2(old_config_file, default_config_file)
85+
shutil.rmtree(old_storage_dir)
86+
6687
# Forcing to use /usr/local/bin for standard Mac OS X framework installs
6788
# Also log to ~/Library/Logs/ for use with the Console.app log viewer
6889
if sys.platform[:6] == 'darwin' and sys.prefix[:16] == '/System/Library/':

pip/req.py

+11-5
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def from_line(cls, name, comes_from=None):
9797
link = Link(name)
9898
elif os.path.isdir(path) and (os.path.sep in name or name.startswith('.')):
9999
if not is_installable_dir(path):
100-
raise InstallationError("Directory %r is not installable. File 'setup.py' not found.", name)
100+
raise InstallationError("Directory %r is not installable. File 'setup.py' not found." % name)
101101
link = Link(path_to_url(name))
102102
elif is_archive_file(path):
103103
if not os.path.isfile(path):
@@ -922,7 +922,9 @@ def locate_files(self):
922922
req_to_install.check_if_exists()
923923
if req_to_install.satisfied_by:
924924
if self.upgrade:
925-
req_to_install.conflicts_with = req_to_install.satisfied_by
925+
#don't uninstall conflict if user install and and conflict is not user install
926+
if not (self.use_user_site and not dist_in_usersite(req_to_install.satisfied_by)):
927+
req_to_install.conflicts_with = req_to_install.satisfied_by
926928
req_to_install.satisfied_by = None
927929
else:
928930
install_needed = False
@@ -974,7 +976,9 @@ def prepare_files(self, finder, force_root_egg_info=False, bundle=False):
974976
req_to_install.url = url.url
975977

976978
if not best_installed:
977-
req_to_install.conflicts_with = req_to_install.satisfied_by
979+
#don't uninstall conflict if user install and conflict is not user install
980+
if not (self.use_user_site and not dist_in_usersite(req_to_install.satisfied_by)):
981+
req_to_install.conflicts_with = req_to_install.satisfied_by
978982
req_to_install.satisfied_by = None
979983
else:
980984
install = False
@@ -1090,7 +1094,9 @@ def prepare_files(self, finder, force_root_egg_info=False, bundle=False):
10901094
req_to_install.check_if_exists()
10911095
if req_to_install.satisfied_by:
10921096
if self.upgrade or self.ignore_installed:
1093-
req_to_install.conflicts_with = req_to_install.satisfied_by
1097+
#don't uninstall conflict if user install and and conflict is not user install
1098+
if not (self.use_user_site and not dist_in_usersite(req_to_install.satisfied_by)):
1099+
req_to_install.conflicts_with = req_to_install.satisfied_by
10941100
req_to_install.satisfied_by = None
10951101
else:
10961102
install = False
@@ -1392,7 +1398,7 @@ def parse_editable(editable_req, default_vcs=None):
13921398

13931399
if os.path.isdir(url_no_extras):
13941400
if not os.path.exists(os.path.join(url_no_extras, 'setup.py')):
1395-
raise InstallationError("Directory %r is not installable. File 'setup.py' not found.", url_no_extras)
1401+
raise InstallationError("Directory %r is not installable. File 'setup.py' not found." % url_no_extras)
13961402
# Treating it as code that has already been checked out
13971403
url_no_extras = path_to_url(url_no_extras)
13981404

pip/util.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ def __ge__(self, other):
161161
def __repr__(self):
162162
return 'Inf'
163163

164-
Inf = _Inf()
164+
165+
Inf = _Inf() #this object is not currently used as a sortable in our code
165166
del _Inf
166167

167168

tests/test_finder.py

+32
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,35 @@ def test_find_wheel(mock_get_supported):
9595
found = finder.find_requirement(req, True)
9696
assert found.url.endswith("simple.dist-0.1-py2.py3-none-any.whl"), found
9797

98+
def test_finder_priority_file_over_page():
99+
"""Test PackageFinder prefers file links over equivalent page links"""
100+
req = InstallRequirement.from_line('gmpy==1.15', None)
101+
finder = PackageFinder([find_links], ["http://pypi.python.org/simple"])
102+
link = finder.find_requirement(req, False)
103+
assert link.url.startswith("file://")
104+
105+
106+
def test_finder_priority_page_over_deplink():
107+
"""Test PackageFinder prefers page links over equivalent dependency links"""
108+
req = InstallRequirement.from_line('gmpy==1.15', None)
109+
finder = PackageFinder([], ["http://pypi.python.org/simple"])
110+
finder.add_dependency_links(['http://c.pypi.python.org/simple/gmpy/'])
111+
link = finder.find_requirement(req, False)
112+
assert link.url.startswith("http://pypi")
113+
114+
115+
def test_finder_priority_nonegg_over_eggfragments():
116+
"""Test PackageFinder prefers non-egg links over "#egg=" links"""
117+
req = InstallRequirement.from_line('bar==1.0', None)
118+
links = ['http://foo/bar.py#egg=bar-1.0', 'http://foo/bar-1.0.tar.gz']
119+
120+
finder = PackageFinder(links, [])
121+
link = finder.find_requirement(req, False)
122+
assert link.url.endswith('tar.gz')
123+
124+
links.reverse()
125+
finder = PackageFinder(links, [])
126+
link = finder.find_requirement(req, False)
127+
assert link.url.endswith('tar.gz')
128+
129+

tests/test_index.py

+24
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,30 @@ def test_html_page_should_be_able_to_scrap_rel_links():
3333
assert len(links) == 1
3434
assert links[0].url == 'http://supervisord.org/'
3535

36+
37+
def test_html_page_should_be_able_to_filter_links_by_rel():
38+
"""
39+
Test selecting links by the rel attribute
40+
"""
41+
page = HTMLPage("""
42+
<a href="http://example.com/page.html">Some page</a>
43+
<a href="http://example.com/archive-1.2.3.tar.gz" rel="download">Download URL</a>
44+
<a href="http://example.com/home.html" rel="homepage">Homepage</a>
45+
""", "archive")
46+
47+
links = list(page.rel_links())
48+
urls = [l.url for l in links]
49+
hlinks = list(page.rel_links(('homepage',)))
50+
dlinks = list(page.rel_links(('download',)))
51+
assert len(links) == 2
52+
assert 'http://example.com/archive-1.2.3.tar.gz' in urls
53+
assert 'http://example.com/home.html' in urls
54+
assert len(hlinks) == 1
55+
assert hlinks[0].url == 'http://example.com/home.html'
56+
assert len(dlinks) == 1
57+
assert dlinks[0].url == 'http://example.com/archive-1.2.3.tar.gz'
58+
59+
3660
@patch('socket.gethostbyname_ex')
3761
def test_get_mirrors(mock_gethostbyname_ex):
3862
# Test when the expected result comes back

tests/test_requirements.py

+13-10
Original file line numberDiff line numberDiff line change
@@ -80,20 +80,23 @@ def test_multiple_requirements_files():
8080
def test_respect_order_in_requirements_file():
8181
env = reset_env()
8282
write_file('frameworks-req.txt', textwrap.dedent("""\
83-
bidict
84-
ordereddict
85-
initools
83+
parent
84+
child
85+
simple
8686
"""))
87-
result = run_pip('install', '-r', env.scratch_path / 'frameworks-req.txt')
87+
88+
find_links = 'file://' + os.path.join(here, 'packages')
89+
result = run_pip('install', '--no-index', '-f', find_links, '-r', env.scratch_path / 'frameworks-req.txt')
90+
8891
downloaded = [line for line in result.stdout.split('\n')
8992
if 'Downloading/unpacking' in line]
9093

91-
assert 'bidict' in downloaded[0], 'First download should ' \
92-
'be "bidict" but was "%s"' % downloaded[0]
93-
assert 'ordereddict' in downloaded[1], 'Second download should ' \
94-
'be "ordereddict" but was "%s"' % downloaded[1]
95-
assert 'initools' in downloaded[2], 'Third download should ' \
96-
'be "initools" but was "%s"' % downloaded[2]
94+
assert 'parent' in downloaded[0], 'First download should ' \
95+
'be "parent" but was "%s"' % downloaded[0]
96+
assert 'child' in downloaded[1], 'Second download should ' \
97+
'be "child" but was "%s"' % downloaded[1]
98+
assert 'simple' in downloaded[2], 'Third download should ' \
99+
'be "simple" but was "%s"' % downloaded[2]
97100

98101

99102
def test_requirements_data_structure_keeps_order():

0 commit comments

Comments
 (0)