3114
3114
class IniFileStore(Store):
3115
3115
"""A config Store using ConfigObj for storage.
3117
:ivar transport: The transport object where the config file is located.
3119
:ivar file_name: The config file basename in the transport directory.
3121
3117
:ivar _config_obj: Private member to hold the ConfigObj instance used to
3122
3118
serialize/deserialize the config file.
3253
3249
value = self._config_obj._unquote(value)
3252
def external_url(self):
3253
# Since an IniFileStore can be used without a file (at least in tests),
3254
# it's better to provide something than raising a NotImplementedError.
3255
# All daughter classes are supposed to provide an implementation
3257
return 'In-Process Store, no URL'
3257
3259
class TransportIniFileStore(IniFileStore):
3258
3260
"""IniFileStore that loads files from a transport.
3262
:ivar transport: The transport object where the config file is located.
3264
:ivar file_name: The config file basename in the transport directory.
3261
3267
def __init__(self, transport, file_name):
3436
3442
class LocationSection(Section):
3438
def __init__(self, section, length, extra_path):
3444
def __init__(self, section, extra_path):
3439
3445
super(LocationSection, self).__init__(section.id, section.options)
3440
self.length = length
3441
3446
self.extra_path = extra_path
3442
3447
self.locals = {'relpath': extra_path,
3443
3448
'basename': urlutils.basename(extra_path)}
3473
class StartingPathMatcher(SectionMatcher):
3474
"""Select sections for a given location respecting the Store order."""
3476
# FIXME: Both local paths and urls can be used for section names as well as
3477
# ``location`` to stay consistent with ``LocationMatcher`` which itself
3478
# inherited the fuzziness from the previous ``LocationConfig``
3479
# implementation. We probably need to revisit which encoding is allowed for
3480
# both ``location`` and section names and how we normalize
3481
# them. http://pad.lv/85479, http://pad.lv/437009 and http://359320 are
3482
# related too. -- vila 2012-01-04
3484
def __init__(self, store, location):
3485
super(StartingPathMatcher, self).__init__(store)
3486
if location.startswith('file://'):
3487
location = urlutils.local_path_from_url(location)
3488
self.location = location
3490
def get_sections(self):
3491
"""Get all sections matching ``location`` in the store.
3493
The most generic sections are described first in the store, then more
3494
specific ones can be provided for reduced scopes.
3496
The returned section are therefore returned in the reversed order so
3497
the most specific ones can be found first.
3499
location_parts = self.location.rstrip('/').split('/')
3502
# Later sections are more specific, they should be returned first
3503
for _, section in reversed(list(store.get_sections())):
3504
if section.id is None:
3505
# The no-name section is always included if present
3506
yield store, LocationSection(section, self.location)
3508
section_path = section.id
3509
if section_path.startswith('file://'):
3510
# the location is already a local path or URL, convert the
3511
# section id to the same format
3512
section_path = urlutils.local_path_from_url(section_path)
3513
if (self.location.startswith(section_path)
3514
or fnmatch.fnmatch(self.location, section_path)):
3515
section_parts = section_path.rstrip('/').split('/')
3516
extra_path = '/'.join(location_parts[len(section_parts):])
3517
yield store, LocationSection(section, extra_path)
3468
3520
class LocationMatcher(SectionMatcher):
3470
3522
def __init__(self, store, location):
3494
3546
matching_sections = []
3495
3547
if no_name_section is not None:
3496
3548
matching_sections.append(
3497
LocationSection(no_name_section, 0, self.location))
3549
(0, LocationSection(no_name_section, self.location)))
3498
3550
for section_id, extra_path, length in filtered_sections:
3499
3551
# a section id is unique for a given store so it's safe to take the
3500
3552
# first matching section while iterating. Also, all filtered
3504
3556
section = iter_all_sections.next()
3505
3557
if section_id == section.id:
3506
3558
matching_sections.append(
3507
LocationSection(section, length, extra_path))
3559
(length, LocationSection(section, extra_path)))
3509
3561
return matching_sections
3513
3565
matching_sections = self._get_matching_sections()
3514
3566
# We want the longest (aka more specific) locations first
3515
3567
sections = sorted(matching_sections,
3516
key=lambda section: (section.length, section.id),
3568
key=lambda (length, section): (length, section.id),
3518
3570
# Sections mentioning 'ignore_parents' restrict the selection
3519
for section in sections:
3571
for _, section in sections:
3520
3572
# FIXME: We really want to use as_bool below -- vila 2011-04-07
3521
3573
ignore = section.get('ignore_parents', None)
3522
3574
if ignore is not None: