Commit f95da2a3 authored by PJ Eby's avatar PJ Eby

PyPI searches now use the exact spelling of requirements specified on

the command line or in a project's ``install_requires``.  Previously, a
normalized form of the name was used, which could lead to unnecessary
full-index searches when a project's name had an underscore (``_``)
in it.

--HG--
branch : setuptools
extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041942
parent 5d732201
...@@ -962,6 +962,12 @@ Known Issues ...@@ -962,6 +962,12 @@ Known Issues
* There's no automatic retry for borked Sourceforge mirrors, which can easily * There's no automatic retry for borked Sourceforge mirrors, which can easily
time out or be missing a file. time out or be missing a file.
0.6a10
* PyPI searches now use the exact spelling of requirements specified on the
command line or in a project's ``install_requires``. Previously, a
normalized form of the name was used, which could lead to unnecessary
full-index searches when a project's name had an underscore (``_``) in it.
0.6a9 0.6a9
* Fixed ``.pth`` file processing picking up nested eggs (i.e. ones inside * Fixed ``.pth`` file processing picking up nested eggs (i.e. ones inside
......
...@@ -1529,8 +1529,8 @@ def yield_lines(strs): ...@@ -1529,8 +1529,8 @@ def yield_lines(strs):
LINE_END = re.compile(r"\s*(#.*)?$").match # whitespace and comment LINE_END = re.compile(r"\s*(#.*)?$").match # whitespace and comment
CONTINUE = re.compile(r"\s*\\\s*(#.*)?$").match # line continuation CONTINUE = re.compile(r"\s*\\\s*(#.*)?$").match # line continuation
DISTRO = re.compile(r"\s*(\w+)").match # Distribution or option DISTRO = re.compile(r"\s*((\w|-)+)").match # Distribution or option
VERSION = re.compile(r"\s*(<=?|>=?|==|!=)\s*((\w|\.)+)").match # version info VERSION = re.compile(r"\s*(<=?|>=?|==|!=)\s*((\w|[-.])+)").match # ver. info
COMMA = re.compile(r"\s*,").match # comma between items COMMA = re.compile(r"\s*,").match # comma between items
OBRACKET = re.compile(r"\s*\[").match OBRACKET = re.compile(r"\s*\[").match
CBRACKET = re.compile(r"\s*\]").match CBRACKET = re.compile(r"\s*\]").match
...@@ -1982,7 +1982,7 @@ def parse_requirements(strs): ...@@ -1982,7 +1982,7 @@ def parse_requirements(strs):
while not TERMINATOR(line,p): while not TERMINATOR(line,p):
if CONTINUE(line,p): if CONTINUE(line,p):
try: try:
line = lines.next().replace('-','_'); p = 0 line = lines.next(); p = 0
except StopIteration: except StopIteration:
raise ValueError( raise ValueError(
"\\ must not appear on the last nonblank line" "\\ must not appear on the last nonblank line"
...@@ -2008,7 +2008,6 @@ def parse_requirements(strs): ...@@ -2008,7 +2008,6 @@ def parse_requirements(strs):
return line, p, items return line, p, items
for line in lines: for line in lines:
line = line.replace('-','_')
match = DISTRO(line) match = DISTRO(line)
if not match: if not match:
raise ValueError("Missing distribution spec", line) raise ValueError("Missing distribution spec", line)
...@@ -2024,8 +2023,8 @@ def parse_requirements(strs): ...@@ -2024,8 +2023,8 @@ def parse_requirements(strs):
) )
line, p, specs = scan_list(VERSION,LINE_END,line,p,(1,2),"version spec") line, p, specs = scan_list(VERSION,LINE_END,line,p,(1,2),"version spec")
specs = [(op,val.replace('_','-')) for op,val in specs] specs = [(op,safe_version(val)) for op,val in specs]
yield Requirement(project_name.replace('_','-'), specs, extras) yield Requirement(project_name, specs, extras)
def _sort_dists(dists): def _sort_dists(dists):
...@@ -2048,9 +2047,11 @@ def _sort_dists(dists): ...@@ -2048,9 +2047,11 @@ def _sort_dists(dists):
class Requirement: class Requirement:
def __init__(self, project_name, specs, extras): def __init__(self, project_name, specs, extras):
"""DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!"""
self.unsafe_name, project_name = project_name, safe_name(project_name)
self.project_name, self.key = project_name, project_name.lower() self.project_name, self.key = project_name, project_name.lower()
index = [(parse_version(v),state_machine[op],op,v) for op,v in specs] index = [(parse_version(v),state_machine[op],op,v) for op,v in specs]
index.sort() index.sort()
...@@ -2068,8 +2069,6 @@ class Requirement: ...@@ -2068,8 +2069,6 @@ class Requirement:
if extras: extras = '[%s]' % extras if extras: extras = '[%s]' % extras
return '%s%s%s' % (self.project_name, extras, specs) return '%s%s%s' % (self.project_name, extras, specs)
def __repr__(self): return "Requirement.parse(%r)" % str(self)
def __eq__(self,other): def __eq__(self,other):
return isinstance(other,Requirement) and self.hashCmp==other.hashCmp return isinstance(other,Requirement) and self.hashCmp==other.hashCmp
...@@ -2089,9 +2088,12 @@ class Requirement: ...@@ -2089,9 +2088,12 @@ class Requirement:
if last is None: last = True # no rules encountered if last is None: last = True # no rules encountered
return last return last
def __hash__(self): def __hash__(self):
return self.__hash return self.__hash
def __repr__(self): return "Requirement.parse(%r)" % str(self)
#@staticmethod #@staticmethod
def parse(s): def parse(s):
reqs = list(parse_requirements(s)) reqs = list(parse_requirements(s))
...@@ -2103,7 +2105,6 @@ class Requirement: ...@@ -2103,7 +2105,6 @@ class Requirement:
parse = staticmethod(parse) parse = staticmethod(parse)
state_machine = { state_machine = {
# =>< # =><
'<' : '--T', '<' : '--T',
...@@ -2122,7 +2123,6 @@ def _get_mro(cls): ...@@ -2122,7 +2123,6 @@ def _get_mro(cls):
return cls.__mro__[1:] return cls.__mro__[1:]
return cls.__mro__ return cls.__mro__
def _find_adapter(registry, ob): def _find_adapter(registry, ob):
"""Return an adapter factory for `ob` from `registry`""" """Return an adapter factory for `ob` from `registry`"""
for t in _get_mro(getattr(ob, '__class__', type(ob))): for t in _get_mro(getattr(ob, '__class__', type(ob))):
......
...@@ -245,12 +245,16 @@ class PackageIndex(Environment): ...@@ -245,12 +245,16 @@ class PackageIndex(Environment):
def find_packages(self, requirement): def find_packages(self, requirement):
self.scan_url(self.index_url + requirement.project_name+'/') self.scan_url(self.index_url + requirement.unsafe_name+'/')
if not self.package_pages.get(requirement.key):
# Fall back to safe version of the name
self.scan_url(self.index_url + requirement.project_name+'/')
if not self.package_pages.get(requirement.key): if not self.package_pages.get(requirement.key):
# We couldn't find the target package, so search the index page too # We couldn't find the target package, so search the index page too
self.warn( self.warn(
"Couldn't find index page for %r (maybe misspelled?)", "Couldn't find index page for %r (maybe misspelled?)",
requirement.project_name requirement.unsafe_name
) )
if self.index_url not in self.fetched_urls: if self.index_url not in self.fetched_urls:
self.warn( self.warn(
...@@ -281,10 +285,6 @@ class PackageIndex(Environment): ...@@ -281,10 +285,6 @@ class PackageIndex(Environment):
"; possible download problem?" "; possible download problem?"
) )
def download(self, spec, tmpdir): def download(self, spec, tmpdir):
"""Locate and/or download `spec` to `tmpdir`, returning a local path """Locate and/or download `spec` to `tmpdir`, returning a local path
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment