~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Jelmer Vernooij
  • Date: 2012-01-18 10:42:07 UTC
  • mfrom: (6437.3.6 2.5)
  • mto: (6437.3.7 2.5)
  • mto: This revision was merged to the branch mainline in revision 6441.
  • Revision ID: jelmer@vernstok.nl-20120118104207-yc5fhsaz4mupjcam
Merge 2.5 branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
73
73
"""
74
74
 
75
75
from __future__ import absolute_import
76
 
 
 
76
from cStringIO import StringIO
77
77
import os
78
78
import sys
79
79
 
83
83
lazy_import(globals(), """
84
84
import fnmatch
85
85
import re
86
 
from cStringIO import StringIO
87
86
 
88
87
from bzrlib import (
89
88
    atomicfile,
3114
3113
class IniFileStore(Store):
3115
3114
    """A config Store using ConfigObj for storage.
3116
3115
 
3117
 
    :ivar transport: The transport object where the config file is located.
3118
 
 
3119
 
    :ivar file_name: The config file basename in the transport directory.
3120
 
 
3121
3116
    :ivar _config_obj: Private member to hold the ConfigObj instance used to
3122
3117
        serialize/deserialize the config file.
3123
3118
    """
3253
3248
            value = self._config_obj._unquote(value)
3254
3249
        return value
3255
3250
 
 
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
 
3255
        # anyway.
 
3256
        return 'In-Process Store, no URL'
3256
3257
 
3257
3258
class TransportIniFileStore(IniFileStore):
3258
3259
    """IniFileStore that loads files from a transport.
 
3260
 
 
3261
    :ivar transport: The transport object where the config file is located.
 
3262
 
 
3263
    :ivar file_name: The config file basename in the transport directory.
3259
3264
    """
3260
3265
 
3261
3266
    def __init__(self, transport, file_name):
3435
3440
 
3436
3441
class LocationSection(Section):
3437
3442
 
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)}
3465
3469
        return value
3466
3470
 
3467
3471
 
 
3472
class StartingPathMatcher(SectionMatcher):
 
3473
    """Select sections for a given location respecting the Store order."""
 
3474
 
 
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
 
3482
 
 
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
 
3488
 
 
3489
    def get_sections(self):
 
3490
        """Get all sections matching ``location`` in the store.
 
3491
 
 
3492
        The most generic sections are described first in the store, then more
 
3493
        specific ones can be provided for reduced scopes.
 
3494
 
 
3495
        The returned section are therefore returned in the reversed order so
 
3496
        the most specific ones can be found first.
 
3497
        """
 
3498
        location_parts = self.location.rstrip('/').split('/')
 
3499
        store = self.store
 
3500
        sections = []
 
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)
 
3506
                continue
 
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)
 
3517
 
 
3518
 
3468
3519
class LocationMatcher(SectionMatcher):
3469
3520
 
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)))
3508
3559
                    break
3509
3560
        return matching_sections
3510
3561
 
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),
3517
3568
                          reverse=True)
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: