Commit ccc2e279 authored by Jérome Perrin's avatar Jérome Perrin Committed by Xavier Thompson

[fix] Escape option values set by recipes

When recipe mutate options, options values are written to
.installed.cfg without escaping buildout substitution syntax,
so if a recipe sets an option value that could be interpreted
as a buildout substitution, it is written as is in .installed.cfg.

This can be a problem if options read from _read_installed_part_options
are accessed, like it's the case with slapos patched buildout which
saves installed options after an error with part installation or after
each part installation when running in verbose mode.
parent 2da14cc5
...@@ -1816,7 +1816,7 @@ class Options(DictMixin): ...@@ -1816,7 +1816,7 @@ class Options(DictMixin):
def __setitem__(self, option, value): def __setitem__(self, option, value):
if not isinstance(value, str): if not isinstance(value, str):
value = dumps(value) value = dumps(value)
self._data[option] = value self._data[option] = value.replace('${', '$${')
def __delitem__(self, key): def __delitem__(self, key):
if key in self._raw: if key in self._raw:
......
...@@ -2337,6 +2337,60 @@ def test_part_pulled_by_recipe(): ...@@ -2337,6 +2337,60 @@ def test_part_pulled_by_recipe():
... ...
""" """
def test_recipe_options_are_escaped():
"""
>>> mkdir(sample_buildout, 'recipes')
>>> write(sample_buildout, 'recipes', 'test.py',
... '''
... class Recipe:
...
... def __init__(self, buildout, name, options):
... options['option'] = '${buildout_syntax_should_be_escaped}'
... print ("Option value: %s" % options['option'])
...
... def install(self):
... return ()
...
... update = install
... ''')
>>> write(sample_buildout, 'recipes', 'setup.py',
... '''
... from setuptools import setup
... setup(
... name = "recipes",
... entry_points = {'zc.buildout': ['test = test:Recipe']},
... )
... ''')
>>> write(sample_buildout, 'buildout.cfg',
... '''
... [buildout]
... develop = recipes
... parts = a
... [a]
... recipe = recipes:test
... ''')
>>> os.chdir(sample_buildout)
>>> buildout = os.path.join(sample_buildout, 'bin', 'buildout')
>>> print_(system(buildout), end='')
Develop: '/sample-buildout/recipes'
Option value: ${buildout_syntax_should_be_escaped}
Installing a.
>>> cat('.installed.cfg') # doctest: +ELLIPSIS
[buildout]
...
[a]
__buildout_installed__ =
__buildout_signature__ = recipes-...
option = $${buildout_syntax_should_be_escaped}
recipe = recipes:test
"""
def read_find_links_to_load_extensions(): def read_find_links_to_load_extensions():
r""" r"""
We'll create a wacky buildout extension that just announces itself when used: We'll create a wacky buildout extension that just announces itself when used:
......
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