1993
2092
self._transport.put_file(self._filename, out_file)
2095
class Section(object):
2096
"""A section defines a dict of options.
2098
This is merely a read-only dict which can add some knowledge about the
2099
options. It is *not* a python dict object though and doesn't try to mimic
2103
def __init__(self, section_id, options):
2104
self.id = section_id
2105
# We re-use the dict-like object received
2106
self.options = options
2108
def get(self, name, default=None):
2109
return self.options.get(name, default)
2112
# Mostly for debugging use
2113
return "<config.%s id=%s>" % (self.__class__.__name__, self.id)
2116
_NewlyCreatedOption = object()
2117
"""Was the option created during the MutableSection lifetime"""
2120
class MutableSection(Section):
2121
"""A section allowing changes and keeping track of the original values."""
2123
def __init__(self, section_id, options):
2124
super(MutableSection, self).__init__(section_id, options)
2127
def set(self, name, value):
2128
if name not in self.options:
2129
# This is a new option
2130
self.orig[name] = _NewlyCreatedOption
2131
elif name not in self.orig:
2132
self.orig[name] = self.get(name, None)
2133
self.options[name] = value
2135
def remove(self, name):
2136
if name not in self.orig:
2137
self.orig[name] = self.get(name, None)
2138
del self.options[name]
2141
class Store(object):
2142
"""Abstract interface to persistent storage for configuration options."""
2144
readonly_section_class = Section
2145
mutable_section_class = MutableSection
2147
def is_loaded(self):
2148
"""Returns True if the Store has been loaded.
2150
This is used to implement lazy loading and ensure the persistent
2151
storage is queried only when needed.
2153
raise NotImplementedError(self.is_loaded)
2156
"""Loads the Store from persistent storage."""
2157
raise NotImplementedError(self.load)
2159
def _load_from_string(self, str_or_unicode):
2160
"""Create a store from a string in configobj syntax.
2162
:param str_or_unicode: A string representing the file content. This will
2163
be encoded to suit store needs internally.
2165
This is for tests and should not be used in production unless a
2166
convincing use case can be demonstrated :)
2168
raise NotImplementedError(self._load_from_string)
2171
"""Saves the Store to persistent storage."""
2172
raise NotImplementedError(self.save)
2174
def external_url(self):
2175
raise NotImplementedError(self.external_url)
2177
def get_sections(self):
2178
"""Returns an ordered iterable of existing sections.
2180
:returns: An iterable of (name, dict).
2182
raise NotImplementedError(self.get_sections)
2184
def get_mutable_section(self, section_name=None):
2185
"""Returns the specified mutable section.
2187
:param section_name: The section identifier
2189
raise NotImplementedError(self.get_mutable_section)
2192
# Mostly for debugging use
2193
return "<config.%s(%s)>" % (self.__class__.__name__,
2194
self.external_url())
2197
class IniFileStore(Store):
2198
"""A config Store using ConfigObj for storage.
2200
:ivar transport: The transport object where the config file is located.
2202
:ivar file_name: The config file basename in the transport directory.
2204
:ivar _config_obj: Private member to hold the ConfigObj instance used to
2205
serialize/deserialize the config file.
2208
def __init__(self, transport, file_name):
2209
"""A config Store using ConfigObj for storage.
2211
:param transport: The transport object where the config file is located.
2213
:param file_name: The config file basename in the transport directory.
2215
super(IniFileStore, self).__init__()
2216
self.transport = transport
2217
self.file_name = file_name
2218
self._config_obj = None
2220
def is_loaded(self):
2221
return self._config_obj != None
2224
"""Load the store from the associated file."""
2225
if self.is_loaded():
2227
content = self.transport.get_bytes(self.file_name)
2228
self._load_from_string(content)
2230
def _load_from_string(self, str_or_unicode):
2231
"""Create a config store from a string.
2233
:param str_or_unicode: A string representing the file content. This will
2234
be utf-8 encoded internally.
2236
This is for tests and should not be used in production unless a
2237
convincing use case can be demonstrated :)
2239
if self.is_loaded():
2240
raise AssertionError('Already loaded: %r' % (self._config_obj,))
2241
co_input = StringIO(str_or_unicode.encode('utf-8'))
2243
# The config files are always stored utf8-encoded
2244
self._config_obj = ConfigObj(co_input, encoding='utf-8')
2245
except configobj.ConfigObjError, e:
2246
self._config_obj = None
2247
raise errors.ParseConfigError(e.errors, self.external_url())
2250
if not self.is_loaded():
2254
self._config_obj.write(out)
2255
self.transport.put_bytes(self.file_name, out.getvalue())
2257
def external_url(self):
2258
# FIXME: external_url should really accepts an optional relpath
2259
# parameter (bug #750169) :-/ -- vila 2011-04-04
2260
# The following will do in the interim but maybe we don't want to
2261
# expose a path here but rather a config ID and its associated
2262
# object </hand wawe>.
2263
return urlutils.join(self.transport.external_url(), self.file_name)
2265
def get_sections(self):
2266
"""Get the configobj section in the file order.
2268
:returns: An iterable of (name, dict).
2270
# We need a loaded store
2273
except errors.NoSuchFile:
2274
# If the file doesn't exist, there is no sections
2276
cobj = self._config_obj
2278
yield self.readonly_section_class(None, cobj)
2279
for section_name in cobj.sections:
2280
yield self.readonly_section_class(section_name, cobj[section_name])
2282
def get_mutable_section(self, section_name=None):
2283
# We need a loaded store
2286
except errors.NoSuchFile:
2287
# The file doesn't exist, let's pretend it was empty
2288
self._load_from_string('')
2289
if section_name is None:
2290
section = self._config_obj
2292
section = self._config_obj.setdefault(section_name, {})
2293
return self.mutable_section_class(section_name, section)
2296
# Note that LockableConfigObjStore inherits from ConfigObjStore because we need
2297
# unlockable stores for use with objects that can already ensure the locking
2298
# (think branches). If different stores (not based on ConfigObj) are created,
2299
# they may face the same issue.
2302
class LockableIniFileStore(IniFileStore):
2303
"""A ConfigObjStore using locks on save to ensure store integrity."""
2305
def __init__(self, transport, file_name, lock_dir_name=None):
2306
"""A config Store using ConfigObj for storage.
2308
:param transport: The transport object where the config file is located.
2310
:param file_name: The config file basename in the transport directory.
2312
if lock_dir_name is None:
2313
lock_dir_name = 'lock'
2314
self.lock_dir_name = lock_dir_name
2315
super(LockableIniFileStore, self).__init__(transport, file_name)
2316
self._lock = lockdir.LockDir(self.transport, self.lock_dir_name)
2318
def lock_write(self, token=None):
2319
"""Takes a write lock in the directory containing the config file.
2321
If the directory doesn't exist it is created.
2323
# FIXME: This doesn't check the ownership of the created directories as
2324
# ensure_config_dir_exists does. It should if the transport is local
2325
# -- vila 2011-04-06
2326
self.transport.create_prefix()
2327
return self._lock.lock_write(token)
2332
def break_lock(self):
2333
self._lock.break_lock()
2337
super(LockableIniFileStore, self).save()
2340
# FIXME: global, bazaar, shouldn't that be 'user' instead or even
2341
# 'user_defaults' as opposed to 'user_overrides', 'system_defaults'
2342
# (/etc/bzr/bazaar.conf) and 'system_overrides' ? -- vila 2011-04-05
2344
# FIXME: Moreover, we shouldn't need classes for these stores either, factory
2345
# functions or a registry will make it easier and clearer for tests, focusing
2346
# on the relevant parts of the API that needs testing -- vila 20110503 (based
2347
# on a poolie's remark)
2348
class GlobalStore(LockableIniFileStore):
2350
def __init__(self, possible_transports=None):
2351
t = transport.get_transport(config_dir(),
2352
possible_transports=possible_transports)
2353
super(GlobalStore, self).__init__(t, 'bazaar.conf')
2356
class LocationStore(LockableIniFileStore):
2358
def __init__(self, possible_transports=None):
2359
t = transport.get_transport(config_dir(),
2360
possible_transports=possible_transports)
2361
super(LocationStore, self).__init__(t, 'locations.conf')
2364
class BranchStore(IniFileStore):
2366
def __init__(self, branch):
2367
super(BranchStore, self).__init__(branch.control_transport,
2370
class SectionMatcher(object):
2371
"""Select sections into a given Store.
2373
This intended to be used to postpone getting an iterable of sections from a
2377
def __init__(self, store):
2380
def get_sections(self):
2381
# This is where we require loading the store so we can see all defined
2383
sections = self.store.get_sections()
2384
# Walk the revisions in the order provided
2389
def match(self, secion):
2390
raise NotImplementedError(self.match)
2393
class LocationSection(Section):
2395
def __init__(self, section, length, extra_path):
2396
super(LocationSection, self).__init__(section.id, section.options)
2397
self.length = length
2398
self.extra_path = extra_path
2400
def get(self, name, default=None):
2401
value = super(LocationSection, self).get(name, default)
2402
if value is not None:
2403
policy_name = self.get(name + ':policy', None)
2404
policy = _policy_value.get(policy_name, POLICY_NONE)
2405
if policy == POLICY_APPENDPATH:
2406
value = urlutils.join(value, self.extra_path)
2410
class LocationMatcher(SectionMatcher):
2412
def __init__(self, store, location):
2413
super(LocationMatcher, self).__init__(store)
2414
if location.startswith('file://'):
2415
location = urlutils.local_path_from_url(location)
2416
self.location = location
2418
def _get_matching_sections(self):
2419
"""Get all sections matching ``location``."""
2420
# We slightly diverge from LocalConfig here by allowing the no-name
2421
# section as the most generic one and the lower priority.
2422
no_name_section = None
2424
# Filter out the no_name_section so _iter_for_location_by_parts can be
2425
# used (it assumes all sections have a name).
2426
for section in self.store.get_sections():
2427
if section.id is None:
2428
no_name_section = section
2430
sections.append(section)
2431
# Unfortunately _iter_for_location_by_parts deals with section names so
2432
# we have to resync.
2433
filtered_sections = _iter_for_location_by_parts(
2434
[s.id for s in sections], self.location)
2435
iter_sections = iter(sections)
2436
matching_sections = []
2437
if no_name_section is not None:
2438
matching_sections.append(
2439
LocationSection(no_name_section, 0, self.location))
2440
for section_id, extra_path, length in filtered_sections:
2441
# a section id is unique for a given store so it's safe to iterate
2443
section = iter_sections.next()
2444
if section_id == section.id:
2445
matching_sections.append(
2446
LocationSection(section, length, extra_path))
2447
return matching_sections
2449
def get_sections(self):
2450
# Override the default implementation as we want to change the order
2451
matching_sections = self._get_matching_sections()
2452
# We want the longest (aka more specific) locations first
2453
sections = sorted(matching_sections,
2454
key=lambda section: (section.length, section.id),
2456
# Sections mentioning 'ignore_parents' restrict the selection
2457
for section in sections:
2458
# FIXME: We really want to use as_bool below -- vila 2011-04-07
2459
ignore = section.get('ignore_parents', None)
2460
if ignore is not None:
2461
ignore = ui.bool_from_string(ignore)
2464
# Finally, we have a valid section
2468
class Stack(object):
2469
"""A stack of configurations where an option can be defined"""
2471
def __init__(self, sections_def, store=None, mutable_section_name=None):
2472
"""Creates a stack of sections with an optional store for changes.
2474
:param sections_def: A list of Section or callables that returns an
2475
iterable of Section. This defines the Sections for the Stack and
2476
can be called repeatedly if needed.
2478
:param store: The optional Store where modifications will be
2479
recorded. If none is specified, no modifications can be done.
2481
:param mutable_section_name: The name of the MutableSection where
2482
changes are recorded. This requires the ``store`` parameter to be
2485
self.sections_def = sections_def
2487
self.mutable_section_name = mutable_section_name
2489
def get(self, name):
2490
"""Return the *first* option value found in the sections.
2492
This is where we guarantee that sections coming from Store are loaded
2493
lazily: the loading is delayed until we need to either check that an
2494
option exists or get its value, which in turn may require to discover
2495
in which sections it can be defined. Both of these (section and option
2496
existence) require loading the store (even partially).
2498
# FIXME: No caching of options nor sections yet -- vila 20110503
2500
# Ensuring lazy loading is achieved by delaying section matching (which
2501
# implies querying the persistent storage) until it can't be avoided
2502
# anymore by using callables to describe (possibly empty) section
2504
for section_or_callable in self.sections_def:
2505
# Each section can expand to multiple ones when a callable is used
2506
if callable(section_or_callable):
2507
sections = section_or_callable()
2509
sections = [section_or_callable]
2510
for section in sections:
2511
value = section.get(name)
2512
if value is not None:
2514
# No definition was found
2517
def _get_mutable_section(self):
2518
"""Get the MutableSection for the Stack.
2520
This is where we guarantee that the mutable section is lazily loaded:
2521
this means we won't load the corresponding store before setting a value
2522
or deleting an option. In practice the store will often be loaded but
2523
this allows helps catching some programming errors.
2525
section = self.store.get_mutable_section(self.mutable_section_name)
2528
def set(self, name, value):
2529
"""Set a new value for the option."""
2530
section = self._get_mutable_section()
2531
section.set(name, value)
2533
def remove(self, name):
2534
"""Remove an existing option."""
2535
section = self._get_mutable_section()
2536
section.remove(name)
2539
# Mostly for debugging use
2540
return "<config.%s(%s)>" % (self.__class__.__name__, id(self))
2543
class GlobalStack(Stack):
2547
gstore = GlobalStore()
2548
super(GlobalStack, self).__init__([gstore.get_sections], gstore)
2551
class LocationStack(Stack):
2553
def __init__(self, location):
2554
lstore = LocationStore()
2555
matcher = LocationMatcher(lstore, location)
2556
gstore = GlobalStore()
2557
super(LocationStack, self).__init__(
2558
[matcher.get_sections, gstore.get_sections], lstore)
2561
class BranchStack(Stack):
2563
def __init__(self, branch):
2564
bstore = BranchStore(branch)
2565
lstore = LocationStore()
2566
matcher = LocationMatcher(lstore, branch.base)
2567
gstore = GlobalStore()
2568
super(BranchStack, self).__init__(
2569
[matcher.get_sections, bstore.get_sections, gstore.get_sections],
1996
2573
class cmd_config(commands.Command):
1997
2574
__doc__ = """Display, set or remove a configuration option.