search_scope.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. # The following comment should be removed at some point in the future.
  2. # mypy: disallow-untyped-defs=False
  3. import itertools
  4. import logging
  5. import os
  6. import posixpath
  7. from pip._vendor.packaging.utils import canonicalize_name
  8. from pip._vendor.six.moves.urllib import parse as urllib_parse
  9. from pip._internal.models.index import PyPI
  10. from pip._internal.utils.compat import HAS_TLS
  11. from pip._internal.utils.misc import normalize_path, redact_auth_from_url
  12. from pip._internal.utils.typing import MYPY_CHECK_RUNNING
  13. if MYPY_CHECK_RUNNING:
  14. from typing import List
  15. logger = logging.getLogger(__name__)
  16. class SearchScope(object):
  17. """
  18. Encapsulates the locations that pip is configured to search.
  19. """
  20. @classmethod
  21. def create(
  22. cls,
  23. find_links, # type: List[str]
  24. index_urls, # type: List[str]
  25. ):
  26. # type: (...) -> SearchScope
  27. """
  28. Create a SearchScope object after normalizing the `find_links`.
  29. """
  30. # Build find_links. If an argument starts with ~, it may be
  31. # a local file relative to a home directory. So try normalizing
  32. # it and if it exists, use the normalized version.
  33. # This is deliberately conservative - it might be fine just to
  34. # blindly normalize anything starting with a ~...
  35. built_find_links = [] # type: List[str]
  36. for link in find_links:
  37. if link.startswith('~'):
  38. new_link = normalize_path(link)
  39. if os.path.exists(new_link):
  40. link = new_link
  41. built_find_links.append(link)
  42. # If we don't have TLS enabled, then WARN if anyplace we're looking
  43. # relies on TLS.
  44. if not HAS_TLS:
  45. for link in itertools.chain(index_urls, built_find_links):
  46. parsed = urllib_parse.urlparse(link)
  47. if parsed.scheme == 'https':
  48. logger.warning(
  49. 'pip is configured with locations that require '
  50. 'TLS/SSL, however the ssl module in Python is not '
  51. 'available.'
  52. )
  53. break
  54. return cls(
  55. find_links=built_find_links,
  56. index_urls=index_urls,
  57. )
  58. def __init__(
  59. self,
  60. find_links, # type: List[str]
  61. index_urls, # type: List[str]
  62. ):
  63. # type: (...) -> None
  64. self.find_links = find_links
  65. self.index_urls = index_urls
  66. def get_formatted_locations(self):
  67. # type: () -> str
  68. lines = []
  69. if self.index_urls and self.index_urls != [PyPI.simple_url]:
  70. lines.append(
  71. 'Looking in indexes: {}'.format(', '.join(
  72. redact_auth_from_url(url) for url in self.index_urls))
  73. )
  74. if self.find_links:
  75. lines.append(
  76. 'Looking in links: {}'.format(', '.join(
  77. redact_auth_from_url(url) for url in self.find_links))
  78. )
  79. return '\n'.join(lines)
  80. def get_index_urls_locations(self, project_name):
  81. # type: (str) -> List[str]
  82. """Returns the locations found via self.index_urls
  83. Checks the url_name on the main (first in the list) index and
  84. use this url_name to produce all locations
  85. """
  86. def mkurl_pypi_url(url):
  87. loc = posixpath.join(
  88. url,
  89. urllib_parse.quote(canonicalize_name(project_name)))
  90. # For maximum compatibility with easy_install, ensure the path
  91. # ends in a trailing slash. Although this isn't in the spec
  92. # (and PyPI can handle it without the slash) some other index
  93. # implementations might break if they relied on easy_install's
  94. # behavior.
  95. if not loc.endswith('/'):
  96. loc = loc + '/'
  97. return loc
  98. return [mkurl_pypi_url(url) for url in self.index_urls]