Commit e1fa4c0f authored by jim's avatar jim

The buildout init command now accepts distribution requirements and

paths to set up a custom interpreter part that has the distributions
or parts in the path. For example::

python bootstrap.py init BeautifulSoup


git-svn-id: http://svn.zope.org/repos/main/zc.buildout/trunk@122989 62d5b8a3-27da-0310-9561-8e5933582275
parent 57e9e71d
Change History Change History
************** **************
1.5.3 (unreleased) 1.6.0 (unreleased)
================== ==================
- The buildout init command now accepts distribution requirements and
paths to set up a custom interpreter part that has the distributions
or parts in the path. For example::
python bootstrap.py init BeautifulSoup
- Introduce a cache for the expensive `buildout._dir_hash` function. - Introduce a cache for the expensive `buildout._dir_hash` function.
- Remove duplicate path from script's sys.path setup. - Remove duplicate path from script's sys.path setup.
......
...@@ -144,7 +144,8 @@ _buildout_default_options = _annotate_section({ ...@@ -144,7 +144,8 @@ _buildout_default_options = _annotate_section({
class Buildout(UserDict.DictMixin): class Buildout(UserDict.DictMixin):
def __init__(self, config_file, cloptions, def __init__(self, config_file, cloptions,
user_defaults=True, windows_restart=False, command=None): user_defaults=True, windows_restart=False,
command=None, args=()):
__doing__ = 'Initializing.' __doing__ = 'Initializing.'
...@@ -159,8 +160,7 @@ class Buildout(UserDict.DictMixin): ...@@ -159,8 +160,7 @@ class Buildout(UserDict.DictMixin):
base = os.path.dirname(config_file) base = os.path.dirname(config_file)
if not os.path.exists(config_file): if not os.path.exists(config_file):
if command == 'init': if command == 'init':
print 'Creating %r.' % config_file self._init_config(config_file, args)
open(config_file, 'w').write('[buildout]\nparts = \n')
elif command == 'setup': elif command == 'setup':
# Sigh. This model of a buildout instance # Sigh. This model of a buildout instance
# with methods is breaking down. :( # with methods is breaking down. :(
...@@ -169,6 +169,9 @@ class Buildout(UserDict.DictMixin): ...@@ -169,6 +169,9 @@ class Buildout(UserDict.DictMixin):
else: else:
raise zc.buildout.UserError( raise zc.buildout.UserError(
"Couldn't open %s" % config_file) "Couldn't open %s" % config_file)
elif command == 'init':
raise zc.buildout.UserError(
"%r already exists." % config_file)
if config_file: if config_file:
data['buildout']['directory'] = (os.path.dirname(config_file), data['buildout']['directory'] = (os.path.dirname(config_file),
...@@ -191,7 +194,8 @@ class Buildout(UserDict.DictMixin): ...@@ -191,7 +194,8 @@ class Buildout(UserDict.DictMixin):
'.buildout', 'default.cfg') '.buildout', 'default.cfg')
if os.path.exists(user_config): if os.path.exists(user_config):
_update(data, _open(os.path.dirname(user_config), user_config, _update(data, _open(os.path.dirname(user_config), user_config,
[], data['buildout'].copy(), override, set())) [], data['buildout'].copy(), override,
set()))
# load configuration files # load configuration files
if config_file: if config_file:
...@@ -415,7 +419,38 @@ class Buildout(UserDict.DictMixin): ...@@ -415,7 +419,38 @@ class Buildout(UserDict.DictMixin):
exec_sitecustomize=self.exec_sitecustomize, exec_sitecustomize=self.exec_sitecustomize,
) )
init = bootstrap def _init_config(self, config_file, args):
print 'Creating %r.' % config_file
f = open(config_file, 'w')
sep = re.compile(r'[\\/]')
if args:
eggs = '\n '.join(a for a in args if not sep.search(a))
paths = '\n '.join(
sep.sub(os.path.sep, a) for a in args if sep.search(a))
f.write('[buildout]\n'
'parts = py\n'
'\n'
'[py]\n'
'recipe = zc.recipe.egg\n'
'interpreter = py\n'
'eggs =\n'
)
if eggs:
f.write(' %s\n' % eggs)
if paths:
f.write('extra-paths =\n %s\n' % paths)
for p in [a for a in args if sep.search(a)]:
if not os.path.exists(p):
os.mkdir(p)
else:
f.write('[buildout]\nparts =\n')
f.close()
def init(self, args):
self.bootstrap(())
if args:
self.install(())
def install(self, install_args): def install(self, install_args):
__doing__ = 'Installing.' __doing__ = 'Installing.'
...@@ -1795,11 +1830,12 @@ def main(args=None): ...@@ -1795,11 +1830,12 @@ def main(args=None):
_error('invalid command:', command) _error('invalid command:', command)
else: else:
command = 'install' command = 'install'
try: try:
try: try:
buildout = Buildout(config_file, options, buildout = Buildout(config_file, options,
user_defaults, windows_restart, command) user_defaults, windows_restart,
command, args)
getattr(buildout, command)(args) getattr(buildout, command)(args)
except Exception, v: except Exception, v:
_doing() _doing()
......
...@@ -2420,7 +2420,15 @@ local buildout scripts. ...@@ -2420,7 +2420,15 @@ local buildout scripts.
Creating directory '/sample-bootstrapped/develop-eggs'. Creating directory '/sample-bootstrapped/develop-eggs'.
Generated script '/sample-bootstrapped/bin/buildout'. Generated script '/sample-bootstrapped/bin/buildout'.
Note that a basic setup.cfg was created for us. Note that a basic setup.cfg was created for us. This is because we
provided an 'init' argument. By default, the generated
``setup.cfg`` is as minimal as it could be:
>>> cat(sample_bootstrapped, 'setup.cfg')
[buildout]
parts =
We also get other buildout artifacts:
>>> ls(sample_bootstrapped) >>> ls(sample_bootstrapped)
d bin d bin
...@@ -2522,6 +2530,93 @@ if there isn't a configuration file: ...@@ -2522,6 +2530,93 @@ if there isn't a configuration file:
Creating directory '/sample-bootstrapped2/develop-eggs'. Creating directory '/sample-bootstrapped2/develop-eggs'.
Generated script '/sample-bootstrapped2/bin/buildout'. Generated script '/sample-bootstrapped2/bin/buildout'.
Similarly, if there is a configuration file and we use the init
command, we'll get an error that the configuration file already
exists:
>>> print system(buildout
... +' -c'+os.path.join(sample_bootstrapped, 'setup.cfg')
... +' init'),
While:
Initializing.
Error: '/sample-bootstrapped/setup.cfg' already exists.
Initial eggs
------------
When using the ``init`` command, you can specify distribution requirements
or paths to use:
>>> cd(sample_bootstrapped)
>>> remove('setup.cfg')
>>> print system(buildout + ' -csetup.cfg init demo other ./src'),
Creating '/sample-bootstrapped/setup.cfg'.
Generated script '/sample-bootstrapped/bin/buildout'.
Getting distribution for 'zc.recipe.egg'.
Got zc.recipe.egg 1.3.3dev.
Installing py.
Getting distribution for 'demo'.
Got demo 0.4c1.
Getting distribution for 'other'.
Got other 1.0.
Getting distribution for 'demoneeded'.
Got demoneeded 1.2c1.
Generated script '/sample-bootstrapped/bin/demo'.
Generated interpreter '/sample-bootstrapped/bin/py'.
This causes a ``py`` part to be included that sets up a custom python
interpreter with the given requirements or paths:
>>> cat('setup.cfg')
[buildout]
parts = py
<BLANKLINE>
[py]
recipe = zc.recipe.egg
interpreter = py
eggs =
demo
other
extra-paths =
./src
Passing requirements or paths causes the the builout to be run as part
of initialization. In the example above, we got a number of
distributions installed and 2 scripts generated. The first, ``demo``,
was defined by the ``demo`` project. The second, ``py`` was defined by
the generated configuration. It's a "custom interpreter" that behaves
like a standard Python interpeter, except that includes the specified
eggs and extra paths in it's Python path.
We specified a source directory that didn't exist. Buildout created it
for us:
>>> ls('.')
- .installed.cfg
d bin
d develop-eggs
d eggs
d parts
- setup.cfg
d src
>>> uncd()
.. Make sure it works if the dir is already there:
>>> cd(sample_bootstrapped)
>>> _ = system(buildout + ' -csetup.cfg buildout:parts=')
>>> remove('setup.cfg')
>>> print system(buildout + ' -csetup.cfg init demo other ./src'),
Creating '/sample-bootstrapped/setup.cfg'.
Installing py.
Generated script '/sample-bootstrapped/bin/demo'.
Generated interpreter '/sample-bootstrapped/bin/py'.
.. cleanup
>>> _ = system(buildout + ' -csetup.cfg buildout:parts=')
>>> uncd()
Newest and Offline Modes Newest and Offline Modes
------------------------ ------------------------
......
...@@ -3837,6 +3837,29 @@ def easy_install_SetUp(test): ...@@ -3837,6 +3837,29 @@ def easy_install_SetUp(test):
zc.buildout.testing.install_develop('zc.recipe.egg', test) zc.buildout.testing.install_develop('zc.recipe.egg', test)
zc.buildout.testing.install_develop('z3c.recipe.scripts', test) zc.buildout.testing.install_develop('z3c.recipe.scripts', test)
def buildout_txt_setup(test):
zc.buildout.testing.buildoutSetUp(test)
mkdir = test.globs['mkdir']
eggs = os.environ['buildout-testing-index-url'][7:]
test.globs['sample_eggs'] = eggs
create_sample_eggs(test)
for name in os.listdir(eggs):
if '-' in name:
pname = name.split('-')[0]
if not os.path.exists(os.path.join(eggs, pname)):
mkdir(eggs, pname)
shutil.move(os.path.join(eggs, name),
os.path.join(eggs, pname, name))
dist = pkg_resources.working_set.find(
pkg_resources.Requirement.parse('zc.recipe.egg'))
mkdir(eggs, 'zc.recipe.egg')
zc.buildout.testing.sdist(
os.path.dirname(dist.location),
os.path.join(eggs, 'zc.recipe.egg'),
)
egg_parse = re.compile('([0-9a-zA-Z_.]+)-([0-9a-zA-Z_.]+)-py(\d[.]\d).egg$' egg_parse = re.compile('([0-9a-zA-Z_.]+)-([0-9a-zA-Z_.]+)-py(\d[.]\d).egg$'
).match ).match
def makeNewRelease(project, ws, dest, version='99.99'): def makeNewRelease(project, ws, dest, version='99.99'):
...@@ -3943,7 +3966,42 @@ hide_first_index_page_message = ( ...@@ -3943,7 +3966,42 @@ hide_first_index_page_message = (
def test_suite(): def test_suite():
test_suite = [ test_suite = [
doctest.DocFileSuite( doctest.DocFileSuite(
'buildout.txt', 'runsetup.txt', 'repeatable.txt', 'setup.txt', 'buildout.txt',
setUp=buildout_txt_setup,
tearDown=zc.buildout.testing.buildoutTearDown,
checker=renormalizing.RENormalizing([
zc.buildout.testing.normalize_path,
zc.buildout.testing.normalize_endings,
zc.buildout.testing.normalize_script,
zc.buildout.testing.normalize_egg_py,
zc.buildout.tests.hide_distribute_additions,
hide_zip_safe_message,
(re.compile('__buildout_signature__ = recipes-\S+'),
'__buildout_signature__ = recipes-SSSSSSSSSSS'),
(re.compile('executable = [\S ]+python\S*', re.I),
'executable = python'),
(re.compile('[-d] (setuptools|distribute)-\S+[.]egg'),
'setuptools.egg'),
(re.compile('zc.buildout(-\S+)?[.]egg(-link)?'),
'zc.buildout.egg'),
(re.compile('creating \S*setup.cfg'), 'creating setup.cfg'),
(re.compile('hello\%ssetup' % os.path.sep), 'hello/setup'),
(re.compile('Picked: (\S+) = \S+'),
'Picked: \\1 = V.V'),
(re.compile(r'We have a develop egg: zc.buildout (\S+)'),
'We have a develop egg: zc.buildout X.X.'),
(re.compile(r'\\[\\]?'), '/'),
(re.compile('WindowsError'), 'OSError'),
(re.compile(r'\[Error \d+\] Cannot create a file '
r'when that file already exists: '),
'[Errno 17] File exists: '
),
(re.compile('distribute'), 'setuptools'),
(re.compile('Got zc.recipe.egg \S+'), 'Got zc.recipe.egg'),
])
),
doctest.DocFileSuite(
'runsetup.txt', 'repeatable.txt', 'setup.txt',
setUp=zc.buildout.testing.buildoutSetUp, setUp=zc.buildout.testing.buildoutSetUp,
tearDown=zc.buildout.testing.buildoutTearDown, tearDown=zc.buildout.testing.buildoutTearDown,
checker=renormalizing.RENormalizing([ checker=renormalizing.RENormalizing([
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment