egg_info.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  1. """setuptools.command.egg_info
  2. Create a distribution's .egg-info directory and contents"""
  3. from distutils.filelist import FileList as _FileList
  4. from distutils.errors import DistutilsInternalError
  5. from distutils.util import convert_path
  6. from distutils import log
  7. import distutils.errors
  8. import distutils.filelist
  9. import functools
  10. import os
  11. import re
  12. import sys
  13. import io
  14. import warnings
  15. import time
  16. import collections
  17. from .._importlib import metadata
  18. from .. import _entry_points
  19. from setuptools import Command
  20. from setuptools.command.sdist import sdist
  21. from setuptools.command.sdist import walk_revctrl
  22. from setuptools.command.setopt import edit_config
  23. from setuptools.command import bdist_egg
  24. from pkg_resources import (
  25. Requirement, safe_name, parse_version,
  26. safe_version, to_filename)
  27. import setuptools.unicode_utils as unicode_utils
  28. from setuptools.glob import glob
  29. from setuptools.extern import packaging
  30. from setuptools.extern.jaraco.text import yield_lines
  31. from setuptools import SetuptoolsDeprecationWarning
  32. def translate_pattern(glob): # noqa: C901 # is too complex (14) # FIXME
  33. """
  34. Translate a file path glob like '*.txt' in to a regular expression.
  35. This differs from fnmatch.translate which allows wildcards to match
  36. directory separators. It also knows about '**/' which matches any number of
  37. directories.
  38. """
  39. pat = ''
  40. # This will split on '/' within [character classes]. This is deliberate.
  41. chunks = glob.split(os.path.sep)
  42. sep = re.escape(os.sep)
  43. valid_char = '[^%s]' % (sep,)
  44. for c, chunk in enumerate(chunks):
  45. last_chunk = c == len(chunks) - 1
  46. # Chunks that are a literal ** are globstars. They match anything.
  47. if chunk == '**':
  48. if last_chunk:
  49. # Match anything if this is the last component
  50. pat += '.*'
  51. else:
  52. # Match '(name/)*'
  53. pat += '(?:%s+%s)*' % (valid_char, sep)
  54. continue # Break here as the whole path component has been handled
  55. # Find any special characters in the remainder
  56. i = 0
  57. chunk_len = len(chunk)
  58. while i < chunk_len:
  59. char = chunk[i]
  60. if char == '*':
  61. # Match any number of name characters
  62. pat += valid_char + '*'
  63. elif char == '?':
  64. # Match a name character
  65. pat += valid_char
  66. elif char == '[':
  67. # Character class
  68. inner_i = i + 1
  69. # Skip initial !/] chars
  70. if inner_i < chunk_len and chunk[inner_i] == '!':
  71. inner_i = inner_i + 1
  72. if inner_i < chunk_len and chunk[inner_i] == ']':
  73. inner_i = inner_i + 1
  74. # Loop till the closing ] is found
  75. while inner_i < chunk_len and chunk[inner_i] != ']':
  76. inner_i = inner_i + 1
  77. if inner_i >= chunk_len:
  78. # Got to the end of the string without finding a closing ]
  79. # Do not treat this as a matching group, but as a literal [
  80. pat += re.escape(char)
  81. else:
  82. # Grab the insides of the [brackets]
  83. inner = chunk[i + 1:inner_i]
  84. char_class = ''
  85. # Class negation
  86. if inner[0] == '!':
  87. char_class = '^'
  88. inner = inner[1:]
  89. char_class += re.escape(inner)
  90. pat += '[%s]' % (char_class,)
  91. # Skip to the end ]
  92. i = inner_i
  93. else:
  94. pat += re.escape(char)
  95. i += 1
  96. # Join each chunk with the dir separator
  97. if not last_chunk:
  98. pat += sep
  99. pat += r'\Z'
  100. return re.compile(pat, flags=re.MULTILINE | re.DOTALL)
  101. class InfoCommon:
  102. tag_build = None
  103. tag_date = None
  104. @property
  105. def name(self):
  106. return safe_name(self.distribution.get_name())
  107. def tagged_version(self):
  108. return safe_version(self._maybe_tag(self.distribution.get_version()))
  109. def _maybe_tag(self, version):
  110. """
  111. egg_info may be called more than once for a distribution,
  112. in which case the version string already contains all tags.
  113. """
  114. return (
  115. version if self.vtags and self._already_tagged(version)
  116. else version + self.vtags
  117. )
  118. def _already_tagged(self, version: str) -> bool:
  119. # Depending on their format, tags may change with version normalization.
  120. # So in addition the regular tags, we have to search for the normalized ones.
  121. return version.endswith(self.vtags) or version.endswith(self._safe_tags())
  122. def _safe_tags(self) -> str:
  123. # To implement this we can rely on `safe_version` pretending to be version 0
  124. # followed by tags. Then we simply discard the starting 0 (fake version number)
  125. return safe_version(f"0{self.vtags}")[1:]
  126. def tags(self) -> str:
  127. version = ''
  128. if self.tag_build:
  129. version += self.tag_build
  130. if self.tag_date:
  131. version += time.strftime("-%Y%m%d")
  132. return version
  133. vtags = property(tags)
  134. class egg_info(InfoCommon, Command):
  135. description = "create a distribution's .egg-info directory"
  136. user_options = [
  137. ('egg-base=', 'e', "directory containing .egg-info directories"
  138. " (default: top of the source tree)"),
  139. ('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"),
  140. ('tag-build=', 'b', "Specify explicit tag to add to version number"),
  141. ('no-date', 'D', "Don't include date stamp [default]"),
  142. ]
  143. boolean_options = ['tag-date']
  144. negative_opt = {
  145. 'no-date': 'tag-date',
  146. }
  147. def initialize_options(self):
  148. self.egg_base = None
  149. self.egg_name = None
  150. self.egg_info = None
  151. self.egg_version = None
  152. self.broken_egg_info = False
  153. self.ignore_egg_info_in_manifest = False
  154. ####################################
  155. # allow the 'tag_svn_revision' to be detected and
  156. # set, supporting sdists built on older Setuptools.
  157. @property
  158. def tag_svn_revision(self):
  159. pass
  160. @tag_svn_revision.setter
  161. def tag_svn_revision(self, value):
  162. pass
  163. ####################################
  164. def save_version_info(self, filename):
  165. """
  166. Materialize the value of date into the
  167. build tag. Install build keys in a deterministic order
  168. to avoid arbitrary reordering on subsequent builds.
  169. """
  170. egg_info = collections.OrderedDict()
  171. # follow the order these keys would have been added
  172. # when PYTHONHASHSEED=0
  173. egg_info['tag_build'] = self.tags()
  174. egg_info['tag_date'] = 0
  175. edit_config(filename, dict(egg_info=egg_info))
  176. def finalize_options(self):
  177. # Note: we need to capture the current value returned
  178. # by `self.tagged_version()`, so we can later update
  179. # `self.distribution.metadata.version` without
  180. # repercussions.
  181. self.egg_name = self.name
  182. self.egg_version = self.tagged_version()
  183. parsed_version = parse_version(self.egg_version)
  184. try:
  185. is_version = isinstance(parsed_version, packaging.version.Version)
  186. spec = "%s==%s" if is_version else "%s===%s"
  187. Requirement(spec % (self.egg_name, self.egg_version))
  188. except ValueError as e:
  189. raise distutils.errors.DistutilsOptionError(
  190. "Invalid distribution name or version syntax: %s-%s" %
  191. (self.egg_name, self.egg_version)
  192. ) from e
  193. if self.egg_base is None:
  194. dirs = self.distribution.package_dir
  195. self.egg_base = (dirs or {}).get('', os.curdir)
  196. self.ensure_dirname('egg_base')
  197. self.egg_info = to_filename(self.egg_name) + '.egg-info'
  198. if self.egg_base != os.curdir:
  199. self.egg_info = os.path.join(self.egg_base, self.egg_info)
  200. if '-' in self.egg_name:
  201. self.check_broken_egg_info()
  202. # Set package version for the benefit of dumber commands
  203. # (e.g. sdist, bdist_wininst, etc.)
  204. #
  205. self.distribution.metadata.version = self.egg_version
  206. # If we bootstrapped around the lack of a PKG-INFO, as might be the
  207. # case in a fresh checkout, make sure that any special tags get added
  208. # to the version info
  209. #
  210. pd = self.distribution._patched_dist
  211. if pd is not None and pd.key == self.egg_name.lower():
  212. pd._version = self.egg_version
  213. pd._parsed_version = parse_version(self.egg_version)
  214. self.distribution._patched_dist = None
  215. def write_or_delete_file(self, what, filename, data, force=False):
  216. """Write `data` to `filename` or delete if empty
  217. If `data` is non-empty, this routine is the same as ``write_file()``.
  218. If `data` is empty but not ``None``, this is the same as calling
  219. ``delete_file(filename)`. If `data` is ``None``, then this is a no-op
  220. unless `filename` exists, in which case a warning is issued about the
  221. orphaned file (if `force` is false), or deleted (if `force` is true).
  222. """
  223. if data:
  224. self.write_file(what, filename, data)
  225. elif os.path.exists(filename):
  226. if data is None and not force:
  227. log.warn(
  228. "%s not set in setup(), but %s exists", what, filename
  229. )
  230. return
  231. else:
  232. self.delete_file(filename)
  233. def write_file(self, what, filename, data):
  234. """Write `data` to `filename` (if not a dry run) after announcing it
  235. `what` is used in a log message to identify what is being written
  236. to the file.
  237. """
  238. log.info("writing %s to %s", what, filename)
  239. data = data.encode("utf-8")
  240. if not self.dry_run:
  241. f = open(filename, 'wb')
  242. f.write(data)
  243. f.close()
  244. def delete_file(self, filename):
  245. """Delete `filename` (if not a dry run) after announcing it"""
  246. log.info("deleting %s", filename)
  247. if not self.dry_run:
  248. os.unlink(filename)
  249. def run(self):
  250. self.mkpath(self.egg_info)
  251. os.utime(self.egg_info, None)
  252. for ep in metadata.entry_points(group='egg_info.writers'):
  253. writer = ep.load()
  254. writer(self, ep.name, os.path.join(self.egg_info, ep.name))
  255. # Get rid of native_libs.txt if it was put there by older bdist_egg
  256. nl = os.path.join(self.egg_info, "native_libs.txt")
  257. if os.path.exists(nl):
  258. self.delete_file(nl)
  259. self.find_sources()
  260. def find_sources(self):
  261. """Generate SOURCES.txt manifest file"""
  262. manifest_filename = os.path.join(self.egg_info, "SOURCES.txt")
  263. mm = manifest_maker(self.distribution)
  264. mm.ignore_egg_info_dir = self.ignore_egg_info_in_manifest
  265. mm.manifest = manifest_filename
  266. mm.run()
  267. self.filelist = mm.filelist
  268. def check_broken_egg_info(self):
  269. bei = self.egg_name + '.egg-info'
  270. if self.egg_base != os.curdir:
  271. bei = os.path.join(self.egg_base, bei)
  272. if os.path.exists(bei):
  273. log.warn(
  274. "-" * 78 + '\n'
  275. "Note: Your current .egg-info directory has a '-' in its name;"
  276. '\nthis will not work correctly with "setup.py develop".\n\n'
  277. 'Please rename %s to %s to correct this problem.\n' + '-' * 78,
  278. bei, self.egg_info
  279. )
  280. self.broken_egg_info = self.egg_info
  281. self.egg_info = bei # make it work for now
  282. class FileList(_FileList):
  283. # Implementations of the various MANIFEST.in commands
  284. def __init__(self, warn=None, debug_print=None, ignore_egg_info_dir=False):
  285. super().__init__(warn, debug_print)
  286. self.ignore_egg_info_dir = ignore_egg_info_dir
  287. def process_template_line(self, line):
  288. # Parse the line: split it up, make sure the right number of words
  289. # is there, and return the relevant words. 'action' is always
  290. # defined: it's the first word of the line. Which of the other
  291. # three are defined depends on the action; it'll be either
  292. # patterns, (dir and patterns), or (dir_pattern).
  293. (action, patterns, dir, dir_pattern) = self._parse_template_line(line)
  294. action_map = {
  295. 'include': self.include,
  296. 'exclude': self.exclude,
  297. 'global-include': self.global_include,
  298. 'global-exclude': self.global_exclude,
  299. 'recursive-include': functools.partial(
  300. self.recursive_include, dir,
  301. ),
  302. 'recursive-exclude': functools.partial(
  303. self.recursive_exclude, dir,
  304. ),
  305. 'graft': self.graft,
  306. 'prune': self.prune,
  307. }
  308. log_map = {
  309. 'include': "warning: no files found matching '%s'",
  310. 'exclude': (
  311. "warning: no previously-included files found "
  312. "matching '%s'"
  313. ),
  314. 'global-include': (
  315. "warning: no files found matching '%s' "
  316. "anywhere in distribution"
  317. ),
  318. 'global-exclude': (
  319. "warning: no previously-included files matching "
  320. "'%s' found anywhere in distribution"
  321. ),
  322. 'recursive-include': (
  323. "warning: no files found matching '%s' "
  324. "under directory '%s'"
  325. ),
  326. 'recursive-exclude': (
  327. "warning: no previously-included files matching "
  328. "'%s' found under directory '%s'"
  329. ),
  330. 'graft': "warning: no directories found matching '%s'",
  331. 'prune': "no previously-included directories found matching '%s'",
  332. }
  333. try:
  334. process_action = action_map[action]
  335. except KeyError:
  336. raise DistutilsInternalError(
  337. "this cannot happen: invalid action '{action!s}'".
  338. format(action=action),
  339. )
  340. # OK, now we know that the action is valid and we have the
  341. # right number of words on the line for that action -- so we
  342. # can proceed with minimal error-checking.
  343. action_is_recursive = action.startswith('recursive-')
  344. if action in {'graft', 'prune'}:
  345. patterns = [dir_pattern]
  346. extra_log_args = (dir, ) if action_is_recursive else ()
  347. log_tmpl = log_map[action]
  348. self.debug_print(
  349. ' '.join(
  350. [action] +
  351. ([dir] if action_is_recursive else []) +
  352. patterns,
  353. )
  354. )
  355. for pattern in patterns:
  356. if not process_action(pattern):
  357. log.warn(log_tmpl, pattern, *extra_log_args)
  358. def _remove_files(self, predicate):
  359. """
  360. Remove all files from the file list that match the predicate.
  361. Return True if any matching files were removed
  362. """
  363. found = False
  364. for i in range(len(self.files) - 1, -1, -1):
  365. if predicate(self.files[i]):
  366. self.debug_print(" removing " + self.files[i])
  367. del self.files[i]
  368. found = True
  369. return found
  370. def include(self, pattern):
  371. """Include files that match 'pattern'."""
  372. found = [f for f in glob(pattern) if not os.path.isdir(f)]
  373. self.extend(found)
  374. return bool(found)
  375. def exclude(self, pattern):
  376. """Exclude files that match 'pattern'."""
  377. match = translate_pattern(pattern)
  378. return self._remove_files(match.match)
  379. def recursive_include(self, dir, pattern):
  380. """
  381. Include all files anywhere in 'dir/' that match the pattern.
  382. """
  383. full_pattern = os.path.join(dir, '**', pattern)
  384. found = [f for f in glob(full_pattern, recursive=True)
  385. if not os.path.isdir(f)]
  386. self.extend(found)
  387. return bool(found)
  388. def recursive_exclude(self, dir, pattern):
  389. """
  390. Exclude any file anywhere in 'dir/' that match the pattern.
  391. """
  392. match = translate_pattern(os.path.join(dir, '**', pattern))
  393. return self._remove_files(match.match)
  394. def graft(self, dir):
  395. """Include all files from 'dir/'."""
  396. found = [
  397. item
  398. for match_dir in glob(dir)
  399. for item in distutils.filelist.findall(match_dir)
  400. ]
  401. self.extend(found)
  402. return bool(found)
  403. def prune(self, dir):
  404. """Filter out files from 'dir/'."""
  405. match = translate_pattern(os.path.join(dir, '**'))
  406. return self._remove_files(match.match)
  407. def global_include(self, pattern):
  408. """
  409. Include all files anywhere in the current directory that match the
  410. pattern. This is very inefficient on large file trees.
  411. """
  412. if self.allfiles is None:
  413. self.findall()
  414. match = translate_pattern(os.path.join('**', pattern))
  415. found = [f for f in self.allfiles if match.match(f)]
  416. self.extend(found)
  417. return bool(found)
  418. def global_exclude(self, pattern):
  419. """
  420. Exclude all files anywhere that match the pattern.
  421. """
  422. match = translate_pattern(os.path.join('**', pattern))
  423. return self._remove_files(match.match)
  424. def append(self, item):
  425. if item.endswith('\r'): # Fix older sdists built on Windows
  426. item = item[:-1]
  427. path = convert_path(item)
  428. if self._safe_path(path):
  429. self.files.append(path)
  430. def extend(self, paths):
  431. self.files.extend(filter(self._safe_path, paths))
  432. def _repair(self):
  433. """
  434. Replace self.files with only safe paths
  435. Because some owners of FileList manipulate the underlying
  436. ``files`` attribute directly, this method must be called to
  437. repair those paths.
  438. """
  439. self.files = list(filter(self._safe_path, self.files))
  440. def _safe_path(self, path):
  441. enc_warn = "'%s' not %s encodable -- skipping"
  442. # To avoid accidental trans-codings errors, first to unicode
  443. u_path = unicode_utils.filesys_decode(path)
  444. if u_path is None:
  445. log.warn("'%s' in unexpected encoding -- skipping" % path)
  446. return False
  447. # Must ensure utf-8 encodability
  448. utf8_path = unicode_utils.try_encode(u_path, "utf-8")
  449. if utf8_path is None:
  450. log.warn(enc_warn, path, 'utf-8')
  451. return False
  452. try:
  453. # ignore egg-info paths
  454. is_egg_info = ".egg-info" in u_path or b".egg-info" in utf8_path
  455. if self.ignore_egg_info_dir and is_egg_info:
  456. return False
  457. # accept is either way checks out
  458. if os.path.exists(u_path) or os.path.exists(utf8_path):
  459. return True
  460. # this will catch any encode errors decoding u_path
  461. except UnicodeEncodeError:
  462. log.warn(enc_warn, path, sys.getfilesystemencoding())
  463. class manifest_maker(sdist):
  464. template = "MANIFEST.in"
  465. def initialize_options(self):
  466. self.use_defaults = 1
  467. self.prune = 1
  468. self.manifest_only = 1
  469. self.force_manifest = 1
  470. self.ignore_egg_info_dir = False
  471. def finalize_options(self):
  472. pass
  473. def run(self):
  474. self.filelist = FileList(ignore_egg_info_dir=self.ignore_egg_info_dir)
  475. if not os.path.exists(self.manifest):
  476. self.write_manifest() # it must exist so it'll get in the list
  477. self.add_defaults()
  478. if os.path.exists(self.template):
  479. self.read_template()
  480. self.add_license_files()
  481. self.prune_file_list()
  482. self.filelist.sort()
  483. self.filelist.remove_duplicates()
  484. self.write_manifest()
  485. def _manifest_normalize(self, path):
  486. path = unicode_utils.filesys_decode(path)
  487. return path.replace(os.sep, '/')
  488. def write_manifest(self):
  489. """
  490. Write the file list in 'self.filelist' to the manifest file
  491. named by 'self.manifest'.
  492. """
  493. self.filelist._repair()
  494. # Now _repairs should encodability, but not unicode
  495. files = [self._manifest_normalize(f) for f in self.filelist.files]
  496. msg = "writing manifest file '%s'" % self.manifest
  497. self.execute(write_file, (self.manifest, files), msg)
  498. def warn(self, msg):
  499. if not self._should_suppress_warning(msg):
  500. sdist.warn(self, msg)
  501. @staticmethod
  502. def _should_suppress_warning(msg):
  503. """
  504. suppress missing-file warnings from sdist
  505. """
  506. return re.match(r"standard file .*not found", msg)
  507. def add_defaults(self):
  508. sdist.add_defaults(self)
  509. self.filelist.append(self.template)
  510. self.filelist.append(self.manifest)
  511. rcfiles = list(walk_revctrl())
  512. if rcfiles:
  513. self.filelist.extend(rcfiles)
  514. elif os.path.exists(self.manifest):
  515. self.read_manifest()
  516. if os.path.exists("setup.py"):
  517. # setup.py should be included by default, even if it's not
  518. # the script called to create the sdist
  519. self.filelist.append("setup.py")
  520. ei_cmd = self.get_finalized_command('egg_info')
  521. self.filelist.graft(ei_cmd.egg_info)
  522. def add_license_files(self):
  523. license_files = self.distribution.metadata.license_files or []
  524. for lf in license_files:
  525. log.info("adding license file '%s'", lf)
  526. pass
  527. self.filelist.extend(license_files)
  528. def prune_file_list(self):
  529. build = self.get_finalized_command('build')
  530. base_dir = self.distribution.get_fullname()
  531. self.filelist.prune(build.build_base)
  532. self.filelist.prune(base_dir)
  533. sep = re.escape(os.sep)
  534. self.filelist.exclude_pattern(r'(^|' + sep + r')(RCS|CVS|\.svn)' + sep,
  535. is_regex=1)
  536. def _safe_data_files(self, build_py):
  537. """
  538. The parent class implementation of this method
  539. (``sdist``) will try to include data files, which
  540. might cause recursion problems when
  541. ``include_package_data=True``.
  542. Therefore, avoid triggering any attempt of
  543. analyzing/building the manifest again.
  544. """
  545. if hasattr(build_py, 'get_data_files_without_manifest'):
  546. return build_py.get_data_files_without_manifest()
  547. warnings.warn(
  548. "Custom 'build_py' does not implement "
  549. "'get_data_files_without_manifest'.\nPlease extend command classes"
  550. " from setuptools instead of distutils.",
  551. SetuptoolsDeprecationWarning
  552. )
  553. return build_py.get_data_files()
  554. def write_file(filename, contents):
  555. """Create a file with the specified name and write 'contents' (a
  556. sequence of strings without line terminators) to it.
  557. """
  558. contents = "\n".join(contents)
  559. # assuming the contents has been vetted for utf-8 encoding
  560. contents = contents.encode("utf-8")
  561. with open(filename, "wb") as f: # always write POSIX-style manifest
  562. f.write(contents)
  563. def write_pkg_info(cmd, basename, filename):
  564. log.info("writing %s", filename)
  565. if not cmd.dry_run:
  566. metadata = cmd.distribution.metadata
  567. metadata.version, oldver = cmd.egg_version, metadata.version
  568. metadata.name, oldname = cmd.egg_name, metadata.name
  569. try:
  570. # write unescaped data to PKG-INFO, so older pkg_resources
  571. # can still parse it
  572. metadata.write_pkg_info(cmd.egg_info)
  573. finally:
  574. metadata.name, metadata.version = oldname, oldver
  575. safe = getattr(cmd.distribution, 'zip_safe', None)
  576. bdist_egg.write_safety_flag(cmd.egg_info, safe)
  577. def warn_depends_obsolete(cmd, basename, filename):
  578. if os.path.exists(filename):
  579. log.warn(
  580. "WARNING: 'depends.txt' is not used by setuptools 0.6!\n"
  581. "Use the install_requires/extras_require setup() args instead."
  582. )
  583. def _write_requirements(stream, reqs):
  584. lines = yield_lines(reqs or ())
  585. def append_cr(line):
  586. return line + '\n'
  587. lines = map(append_cr, lines)
  588. stream.writelines(lines)
  589. def write_requirements(cmd, basename, filename):
  590. dist = cmd.distribution
  591. data = io.StringIO()
  592. _write_requirements(data, dist.install_requires)
  593. extras_require = dist.extras_require or {}
  594. for extra in sorted(extras_require):
  595. data.write('\n[{extra}]\n'.format(**vars()))
  596. _write_requirements(data, extras_require[extra])
  597. cmd.write_or_delete_file("requirements", filename, data.getvalue())
  598. def write_setup_requirements(cmd, basename, filename):
  599. data = io.StringIO()
  600. _write_requirements(data, cmd.distribution.setup_requires)
  601. cmd.write_or_delete_file("setup-requirements", filename, data.getvalue())
  602. def write_toplevel_names(cmd, basename, filename):
  603. pkgs = dict.fromkeys(
  604. [
  605. k.split('.', 1)[0]
  606. for k in cmd.distribution.iter_distribution_names()
  607. ]
  608. )
  609. cmd.write_file("top-level names", filename, '\n'.join(sorted(pkgs)) + '\n')
  610. def overwrite_arg(cmd, basename, filename):
  611. write_arg(cmd, basename, filename, True)
  612. def write_arg(cmd, basename, filename, force=False):
  613. argname = os.path.splitext(basename)[0]
  614. value = getattr(cmd.distribution, argname, None)
  615. if value is not None:
  616. value = '\n'.join(value) + '\n'
  617. cmd.write_or_delete_file(argname, filename, value, force)
  618. def write_entries(cmd, basename, filename):
  619. eps = _entry_points.load(cmd.distribution.entry_points)
  620. defn = _entry_points.render(eps)
  621. cmd.write_or_delete_file('entry points', filename, defn, True)
  622. def get_pkg_info_revision():
  623. """
  624. Get a -r### off of PKG-INFO Version in case this is an sdist of
  625. a subversion revision.
  626. """
  627. warnings.warn(
  628. "get_pkg_info_revision is deprecated.", EggInfoDeprecationWarning)
  629. if os.path.exists('PKG-INFO'):
  630. with io.open('PKG-INFO') as f:
  631. for line in f:
  632. match = re.match(r"Version:.*-r(\d+)\s*$", line)
  633. if match:
  634. return int(match.group(1))
  635. return 0
  636. class EggInfoDeprecationWarning(SetuptoolsDeprecationWarning):
  637. """Deprecated behavior warning for EggInfo, bypassing suppression."""