2094
2094
self._transport.put_file(self._filename, out_file)
2097
class Section(object):
2098
"""A section defines a dict of options.
2100
This is merely a read-only dict which can add some knowledge about the
2101
options. It is *not* a python dict object though and doesn't try to mimic
2105
def __init__(self, section_id, options):
2106
self.id = section_id
2107
# We re-use the dict-like object received
2108
self.options = options
2110
def get(self, name, default=None):
2111
return self.options.get(name, default)
2114
# Mostly for debugging use
2115
return "<config.%s id=%s>" % (self.__class__.__name__, self.id)
2118
_NewlyCreatedOption = object()
2119
"""Was the option created during the MutableSection lifetime"""
2122
class MutableSection(Section):
2123
"""A section allowing changes and keeping track of the original values."""
2125
def __init__(self, section_id, options):
2126
super(MutableSection, self).__init__(section_id, options)
2129
def set(self, name, value):
2130
if name not in self.options:
2131
# This is a new option
2132
self.orig[name] = _NewlyCreatedOption
2133
elif name not in self.orig:
2134
self.orig[name] = self.get(name, None)
2135
self.options[name] = value
2137
def remove(self, name):
2138
if name not in self.orig:
2139
self.orig[name] = self.get(name, None)
2140
del self.options[name]
2143
class Store(object):
2144
"""Abstract interface to persistent storage for configuration options."""
2146
readonly_section_class = Section
2147
mutable_section_class = MutableSection
2149
def is_loaded(self):
2150
"""Returns True if the Store has been loaded.
2152
This is used to implement lazy loading and ensure the persistent
2153
storage is queried only when needed.
2155
raise NotImplementedError(self.is_loaded)
2158
"""Loads the Store from persistent storage."""
2159
raise NotImplementedError(self.load)
2161
def _load_from_string(self, str_or_unicode):
2162
"""Create a store from a string in configobj syntax.
2164
:param str_or_unicode: A string representing the file content. This will
2165
be encoded to suit store needs internally.
2167
This is for tests and should not be used in production unless a
2168
convincing use case can be demonstrated :)
2170
raise NotImplementedError(self._load_from_string)
2173
"""Saves the Store to persistent storage."""
2174
raise NotImplementedError(self.save)
2176
def external_url(self):
2177
raise NotImplementedError(self.external_url)
2179
def get_sections(self):
2180
"""Returns an ordered iterable of existing sections.
2182
:returns: An iterable of (name, dict).
2184
raise NotImplementedError(self.get_sections)
2186
def get_mutable_section(self, section_name=None):
2187
"""Returns the specified mutable section.
2189
:param section_name: The section identifier
2191
raise NotImplementedError(self.get_mutable_section)
2194
# Mostly for debugging use
2195
return "<config.%s(%s)>" % (self.__class__.__name__,
2196
self.external_url())
2199
class IniFileStore(Store):
2200
"""A config Store using ConfigObj for storage.
2202
:ivar transport: The transport object where the config file is located.
2204
:ivar file_name: The config file basename in the transport directory.
2206
:ivar _config_obj: Private member to hold the ConfigObj instance used to
2207
serialize/deserialize the config file.
2210
def __init__(self, transport, file_name):
2211
"""A config Store using ConfigObj for storage.
2213
:param transport: The transport object where the config file is located.
2215
:param file_name: The config file basename in the transport directory.
2217
super(IniFileStore, self).__init__()
2218
self.transport = transport
2219
self.file_name = file_name
2220
self._config_obj = None
2222
def is_loaded(self):
2223
return self._config_obj != None
2226
"""Load the store from the associated file."""
2227
if self.is_loaded():
2229
content = self.transport.get_bytes(self.file_name)
2230
self._load_from_string(content)
2232
def _load_from_string(self, str_or_unicode):
2233
"""Create a config store from a string.
2235
:param str_or_unicode: A string representing the file content. This will
2236
be utf-8 encoded internally.
2238
This is for tests and should not be used in production unless a
2239
convincing use case can be demonstrated :)
2241
if self.is_loaded():
2242
raise AssertionError('Already loaded: %r' % (self._config_obj,))
2243
co_input = StringIO(str_or_unicode.encode('utf-8'))
2245
# The config files are always stored utf8-encoded
2246
self._config_obj = ConfigObj(co_input, encoding='utf-8')
2247
except configobj.ConfigObjError, e:
2248
self._config_obj = None
2249
raise errors.ParseConfigError(e.errors, self.external_url())
2252
if not self.is_loaded():
2256
self._config_obj.write(out)
2257
self.transport.put_bytes(self.file_name, out.getvalue())
2259
def external_url(self):
2260
# FIXME: external_url should really accepts an optional relpath
2261
# parameter (bug #750169) :-/ -- vila 2011-04-04
2262
# The following will do in the interim but maybe we don't want to
2263
# expose a path here but rather a config ID and its associated
2264
# object </hand wawe>.
2265
return urlutils.join(self.transport.external_url(), self.file_name)
2267
def get_sections(self):
2268
"""Get the configobj section in the file order.
2270
:returns: An iterable of (name, dict).
2272
# We need a loaded store
2274
cobj = self._config_obj
2276
yield self.readonly_section_class(None, cobj)
2277
for section_name in cobj.sections:
2278
yield self.readonly_section_class(section_name, cobj[section_name])
2280
def get_mutable_section(self, section_name=None):
2281
# We need a loaded store
2284
except errors.NoSuchFile:
2285
# The file doesn't exist, let's pretend it was empty
2286
self._load_from_string('')
2287
if section_name is None:
2288
section = self._config_obj
2290
section = self._config_obj.setdefault(section_name, {})
2291
return self.mutable_section_class(section_name, section)
2294
# Note that LockableConfigObjStore inherits from ConfigObjStore because we need
2295
# unlockable stores for use with objects that can already ensure the locking
2296
# (think branches). If different stores (not based on ConfigObj) are created,
2297
# they may face the same issue.
2300
class LockableIniFileStore(IniFileStore):
2301
"""A ConfigObjStore using locks on save to ensure store integrity."""
2303
def __init__(self, transport, file_name, lock_dir_name=None):
2304
"""A config Store using ConfigObj for storage.
2306
:param transport: The transport object where the config file is located.
2308
:param file_name: The config file basename in the transport directory.
2310
if lock_dir_name is None:
2311
lock_dir_name = 'lock'
2312
self.lock_dir_name = lock_dir_name
2313
super(LockableIniFileStore, self).__init__(transport, file_name)
2314
self._lock = lockdir.LockDir(self.transport, self.lock_dir_name)
2316
def lock_write(self, token=None):
2317
"""Takes a write lock in the directory containing the config file.
2319
If the directory doesn't exist it is created.
2321
# FIXME: This doesn't check the ownership of the created directories as
2322
# ensure_config_dir_exists does. It should if the transport is local
2323
# -- vila 2011-04-06
2324
self.transport.create_prefix()
2325
return self._lock.lock_write(token)
2330
def break_lock(self):
2331
self._lock.break_lock()
2335
super(LockableIniFileStore, self).save()
2338
# FIXME: global, bazaar, shouldn't that be 'user' instead or even
2339
# 'user_defaults' as opposed to 'user_overrides', 'system_defaults'
2340
# (/etc/bzr/bazaar.conf) and 'system_overrides' ? -- vila 2011-04-05
2342
# FIXME: Moreover, we shouldn't need classes for these stores either, factory
2343
# functions or a registry will make it easier and clearer for tests, focusing
2344
# on the relevant parts of the API that needs testing -- vila 20110503 (based
2345
# on a poolie's remark)
2346
class GlobalStore(LockableIniFileStore):
2348
def __init__(self, possible_transports=None):
2349
t = transport.get_transport(config_dir(),
2350
possible_transports=possible_transports)
2351
super(GlobalStore, self).__init__(t, 'bazaar.conf')
2354
class LocationStore(LockableIniFileStore):
2356
def __init__(self, possible_transports=None):
2357
t = transport.get_transport(config_dir(),
2358
possible_transports=possible_transports)
2359
super(LocationStore, self).__init__(t, 'locations.conf')
2362
class BranchStore(IniFileStore):
2364
def __init__(self, branch):
2365
super(BranchStore, self).__init__(branch.control_transport,
2368
class SectionMatcher(object):
2369
"""Select sections into a given Store.
2371
This intended to be used to postpone getting an iterable of sections from a
2375
def __init__(self, store):
2378
def get_sections(self):
2379
# This is where we require loading the store so we can see all defined
2381
sections = self.store.get_sections()
2382
# Walk the revisions in the order provided
2387
def match(self, secion):
2388
raise NotImplementedError(self.match)
2391
class LocationSection(Section):
2393
def __init__(self, section, length, extra_path):
2394
super(LocationSection, self).__init__(section.id, section.options)
2395
self.length = length
2396
self.extra_path = extra_path
2398
def get(self, name, default=None):
2399
value = super(LocationSection, self).get(name, default)
2400
if value is not None:
2401
policy_name = self.get(name + ':policy', None)
2402
policy = _policy_value.get(policy_name, POLICY_NONE)
2403
if policy == POLICY_APPENDPATH:
2404
value = urlutils.join(value, self.extra_path)
2408
class LocationMatcher(SectionMatcher):
2410
def __init__(self, store, location):
2411
super(LocationMatcher, self).__init__(store)
2412
self.location = location
2414
def get_sections(self):
2415
# Override the default implementation as we want to change the order
2417
# The following is a bit hackish but ensures compatibility with
2418
# LocationConfig by reusing the same code
2419
sections = list(self.store.get_sections())
2420
filtered_sections = _iter_for_location_by_parts(
2421
[s.id for s in sections], self.location)
2422
iter_sections = iter(sections)
2423
matching_sections = []
2424
for section_id, extra_path, length in filtered_sections:
2425
# a section id is unique for a given store so it's safe to iterate
2427
section = iter_sections.next()
2428
if section_id == section.id:
2429
matching_sections.append(
2430
LocationSection(section, length, extra_path))
2431
# We want the longest (aka more specific) locations first
2432
sections = sorted(matching_sections,
2433
key=lambda section: (section.length, section.id),
2435
# Sections mentioning 'ignore_parents' restrict the selection
2436
for section in sections:
2437
# FIXME: We really want to use as_bool below -- vila 2011-04-07
2438
ignore = section.get('ignore_parents', None)
2439
if ignore is not None:
2440
ignore = ui.bool_from_string(ignore)
2443
# Finally, we have a valid section
2447
class Stack(object):
2448
"""A stack of configurations where an option can be defined"""
2450
def __init__(self, sections_def, store=None, mutable_section_name=None):
2451
"""Creates a stack of sections with an optional store for changes.
2453
:param sections_def: A list of Section or callables that returns an
2454
iterable of Section. This defines the Sections for the Stack and
2455
can be called repeatedly if needed.
2457
:param store: The optional Store where modifications will be
2458
recorded. If none is specified, no modifications can be done.
2460
:param mutable_section_name: The name of the MutableSection where
2461
changes are recorded. This requires the ``store`` parameter to be
2464
self.sections_def = sections_def
2466
self.mutable_section_name = mutable_section_name
2468
def get(self, name):
2469
"""Return the *first* option value found in the sections.
2471
This is where we guarantee that sections coming from Store are loaded
2472
lazily: the loading is delayed until we need to either check that an
2473
option exists or get its value, which in turn may require to discover
2474
in which sections it can be defined. Both of these (section and option
2475
existence) require loading the store (even partially).
2477
# FIXME: No caching of options nor sections yet -- vila 20110503
2479
# Ensuring lazy loading is achieved by delaying section matching until
2480
# it can be avoided anymore by using callables to describe (possibly
2481
# empty) section lists.
2482
for section_or_callable in self.sections_def:
2483
# Each section can expand to multiple ones when a callable is used
2484
if callable(section_or_callable):
2485
sections = section_or_callable()
2487
sections = [section_or_callable]
2488
for section in sections:
2489
value = section.get(name)
2490
if value is not None:
2492
# No definition was found
2495
def _get_mutable_section(self):
2496
"""Get the MutableSection for the Stack.
2498
This is where we guarantee that the mutable section is lazily loaded:
2499
this means we won't load the corresponding store before setting a value
2500
or deleting an option. In practice the store will often be loaded but
2501
this allows catching some programming errors.
2503
section = self.store.get_mutable_section(self.mutable_section_name)
2506
def set(self, name, value):
2507
"""Set a new value for the option."""
2508
section = self._get_mutable_section()
2509
section.set(name, value)
2511
def remove(self, name):
2512
"""Remove an existing option."""
2513
section = self._get_mutable_section()
2514
section.remove(name)
2517
# Mostly for debugging use
2518
return "<config.%s(%s)>" % (self.__class__.__name__, id(self))
2097
2521
class cmd_config(commands.Command):
2098
2522
__doc__ = """Display, set or remove a configuration option.