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