Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
slapos.buildout
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Jérome Perrin
slapos.buildout
Commits
816245bd
Commit
816245bd
authored
Feb 25, 2013
by
Jim Fulton
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
added meta-recipe support
parent
5869c2fb
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
305 additions
and
17 deletions
+305
-17
CHANGES.rst
CHANGES.rst
+5
-0
setup.py
setup.py
+2
-0
src/zc/buildout/buildout.py
src/zc/buildout/buildout.py
+50
-14
src/zc/buildout/buildout.txt
src/zc/buildout/buildout.txt
+1
-1
src/zc/buildout/meta-recipes.txt
src/zc/buildout/meta-recipes.txt
+232
-0
src/zc/buildout/testing.py
src/zc/buildout/testing.py
+13
-0
src/zc/buildout/tests.py
src/zc/buildout/tests.py
+2
-2
No files found.
CHANGES.rst
View file @
816245bd
Change History
**************
2.1.0 (2013-??-??)
==================
- Added `meta-recipe support`_.
2.0.1 (2013-02-16)
==================
...
...
setup.py
View file @
816245bd
...
...
@@ -53,6 +53,8 @@ long_description=(
+
'
\
n
'
+
read
(
'src'
,
'zc'
,
'buildout'
,
'debugging.txt'
)
+
'
\
n
'
+
read
(
'src'
,
'zc'
,
'buildout'
,
'meta-recipes.txt'
)
+
'
\
n
'
+
read
(
'src'
,
'zc'
,
'buildout'
,
'testing.txt'
)
+
'
\
n
'
+
read
(
'src'
,
'zc'
,
'buildout'
,
'easy_install.txt'
)
...
...
src/zc/buildout/buildout.py
View file @
816245bd
...
...
@@ -157,7 +157,7 @@ class Buildout(DictMixin):
data
=
dict
(
buildout
=
_buildout_default_options
.
copy
())
self
.
_buildout_dir
=
os
.
getcwd
()
if
not
_isurl
(
config_file
):
if
config_file
and
not
_isurl
(
config_file
):
config_file
=
os
.
path
.
abspath
(
config_file
)
base
=
os
.
path
.
dirname
(
config_file
)
if
not
os
.
path
.
exists
(
config_file
):
...
...
@@ -762,11 +762,11 @@ class Buildout(DictMixin):
for
k
,
v
in
_spacey_defaults
:
value
=
value
.
replace
(
k
,
v
)
options
[
option
]
=
value
result
[
section
]
=
Options
(
self
,
section
,
options
)
result
[
section
]
=
self
.
Options
(
self
,
section
,
options
)
return
result
,
True
else
:
return
({
'buildout'
:
Options
(
self
,
'buildout'
,
{
'parts'
:
''
})},
return
({
'buildout'
:
self
.
Options
(
self
,
'buildout'
,
{
'parts'
:
''
})},
False
,
)
...
...
@@ -829,7 +829,8 @@ class Buildout(DictMixin):
try
:
timeout
=
int
(
timeout
)
import
socket
self
.
_logger
.
info
(
'Setting socket time out to %d seconds.'
,
timeout
)
self
.
_logger
.
info
(
'Setting socket time out to %d seconds.'
,
timeout
)
socket
.
setdefaulttimeout
(
timeout
)
except
ValueError
:
self
.
_logger
.
warning
(
"Default socket timeout is used !
\
n
"
...
...
@@ -1062,9 +1063,21 @@ class Buildout(DictMixin):
runsetup
=
setup
# backward compat.
def
annotate
(
self
,
args
):
def
annotate
(
self
,
args
=
None
):
_print_annotate
(
self
.
_annotated
)
def
print_options
(
self
):
for
section
in
sorted
(
self
.
_data
):
if
section
==
'buildout'
or
section
==
self
[
'buildout'
][
'versions'
]:
continue
print_
(
'['
+
section
+
']'
)
for
k
,
v
in
sorted
(
self
.
_data
[
section
].
items
()):
if
'
\
n
'
in
v
:
v
=
'
\
n
'
+
v
.
replace
(
'
\
n
'
,
'
\
n
'
)
else
:
v
=
' '
+
v
print_
(
"%s =%s"
%
(
k
,
v
))
def
__getitem__
(
self
,
section
):
__doing__
=
'Getting section %s.'
,
section
try
:
...
...
@@ -1077,13 +1090,34 @@ class Buildout(DictMixin):
except
KeyError
:
raise
MissingSection
(
section
)
options
=
Options
(
self
,
section
,
data
)
options
=
self
.
Options
(
self
,
section
,
data
)
self
.
_data
[
section
]
=
options
options
.
_initialize
()
return
options
def
__setitem__
(
self
,
key
,
value
):
raise
NotImplementedError
(
'__setitem__'
)
def
__setitem__
(
self
,
name
,
data
):
if
name
in
self
.
_raw
:
raise
KeyError
(
"Section already exists"
,
name
)
self
.
_raw
[
name
]
=
dict
((
k
,
str
(
v
))
for
(
k
,
v
)
in
data
.
items
())
self
[
name
]
# Add to parts
def
parse
(
self
,
data
):
try
:
from
cStringIO
import
StringIO
except
ImportError
:
from
io
import
StringIO
import
textwrap
sections
=
zc
.
buildout
.
configparser
.
parse
(
StringIO
(
textwrap
.
dedent
(
data
)),
''
)
for
name
in
sections
:
if
name
in
self
.
_raw
:
raise
KeyError
(
"Section already exists"
,
name
)
self
.
_raw
[
name
]
=
dict
((
k
,
str
(
v
))
for
(
k
,
v
)
in
sections
[
name
].
items
())
for
name
in
sections
:
self
[
name
]
# Add to parts
def
__delitem__
(
self
,
key
):
raise
NotImplementedError
(
'__delitem__'
)
...
...
@@ -1158,20 +1192,20 @@ class Options(DictMixin):
if
'${'
in
v
:
self
.
_dosub
(
k
,
v
)
if
self
.
name
==
'buildout'
:
if
name
==
'buildout'
:
return
# buildout section can never be a part
recipe
=
self
.
get
(
'recipe'
)
if
not
recipe
:
return
if
self
.
get
(
'recipe'
):
self
.
initialize
()
self
.
buildout
.
_parts
.
append
(
name
)
def
initialize
(
self
):
reqs
,
entry
=
_recipe
(
self
.
_data
)
buildout
=
self
.
buildout
recipe_class
=
_install_and_load
(
reqs
,
'zc.buildout'
,
entry
,
buildout
)
__doing__
=
'Initializing part %s.'
,
name
name
=
self
.
name
self
.
recipe
=
recipe_class
(
buildout
,
name
,
self
)
buildout
.
_parts
.
append
(
name
)
def
_do_extend_raw
(
self
,
name
,
data
,
doing
):
if
name
==
'buildout'
:
...
...
@@ -1356,6 +1390,8 @@ class Options(DictMixin):
def
__repr__
(
self
):
return
repr
(
dict
(
self
))
Buildout
.
Options
=
Options
_spacey_nl
=
re
.
compile
(
'[
\
t
\
r
\
f
\
v
]*
\
n
[
\
t
\
r
\
f
\
v
\
n
]*'
'|'
'^[
\
t
\
r
\
f
\
v
]+'
...
...
src/zc/buildout/buildout.txt
View file @
816245bd
...
...
@@ -390,7 +390,7 @@ We'll get a user error, not a traceback.
While:
Installing.
Getting section data-dir.
Initializing
part
data-dir.
Initializing
section
data-dir.
Error: Invalid Path
...
...
src/zc/buildout/meta-recipes.txt
0 → 100644
View file @
816245bd
Meta-recipe support
===================
Buildout recipes provide reusable Python modules for common
configuration tasks. The most widely used recipes tend to provide
low-level functions, like installing eggs or software distributions,
creating configuration files, and so on. The normal recipe framework
is fairly well suited to building these general components.
Full-blown applications may require many, often tens, of parts.
Defining the many parts that make up an application can be tedious and
often entails a lot of repetition. Buildout provides a number of
mechanisms to avoid repetition, including merging of configuration
files and macros, but these, while useful to an extent, don't scale
very well. Buildout isn't and shouldn't be a programming language.
Meta-recipes allow us to bring Python to bear to provide higher-level
abstractions for buildouts.
A meta-recipe is a regular Python recipe that primarily operates by
creating parts. A meta recipe isn't merely a high level recipe. It's
a recipe that defers most or all of it's work to lower-level recipes by
manipulating the buildout database.
A `presentation at PyCon 2011
<http://blip.tv/pycon-us-videos-2009-2010-2011/pycon-2011-deploying-applications-with-zc-buildout-4897770>`_
described early work with meta recipes.
A simple meta-recipe example
============================
Let's look at a fairly simple meta-recipe example. First, consider a
buildout configuration that builds a database deployment::
[buildout]
parts = ctl pack
[deployment]
recipe = zc.recipe.deployment
name = ample
user = zope
[ctl]
recipe = zc.recipe.rhrc
deployment = deployment
chkconfig = 345 99 10
parts = main
[main]
recipe = zc.zodbrecipes:server
deployment = deployment
address = 8100
path = /var/databases/ample/main.fs
zeo.conf =
<zeo>
address ${:address}
</zeo>
%import zc.zlibstorage
<zlibstorage>
<filestorage>
path ${:path}
</filestorage>
</zlibstorage>
[pack]
recipe = zc.recipe.deployment:crontab
deployment = deployment
times = 1 2 * * 6
command = ${buildout:bin-directory}/zeopack -d3 -t00 ${main:address}
.. -> low_level
This buildout doesn't build software. Rather it builds configuration
for deploying a database configuration using already-deployed
software. For the purpose of this document, however, the details are
totally unimportant.
Rather than crafting the configuration above every time, we can write
a meta-recipe that crafts it for us. We'll use our meta-recipe as
follows::
[buildout]
parts = ample
[ample]
recipe = com.example.ample:db
path = /var/databases/ample/main.fs
The idea here is that the meta recipe allows us to specify the minimal
information necessary. A meta-recipe often automates policies and
assumptions that are application and organization dependent. The
example above assumes, for example, that we want to pack to 3
days in the past on Saturdays.
So now, let's see the meta recipe that automates this::
class Recipe:
def __init__(self, buildout, name, options):
buildout.parse('''
[deployment]
recipe = zc.recipe.deployment
name = %s
user = zope
''' % name)
buildout['main'] = dict(
recipe = 'zc.zodbrecipes:server',
deployment = 'deployment',
address = 8100,
path = options['path'],
**{
'zeo.conf': '''
<zeo>
address ${:address}
</zeo>
%import zc.zlibstorage
<zlibstorage>
<filestorage>
path ${:path}
</filestorage>
</zlibstorage>
'''}
)
buildout.parse('''
[pack]
recipe = zc.recipe.deployment:crontab
deployment = deployment
times = 1 2 * * 6
command =
${buildout:bin-directory}/zeopack -d3 -t00 ${main:address}
[ctl]
recipe = zc.recipe.rhrc
deployment = deployment
chkconfig = 345 99 10
parts = main
''')
def install(self):
pass
update = install
.. -> source
>>> exec(source)
The meta recipe just adds parts to the buildout. It does this by
setting items and and calling the ``parse`` method. The ``parse``
method just takes a string in buildout configuration syntax. It's
useful when we want to add static, or nearly static part data. The
setting items syntax is useful when we have non-trivial computation
for part data.
The order that we add parts is important. When adding a part, any
string substitutions and other dependencies are evaluated, so the
referenced parts must be defined first. This is why, for example, the
``pack`` part is added after the ``main`` part.
Note that the meta recipe supplied an integer for one of the
options. In addition to strings, it's legal to supply integer values.
There are a few things to note about this example:
- The install and update methods are empty.
While not required, this is a very common pattern for meta
recipes. Most meta recipes, simply invoke other recipes.
- Setting a buildout item or calling parse, adds any sections with
recipes as parts.
- An exception will be raised if a section already exists.
Testing
-------
Now, let's test our meta recipe. We'll test it without actually
running buildout. Rather, we'll use a specialized buildout provided by
the zc.buildout.testing module.
>>> import zc.buildout.testing
>>> buildout = zc.buildout.testing.Buildout()
The testing buildout is intended to be passed to recipes being
tested:
>>> _ = Recipe(buildout, 'ample', dict(path='/var/databases/ample/main.fs'))
After running the recipe, we should see the buildout database
populated by the recipe:
>>> buildout.print_options()
[ctl]
chkconfig = 345 99 10
deployment = deployment
parts = main
recipe = zc.recipe.rhrc
[deployment]
name = ample
recipe = zc.recipe.deployment
user = zope
[main]
address = 8100
deployment = deployment
path = /var/databases/ample/main.fs
recipe = zc.zodbrecipes:server
zeo.conf =
<BLANKLINE>
<zeo>
address 8100
</zeo>
<BLANKLINE>
%import zc.zlibstorage
<BLANKLINE>
<zlibstorage>
<filestorage>
path /var/databases/ample/main.fs
</filestorage>
</zlibstorage>
<BLANKLINE>
[pack]
command = /sample-buildout/bin/zeopack -d3 -t00 8100
deployment = deployment
recipe = zc.recipe.deployment:crontab
times = 1 2 * * 6
src/zc/buildout/testing.py
View file @
816245bd
...
...
@@ -166,6 +166,19 @@ def wait_until(label, func, *args, **kw):
time
.
sleep
(
0.01
)
raise
ValueError
(
'Timed out waiting for: '
+
label
)
class
TestOptions
(
zc
.
buildout
.
buildout
.
Options
):
def
initialize
(
self
):
pass
class
Buildout
(
zc
.
buildout
.
buildout
.
Buildout
):
def
__init__
(
self
):
zc
.
buildout
.
buildout
.
Buildout
.
__init__
(
self
,
''
,
[(
'buildout'
,
'directory'
,
os
.
getcwd
())])
Options
=
TestOptions
def
buildoutSetUp
(
test
):
test
.
globs
[
'__tear_downs'
]
=
__tear_downs
=
[]
...
...
src/zc/buildout/tests.py
View file @
816245bd
...
...
@@ -1381,7 +1381,7 @@ def internal_errors():
While:
Installing.
Getting section data-dir.
Initializing
part
data-dir.
Initializing
section
data-dir.
<BLANKLINE>
An internal error occured due to a bug in either zc.buildout or in a
recipe being used:
...
...
@@ -3227,7 +3227,7 @@ def test_suite():
'
zc
.
\
1
=
>=
1.99
'),
])
) + manuel.capture.Manuel(),
'
buildout
.
txt
',
'
buildout
.
txt
',
'
meta
-
recipes
.
txt
',
setUp=buildout_txt_setup,
tearDown=zc.buildout.testing.buildoutTearDown,
),
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment