PageRenderTime 51ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/src/zc/buildout/easy_install.py

https://github.com/koodaamo/buildout
Python | 1388 lines | 1175 code | 128 blank | 85 comment | 165 complexity | 2e7ca2ecabd003f016bad1ad59ce8a19 MD5 | raw file
Possible License(s): GPL-2.0
  1. #############################################################################
  2. #
  3. # Copyright (c) 2005 Zope Foundation and Contributors.
  4. # All Rights Reserved.
  5. #
  6. # This software is subject to the provisions of the Zope Public License,
  7. # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
  8. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
  9. # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  10. # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
  11. # FOR A PARTICULAR PURPOSE.
  12. #
  13. ##############################################################################
  14. """Python easy_install API
  15. This module provides a high-level Python API for installing packages.
  16. It doesn't install scripts. It uses distribute and requires it to be
  17. installed.
  18. """
  19. import distutils.errors
  20. import glob
  21. import logging
  22. import os
  23. import pkg_resources
  24. import py_compile
  25. import re
  26. import setuptools.archive_util
  27. import setuptools.command.setopt
  28. import setuptools.package_index
  29. import shutil
  30. import subprocess
  31. import sys
  32. import tempfile
  33. import zc.buildout
  34. import zipimport
  35. _oprp = getattr(os.path, 'realpath', lambda path: path)
  36. def realpath(path):
  37. return os.path.normcase(os.path.abspath(_oprp(path)))
  38. default_index_url = os.environ.get(
  39. 'buildout-testing-index-url',
  40. 'http://pypi.python.org/simple',
  41. )
  42. logger = logging.getLogger('zc.buildout.easy_install')
  43. url_match = re.compile('[a-z0-9+.-]+://').match
  44. is_win32 = sys.platform == 'win32'
  45. is_jython = sys.platform.startswith('java')
  46. if is_jython:
  47. import java.lang.System
  48. jython_os_name = (java.lang.System.getProperties()['os.name']).lower()
  49. distribute_loc = pkg_resources.working_set.find(
  50. pkg_resources.Requirement.parse('distribute')
  51. ).location
  52. # Include buildout and distribute eggs in paths
  53. buildout_and_distribute_path = [
  54. distribute_loc,
  55. pkg_resources.working_set.find(
  56. pkg_resources.Requirement.parse('zc.buildout')).location,
  57. ]
  58. FILE_SCHEME = re.compile('file://', re.I).match
  59. class AllowHostsPackageIndex(setuptools.package_index.PackageIndex):
  60. """Will allow urls that are local to the system.
  61. No matter what is allow_hosts.
  62. """
  63. def url_ok(self, url, fatal=False):
  64. if FILE_SCHEME(url):
  65. return True
  66. return setuptools.package_index.PackageIndex.url_ok(self, url, False)
  67. _indexes = {}
  68. def _get_index(index_url, find_links, allow_hosts=('*',)):
  69. key = index_url, tuple(find_links)
  70. index = _indexes.get(key)
  71. if index is not None:
  72. return index
  73. if index_url is None:
  74. index_url = default_index_url
  75. if index_url.startswith('file://'):
  76. index_url = index_url[7:]
  77. index = AllowHostsPackageIndex(index_url, hosts=allow_hosts)
  78. if find_links:
  79. index.add_find_links(find_links)
  80. _indexes[key] = index
  81. return index
  82. clear_index_cache = _indexes.clear
  83. if is_win32:
  84. # work around spawn lamosity on windows
  85. # XXX need safe quoting (see the subproces.list2cmdline) and test
  86. def _safe_arg(arg):
  87. return '"%s"' % arg
  88. else:
  89. _safe_arg = str
  90. def call_subprocess(args, **kw):
  91. if subprocess.call(args, **kw) != 0:
  92. raise Exception(
  93. "Failed to run command:\n%s"
  94. % repr(args)[1:-1])
  95. _easy_install_cmd = 'from setuptools.command.easy_install import main; main()'
  96. class Installer:
  97. _versions = {}
  98. _download_cache = None
  99. _install_from_cache = False
  100. _prefer_final = True
  101. _use_dependency_links = True
  102. _allow_picked_versions = True
  103. def __init__(self,
  104. dest=None,
  105. links=(),
  106. index=None,
  107. executable=sys.executable,
  108. always_unzip=None, # Backward compat :/
  109. path=None,
  110. newest=True,
  111. versions=None,
  112. use_dependency_links=None,
  113. allow_hosts=('*',)
  114. ):
  115. assert executable == sys.executable, (executable, sys.executable)
  116. self._dest = dest
  117. self._allow_hosts = allow_hosts
  118. if self._install_from_cache:
  119. if not self._download_cache:
  120. raise ValueError("install_from_cache set to true with no"
  121. " download cache")
  122. links = ()
  123. index = 'file://' + self._download_cache
  124. if use_dependency_links is not None:
  125. self._use_dependency_links = use_dependency_links
  126. self._links = links = list(_fix_file_links(links))
  127. if self._download_cache and (self._download_cache not in links):
  128. links.insert(0, self._download_cache)
  129. self._index_url = index
  130. path = (path and path[:] or []) + buildout_and_distribute_path
  131. if dest is not None and dest not in path:
  132. path.insert(0, dest)
  133. self._path = path
  134. if self._dest is None:
  135. newest = False
  136. self._newest = newest
  137. self._env = pkg_resources.Environment(path)
  138. self._index = _get_index(index, links, self._allow_hosts)
  139. if versions is not None:
  140. self._versions = versions
  141. def _satisfied(self, req, source=None):
  142. dists = [dist for dist in self._env[req.project_name] if dist in req]
  143. if not dists:
  144. logger.debug('We have no distributions for %s that satisfies %r.',
  145. req.project_name, str(req))
  146. return None, self._obtain(req, source)
  147. # Note that dists are sorted from best to worst, as promised by
  148. # env.__getitem__
  149. for dist in dists:
  150. if (dist.precedence == pkg_resources.DEVELOP_DIST):
  151. logger.debug('We have a develop egg: %s', dist)
  152. return dist, None
  153. # Special common case, we have a specification for a single version:
  154. specs = req.specs
  155. if len(specs) == 1 and specs[0][0] == '==':
  156. logger.debug('We have the distribution that satisfies %r.',
  157. str(req))
  158. return dists[0], None
  159. if self._prefer_final:
  160. fdists = [dist for dist in dists
  161. if _final_version(dist.parsed_version)
  162. ]
  163. if fdists:
  164. # There are final dists, so only use those
  165. dists = fdists
  166. if not self._newest:
  167. # We don't need the newest, so we'll use the newest one we
  168. # find, which is the first returned by
  169. # Environment.__getitem__.
  170. return dists[0], None
  171. best_we_have = dists[0] # Because dists are sorted from best to worst
  172. # We have some installed distros. There might, theoretically, be
  173. # newer ones. Let's find out which ones are available and see if
  174. # any are newer. We only do this if we're willing to install
  175. # something, which is only true if dest is not None:
  176. if self._dest is not None:
  177. best_available = self._obtain(req, source)
  178. else:
  179. best_available = None
  180. if best_available is None:
  181. # That's a bit odd. There aren't any distros available.
  182. # We should use the best one we have that meets the requirement.
  183. logger.debug(
  184. 'There are no distros available that meet %r.\n'
  185. 'Using our best, %s.',
  186. str(req), best_available)
  187. return best_we_have, None
  188. if self._prefer_final:
  189. if _final_version(best_available.parsed_version):
  190. if _final_version(best_we_have.parsed_version):
  191. if (best_we_have.parsed_version
  192. <
  193. best_available.parsed_version
  194. ):
  195. return None, best_available
  196. else:
  197. return None, best_available
  198. else:
  199. if (not _final_version(best_we_have.parsed_version)
  200. and
  201. (best_we_have.parsed_version
  202. <
  203. best_available.parsed_version
  204. )
  205. ):
  206. return None, best_available
  207. else:
  208. if (best_we_have.parsed_version
  209. <
  210. best_available.parsed_version
  211. ):
  212. return None, best_available
  213. logger.debug(
  214. 'We have the best distribution that satisfies %r.',
  215. str(req))
  216. return best_we_have, None
  217. def _load_dist(self, dist):
  218. dists = pkg_resources.Environment(dist.location)[dist.project_name]
  219. assert len(dists) == 1
  220. return dists[0]
  221. def _call_easy_install(self, spec, ws, dest, dist):
  222. tmp = tempfile.mkdtemp(dir=dest)
  223. try:
  224. path = distribute_loc
  225. args = [sys.executable, '-c', _easy_install_cmd, '-mZUNxd', tmp]
  226. level = logger.getEffectiveLevel()
  227. if level > 0:
  228. args.append('-q')
  229. elif level < 0:
  230. args.append('-v')
  231. args.append(spec)
  232. if level <= logging.DEBUG:
  233. logger.debug('Running easy_install:\n"%s"\npath=%s\n',
  234. '" "'.join(args), path)
  235. sys.stdout.flush() # We want any pending output first
  236. exit_code = subprocess.call(
  237. list(args),
  238. env=dict(os.environ, PYTHONPATH=path))
  239. dists = []
  240. env = pkg_resources.Environment([tmp])
  241. for project in env:
  242. dists.extend(env[project])
  243. if exit_code:
  244. logger.error(
  245. "An error occured when trying to install %s. "
  246. "Look above this message for any errors that "
  247. "were output by easy_install.",
  248. dist)
  249. if not dists:
  250. raise zc.buildout.UserError("Couldn't install: %s" % dist)
  251. if len(dists) > 1:
  252. logger.warn("Installing %s\n"
  253. "caused multiple distributions to be installed:\n"
  254. "%s\n",
  255. dist, '\n'.join(map(str, dists)))
  256. else:
  257. d = dists[0]
  258. if d.project_name != dist.project_name:
  259. logger.warn("Installing %s\n"
  260. "Caused installation of a distribution:\n"
  261. "%s\n"
  262. "with a different project name.",
  263. dist, d)
  264. if d.version != dist.version:
  265. logger.warn("Installing %s\n"
  266. "Caused installation of a distribution:\n"
  267. "%s\n"
  268. "with a different version.",
  269. dist, d)
  270. result = []
  271. for d in dists:
  272. newloc = os.path.join(dest, os.path.basename(d.location))
  273. if os.path.exists(newloc):
  274. if os.path.isdir(newloc):
  275. shutil.rmtree(newloc)
  276. else:
  277. os.remove(newloc)
  278. os.rename(d.location, newloc)
  279. [d] = pkg_resources.Environment([newloc])[d.project_name]
  280. result.append(d)
  281. return result
  282. finally:
  283. shutil.rmtree(tmp)
  284. def _obtain(self, requirement, source=None):
  285. # initialize out index for this project:
  286. index = self._index
  287. if index.obtain(requirement) is None:
  288. # Nothing is available.
  289. return None
  290. # Filter the available dists for the requirement and source flag
  291. dists = [dist for dist in index[requirement.project_name]
  292. if ((dist in requirement)
  293. and
  294. ((not source) or
  295. (dist.precedence == pkg_resources.SOURCE_DIST)
  296. )
  297. )
  298. ]
  299. # If we prefer final dists, filter for final and use the
  300. # result if it is non empty.
  301. if self._prefer_final:
  302. fdists = [dist for dist in dists
  303. if _final_version(dist.parsed_version)
  304. ]
  305. if fdists:
  306. # There are final dists, so only use those
  307. dists = fdists
  308. # Now find the best one:
  309. best = []
  310. bestv = ()
  311. for dist in dists:
  312. distv = dist.parsed_version
  313. if distv > bestv:
  314. best = [dist]
  315. bestv = distv
  316. elif distv == bestv:
  317. best.append(dist)
  318. if not best:
  319. return None
  320. if len(best) == 1:
  321. return best[0]
  322. if self._download_cache:
  323. for dist in best:
  324. if (realpath(os.path.dirname(dist.location))
  325. ==
  326. self._download_cache
  327. ):
  328. return dist
  329. best.sort()
  330. return best[-1]
  331. def _fetch(self, dist, tmp, download_cache):
  332. if (download_cache
  333. and (realpath(os.path.dirname(dist.location)) == download_cache)
  334. ):
  335. return dist
  336. new_location = self._index.download(dist.location, tmp)
  337. if (download_cache
  338. and (realpath(new_location) == realpath(dist.location))
  339. and os.path.isfile(new_location)
  340. ):
  341. # distribute avoids making extra copies, but we want to copy
  342. # to the download cache
  343. shutil.copy2(new_location, tmp)
  344. new_location = os.path.join(tmp, os.path.basename(new_location))
  345. return dist.clone(location=new_location)
  346. def _get_dist(self, requirement, ws):
  347. __doing__ = 'Getting distribution for %r.', str(requirement)
  348. # Maybe an existing dist is already the best dist that satisfies the
  349. # requirement
  350. dist, avail = self._satisfied(requirement)
  351. if dist is None:
  352. if self._dest is not None:
  353. logger.info(*__doing__)
  354. # Retrieve the dist:
  355. if avail is None:
  356. self._index.obtain(requirement)
  357. raise MissingDistribution(requirement, ws)
  358. # We may overwrite distributions, so clear importer
  359. # cache.
  360. sys.path_importer_cache.clear()
  361. tmp = self._download_cache
  362. if tmp is None:
  363. tmp = tempfile.mkdtemp('get_dist')
  364. try:
  365. dist = self._fetch(avail, tmp, self._download_cache)
  366. if dist is None:
  367. raise zc.buildout.UserError(
  368. "Couln't download distribution %s." % avail)
  369. if dist.precedence == pkg_resources.EGG_DIST:
  370. # It's already an egg, just fetch it into the dest
  371. newloc = os.path.join(
  372. self._dest, os.path.basename(dist.location))
  373. if os.path.isdir(dist.location):
  374. # we got a directory. It must have been
  375. # obtained locally. Just copy it.
  376. shutil.copytree(dist.location, newloc)
  377. else:
  378. setuptools.archive_util.unpack_archive(
  379. dist.location, newloc)
  380. redo_pyc(newloc)
  381. # Getting the dist from the environment causes the
  382. # distribution meta data to be read. Cloning isn't
  383. # good enough.
  384. dists = pkg_resources.Environment([newloc])[
  385. dist.project_name]
  386. else:
  387. # It's some other kind of dist. We'll let easy_install
  388. # deal with it:
  389. dists = self._call_easy_install(
  390. dist.location, ws, self._dest, dist)
  391. for dist in dists:
  392. redo_pyc(dist.location)
  393. finally:
  394. if tmp != self._download_cache:
  395. shutil.rmtree(tmp)
  396. self._env.scan([self._dest])
  397. dist = self._env.best_match(requirement, ws)
  398. logger.info("Got %s.", dist)
  399. else:
  400. dists = [dist]
  401. for dist in dists:
  402. if (dist.has_metadata('dependency_links.txt')
  403. and not self._install_from_cache
  404. and self._use_dependency_links
  405. ):
  406. for link in dist.get_metadata_lines('dependency_links.txt'):
  407. link = link.strip()
  408. if link not in self._links:
  409. logger.debug('Adding find link %r from %s', link, dist)
  410. self._links.append(link)
  411. self._index = _get_index(self._index_url, self._links,
  412. self._allow_hosts)
  413. for dist in dists:
  414. # Check whether we picked a version and, if we did, report it:
  415. if not (
  416. dist.precedence == pkg_resources.DEVELOP_DIST
  417. or
  418. (len(requirement.specs) == 1
  419. and
  420. requirement.specs[0][0] == '==')
  421. ):
  422. logger.debug('Picked: %s = %s',
  423. dist.project_name, dist.version)
  424. if not self._allow_picked_versions:
  425. raise zc.buildout.UserError(
  426. 'Picked: %s = %s' % (dist.project_name, dist.version)
  427. )
  428. return dists
  429. def _maybe_add_distribute(self, ws, dist):
  430. if dist.has_metadata('namespace_packages.txt'):
  431. for r in dist.requires():
  432. if r.project_name in ('setuptools', 'distribute'):
  433. break
  434. else:
  435. # We have a namespace package but no requirement for distribute
  436. if dist.precedence == pkg_resources.DEVELOP_DIST:
  437. logger.warn(
  438. "Develop distribution: %s\n"
  439. "uses namespace packages but the distribution "
  440. "does not require distribute.",
  441. dist)
  442. requirement = self._constrain(
  443. pkg_resources.Requirement.parse('distribute')
  444. )
  445. if ws.find(requirement) is None:
  446. for dist in self._get_dist(requirement, ws):
  447. ws.add(dist)
  448. def _constrain(self, requirement):
  449. constraint = self._versions.get(requirement.project_name)
  450. if constraint:
  451. requirement = _constrained_requirement(constraint, requirement)
  452. return requirement
  453. def install(self, specs, working_set=None):
  454. logger.debug('Installing %s.', repr(specs)[1:-1])
  455. path = self._path
  456. dest = self._dest
  457. if dest is not None and dest not in path:
  458. path.insert(0, dest)
  459. requirements = [self._constrain(pkg_resources.Requirement.parse(spec))
  460. for spec in specs]
  461. if working_set is None:
  462. ws = pkg_resources.WorkingSet([])
  463. else:
  464. ws = working_set
  465. for requirement in requirements:
  466. for dist in self._get_dist(requirement, ws):
  467. ws.add(dist)
  468. self._maybe_add_distribute(ws, dist)
  469. # OK, we have the requested distributions and they're in the working
  470. # set, but they may have unmet requirements. We'll simply keep
  471. # trying to resolve requirements, adding missing requirements as they
  472. # are reported.
  473. #
  474. # Note that we don't pass in the environment, because we want
  475. # to look for new eggs unless what we have is the best that
  476. # matches the requirement.
  477. while 1:
  478. try:
  479. ws.resolve(requirements)
  480. except pkg_resources.DistributionNotFound:
  481. err = sys.exc_info()[1]
  482. [requirement] = err.args
  483. requirement = self._constrain(requirement)
  484. if dest:
  485. logger.debug('Getting required %r', str(requirement))
  486. else:
  487. logger.debug('Adding required %r', str(requirement))
  488. _log_requirement(ws, requirement)
  489. for dist in self._get_dist(requirement, ws):
  490. ws.add(dist)
  491. self._maybe_add_distribute(ws, dist)
  492. except pkg_resources.VersionConflict:
  493. err = sys.exc_info()[1]
  494. raise VersionConflict(err, ws)
  495. else:
  496. break
  497. return ws
  498. def build(self, spec, build_ext):
  499. requirement = self._constrain(pkg_resources.Requirement.parse(spec))
  500. dist, avail = self._satisfied(requirement, 1)
  501. if dist is not None:
  502. return [dist.location]
  503. # Retrieve the dist:
  504. if avail is None:
  505. raise zc.buildout.UserError(
  506. "Couldn't find a source distribution for %r."
  507. % str(requirement))
  508. logger.debug('Building %r', spec)
  509. tmp = self._download_cache
  510. if tmp is None:
  511. tmp = tempfile.mkdtemp('get_dist')
  512. try:
  513. dist = self._fetch(avail, tmp, self._download_cache)
  514. build_tmp = tempfile.mkdtemp('build')
  515. try:
  516. setuptools.archive_util.unpack_archive(dist.location,
  517. build_tmp)
  518. if os.path.exists(os.path.join(build_tmp, 'setup.py')):
  519. base = build_tmp
  520. else:
  521. setups = glob.glob(
  522. os.path.join(build_tmp, '*', 'setup.py'))
  523. if not setups:
  524. raise distutils.errors.DistutilsError(
  525. "Couldn't find a setup script in %s"
  526. % os.path.basename(dist.location)
  527. )
  528. if len(setups) > 1:
  529. raise distutils.errors.DistutilsError(
  530. "Multiple setup scripts in %s"
  531. % os.path.basename(dist.location)
  532. )
  533. base = os.path.dirname(setups[0])
  534. setup_cfg = os.path.join(base, 'setup.cfg')
  535. if not os.path.exists(setup_cfg):
  536. f = open(setup_cfg, 'w')
  537. f.close()
  538. setuptools.command.setopt.edit_config(
  539. setup_cfg, dict(build_ext=build_ext))
  540. dists = self._call_easy_install(
  541. base, pkg_resources.WorkingSet(),
  542. self._dest, dist)
  543. for dist in dists:
  544. redo_pyc(dist.location)
  545. return [dist.location for dist in dists]
  546. finally:
  547. shutil.rmtree(build_tmp)
  548. finally:
  549. if tmp != self._download_cache:
  550. shutil.rmtree(tmp)
  551. def default_versions(versions=None):
  552. old = Installer._versions
  553. if versions is not None:
  554. Installer._versions = versions
  555. return old
  556. def download_cache(path=-1):
  557. old = Installer._download_cache
  558. if path != -1:
  559. if path:
  560. path = realpath(path)
  561. Installer._download_cache = path
  562. return old
  563. def install_from_cache(setting=None):
  564. old = Installer._install_from_cache
  565. if setting is not None:
  566. Installer._install_from_cache = bool(setting)
  567. return old
  568. def prefer_final(setting=None):
  569. old = Installer._prefer_final
  570. if setting is not None:
  571. Installer._prefer_final = bool(setting)
  572. return old
  573. def use_dependency_links(setting=None):
  574. old = Installer._use_dependency_links
  575. if setting is not None:
  576. Installer._use_dependency_links = bool(setting)
  577. return old
  578. def allow_picked_versions(setting=None):
  579. old = Installer._allow_picked_versions
  580. if setting is not None:
  581. Installer._allow_picked_versions = bool(setting)
  582. return old
  583. def install(specs, dest,
  584. links=(), index=None,
  585. executable=sys.executable,
  586. always_unzip=None, # Backward compat :/
  587. path=None, working_set=None, newest=True, versions=None,
  588. use_dependency_links=None, allow_hosts=('*',),
  589. include_site_packages=None,
  590. allowed_eggs_from_site_packages=None,
  591. ):
  592. assert executable == sys.executable, (executable, sys.executable)
  593. assert include_site_packages is None
  594. assert allowed_eggs_from_site_packages is None
  595. installer = Installer(dest, links, index, sys.executable,
  596. always_unzip, path,
  597. newest, versions, use_dependency_links,
  598. allow_hosts=allow_hosts)
  599. return installer.install(specs, working_set)
  600. def build(spec, dest, build_ext,
  601. links=(), index=None,
  602. executable=sys.executable,
  603. path=None, newest=True, versions=None, allow_hosts=('*',)):
  604. assert executable == sys.executable, (executable, sys.executable)
  605. installer = Installer(dest, links, index, executable,
  606. True, path, newest,
  607. versions, allow_hosts=allow_hosts)
  608. return installer.build(spec, build_ext)
  609. def _rm(*paths):
  610. for path in paths:
  611. if os.path.isdir(path):
  612. shutil.rmtree(path)
  613. elif os.path.exists(path):
  614. os.remove(path)
  615. def _copyeggs(src, dest, suffix, undo):
  616. result = []
  617. undo.append(lambda : _rm(*result))
  618. for name in os.listdir(src):
  619. if name.endswith(suffix):
  620. new = os.path.join(dest, name)
  621. _rm(new)
  622. os.rename(os.path.join(src, name), new)
  623. result.append(new)
  624. assert len(result) == 1, str(result)
  625. undo.pop()
  626. return result[0]
  627. def develop(setup, dest,
  628. build_ext=None,
  629. executable=sys.executable):
  630. assert executable == sys.executable, (executable, sys.executable)
  631. if os.path.isdir(setup):
  632. directory = setup
  633. setup = os.path.join(directory, 'setup.py')
  634. else:
  635. directory = os.path.dirname(setup)
  636. undo = []
  637. try:
  638. if build_ext:
  639. setup_cfg = os.path.join(directory, 'setup.cfg')
  640. if os.path.exists(setup_cfg):
  641. os.rename(setup_cfg, setup_cfg+'-develop-aside')
  642. def restore_old_setup():
  643. if os.path.exists(setup_cfg):
  644. os.remove(setup_cfg)
  645. os.rename(setup_cfg+'-develop-aside', setup_cfg)
  646. undo.append(restore_old_setup)
  647. else:
  648. open(setup_cfg, 'w')
  649. undo.append(lambda: os.remove(setup_cfg))
  650. setuptools.command.setopt.edit_config(
  651. setup_cfg, dict(build_ext=build_ext))
  652. fd, tsetup = tempfile.mkstemp()
  653. undo.append(lambda: os.remove(tsetup))
  654. undo.append(lambda: os.close(fd))
  655. os.write(fd, (runsetup_template % dict(
  656. distribute=distribute_loc,
  657. setupdir=directory,
  658. setup=setup,
  659. __file__ = setup,
  660. )).encode())
  661. tmp3 = tempfile.mkdtemp('build', dir=dest)
  662. undo.append(lambda : shutil.rmtree(tmp3))
  663. args = [executable, tsetup, '-q', 'develop', '-mxN', '-d', tmp3]
  664. log_level = logger.getEffectiveLevel()
  665. if log_level <= 0:
  666. if log_level == 0:
  667. del args[2]
  668. else:
  669. args[2] == '-v'
  670. if log_level < logging.DEBUG:
  671. logger.debug("in: %r\n%s", directory, ' '.join(args))
  672. call_subprocess(args)
  673. return _copyeggs(tmp3, dest, '.egg-link', undo)
  674. finally:
  675. undo.reverse()
  676. [f() for f in undo]
  677. def working_set(specs, executable, path=None,
  678. include_site_packages=None,
  679. allowed_eggs_from_site_packages=None):
  680. # Backward compat:
  681. if path is None:
  682. path = executable
  683. else:
  684. assert executable == sys.executable, (executable, sys.executable)
  685. assert include_site_packages is None
  686. assert allowed_eggs_from_site_packages is None
  687. return install(specs, None, path=path)
  688. def scripts(reqs, working_set, executable, dest=None,
  689. scripts=None,
  690. extra_paths=(),
  691. arguments='',
  692. interpreter=None,
  693. initialization='',
  694. relative_paths=False,
  695. ):
  696. assert executable == sys.executable, (executable, sys.executable)
  697. path = [dist.location for dist in working_set]
  698. path.extend(extra_paths)
  699. # order preserving unique
  700. unique_path = []
  701. for p in path:
  702. if p not in unique_path:
  703. unique_path.append(p)
  704. path = list(map(realpath, unique_path))
  705. generated = []
  706. if isinstance(reqs, str):
  707. raise TypeError('Expected iterable of requirements or entry points,'
  708. ' got string.')
  709. if initialization:
  710. initialization = '\n'+initialization+'\n'
  711. entry_points = []
  712. distutils_scripts = []
  713. for req in reqs:
  714. if isinstance(req, str):
  715. req = pkg_resources.Requirement.parse(req)
  716. dist = working_set.find(req)
  717. # regular console_scripts entry points
  718. for name in pkg_resources.get_entry_map(dist, 'console_scripts'):
  719. entry_point = dist.get_entry_info('console_scripts', name)
  720. entry_points.append(
  721. (name, entry_point.module_name,
  722. '.'.join(entry_point.attrs))
  723. )
  724. # The metadata on "old-style" distutils scripts is not retained by
  725. # distutils/setuptools, except by placing the original scripts in
  726. # /EGG-INFO/scripts/.
  727. if dist.metadata_isdir('scripts'):
  728. for name in dist.metadata_listdir('scripts'):
  729. contents = dist.get_metadata('scripts/' + name)
  730. distutils_scripts.append((name, contents))
  731. else:
  732. entry_points.append(req)
  733. for name, module_name, attrs in entry_points:
  734. if scripts is not None:
  735. sname = scripts.get(name)
  736. if sname is None:
  737. continue
  738. else:
  739. sname = name
  740. sname = os.path.join(dest, sname)
  741. spath, rpsetup = _relative_path_and_setup(sname, path, relative_paths)
  742. generated.extend(
  743. _script(module_name, attrs, spath, sname, arguments,
  744. initialization, rpsetup)
  745. )
  746. for name, contents in distutils_scripts:
  747. if scripts is not None:
  748. sname = scripts.get(name)
  749. if sname is None:
  750. continue
  751. else:
  752. sname = name
  753. sname = os.path.join(dest, sname)
  754. spath, rpsetup = _relative_path_and_setup(sname, path, relative_paths)
  755. generated.extend(
  756. _distutils_script(spath, sname, contents, initialization, rpsetup)
  757. )
  758. if interpreter:
  759. sname = os.path.join(dest, interpreter)
  760. spath, rpsetup = _relative_path_and_setup(sname, path, relative_paths)
  761. generated.extend(_pyscript(spath, sname, rpsetup, initialization))
  762. return generated
  763. def _relative_path_and_setup(sname, path, relative_paths):
  764. if relative_paths:
  765. relative_paths = os.path.normcase(relative_paths)
  766. sname = os.path.normcase(os.path.abspath(sname))
  767. spath = ',\n '.join(
  768. [_relativitize(os.path.normcase(path_item), sname, relative_paths)
  769. for path_item in path]
  770. )
  771. rpsetup = relative_paths_setup
  772. for i in range(_relative_depth(relative_paths, sname)):
  773. rpsetup += "base = os.path.dirname(base)\n"
  774. else:
  775. spath = repr(path)[1:-1].replace(', ', ',\n ')
  776. rpsetup = ''
  777. return spath, rpsetup
  778. def _relative_depth(common, path):
  779. n = 0
  780. while 1:
  781. dirname = os.path.dirname(path)
  782. if dirname == path:
  783. raise AssertionError("dirname of %s is the same" % dirname)
  784. if dirname == common:
  785. break
  786. n += 1
  787. path = dirname
  788. return n
  789. def _relative_path(common, path):
  790. r = []
  791. while 1:
  792. dirname, basename = os.path.split(path)
  793. r.append(basename)
  794. if dirname == common:
  795. break
  796. if dirname == path:
  797. raise AssertionError("dirname of %s is the same" % dirname)
  798. path = dirname
  799. r.reverse()
  800. return os.path.join(*r)
  801. def _relativitize(path, script, relative_paths):
  802. if path == script:
  803. raise AssertionError("path == script")
  804. common = os.path.dirname(os.path.commonprefix([path, script]))
  805. if (common == relative_paths or
  806. common.startswith(os.path.join(relative_paths, ''))
  807. ):
  808. return "join(base, %r)" % _relative_path(common, path)
  809. else:
  810. return repr(path)
  811. relative_paths_setup = """
  812. import os
  813. join = os.path.join
  814. base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
  815. """
  816. def _script(module_name, attrs, path, dest, arguments, initialization, rsetup):
  817. generated = []
  818. script = dest
  819. if is_win32:
  820. dest += '-script.py'
  821. python = _safe_arg(sys.executable)
  822. contents = script_template % dict(
  823. python = python,
  824. path = path,
  825. module_name = module_name,
  826. attrs = attrs,
  827. arguments = arguments,
  828. initialization = initialization,
  829. relative_paths_setup = rsetup,
  830. )
  831. return _create_script(contents, dest)
  832. def _distutils_script(path, dest, script_content, initialization, rsetup):
  833. lines = script_content.splitlines(True)
  834. if not ('#!' in lines[0]) and ('python' in lines[0]):
  835. # The script doesn't follow distutil's rules. Ignore it.
  836. return []
  837. original_content = ''.join(lines[1:])
  838. python = _safe_arg(sys.executable)
  839. contents = distutils_script_template % dict(
  840. python = python,
  841. path = path,
  842. initialization = initialization,
  843. relative_paths_setup = rsetup,
  844. original_content = original_content
  845. )
  846. return _create_script(contents, dest)
  847. def _create_script(contents, dest):
  848. generated = []
  849. script = dest
  850. changed = not (os.path.exists(dest) and open(dest).read() == contents)
  851. if is_win32:
  852. # generate exe file and give the script a magic name:
  853. win32_exe = os.path.splitext(dest)[0] # remove ".py"
  854. if win32_exe.endswith('-script'):
  855. win32_exe = win32_exe[:-7] # remove "-script"
  856. win32_exe = win32_exe + '.exe' # add ".exe"
  857. new_data = pkg_resources.resource_string('setuptools', 'cli.exe')
  858. if (not os.path.exists(win32_exe) or
  859. (open(win32_exe, 'rb').read() != new_data)
  860. ):
  861. # Only write it if it's different.
  862. open(win32_exe, 'wb').write(new_data)
  863. generated.append(win32_exe)
  864. if changed:
  865. open(dest, 'w').write(contents)
  866. logger.info(
  867. "Generated script %r.",
  868. # Normalize for windows
  869. script.endswith('-script.py') and script[:-10] or script)
  870. try:
  871. os.chmod(dest, 493) # 0755
  872. except (AttributeError, os.error):
  873. pass
  874. generated.append(dest)
  875. return generated
  876. if is_jython and jython_os_name == 'linux':
  877. script_header = '#!/usr/bin/env %(python)s'
  878. else:
  879. script_header = '#!%(python)s'
  880. script_template = script_header + '''\
  881. %(relative_paths_setup)s
  882. import sys
  883. sys.path[0:0] = [
  884. %(path)s,
  885. ]
  886. %(initialization)s
  887. import %(module_name)s
  888. if __name__ == '__main__':
  889. sys.exit(%(module_name)s.%(attrs)s(%(arguments)s))
  890. '''
  891. distutils_script_template = script_header + '''\
  892. %(relative_paths_setup)s
  893. import sys
  894. sys.path[0:0] = [
  895. %(path)s,
  896. ]
  897. %(initialization)s
  898. %(original_content)s'''
  899. def _pyscript(path, dest, rsetup, initialization=''):
  900. generated = []
  901. script = dest
  902. if is_win32:
  903. dest += '-script.py'
  904. python = _safe_arg(sys.executable)
  905. contents = py_script_template % dict(
  906. python = python,
  907. path = path,
  908. relative_paths_setup = rsetup,
  909. initialization=initialization,
  910. )
  911. changed = not (os.path.exists(dest) and open(dest).read() == contents)
  912. if is_win32:
  913. # generate exe file and give the script a magic name:
  914. exe = script + '.exe'
  915. open(exe, 'wb').write(
  916. pkg_resources.resource_string('setuptools', 'cli.exe')
  917. )
  918. generated.append(exe)
  919. if changed:
  920. open(dest, 'w').write(contents)
  921. try:
  922. os.chmod(dest, 493) # 0755
  923. except (AttributeError, os.error):
  924. pass
  925. logger.info("Generated interpreter %r.", script)
  926. generated.append(dest)
  927. return generated
  928. py_script_template = script_header + '''\
  929. %(relative_paths_setup)s
  930. import sys
  931. sys.path[0:0] = [
  932. %(path)s,
  933. ]
  934. %(initialization)s
  935. _interactive = True
  936. if len(sys.argv) > 1:
  937. _options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:')
  938. _interactive = False
  939. for (_opt, _val) in _options:
  940. if _opt == '-i':
  941. _interactive = True
  942. elif _opt == '-c':
  943. exec(_val)
  944. elif _opt == '-m':
  945. sys.argv[1:] = _args
  946. _args = []
  947. __import__("runpy").run_module(
  948. _val, {}, "__main__", alter_sys=True)
  949. if _args:
  950. sys.argv[:] = _args
  951. __file__ = _args[0]
  952. del _options, _args
  953. __file__f = open(__file__)
  954. exec(compile(__file__f.read(), __file__, "exec"))
  955. __file__f.close(); del __file__f
  956. if _interactive:
  957. del _interactive
  958. __import__("code").interact(banner="", local=globals())
  959. '''
  960. runsetup_template = """
  961. import sys
  962. sys.path.insert(0, %(setupdir)r)
  963. sys.path.insert(0, %(distribute)r)
  964. import os, setuptools
  965. __file__ = %(__file__)r
  966. os.chdir(%(setupdir)r)
  967. sys.argv[0] = %(setup)r
  968. exec(compile(open(%(setup)r).read(), %(setup)r, 'exec'))
  969. """
  970. class VersionConflict(zc.buildout.UserError):
  971. def __init__(self, err, ws):
  972. ws = list(ws)
  973. ws.sort()
  974. self.err, self.ws = err, ws
  975. def __str__(self):
  976. existing_dist, req = self.err.args
  977. result = ["There is a version conflict.",
  978. "We already have: %s" % existing_dist,
  979. ]
  980. for dist in self.ws:
  981. if req in dist.requires():
  982. result.append("but %s requires %r." % (dist, str(req)))
  983. return '\n'.join(result)
  984. class MissingDistribution(zc.buildout.UserError):
  985. def __init__(self, req, ws):
  986. ws = list(ws)
  987. ws.sort()
  988. self.data = req, ws
  989. def __str__(self):
  990. req, ws = self.data
  991. return "Couldn't find a distribution for %r." % str(req)
  992. def _log_requirement(ws, req):
  993. if not logger.isEnabledFor(logging.DEBUG):
  994. # Sorting the working set and iterating over it's requirements
  995. # is expensive, so short cirtuit the work if it won't even be
  996. # logged. When profiling a simple buildout with 10 parts with
  997. # identical and large working sets, this resulted in a
  998. # decrease of run time from 93.411 to 15.068 seconds, about a
  999. # 6 fold improvement.
  1000. return
  1001. ws = list(ws)
  1002. ws.sort()
  1003. for dist in ws:
  1004. if req in dist.requires():
  1005. logger.debug(" required by %s." % dist)
  1006. def _fix_file_links(links):
  1007. for link in links:
  1008. if link.startswith('file://') and link[-1] != '/':
  1009. if os.path.isdir(link[7:]):
  1010. # work around excessive restriction in setuptools:
  1011. link += '/'
  1012. yield link
  1013. _final_parts = '*final-', '*final'
  1014. def _final_version(parsed_version):
  1015. for part in parsed_version:
  1016. if (part[:1] == '*') and (part not in _final_parts):
  1017. return False
  1018. return True
  1019. def redo_pyc(egg):
  1020. if not os.path.isdir(egg):
  1021. return
  1022. for dirpath, dirnames, filenames in os.walk(egg):
  1023. for filename in filenames:
  1024. if not filename.endswith('.py'):
  1025. continue
  1026. filepath = os.path.join(dirpath, filename)
  1027. if not (os.path.exists(filepath+'c')
  1028. or os.path.exists(filepath+'o')):
  1029. # If it wasn't compiled, it may not be compilable
  1030. continue
  1031. # OK, it looks like we should try to compile.
  1032. # Remove old files.
  1033. for suffix in 'co':
  1034. if os.path.exists(filepath+suffix):
  1035. os.remove(filepath+suffix)
  1036. # Compile under current optimization
  1037. try:
  1038. py_compile.compile(filepath)
  1039. except py_compile.PyCompileError:
  1040. logger.warning("Couldn't compile %s", filepath)
  1041. else:
  1042. # Recompile under other optimization. :)
  1043. args = [sys.executable]
  1044. if __debug__:
  1045. args.append('-O')
  1046. args.extend(['-m', 'py_compile', filepath])
  1047. call_subprocess(args)
  1048. def _constrained_requirement(constraint, requirement):
  1049. return pkg_resources.Requirement.parse(
  1050. "%s[%s]%s" % (
  1051. requirement.project_name,
  1052. ','.join(requirement.extras),
  1053. _constrained_requirement_constraint(constraint, requirement)
  1054. )
  1055. )
  1056. class IncompatibleConstraintError(zc.buildout.UserError):
  1057. """A specified version is incompatible with a given requirement.
  1058. """
  1059. def bad_constraint(constraint, requirement):
  1060. logger.error("The constraint, %s, is not consistent with the "
  1061. "requirement, %r.", constraint, str(requirement))
  1062. raise IncompatibleConstraintError("Bad constraint", constraint, requirement)
  1063. _parse_constraint = re.compile(r'([<>]=?)(\S+)').match
  1064. _comparef = {
  1065. '>' : lambda x, y: x > y,
  1066. '>=': lambda x, y: x >= y,
  1067. '<' : lambda x, y: x < y,
  1068. '<=': lambda x, y: x <= y,
  1069. }
  1070. _opop = {'<': '>', '>': '<'}
  1071. _opeqop = {'<': '>=', '>': '<='}
  1072. def _constrained_requirement_constraint(constraint, requirement):
  1073. # Simple cases:
  1074. # No specs tp merge with:
  1075. if not requirement.specs:
  1076. if not constraint[0] in '<=>':
  1077. constraint = '==' + constraint
  1078. return constraint
  1079. # Simple single-version constraint:
  1080. if constraint[0] not in '<>':
  1081. if constraint.startswith('='):
  1082. assert constraint.startswith('==')
  1083. constraint = constraint[2:]
  1084. if constraint in requirement:
  1085. return '=='+constraint
  1086. bad_constraint(constraint, requirement)
  1087. # OK, we have a complex constraint (<. <=, >=, or >) and specs.
  1088. # In many cases, the spec needs to filter constraints.
  1089. # In other cases, the constraints need to limit the constraint.
  1090. specs = requirement.specs
  1091. cop, cv = _parse_constraint(constraint).group(1, 2)
  1092. pcv = pkg_resources.parse_version(cv)
  1093. # Special case, all of the specs are == specs:
  1094. if not [op for (op, v) in specs if op != '==']:
  1095. # There aren't any non-== specs.
  1096. # See if any of the specs satisfy the constraint:
  1097. specs = [op+v for (op, v) in specs
  1098. if _comparef[cop](pkg_resources.parse_version(v), pcv)]
  1099. if specs:
  1100. return ','.join(specs)
  1101. bad_constraint(constraint, requirement)
  1102. cop0 = cop[0]
  1103. # Normalize specs by splitting >= and <= specs. We meed tp do this
  1104. # becaise these have really weird semantics. Also cache parsed
  1105. # versions, which we'll need for comparisons:
  1106. specs = []
  1107. for op, v in requirement.specs:
  1108. pv = pkg_resources.parse_version(v)
  1109. if op == _opeqop[cop0]:
  1110. specs.append((op[0], v, pv))
  1111. specs.append(('==', v, pv))
  1112. else:
  1113. specs.append((op, v, pv))
  1114. # Error if there are opposite specs that conflict with the constraint
  1115. # and there are no equal specs that satisfy the constraint:
  1116. if [v for (op, v, pv) in specs
  1117. if op == _opop[cop0] and _comparef[_opop[cop0]](pv, pcv)
  1118. ]:
  1119. eqspecs = [op+v for (op, v, pv) in specs
  1120. if _comparef[cop](pv, pcv)]
  1121. if eqspecs:
  1122. # OK, we do, use these:
  1123. return ','.join(eqspecs)
  1124. bad_constraint(constraint, requirement)
  1125. # We have a combination of range constraints and eq specs that
  1126. # satisfy the requirement.
  1127. # Return the constraint + the filtered specs
  1128. return ','.join(
  1129. op+v
  1130. for (op, v) in (
  1131. [(cop, cv)] +
  1132. [(op, v) for (op, v, pv) in specs if _comparef[cop](pv, pcv)]
  1133. )
  1134. )