PageRenderTime 63ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/src/zc/buildout/buildout.py

https://github.com/koodaamo/buildout
Python | 1777 lines | 1672 code | 63 blank | 42 comment | 103 complexity | ae5705289d8a648caf304c5e4eede2a3 MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. ##############################################################################
  2. #
  3. # Copyright (c) 2005-2009 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. """Buildout main script
  15. """
  16. from zc.buildout.rmtree import rmtree
  17. import zc.buildout.easy_install
  18. try:
  19. from hashlib import md5
  20. except ImportError:
  21. from md5 import md5
  22. try:
  23. from UserDict import DictMixin
  24. except ImportError:
  25. from collections import MutableMapping as DictMixin
  26. import zc.buildout.configparser
  27. import copy
  28. import distutils.errors
  29. import glob
  30. import itertools
  31. import logging
  32. import os
  33. import pkg_resources
  34. import re
  35. import shutil
  36. import subprocess
  37. import sys
  38. import tempfile
  39. import zc.buildout
  40. import zc.buildout.download
  41. def _print_options(sep=' ', end='\n', file=None):
  42. return sep, end, file
  43. def print_(*args, **kw):
  44. sep, end, file = _print_options(**kw)
  45. if file is None:
  46. file = sys.stdout
  47. file.write(sep.join(map(str, args))+end)
  48. realpath = zc.buildout.easy_install.realpath
  49. pkg_resources_loc = pkg_resources.working_set.find(
  50. pkg_resources.Requirement.parse('distribute')).location
  51. _isurl = re.compile('([a-zA-Z0-9+.-]+)://').match
  52. class MissingOption(zc.buildout.UserError, KeyError):
  53. """A required option was missing.
  54. """
  55. class MissingSection(zc.buildout.UserError, KeyError):
  56. """A required section is missing.
  57. """
  58. def __str__(self):
  59. return "The referenced section, %r, was not defined." % self.args[0]
  60. def _annotate_section(section, note):
  61. for key in section:
  62. section[key] = (section[key], note)
  63. return section
  64. def _annotate(data, note):
  65. for key in data:
  66. data[key] = _annotate_section(data[key], note)
  67. return data
  68. def _print_annotate(data):
  69. sections = list(data.keys())
  70. sections.sort()
  71. print_()
  72. print_("Annotated sections")
  73. print_("="*len("Annotated sections"))
  74. for section in sections:
  75. print_()
  76. print_('[%s]' % section)
  77. keys = list(data[section].keys())
  78. keys.sort()
  79. for key in keys:
  80. value, notes = data[section][key]
  81. keyvalue = "%s= %s" % (key, value)
  82. print_(keyvalue)
  83. line = ' '
  84. for note in notes.split():
  85. if note == '[+]':
  86. line = '+= '
  87. elif note == '[-]':
  88. line = '-= '
  89. else:
  90. print_(line, note)
  91. line = ' '
  92. print_()
  93. def _unannotate_section(section):
  94. for key in section:
  95. value, note = section[key]
  96. section[key] = value
  97. return section
  98. def _unannotate(data):
  99. for key in data:
  100. data[key] = _unannotate_section(data[key])
  101. return data
  102. _buildout_default_options = _annotate_section({
  103. 'allow-hosts': '*',
  104. 'allow-picked-versions': 'true',
  105. 'bin-directory': 'bin',
  106. 'develop-eggs-directory': 'develop-eggs',
  107. 'eggs-directory': 'eggs',
  108. 'executable': sys.executable,
  109. 'find-links': '',
  110. 'install-from-cache': 'false',
  111. 'installed': '.installed.cfg',
  112. 'log-format': '',
  113. 'log-level': 'INFO',
  114. 'newest': 'true',
  115. 'offline': 'false',
  116. 'parts-directory': 'parts',
  117. 'prefer-final': 'true',
  118. 'python': 'buildout',
  119. 'socket-timeout': '',
  120. 'use-dependency-links': 'true',
  121. }, 'DEFAULT_VALUE')
  122. class Buildout(DictMixin):
  123. def __init__(self, config_file, cloptions,
  124. user_defaults=True, windows_restart=False,
  125. command=None, args=()):
  126. __doing__ = 'Initializing.'
  127. self.__windows_restart = windows_restart
  128. # default options
  129. data = dict(buildout=_buildout_default_options.copy())
  130. self._buildout_dir = os.getcwd()
  131. if not _isurl(config_file):
  132. config_file = os.path.abspath(config_file)
  133. base = os.path.dirname(config_file)
  134. if not os.path.exists(config_file):
  135. if command == 'init':
  136. self._init_config(config_file, args)
  137. elif command == 'setup':
  138. # Sigh. This model of a buildout instance
  139. # with methods is breaking down. :(
  140. config_file = None
  141. data['buildout']['directory'] = ('.', 'COMPUTED_VALUE')
  142. else:
  143. raise zc.buildout.UserError(
  144. "Couldn't open %s" % config_file)
  145. elif command == 'init':
  146. raise zc.buildout.UserError(
  147. "%r already exists." % config_file)
  148. if config_file:
  149. data['buildout']['directory'] = (os.path.dirname(config_file),
  150. 'COMPUTED_VALUE')
  151. else:
  152. base = None
  153. cloptions = dict(
  154. (section, dict((option, (value, 'COMMAND_LINE_VALUE'))
  155. for (_, option, value) in v))
  156. for (section, v) in itertools.groupby(sorted(cloptions),
  157. lambda v: v[0])
  158. )
  159. override = cloptions.get('buildout', {}).copy()
  160. # load user defaults, which override defaults
  161. if user_defaults:
  162. user_config = os.path.join(os.path.expanduser('~'),
  163. '.buildout', 'default.cfg')
  164. if os.path.exists(user_config):
  165. _update(data, _open(os.path.dirname(user_config), user_config,
  166. [], data['buildout'].copy(), override,
  167. set()))
  168. # load configuration files
  169. if config_file:
  170. _update(data, _open(os.path.dirname(config_file), config_file, [],
  171. data['buildout'].copy(), override, set()))
  172. # apply command-line options
  173. _update(data, cloptions)
  174. self._annotated = copy.deepcopy(data)
  175. self._raw = _unannotate(data)
  176. self._data = {}
  177. self._parts = []
  178. # provide some defaults before options are parsed
  179. # because while parsing options those attributes might be
  180. # used already (Gottfried Ganssauge)
  181. buildout_section = data['buildout']
  182. # Try to make sure we have absolute paths for standard
  183. # directories. We do this before doing substitutions, in case
  184. # a one of these gets read by another section. If any
  185. # variable references are used though, we leave it as is in
  186. # _buildout_path.
  187. if 'directory' in buildout_section:
  188. self._buildout_dir = buildout_section['directory']
  189. for name in ('bin', 'parts', 'eggs', 'develop-eggs'):
  190. d = self._buildout_path(buildout_section[name+'-directory'])
  191. buildout_section[name+'-directory'] = d
  192. # Attributes on this buildout object shouldn't be used by
  193. # recipes in their __init__. It can cause bugs, because the
  194. # recipes will be instantiated below (``options = self['buildout']``)
  195. # before this has completed initializing. These attributes are
  196. # left behind for legacy support but recipe authors should
  197. # beware of using them. A better practice is for a recipe to
  198. # use the buildout['buildout'] options.
  199. links = buildout_section['find-links']
  200. self._links = links and links.split() or ()
  201. allow_hosts = buildout_section['allow-hosts'].split('\n')
  202. self._allow_hosts = tuple([host.strip() for host in allow_hosts
  203. if host.strip() != ''])
  204. self._logger = logging.getLogger('zc.buildout')
  205. self.offline = (buildout_section['offline'] == 'true')
  206. self.newest = (buildout_section['newest'] == 'true')
  207. ##################################################################
  208. ## WARNING!!!
  209. ## ALL ATTRIBUTES MUST HAVE REASONABLE DEFAULTS AT THIS POINT
  210. ## OTHERWISE ATTRIBUTEERRORS MIGHT HAPPEN ANY TIME FROM RECIPES.
  211. ## RECIPES SHOULD GENERALLY USE buildout['buildout'] OPTIONS, NOT
  212. ## BUILDOUT ATTRIBUTES.
  213. ##################################################################
  214. # initialize some attrs and buildout directories.
  215. options = self['buildout']
  216. # now reinitialize
  217. links = options.get('find-links', '')
  218. self._links = links and links.split() or ()
  219. allow_hosts = options['allow-hosts'].split('\n')
  220. self._allow_hosts = tuple([host.strip() for host in allow_hosts
  221. if host.strip() != ''])
  222. self._buildout_dir = options['directory']
  223. # Make sure we have absolute paths for standard directories. We do this
  224. # a second time here in case someone overrode these in their configs.
  225. for name in ('bin', 'parts', 'eggs', 'develop-eggs'):
  226. d = self._buildout_path(options[name+'-directory'])
  227. options[name+'-directory'] = d
  228. if options['installed']:
  229. options['installed'] = os.path.join(options['directory'],
  230. options['installed'])
  231. self._setup_logging()
  232. self._setup_socket_timeout()
  233. offline = options['offline']
  234. if offline not in ('true', 'false'):
  235. self._error('Invalid value for offline option: %s', offline)
  236. self.offline = (offline == 'true')
  237. if self.offline:
  238. newest = options['newest'] = 'false'
  239. else:
  240. newest = options['newest']
  241. if newest not in ('true', 'false'):
  242. self._error('Invalid value for newest option: %s', newest)
  243. self.newest = (newest == 'true')
  244. versions = {'zc.recipe.egg': '>=2.0.0a3'}
  245. self.versions = versions
  246. versions_section = options.get('versions')
  247. if versions_section:
  248. versions.update(dict(self[versions_section]))
  249. zc.buildout.easy_install.default_versions(versions)
  250. prefer_final = options['prefer-final']
  251. if prefer_final not in ('true', 'false'):
  252. self._error('Invalid value for prefer-final option: %s',
  253. prefer_final)
  254. zc.buildout.easy_install.prefer_final(prefer_final=='true')
  255. use_dependency_links = options['use-dependency-links']
  256. if use_dependency_links not in ('true', 'false'):
  257. self._error('Invalid value for use-dependency-links option: %s',
  258. use_dependency_links)
  259. zc.buildout.easy_install.use_dependency_links(
  260. use_dependency_links == 'true')
  261. allow_picked_versions = options['allow-picked-versions']
  262. if allow_picked_versions not in ('true', 'false'):
  263. self._error('Invalid value for allow-picked-versions option: %s',
  264. allow_picked_versions)
  265. zc.buildout.easy_install.allow_picked_versions(
  266. allow_picked_versions == 'true')
  267. download_cache = options.get('download-cache')
  268. if download_cache:
  269. download_cache = os.path.join(options['directory'], download_cache)
  270. if not os.path.isdir(download_cache):
  271. raise zc.buildout.UserError(
  272. 'The specified download cache:\n'
  273. '%r\n'
  274. "Doesn't exist.\n"
  275. % download_cache)
  276. download_cache = os.path.join(download_cache, 'dist')
  277. if not os.path.isdir(download_cache):
  278. os.mkdir(download_cache)
  279. zc.buildout.easy_install.download_cache(download_cache)
  280. install_from_cache = options['install-from-cache']
  281. if install_from_cache not in ('true', 'false'):
  282. self._error('Invalid value for install-from-cache option: %s',
  283. install_from_cache)
  284. zc.buildout.easy_install.install_from_cache(
  285. install_from_cache=='true')
  286. # "Use" each of the defaults so they aren't reported as unused options.
  287. for name in _buildout_default_options:
  288. options[name]
  289. # Do the same for extends-cache which is not among the defaults but
  290. # wasn't recognized as having been used since it was used before
  291. # tracking was turned on.
  292. options.get('extends-cache')
  293. os.chdir(options['directory'])
  294. def _buildout_path(self, name):
  295. if '${' in name:
  296. return name
  297. return os.path.join(self._buildout_dir, name)
  298. def bootstrap(self, args):
  299. __doing__ = 'Bootstrapping.'
  300. self._setup_directories()
  301. # Now copy buildout and distribute eggs, and record destination eggs:
  302. entries = []
  303. for name in 'distribute', 'zc.buildout':
  304. r = pkg_resources.Requirement.parse(name)
  305. dist = pkg_resources.working_set.find(r)
  306. if dist.precedence == pkg_resources.DEVELOP_DIST:
  307. dest = os.path.join(self['buildout']['develop-eggs-directory'],
  308. name+'.egg-link')
  309. open(dest, 'w').write(dist.location)
  310. entries.append(dist.location)
  311. else:
  312. dest = os.path.join(self['buildout']['eggs-directory'],
  313. os.path.basename(dist.location))
  314. entries.append(dest)
  315. if not os.path.exists(dest):
  316. if os.path.isdir(dist.location):
  317. shutil.copytree(dist.location, dest)
  318. else:
  319. shutil.copy2(dist.location, dest)
  320. # Create buildout script
  321. ws = pkg_resources.WorkingSet(entries)
  322. ws.require('zc.buildout')
  323. zc.buildout.easy_install.scripts(
  324. ['zc.buildout'], ws, sys.executable,
  325. self['buildout']['bin-directory'])
  326. def _init_config(self, config_file, args):
  327. print_('Creating %r.' % config_file)
  328. f = open(config_file, 'w')
  329. sep = re.compile(r'[\\/]')
  330. if args:
  331. eggs = '\n '.join(a for a in args if not sep.search(a))
  332. sepsub = os.path.sep == '/' and '/' or re.escape(os.path.sep)
  333. paths = '\n '.join(
  334. sep.sub(sepsub, a)
  335. for a in args if sep.search(a))
  336. f.write('[buildout]\n'
  337. 'parts = py\n'
  338. '\n'
  339. '[py]\n'
  340. 'recipe = zc.recipe.egg\n'
  341. 'interpreter = py\n'
  342. 'eggs =\n'
  343. )
  344. if eggs:
  345. f.write(' %s\n' % eggs)
  346. if paths:
  347. f.write('extra-paths =\n %s\n' % paths)
  348. for p in [a for a in args if sep.search(a)]:
  349. if not os.path.exists(p):
  350. os.mkdir(p)
  351. else:
  352. f.write('[buildout]\nparts =\n')
  353. f.close()
  354. def init(self, args):
  355. self.bootstrap(())
  356. if args:
  357. self.install(())
  358. def install(self, install_args):
  359. __doing__ = 'Installing.'
  360. self._load_extensions()
  361. self._setup_directories()
  362. # Add develop-eggs directory to path so that it gets searched
  363. # for eggs:
  364. sys.path.insert(0, self['buildout']['develop-eggs-directory'])
  365. # Check for updates. This could cause the process to be rstarted
  366. self._maybe_upgrade()
  367. # load installed data
  368. (installed_part_options, installed_exists
  369. )= self._read_installed_part_options()
  370. # Remove old develop eggs
  371. self._uninstall(
  372. installed_part_options['buildout'].get(
  373. 'installed_develop_eggs', '')
  374. )
  375. # Build develop eggs
  376. installed_develop_eggs = self._develop()
  377. installed_part_options['buildout']['installed_develop_eggs'
  378. ] = installed_develop_eggs
  379. if installed_exists:
  380. self._update_installed(
  381. installed_develop_eggs=installed_develop_eggs)
  382. # get configured and installed part lists
  383. conf_parts = self['buildout']['parts']
  384. conf_parts = conf_parts and conf_parts.split() or []
  385. installed_parts = installed_part_options['buildout']['parts']
  386. installed_parts = installed_parts and installed_parts.split() or []
  387. if install_args:
  388. install_parts = install_args
  389. uninstall_missing = False
  390. else:
  391. install_parts = conf_parts
  392. uninstall_missing = True
  393. # load and initialize recipes
  394. [self[part]['recipe'] for part in install_parts]
  395. if not install_args:
  396. install_parts = self._parts
  397. if self._log_level < logging.DEBUG:
  398. sections = list(self)
  399. sections.sort()
  400. print_()
  401. print_('Configuration data:')
  402. for section in self._data:
  403. _save_options(section, self[section], sys.stdout)
  404. print_()
  405. # compute new part recipe signatures
  406. self._compute_part_signatures(install_parts)
  407. # uninstall parts that are no-longer used or who's configs
  408. # have changed
  409. for part in reversed(installed_parts):
  410. if part in install_parts:
  411. old_options = installed_part_options[part].copy()
  412. installed_files = old_options.pop('__buildout_installed__')
  413. new_options = self.get(part)
  414. if old_options == new_options:
  415. # The options are the same, but are all of the
  416. # installed files still there? If not, we should
  417. # reinstall.
  418. if not installed_files:
  419. continue
  420. for f in installed_files.split('\n'):
  421. if not os.path.exists(self._buildout_path(f)):
  422. break
  423. else:
  424. continue
  425. # output debugging info
  426. if self._logger.getEffectiveLevel() < logging.DEBUG:
  427. for k in old_options:
  428. if k not in new_options:
  429. self._logger.debug("Part %s, dropped option %s.",
  430. part, k)
  431. elif old_options[k] != new_options[k]:
  432. self._logger.debug(
  433. "Part %s, option %s changed:\n%r != %r",
  434. part, k, new_options[k], old_options[k],
  435. )
  436. for k in new_options:
  437. if k not in old_options:
  438. self._logger.debug("Part %s, new option %s.",
  439. part, k)
  440. elif not uninstall_missing:
  441. continue
  442. self._uninstall_part(part, installed_part_options)
  443. installed_parts = [p for p in installed_parts if p != part]
  444. if installed_exists:
  445. self._update_installed(parts=' '.join(installed_parts))
  446. # Check for unused buildout options:
  447. _check_for_unused_options_in_section(self, 'buildout')
  448. # install new parts
  449. for part in install_parts:
  450. signature = self[part].pop('__buildout_signature__')
  451. saved_options = self[part].copy()
  452. recipe = self[part].recipe
  453. if part in installed_parts: # update
  454. need_to_save_installed = False
  455. __doing__ = 'Updating %s.', part
  456. self._logger.info(*__doing__)
  457. old_options = installed_part_options[part]
  458. old_installed_files = old_options['__buildout_installed__']
  459. try:
  460. update = recipe.update
  461. except AttributeError:
  462. update = recipe.install
  463. self._logger.warning(
  464. "The recipe for %s doesn't define an update "
  465. "method. Using its install method.",
  466. part)
  467. try:
  468. installed_files = self[part]._call(update)
  469. except:
  470. installed_parts.remove(part)
  471. self._uninstall(old_installed_files)
  472. if installed_exists:
  473. self._update_installed(
  474. parts=' '.join(installed_parts))
  475. raise
  476. old_installed_files = old_installed_files.split('\n')
  477. if installed_files is None:
  478. installed_files = old_installed_files
  479. else:
  480. if isinstance(installed_files, str):
  481. installed_files = [installed_files]
  482. else:
  483. installed_files = list(installed_files)
  484. need_to_save_installed = [
  485. p for p in installed_files
  486. if p not in old_installed_files]
  487. if need_to_save_installed:
  488. installed_files = (old_installed_files
  489. + need_to_save_installed)
  490. else: # install
  491. need_to_save_installed = True
  492. __doing__ = 'Installing %s.', part
  493. self._logger.info(*__doing__)
  494. installed_files = self[part]._call(recipe.install)
  495. if installed_files is None:
  496. self._logger.warning(
  497. "The %s install returned None. A path or "
  498. "iterable os paths should be returned.",
  499. part)
  500. installed_files = ()
  501. elif isinstance(installed_files, str):
  502. installed_files = [installed_files]
  503. else:
  504. installed_files = list(installed_files)
  505. installed_part_options[part] = saved_options
  506. saved_options['__buildout_installed__'
  507. ] = '\n'.join(installed_files)
  508. saved_options['__buildout_signature__'] = signature
  509. installed_parts = [p for p in installed_parts if p != part]
  510. installed_parts.append(part)
  511. _check_for_unused_options_in_section(self, part)
  512. if need_to_save_installed:
  513. installed_part_options['buildout']['parts'] = (
  514. ' '.join(installed_parts))
  515. self._save_installed_options(installed_part_options)
  516. installed_exists = True
  517. else:
  518. assert installed_exists
  519. self._update_installed(parts=' '.join(installed_parts))
  520. if installed_develop_eggs:
  521. if not installed_exists:
  522. self._save_installed_options(installed_part_options)
  523. elif (not installed_parts) and installed_exists:
  524. os.remove(self['buildout']['installed'])
  525. self._unload_extensions()
  526. def _update_installed(self, **buildout_options):
  527. installed = self['buildout']['installed']
  528. f = open(installed, 'a')
  529. f.write('\n[buildout]\n')
  530. for option, value in list(buildout_options.items()):
  531. _save_option(option, value, f)
  532. f.close()
  533. def _uninstall_part(self, part, installed_part_options):
  534. # ununstall part
  535. __doing__ = 'Uninstalling %s.', part
  536. self._logger.info(*__doing__)
  537. # run uinstall recipe
  538. recipe, entry = _recipe(installed_part_options[part])
  539. try:
  540. uninstaller = _install_and_load(
  541. recipe, 'zc.buildout.uninstall', entry, self)
  542. self._logger.info('Running uninstall recipe.')
  543. uninstaller(part, installed_part_options[part])
  544. except (ImportError, pkg_resources.DistributionNotFound):
  545. pass
  546. # remove created files and directories
  547. self._uninstall(
  548. installed_part_options[part]['__buildout_installed__'])
  549. def _setup_directories(self):
  550. __doing__ = 'Setting up buildout directories'
  551. # Create buildout directories
  552. for name in ('bin', 'parts', 'eggs', 'develop-eggs'):
  553. d = self['buildout'][name+'-directory']
  554. if not os.path.exists(d):
  555. self._logger.info('Creating directory %r.', d)
  556. os.mkdir(d)
  557. def _develop(self):
  558. """Install sources by running setup.py develop on them
  559. """
  560. __doing__ = 'Processing directories listed in the develop option'
  561. develop = self['buildout'].get('develop')
  562. if not develop:
  563. return ''
  564. dest = self['buildout']['develop-eggs-directory']
  565. old_files = os.listdir(dest)
  566. env = dict(os.environ, PYTHONPATH=pkg_resources_loc)
  567. here = os.getcwd()
  568. try:
  569. try:
  570. for setup in develop.split():
  571. setup = self._buildout_path(setup)
  572. files = glob.glob(setup)
  573. if not files:
  574. self._logger.warn("Couldn't develop %r (not found)",
  575. setup)
  576. else:
  577. files.sort()
  578. for setup in files:
  579. self._logger.info("Develop: %r", setup)
  580. __doing__ = 'Processing develop directory %r.', setup
  581. zc.buildout.easy_install.develop(setup, dest)
  582. except:
  583. # if we had an error, we need to roll back changes, by
  584. # removing any files we created.
  585. self._sanity_check_develop_eggs_files(dest, old_files)
  586. self._uninstall('\n'.join(
  587. [os.path.join(dest, f)
  588. for f in os.listdir(dest)
  589. if f not in old_files
  590. ]))
  591. raise
  592. else:
  593. self._sanity_check_develop_eggs_files(dest, old_files)
  594. return '\n'.join([os.path.join(dest, f)
  595. for f in os.listdir(dest)
  596. if f not in old_files
  597. ])
  598. finally:
  599. os.chdir(here)
  600. def _sanity_check_develop_eggs_files(self, dest, old_files):
  601. for f in os.listdir(dest):
  602. if f in old_files:
  603. continue
  604. if not (os.path.isfile(os.path.join(dest, f))
  605. and f.endswith('.egg-link')):
  606. self._logger.warning(
  607. "Unexpected entry, %r, in develop-eggs directory.", f)
  608. def _compute_part_signatures(self, parts):
  609. # Compute recipe signature and add to options
  610. for part in parts:
  611. options = self.get(part)
  612. if options is None:
  613. options = self[part] = {}
  614. recipe, entry = _recipe(options)
  615. req = pkg_resources.Requirement.parse(recipe)
  616. sig = _dists_sig(pkg_resources.working_set.resolve([req]))
  617. options['__buildout_signature__'] = ' '.join(sig)
  618. def _read_installed_part_options(self):
  619. old = self['buildout']['installed']
  620. if old and os.path.isfile(old):
  621. fp = open(old)
  622. sections = zc.buildout.configparser.parse(fp, old)
  623. fp.close()
  624. result = {}
  625. for section, options in sections.items():
  626. for option, value in options.items():
  627. if '%(' in value:
  628. for k, v in _spacey_defaults:
  629. value = value.replace(k, v)
  630. options[option] = value
  631. result[section] = Options(self, section, options)
  632. return result, True
  633. else:
  634. return ({'buildout': Options(self, 'buildout', {'parts': ''})},
  635. False,
  636. )
  637. def _uninstall(self, installed):
  638. for f in installed.split('\n'):
  639. if not f:
  640. continue
  641. f = self._buildout_path(f)
  642. if os.path.isfile(f) or os.path.islink(f):
  643. try:
  644. os.remove(f)
  645. except OSError:
  646. if not (
  647. sys.platform == 'win32' and
  648. (realpath(os.path.join(os.path.dirname(sys.argv[0]),
  649. 'buildout.exe'))
  650. ==
  651. realpath(f)
  652. )
  653. # Sigh. This is the exectable used to run the buildout
  654. # and, of course, it's in use. Leave it.
  655. ):
  656. raise
  657. elif os.path.isdir(f):
  658. rmtree(f)
  659. def _install(self, part):
  660. options = self[part]
  661. recipe, entry = _recipe(options)
  662. recipe_class = pkg_resources.load_entry_point(
  663. recipe, 'zc.buildout', entry)
  664. installed = recipe_class(self, part, options).install()
  665. if installed is None:
  666. installed = []
  667. elif isinstance(installed, str):
  668. installed = [installed]
  669. base = self._buildout_path('')
  670. installed = [d.startswith(base) and d[len(base):] or d
  671. for d in installed]
  672. return ' '.join(installed)
  673. def _save_installed_options(self, installed_options):
  674. installed = self['buildout']['installed']
  675. if not installed:
  676. return
  677. f = open(installed, 'w')
  678. _save_options('buildout', installed_options['buildout'], f)
  679. for part in installed_options['buildout']['parts'].split():
  680. print_(file=f)
  681. _save_options(part, installed_options[part], f)
  682. f.close()
  683. def _error(self, message, *args):
  684. raise zc.buildout.UserError(message % args)
  685. def _setup_socket_timeout(self):
  686. timeout = self['buildout']['socket-timeout']
  687. if timeout != '':
  688. try:
  689. timeout = int(timeout)
  690. import socket
  691. self._logger.info('Setting socket time out to %d seconds.', timeout)
  692. socket.setdefaulttimeout(timeout)
  693. except ValueError:
  694. self._logger.warning("Default socket timeout is used !\n"
  695. "Value in configuration is not numeric: [%s].\n",
  696. timeout)
  697. def _setup_logging(self):
  698. root_logger = logging.getLogger()
  699. self._logger = logging.getLogger('zc.buildout')
  700. handler = logging.StreamHandler(sys.stdout)
  701. log_format = self['buildout']['log-format']
  702. if not log_format:
  703. # No format specified. Use different formatter for buildout
  704. # and other modules, showing logger name except for buildout
  705. log_format = '%(name)s: %(message)s'
  706. buildout_handler = logging.StreamHandler(sys.stdout)
  707. buildout_handler.setFormatter(logging.Formatter('%(message)s'))
  708. self._logger.propagate = False
  709. self._logger.addHandler(buildout_handler)
  710. handler.setFormatter(logging.Formatter(log_format))
  711. root_logger.addHandler(handler)
  712. level = self['buildout']['log-level']
  713. if level in ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'):
  714. level = getattr(logging, level)
  715. else:
  716. try:
  717. level = int(level)
  718. except ValueError:
  719. self._error("Invalid logging level %s", level)
  720. verbosity = self['buildout'].get('verbosity', 0)
  721. try:
  722. verbosity = int(verbosity)
  723. except ValueError:
  724. self._error("Invalid verbosity %s", verbosity)
  725. level -= verbosity
  726. root_logger.setLevel(level)
  727. self._log_level = level
  728. def _maybe_upgrade(self):
  729. # See if buildout or distribute need to be upgraded.
  730. # If they do, do the upgrade and restart the buildout process.
  731. __doing__ = 'Checking for upgrades.'
  732. if not self.newest:
  733. return
  734. # Prevent downgrading due to prefer-final:
  735. options = self['buildout']
  736. if not ('zc.buildout-version' in options
  737. or
  738. 'zc.buildout' in self.versions):
  739. v = pkg_resources.working_set.find(
  740. pkg_resources.Requirement.parse('zc.buildout')
  741. ).version
  742. options['zc.buildout-version'] = '>=' + v
  743. ws = zc.buildout.easy_install.install(
  744. [
  745. (spec + ' ' + self['buildout'].get(spec+'-version', '')).strip()
  746. for spec in ('zc.buildout', 'distribute')
  747. ],
  748. self['buildout']['eggs-directory'],
  749. links = self['buildout'].get('find-links', '').split(),
  750. index = self['buildout'].get('index'),
  751. path = [self['buildout']['develop-eggs-directory']],
  752. allow_hosts = self._allow_hosts
  753. )
  754. upgraded = []
  755. for project in 'zc.buildout', 'distribute':
  756. req = pkg_resources.Requirement.parse(project)
  757. project_location = pkg_resources.working_set.find(req).location
  758. if ws.find(req).location != project_location:
  759. upgraded.append(ws.find(req))
  760. if not upgraded:
  761. return
  762. __doing__ = 'Upgrading.'
  763. should_run = realpath(
  764. os.path.join(os.path.abspath(self['buildout']['bin-directory']),
  765. 'buildout')
  766. )
  767. if sys.platform == 'win32':
  768. should_run += '-script.py'
  769. if (realpath(os.path.abspath(sys.argv[0])) != should_run):
  770. self._logger.debug("Running %r.", realpath(sys.argv[0]))
  771. self._logger.debug("Local buildout is %r.", should_run)
  772. self._logger.warn("Not upgrading because not running a local "
  773. "buildout command.")
  774. return
  775. if sys.platform == 'win32' and not self.__windows_restart:
  776. args = list(map(zc.buildout.easy_install._safe_arg, sys.argv))
  777. args.insert(1, '-W')
  778. if not __debug__:
  779. args.insert(0, '-O')
  780. args.insert(0, zc.buildout.easy_install._safe_arg (sys.executable))
  781. os.execv(sys.executable, args)
  782. self._logger.info("Upgraded:\n %s;\nrestarting.",
  783. ",\n ".join([("%s version %s"
  784. % (dist.project_name, dist.version)
  785. )
  786. for dist in upgraded
  787. ]
  788. ),
  789. )
  790. # the new dist is different, so we've upgraded.
  791. # Update the scripts and return True
  792. zc.buildout.easy_install.scripts(
  793. ['zc.buildout'], ws, sys.executable,
  794. self['buildout']['bin-directory'],
  795. )
  796. # Restart
  797. args = sys.argv[:]
  798. if not __debug__:
  799. args.insert(0, '-O')
  800. args.insert(0, sys.executable)
  801. sys.exit(subprocess.call(args))
  802. def _load_extensions(self):
  803. __doing__ = 'Loading extensions.'
  804. specs = self['buildout'].get('extensions', '').split()
  805. if specs:
  806. path = [self['buildout']['develop-eggs-directory']]
  807. if self.offline:
  808. dest = None
  809. path.append(self['buildout']['eggs-directory'])
  810. else:
  811. dest = self['buildout']['eggs-directory']
  812. if not os.path.exists(dest):
  813. self._logger.info('Creating directory %r.', dest)
  814. os.mkdir(dest)
  815. zc.buildout.easy_install.install(
  816. specs, dest, path=path,
  817. working_set=pkg_resources.working_set,
  818. links = self['buildout'].get('find-links', '').split(),
  819. index = self['buildout'].get('index'),
  820. newest=self.newest, allow_hosts=self._allow_hosts)
  821. # Clear cache because extensions might now let us read pages we
  822. # couldn't read before.
  823. zc.buildout.easy_install.clear_index_cache()
  824. for ep in pkg_resources.iter_entry_points('zc.buildout.extension'):
  825. ep.load()(self)
  826. def _unload_extensions(self):
  827. __doing__ = 'Unloading extensions.'
  828. specs = self['buildout'].get('extensions', '').split()
  829. if specs:
  830. for ep in pkg_resources.iter_entry_points(
  831. 'zc.buildout.unloadextension'):
  832. ep.load()(self)
  833. def setup(self, args):
  834. if not args:
  835. raise zc.buildout.UserError(
  836. "The setup command requires the path to a setup script or \n"
  837. "directory containing a setup script, and its arguments."
  838. )
  839. setup = args.pop(0)
  840. if os.path.isdir(setup):
  841. setup = os.path.join(setup, 'setup.py')
  842. self._logger.info("Running setup script %r.", setup)
  843. setup = os.path.abspath(setup)
  844. fd, tsetup = tempfile.mkstemp()
  845. try:
  846. os.write(fd, (zc.buildout.easy_install.runsetup_template % dict(
  847. distribute=pkg_resources_loc,
  848. setupdir=os.path.dirname(setup),
  849. setup=setup,
  850. __file__ = setup,
  851. )).encode())
  852. args = [sys.executable, tsetup] + args
  853. zc.buildout.easy_install.call_subprocess(args)
  854. finally:
  855. os.close(fd)
  856. os.remove(tsetup)
  857. runsetup = setup # backward compat.
  858. def annotate(self, args):
  859. _print_annotate(self._annotated)
  860. def __getitem__(self, section):
  861. __doing__ = 'Getting section %s.', section
  862. try:
  863. return self._data[section]
  864. except KeyError:
  865. pass
  866. try:
  867. data = self._raw[section]
  868. except KeyError:
  869. raise MissingSection(section)
  870. options = Options(self, section, data)
  871. self._data[section] = options
  872. options._initialize()
  873. return options
  874. def __setitem__(self, key, value):
  875. raise NotImplementedError('__setitem__')
  876. def __delitem__(self, key):
  877. raise NotImplementedError('__delitem__')
  878. def keys(self):
  879. return list(self._raw.keys())
  880. def __iter__(self):
  881. return iter(self._raw)
  882. def __len__(self):
  883. return len(self._raw)
  884. def _install_and_load(spec, group, entry, buildout):
  885. __doing__ = 'Loading recipe %r.', spec
  886. try:
  887. req = pkg_resources.Requirement.parse(spec)
  888. buildout_options = buildout['buildout']
  889. if pkg_resources.working_set.find(req) is None:
  890. __doing__ = 'Installing recipe %s.', spec
  891. if buildout.offline:
  892. dest = None
  893. path = [buildout_options['develop-eggs-directory'],
  894. buildout_options['eggs-directory'],
  895. ]
  896. else:
  897. dest = buildout_options['eggs-directory']
  898. path = [buildout_options['develop-eggs-directory']]
  899. zc.buildout.easy_install.install(
  900. [spec], dest,
  901. links=buildout._links,
  902. index=buildout_options.get('index'),
  903. path=path,
  904. working_set=pkg_resources.working_set,
  905. newest=buildout.newest,
  906. allow_hosts=buildout._allow_hosts
  907. )
  908. __doing__ = 'Loading %s recipe entry %s:%s.', group, spec, entry
  909. return pkg_resources.load_entry_point(
  910. req.project_name, group, entry)
  911. except Exception:
  912. v = sys.exc_info()[1]
  913. buildout._logger.log(
  914. 1,
  915. "Could't load %s entry point %s\nfrom %s:\n%s.",
  916. group, entry, spec, v)
  917. raise
  918. class Options(DictMixin):
  919. def __init__(self, buildout, section, data):
  920. self.buildout = buildout
  921. self.name = section
  922. self._raw = data
  923. self._cooked = {}
  924. self._data = {}
  925. def _initialize(self):
  926. name = self.name
  927. __doing__ = 'Initializing section %s.', name
  928. if '<' in self._raw:
  929. self._raw = self._do_extend_raw(name, self._raw, [])
  930. # force substitutions
  931. for k, v in sorted(self._raw.items()):
  932. if '${' in v:
  933. self._dosub(k, v)
  934. if self.name == 'buildout':
  935. return # buildout section can never be a part
  936. recipe = self.get('recipe')
  937. if not recipe:
  938. return
  939. reqs, entry = _recipe(self._data)
  940. buildout = self.buildout
  941. recipe_class = _install_and_load(reqs, 'zc.buildout', entry, buildout)
  942. __doing__ = 'Initializing part %s.', name
  943. self.recipe = recipe_class(buildout, name, self)
  944. buildout._parts.append(name)
  945. def _do_extend_raw(self, name, data, doing):
  946. if name == 'buildout':
  947. return data
  948. if name in doing:
  949. raise zc.buildout.UserError("Infinite extending loop %r" % name)
  950. doing.append(name)
  951. try:
  952. to_do = data.get('<', None)
  953. if to_do is None:
  954. return data
  955. __doing__ = 'Loading input sections for %r', name
  956. result = {}
  957. for iname in to_do.split('\n'):
  958. iname = iname.strip()
  959. if not iname:
  960. continue
  961. raw = self.buildout._raw.get(iname)
  962. if raw is None:
  963. raise zc.buildout.UserError("No section named %r" % iname)
  964. result.update(self._do_extend_raw(iname, raw, doing))
  965. result.update(data)
  966. result.pop('<', None)
  967. return result
  968. finally:
  969. assert doing.pop() == name
  970. def _dosub(self, option, v):
  971. __doing__ = 'Getting option %s:%s.', self.name, option
  972. seen = [(self.name, option)]
  973. v = '$$'.join([self._sub(s, seen) for s in v.split('$$')])
  974. self._cooked[option] = v
  975. def get(self, option, default=None, seen=None):
  976. try:
  977. return self._data[option]
  978. except KeyError:
  979. pass
  980. v = self._cooked.get(option)
  981. if v is None:
  982. v = self._raw.get(option)
  983. if v is None:
  984. return default
  985. __doing__ = 'Getting option %s:%s.', self.name, option
  986. if '${' in v:
  987. key = self.name, option
  988. if seen is None:
  989. seen = [key]
  990. elif key in seen:
  991. raise zc.buildout.UserError(
  992. "Circular reference in substitutions.\n"
  993. )
  994. else:
  995. seen.append(key)
  996. v = '$$'.join([self._sub(s, seen) for s in v.split('$$')])
  997. seen.pop()
  998. self._data[option] = v
  999. return v
  1000. _template_split = re.compile('([$]{[^}]*})').split
  1001. _simple = re.compile('[-a-zA-Z0-9 ._]+$').match
  1002. _valid = re.compile('\${[-a-zA-Z0-9 ._]*:[-a-zA-Z0-9 ._]+}$').match
  1003. def _sub(self, template, seen):
  1004. value = self._template_split(template)
  1005. subs = []
  1006. for ref in value[1::2]:
  1007. s = tuple(ref[2:-1].split(':'))
  1008. if not self._valid(ref):
  1009. if len(s) < 2:
  1010. raise zc.buildout.UserError("The substitution, %s,\n"
  1011. "doesn't contain a colon."
  1012. % ref)
  1013. if len(s) > 2:
  1014. raise zc.buildout.UserError("The substitution, %s,\n"
  1015. "has too many colons."
  1016. % ref)
  1017. if not self._simple(s[0]):
  1018. raise zc.buildout.UserError(
  1019. "The section name in substitution, %s,\n"
  1020. "has invalid characters."
  1021. % ref)
  1022. if not self._simple(s[1]):
  1023. raise zc.buildout.UserError(
  1024. "The option name in substitution, %s,\n"
  1025. "has invalid characters."
  1026. % ref)
  1027. section, option = s
  1028. if not section:
  1029. section = self.name
  1030. v = self.buildout[section].get(option, None, seen)
  1031. if v is None:
  1032. if option == '_buildout_section_name_':
  1033. v = self.name
  1034. else:
  1035. raise MissingOption("Referenced option does not exist:",
  1036. section, option)
  1037. subs.append(v)
  1038. subs.append('')
  1039. return ''.join([''.join(v) for v in zip(value[::2], subs)])
  1040. def __getitem__(self, key):
  1041. try:
  1042. return self._data[key]
  1043. except KeyError:
  1044. pass
  1045. v = self.get(key)
  1046. if v is None:
  1047. raise MissingOption("Missing option: %s:%s" % (self.name, key))
  1048. return v
  1049. def __setitem__(self, option, value):
  1050. if not isinstance(value, str):
  1051. raise TypeError('Option values must be strings', value)
  1052. self._data[option] = value
  1053. def __delitem__(self, key):
  1054. if key in self._raw:
  1055. del self._raw[key]
  1056. if key in self._data:
  1057. del self._data[key]
  1058. if key in self._cooked:
  1059. del self._cooked[key]
  1060. elif key in self._data:
  1061. del self._data[key]
  1062. else:
  1063. raise KeyError(key)
  1064. def keys(self):
  1065. raw = self._raw
  1066. return list(self._raw) + [k for k in self._data if k not in raw]
  1067. def __iter__(self):
  1068. return iter(self.keys())
  1069. def __len__(self):
  1070. return len(self.keys())
  1071. def copy(self):
  1072. result = self._raw.copy()
  1073. result.update(self._cooked)
  1074. result.update(self._data)
  1075. return result
  1076. def _call(self, f):
  1077. buildout_directory = self.buildout['buildout']['directory']
  1078. self._created = []
  1079. try:
  1080. try:
  1081. os.chdir(buildout_directory)
  1082. return f()
  1083. except:
  1084. for p in self._created:
  1085. if os.path.isdir(p):
  1086. rmtree(p)
  1087. elif os.path.isfile(p):
  1088. os.remove(p)
  1089. else:
  1090. self.buildout._logger.warn("Couldn't clean up %r.", p)
  1091. raise
  1092. finally:
  1093. self._created = None
  1094. os.chdir(buildout_directory)
  1095. def created(self, *paths):
  1096. try:
  1097. self._created.extend(paths)
  1098. except AttributeError:
  1099. raise TypeError(
  1100. "Attempt to register a created path while not installing",
  1101. self.name)
  1102. return self._created
  1103. _spacey_nl = re.compile('[ \t\r\f\v]*\n[ \t\r\f\v\n]*'
  1104. '|'
  1105. '^[ \t\r\f\v]+'
  1106. '|'
  1107. '[ \t\r\f\v]+$'
  1108. )
  1109. _spacey_defaults = [
  1110. ('%(__buildout_space__)s', ' '),
  1111. ('%(__buildout_space_n__)s', '\n'),
  1112. ('%(__buildout_space_r__)s', '\r'),
  1113. ('%(__buildout_space_f__)s', '\f'),
  1114. ('%(__buildout_space_v__)s', '\v'),
  1115. ]
  1116. def _quote_spacey_nl(match):
  1117. match = match.group(0).split('\n', 1)
  1118. result = '\n\t'.join(
  1119. [(s
  1120. .replace(' ', '%(__buildout_space__)s')
  1121. .replace('\r', '%(__buildout_space_r__)s')
  1122. .replace('\f', '%(__buildout_space_f__)s')
  1123. .replace('\v', '%(__buildout_space_v__)s')
  1124. .replace('\n', '%(__buildout_space_n__)s')
  1125. )
  1126. for s in match]
  1127. )
  1128. return result
  1129. def _save_option(option, value, f):
  1130. value = _spacey_nl.sub(_quote_spacey_nl, value)
  1131. if value.startswith('\n\t'):
  1132. value = '%(__buildout_space_n__)s' + value[2:]
  1133. if value.endswith('\n\t'):
  1134. value = value[:-2] + '%(__buildout_space_n__)s'
  1135. print_(option, '=', value, file=f)
  1136. def _save_options(section, options, f):
  1137. print_('[%s]' % section, file=f)
  1138. items = list(options.items())
  1139. items.sort()
  1140. for option, value in items:
  1141. _save_option(option, value, f)
  1142. def _convert_bool(name, value):
  1143. if value not in ('true', 'false'):
  1144. raise zc.buildout.UserError(
  1145. 'Invalid value for %s option: %s' % (name, value))
  1146. else:
  1147. return value == 'true'
  1148. def _open(base, filename, seen, dl_options, override, downloaded):
  1149. """Open a configuration file and return the result as a dictionary,
  1150. Recursively open other files based on buildout options found.
  1151. """
  1152. _update_section(dl_options, override)
  1153. _dl_options = _unannotate_section(dl_options.copy())
  1154. newest = _convert_bool('newest', _dl_options.get('newest', 'false'))
  1155. fallback = newest and not (filename in downloaded)
  1156. download = zc.buildout.download.Download(
  1157. _dl_options, cache=_dl_options.get('extends-cache'),
  1158. fallback=fallback, hash_name=True)
  1159. is_temp = False
  1160. if _isurl(filename):
  1161. path, is_temp = download(filename)
  1162. fp = open(path)
  1163. base = filename[:filename.rfind('/')]
  1164. elif _isurl(base):
  1165. if os.path.isabs(filename):
  1166. fp = open(filename)
  1167. base = os.path.dirname(filename)

Large files files are truncated, but you can click here to view the full file