3 # Thomas Nagy, 2005-2018 (ita)
7 ConfigSet: a special dict
9 The values put in :py:class:`ConfigSet` must be serializable (dicts, lists, strings)
13 from waflib
import Logs
, Utils
14 re_imp
= re
.compile(r
'^(#)*?([^#=]*?)\ =\ (.*?)$', re
.M
)
16 class ConfigSet(object):
18 A copy-on-write dict with human-readable serialized format. The serialization format
19 is human-readable (python-like) and performed by using eval() and repr().
20 For high performance prefer pickle. Do not store functions as they are not serializable.
22 The values can be accessed by attributes or by keys::
24 from waflib.ConfigSet import ConfigSet
29 __slots__
= ('table', 'parent')
30 def __init__(self
, filename
=None):
33 Internal dict holding the object values
40 def __contains__(self
, key
):
42 Enables the *in* syntax::
50 return self
.parent
.__contains
__(key
)
51 except AttributeError:
52 return False # parent may not exist
59 keys
.update(cur
.table
.keys())
60 cur
= getattr(cur
, 'parent', None)
66 return iter(self
.keys())
69 """Text representation of the ConfigSet (for debugging purposes)"""
70 return "\n".join(["%r %r" % (x
, self
.__getitem
__(x
)) for x
in self
.keys()])
72 def __getitem__(self
, key
):
74 Dictionary interface: get value from key::
82 x
= self
.table
.get(key
)
86 except AttributeError:
89 def __setitem__(self
, key
, value
):
91 Dictionary interface: set value from key
93 self
.table
[key
] = value
95 def __delitem__(self
, key
):
97 Dictionary interface: mark the value as missing
101 def __getattr__(self
, name
):
103 Attribute access provided for convenience. The following forms are equivalent::
109 if name
in self
.__slots
__:
110 return object.__getattribute
__(self
, name
)
114 def __setattr__(self
, name
, value
):
116 Attribute access provided for convenience. The following forms are equivalent::
122 if name
in self
.__slots
__:
123 object.__setattr
__(self
, name
, value
)
127 def __delattr__(self
, name
):
129 Attribute access provided for convenience. The following forms are equivalent::
135 if name
in self
.__slots
__:
136 object.__delattr
__(self
, name
)
142 Returns a new ConfigSet deriving from self. The copy returned
143 will be a shallow copy::
145 from waflib.ConfigSet import ConfigSet
147 env.append_value('CFLAGS', ['-O2'])
149 child.CFLAGS.append('test') # warning! this will modify 'env'
150 child.CFLAGS = ['-O3'] # new list, ok
151 child.append_value('CFLAGS', ['-O3']) # ok
153 Use :py:func:`ConfigSet.detach` to detach the child from the parent.
161 Detaches this instance from its parent (if present)
163 Modifying the parent :py:class:`ConfigSet` will not change the current object
164 Modifying this :py:class:`ConfigSet` will not modify the parent one.
166 tbl
= self
.get_merged_dict()
168 delattr(self
, 'parent')
169 except AttributeError:
174 tbl
[x
] = copy
.deepcopy(tbl
[x
])
178 def get_flat(self
, key
):
180 Returns a value as a string. If the input is a list, the value returned is space-separated.
182 :param key: key to use
186 if isinstance(s
, str):
190 def _get_list_value_for_modification(self
, key
):
192 Returns a list value for further modification.
194 The list may be modified inplace and there is no need to do this afterwards::
196 self.table[var] = value
199 value
= self
.table
[key
]
202 value
= self
.parent
[key
]
203 except AttributeError:
206 if isinstance(value
, list):
211 self
.table
[key
] = value
213 if not isinstance(value
, list):
214 self
.table
[key
] = value
= [value
]
217 def append_value(self
, var
, val
):
219 Appends a value to the specified config key::
222 bld.env.append_value('CFLAGS', ['-O2'])
224 The value must be a list or a tuple
226 if isinstance(val
, str): # if there were string everywhere we could optimize this
228 current_value
= self
._get
_list
_value
_for
_modification
(var
)
229 current_value
.extend(val
)
231 def prepend_value(self
, var
, val
):
233 Prepends a value to the specified item::
236 conf.env.prepend_value('CFLAGS', ['-O2'])
238 The value must be a list or a tuple
240 if isinstance(val
, str):
242 self
.table
[var
] = val
+ self
._get
_list
_value
_for
_modification
(var
)
244 def append_unique(self
, var
, val
):
246 Appends a value to the specified item only if it's not already present::
249 bld.env.append_unique('CFLAGS', ['-O2', '-g'])
251 The value must be a list or a tuple
253 if isinstance(val
, str):
255 current_value
= self
._get
_list
_value
_for
_modification
(var
)
258 if x
not in current_value
:
259 current_value
.append(x
)
261 def get_merged_dict(self
):
263 Computes the merged dictionary from the fusion of self and all its parent
265 :rtype: a ConfigSet object
270 table_list
.insert(0, env
.table
)
273 except AttributeError:
276 for table
in table_list
:
277 merged_table
.update(table
)
280 def store(self
, filename
):
282 Serializes the :py:class:`ConfigSet` data to a file. See :py:meth:`ConfigSet.load` for reading such files.
284 :param filename: file to use
285 :type filename: string
288 os
.makedirs(os
.path
.split(filename
)[0])
293 merged_table
= self
.get_merged_dict()
294 keys
= list(merged_table
.keys())
303 if k
!= 'undo_stack':
304 buf
.append('%s = %s\n' % (k
, fun(merged_table
[k
])))
305 Utils
.writef(filename
, ''.join(buf
))
307 def load(self
, filename
):
309 Restores contents from a file (current values are not cleared). Files are written using :py:meth:`ConfigSet.store`.
311 :param filename: file to use
312 :type filename: string
315 code
= Utils
.readf(filename
, m
='r')
316 for m
in re_imp
.finditer(code
):
318 tbl
[g(2)] = eval(g(3))
319 Logs
.debug('env: %s', self
.table
)
323 Dictionary interface: replace values with the ones from another dict
325 :param d: object to use the value from
326 :type d: dict-like object
332 Stores the object state to provide transactionality semantics::
337 env.append_value('CFLAGS', '-O3')
338 call_some_method(env)
342 The history is kept in a stack, and is lost during the serialization by :py:meth:`ConfigSet.store`
345 tbl
= self
.table
= self
.table
.copy()
347 tbl
[x
] = copy
.deepcopy(tbl
[x
])
348 self
.undo_stack
= self
.undo_stack
+ [orig
]
352 Commits transactional changes. See :py:meth:`ConfigSet.stash`
354 self
.undo_stack
.pop(-1)
358 Reverts the object to a previous state. See :py:meth:`ConfigSet.stash`
360 self
.table
= self
.undo_stack
.pop(-1)