3 # Thomas Nagy, 2005-2010 (ita)
7 ConfigSet: a special dict
9 The values put in :py:class:`ConfigSet` must be lists
13 from waflib
import Logs
, Utils
14 re_imp
= re
.compile('^(#)*?([^#=]*?)\ =\ (.*?)$', re
.M
)
16 class ConfigSet(object):
18 A dict that honor serialization and parent relationships. 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 Enable the *in* syntax::
47 if key
in self
.table
: return True
48 try: return self
.parent
.__contains
__(key
)
49 except AttributeError: return False # parent may not exist
52 """Dict interface (unknown purpose)"""
56 keys
.update(cur
.table
.keys())
57 cur
= getattr(cur
, 'parent', None)
63 """Text representation of the ConfigSet (for debugging purposes)"""
64 return "\n".join(["%r %r" % (x
, self
.__getitem
__(x
)) for x
in self
.keys()])
66 def __getitem__(self
, key
):
68 Dictionary interface: get value from key::
76 x
= self
.table
.get(key
, None)
80 except AttributeError:
83 def __setitem__(self
, key
, value
):
85 Dictionary interface: get value from key
87 self
.table
[key
] = value
89 def __delitem__(self
, key
):
91 Dictionary interface: get value from key
95 def __getattr__(self
, name
):
97 Attribute access provided for convenience. The following forms are equivalent::
103 if name
in self
.__slots
__:
104 return object.__getattr
__(self
, name
)
108 def __setattr__(self
, name
, value
):
110 Attribute access provided for convenience. The following forms are equivalent::
116 if name
in self
.__slots
__:
117 object.__setattr
__(self
, name
, value
)
121 def __delattr__(self
, name
):
123 Attribute access provided for convenience. The following forms are equivalent::
129 if name
in self
.__slots
__:
130 object.__delattr
__(self
, name
)
136 Returns a new ConfigSet deriving from self. The copy returned
137 will be a shallow copy::
139 from waflib.ConfigSet import ConfigSet
141 env.append_value('CFLAGS', ['-O2'])
143 child.CFLAGS.append('test') # warning! this will modify 'env'
144 child.CFLAGS = ['-O3'] # new list, ok
145 child.append_value('CFLAGS', ['-O3']) # ok
147 Use :py:func:`ConfigSet.detach` to detach the child from the parent.
155 Detach self from its parent (if existing)
157 Modifying the parent :py:class:`ConfigSet` will not change the current object
158 Modifying this :py:class:`ConfigSet` will not modify the parent one.
160 tbl
= self
.get_merged_dict()
162 delattr(self
, 'parent')
163 except AttributeError:
168 tbl
[x
] = copy
.deepcopy(tbl
[x
])
172 def get_flat(self
, key
):
174 Return a value as a string. If the input is a list, the value returned is space-separated.
176 :param key: key to use
180 if isinstance(s
, str): return s
183 def _get_list_value_for_modification(self
, key
):
185 Return a list value for further modification.
187 The list may be modified inplace and there is no need to do this afterwards::
189 self.table[var] = value
192 value
= self
.table
[key
]
194 try: value
= self
.parent
[key
]
195 except AttributeError: value
= []
196 if isinstance(value
, list):
201 if not isinstance(value
, list):
203 self
.table
[key
] = value
206 def append_value(self
, var
, val
):
208 Appends a value to the specified config key::
211 bld.env.append_value('CFLAGS', ['-O2'])
213 The value must be a list or a tuple
215 if isinstance(val
, str): # if there were string everywhere we could optimize this
217 current_value
= self
._get
_list
_value
_for
_modification
(var
)
218 current_value
.extend(val
)
220 def prepend_value(self
, var
, val
):
222 Prepends a value to the specified item::
225 conf.env.prepend_value('CFLAGS', ['-O2'])
227 The value must be a list or a tuple
229 if isinstance(val
, str):
231 self
.table
[var
] = val
+ self
._get
_list
_value
_for
_modification
(var
)
233 def append_unique(self
, var
, val
):
235 Append a value to the specified item only if it's not already present::
238 bld.env.append_unique('CFLAGS', ['-O2', '-g'])
240 The value must be a list or a tuple
242 if isinstance(val
, str):
244 current_value
= self
._get
_list
_value
_for
_modification
(var
)
247 if x
not in current_value
:
248 current_value
.append(x
)
250 def get_merged_dict(self
):
252 Compute the merged dictionary from the fusion of self and all its parent
254 :rtype: a ConfigSet object
259 table_list
.insert(0, env
.table
)
260 try: env
= env
.parent
261 except AttributeError: break
263 for table
in table_list
:
264 merged_table
.update(table
)
267 def store(self
, filename
):
269 Write the :py:class:`ConfigSet` data into a file. See :py:meth:`ConfigSet.load` for reading such files.
271 :param filename: file to use
272 :type filename: string
275 os
.makedirs(os
.path
.split(filename
)[0])
280 merged_table
= self
.get_merged_dict()
281 keys
= list(merged_table
.keys())
290 if k
!= 'undo_stack':
291 buf
.append('%s = %s\n' % (k
, fun(merged_table
[k
])))
292 Utils
.writef(filename
, ''.join(buf
))
294 def load(self
, filename
):
296 Retrieve the :py:class:`ConfigSet` data from a file. See :py:meth:`ConfigSet.store` for writing such files
298 :param filename: file to use
299 :type filename: string
302 code
= Utils
.readf(filename
, m
='rU')
303 for m
in re_imp
.finditer(code
):
305 tbl
[g(2)] = eval(g(3))
306 Logs
.debug('env: %s' % str(self
.table
))
310 Dictionary interface: replace values from another dict
312 :param d: object to use the value from
313 :type d: dict-like object
315 for k
, v
in d
.items():
320 Store the object state, to provide a kind of transaction support::
325 env.append_value('CFLAGS', '-O3')
326 call_some_method(env)
330 The history is kept in a stack, and is lost during the serialization by :py:meth:`ConfigSet.store`
333 tbl
= self
.table
= self
.table
.copy()
335 tbl
[x
] = copy
.deepcopy(tbl
[x
])
336 self
.undo_stack
= self
.undo_stack
+ [orig
]
340 Reverts the object to a previous state. See :py:meth:`ConfigSet.stash`
342 self
.table
= self
.undo_stack
.pop(-1)