freeze.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. from __future__ import absolute_import
  2. import logging
  3. import re
  4. import pip
  5. from pip.req import InstallRequirement
  6. from pip.req.req_file import COMMENT_RE
  7. from pip.utils import get_installed_distributions
  8. from pip._vendor import pkg_resources
  9. from pip._vendor.packaging.utils import canonicalize_name
  10. from pip._vendor.pkg_resources import RequirementParseError
  11. logger = logging.getLogger(__name__)
  12. def freeze(
  13. requirement=None,
  14. find_links=None, local_only=None, user_only=None, skip_regex=None,
  15. default_vcs=None,
  16. isolated=False,
  17. wheel_cache=None,
  18. skip=()):
  19. find_links = find_links or []
  20. skip_match = None
  21. if skip_regex:
  22. skip_match = re.compile(skip_regex).search
  23. dependency_links = []
  24. for dist in pkg_resources.working_set:
  25. if dist.has_metadata('dependency_links.txt'):
  26. dependency_links.extend(
  27. dist.get_metadata_lines('dependency_links.txt')
  28. )
  29. for link in find_links:
  30. if '#egg=' in link:
  31. dependency_links.append(link)
  32. for link in find_links:
  33. yield '-f %s' % link
  34. installations = {}
  35. for dist in get_installed_distributions(local_only=local_only,
  36. skip=(),
  37. user_only=user_only):
  38. try:
  39. req = pip.FrozenRequirement.from_dist(
  40. dist,
  41. dependency_links
  42. )
  43. except RequirementParseError:
  44. logger.warning(
  45. "Could not parse requirement: %s",
  46. dist.project_name
  47. )
  48. continue
  49. installations[req.name] = req
  50. if requirement:
  51. # the options that don't get turned into an InstallRequirement
  52. # should only be emitted once, even if the same option is in multiple
  53. # requirements files, so we need to keep track of what has been emitted
  54. # so that we don't emit it again if it's seen again
  55. emitted_options = set()
  56. for req_file_path in requirement:
  57. with open(req_file_path) as req_file:
  58. for line in req_file:
  59. if (not line.strip() or
  60. line.strip().startswith('#') or
  61. (skip_match and skip_match(line)) or
  62. line.startswith((
  63. '-r', '--requirement',
  64. '-Z', '--always-unzip',
  65. '-f', '--find-links',
  66. '-i', '--index-url',
  67. '--pre',
  68. '--trusted-host',
  69. '--process-dependency-links',
  70. '--extra-index-url'))):
  71. line = line.rstrip()
  72. if line not in emitted_options:
  73. emitted_options.add(line)
  74. yield line
  75. continue
  76. if line.startswith('-e') or line.startswith('--editable'):
  77. if line.startswith('-e'):
  78. line = line[2:].strip()
  79. else:
  80. line = line[len('--editable'):].strip().lstrip('=')
  81. line_req = InstallRequirement.from_editable(
  82. line,
  83. default_vcs=default_vcs,
  84. isolated=isolated,
  85. wheel_cache=wheel_cache,
  86. )
  87. else:
  88. line_req = InstallRequirement.from_line(
  89. COMMENT_RE.sub('', line).strip(),
  90. isolated=isolated,
  91. wheel_cache=wheel_cache,
  92. )
  93. if not line_req.name:
  94. logger.info(
  95. "Skipping line in requirement file [%s] because "
  96. "it's not clear what it would install: %s",
  97. req_file_path, line.strip(),
  98. )
  99. logger.info(
  100. " (add #egg=PackageName to the URL to avoid"
  101. " this warning)"
  102. )
  103. elif line_req.name not in installations:
  104. logger.warning(
  105. "Requirement file [%s] contains %s, but that "
  106. "package is not installed",
  107. req_file_path, COMMENT_RE.sub('', line).strip(),
  108. )
  109. else:
  110. yield str(installations[line_req.name]).rstrip()
  111. del installations[line_req.name]
  112. yield(
  113. '## The following requirements were added by '
  114. 'pip freeze:'
  115. )
  116. for installation in sorted(
  117. installations.values(), key=lambda x: x.name.lower()):
  118. if canonicalize_name(installation.name) not in skip:
  119. yield str(installation).rstrip()