Commit 01e63a63 authored by Vincent Pelletier's avatar Vincent Pelletier

Add support for a few built-in python object types as values.

Useful when recipes generate non-string values to be reused by other
recipes.
parent 1a2d9c89
......@@ -36,10 +36,65 @@ import tempfile
import UserDict
import warnings
import subprocess
import pprint
import zc.buildout
import zc.buildout.download
import zc.buildout.easy_install
class BuildoutSerialiser(object):
# XXX: I would like to access pprint._safe_repr, but it's not
# officially available. PrettyPrinter class has a functionally-speaking
# static method "format" which just calls _safe_repr, but it is not
# declared as static... So I must create an instance of it.
_format = pprint.PrettyPrinter().format
_dollar = '\\x%02x' % ord('$')
_semicolon = '\\x%02x' % ord(';')
_safe_globals = {'__builtins__': {
# Types which are represented as calls to their constructor.
'bytearray': bytearray,
'complex': complex,
'frozenset': frozenset,
'set': set,
# Those buildins are available through keywords, which allow creating
# instances which in turn give back access to classes. So no point in
# hiding them.
'dict': dict,
'list': list,
'str': str,
'tuple': tuple,
}}
def loads(self, value):
return eval(value, self._safe_globals)
def dumps(self, value):
value, isreadable, _ = self._format(value, {}, 0, 0)
if not isreadable:
raise ValueError('Value cannot be serialised: %s' % (value, ))
return value.replace('$', self._dollar).replace(';', self._semicolon)
SERIALISED_VALUE_MAGIC = '!py'
SERIALISED = re.compile(SERIALISED_VALUE_MAGIC + '([^!]*)!(.*)')
SERIALISER_REGISTRY = {
'': BuildoutSerialiser(),
}
SERIALISER_VERSION = ''
SERIALISER = SERIALISER_REGISTRY[SERIALISER_VERSION]
# Used only to compose data
SERIALISER_PREFIX = SERIALISED_VALUE_MAGIC + SERIALISER_VERSION + '!'
assert SERIALISED.match(SERIALISER_PREFIX).groups() == (
SERIALISER_VERSION, ''), SERIALISED.match(SERIALISER_PREFIX).groups()
def dumps(value):
orig_value = value
value = SERIALISER.dumps(value)
assert SERIALISER.loads(value) == orig_value, (repr(value), orig_value)
return SERIALISER_PREFIX + value
def loads(value):
assert value.startswith(SERIALISED_VALUE_MAGIC), repr(value)
version, data = SERIALISED.match(value).groups()
return SERIALISER_REGISTRY[version].loads(data)
realpath = zc.buildout.easy_install.realpath
......@@ -1289,11 +1344,13 @@ class Options(UserDict.DictMixin):
v = self.get(key)
if v is None:
raise MissingOption("Missing option: %s:%s" % (self.name, key))
elif v.startswith(SERIALISED_VALUE_MAGIC):
v = loads(v)
return v
def __setitem__(self, option, value):
if not isinstance(value, str):
raise TypeError('Option values must be strings', value)
value = dumps(value)
self._data[option] = value
def __delitem__(self, key):
......@@ -1412,10 +1469,8 @@ def _save_option(option, value, f):
def _save_options(section, options, f):
print >>f, '[%s]' % section
items = options.items()
items.sort()
for option, value in items:
_save_option(option, value, f)
for option in sorted(options.keys()):
_save_option(option, options.get(option), f)
def _open(base, filename, seen, dl_options, override, downloaded):
"""Open a configuration file and return the result as a dictionary,
......
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