~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Jelmer Vernooij
  • Date: 2011-05-16 13:39:39 UTC
  • mto: (5923.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 5925.
  • Revision ID: jelmer@samba.org-20110516133939-8u1pc9utas3uw1lt
Require a unicode prompt to be passed into all methods that prompt.

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
create_signatures=always|never|when-required(default)
30
30
gpg_signing_command=name-of-program
31
31
log_format=name-of-format
32
 
validate_signatures_in_log=true|false(default)
33
 
acceptable_keys=pattern1,pattern2
34
32
 
35
33
in locations.conf, you specify the url of a branch and options for it.
36
34
Wildcards may be used - * and ? as normal in shell completion. Options
41
39
email= as above
42
40
check_signatures= as above
43
41
create_signatures= as above.
44
 
validate_signatures_in_log=as above
45
 
acceptable_keys=as above
46
42
 
47
43
explanation of options
48
44
----------------------
49
45
editor - this option sets the pop up editor to use during commits.
50
46
email - this option sets the user id bzr will use when committing.
51
 
check_signatures - this option will control whether bzr will require good gpg
 
47
check_signatures - this option controls whether bzr will require good gpg
52
48
                   signatures, ignore them, or check them if they are
53
 
                   present.  Currently it is unused except that check_signatures
54
 
                   turns on create_signatures.
 
49
                   present.
55
50
create_signatures - this option controls whether bzr will always create
56
 
                    gpg signatures or not on commits.  There is an unused
57
 
                    option which in future is expected to work if               
58
 
                    branch settings require signatures.
 
51
                    gpg signatures, never create them, or create them if the
 
52
                    branch is configured to require them.
59
53
log_format - this option sets the default log format.  Possible values are
60
54
             long, short, line, or a plugin can register new formats.
61
 
validate_signatures_in_log - show GPG signature validity in log output
62
 
acceptable_keys - comma separated list of key patterns acceptable for
63
 
                  verify-signatures command
64
55
 
65
56
In bazaar.conf you can also define aliases in the ALIASES sections, example
66
57
 
75
66
import string
76
67
import sys
77
68
 
78
 
 
 
69
from bzrlib import commands
79
70
from bzrlib.decorators import needs_write_lock
80
71
from bzrlib.lazy_import import lazy_import
81
72
lazy_import(globals(), """
83
74
import re
84
75
from cStringIO import StringIO
85
76
 
 
77
import bzrlib
86
78
from bzrlib import (
87
79
    atomicfile,
88
80
    bzrdir,
89
81
    debug,
90
82
    errors,
91
 
    lazy_regex,
92
83
    lockdir,
93
84
    mail_client,
94
85
    mergetools,
95
86
    osutils,
 
87
    registry,
96
88
    symbol_versioning,
97
89
    trace,
98
90
    transport,
102
94
    )
103
95
from bzrlib.util.configobj import configobj
104
96
""")
105
 
from bzrlib import (
106
 
    commands,
107
 
    hooks,
108
 
    registry,
109
 
    )
110
 
from bzrlib.symbol_versioning import (
111
 
    deprecated_in,
112
 
    deprecated_method,
113
 
    )
114
97
 
115
98
 
116
99
CHECK_IF_POSSIBLE=0
155
138
                                        interpolation=False,
156
139
                                        **kwargs)
157
140
 
 
141
 
158
142
    def get_bool(self, section, key):
159
143
        return self[section].as_bool(key)
160
144
 
168
152
        return self[section][name]
169
153
 
170
154
 
171
 
# FIXME: Until we can guarantee that each config file is loaded once and
 
155
# FIXME: Until we can guarantee that each config file is loaded once and and
172
156
# only once for a given bzrlib session, we don't want to re-read the file every
173
157
# time we query for an option so we cache the value (bad ! watch out for tests
174
158
# needing to restore the proper value).This shouldn't be part of 2.4.0 final,
201
185
        """Returns a unique ID for the config."""
202
186
        raise NotImplementedError(self.config_id)
203
187
 
204
 
    @deprecated_method(deprecated_in((2, 4, 0)))
205
188
    def get_editor(self):
206
189
        """Get the users pop up editor."""
207
190
        raise NotImplementedError
214
197
        return diff.DiffFromTool.from_string(cmd, old_tree, new_tree,
215
198
                                             sys.stdout)
216
199
 
 
200
 
217
201
    def get_mail_client(self):
218
202
        """Get a mail client to use"""
219
203
        selected_client = self.get_user_option('mail_client')
379
363
                              % (option_name,))
380
364
            else:
381
365
                value = self._expand_options_in_string(value)
382
 
        for hook in OldConfigHooks['get']:
383
 
            hook(self, option_name, value)
384
366
        return value
385
367
 
386
 
    def get_user_option_as_bool(self, option_name, expand=None, default=None):
387
 
        """Get a generic option as a boolean.
 
368
    def get_user_option_as_bool(self, option_name, expand=None):
 
369
        """Get a generic option as a boolean - no special process, no default.
388
370
 
389
 
        :param expand: Allow expanding references to other config values.
390
 
        :param default: Default value if nothing is configured
391
371
        :return None if the option doesn't exist or its value can't be
392
372
            interpreted as a boolean. Returns True or False otherwise.
393
373
        """
394
374
        s = self.get_user_option(option_name, expand=expand)
395
375
        if s is None:
396
376
            # The option doesn't exist
397
 
            return default
 
377
            return None
398
378
        val = ui.bool_from_string(s)
399
379
        if val is None:
400
380
            # The value can't be interpreted as a boolean
437
417
        """See log_format()."""
438
418
        return None
439
419
 
440
 
    def validate_signatures_in_log(self):
441
 
        """Show GPG signature validity in log"""
442
 
        result = self._validate_signatures_in_log()
443
 
        if result == "true":
444
 
            result = True
445
 
        else:
446
 
            result = False
447
 
        return result
448
 
 
449
 
    def _validate_signatures_in_log(self):
450
 
        """See validate_signatures_in_log()."""
451
 
        return None
452
 
 
453
 
    def acceptable_keys(self):
454
 
        """Comma separated list of key patterns acceptable to 
455
 
        verify-signatures command"""
456
 
        result = self._acceptable_keys()
457
 
        return result
458
 
 
459
 
    def _acceptable_keys(self):
460
 
        """See acceptable_keys()."""
461
 
        return None
462
 
 
463
420
    def post_commit(self):
464
421
        """An ordered list of python functions to call.
465
422
 
528
485
        if policy is None:
529
486
            policy = self._get_signature_checking()
530
487
            if policy is not None:
531
 
                #this warning should go away once check_signatures is
532
 
                #implemented (if not before)
533
488
                trace.warning("Please use create_signatures,"
534
489
                              " not check_signatures to set signing policy.")
 
490
            if policy == CHECK_ALWAYS:
 
491
                return True
535
492
        elif policy == SIGN_ALWAYS:
536
493
            return True
537
494
        return False
580
537
        return tools
581
538
 
582
539
    def find_merge_tool(self, name):
583
 
        # We fake a defaults mechanism here by checking if the given name can
 
540
        # We fake a defaults mechanism here by checking if the given name can 
584
541
        # be found in the known_merge_tools if it's not found in the config.
585
542
        # This should be done through the proposed config defaults mechanism
586
543
        # when it becomes available in the future.
590
547
        return command_line
591
548
 
592
549
 
593
 
class _ConfigHooks(hooks.Hooks):
594
 
    """A dict mapping hook names and a list of callables for configs.
595
 
    """
596
 
 
597
 
    def __init__(self):
598
 
        """Create the default hooks.
599
 
 
600
 
        These are all empty initially, because by default nothing should get
601
 
        notified.
602
 
        """
603
 
        super(_ConfigHooks, self).__init__('bzrlib.config', 'ConfigHooks')
604
 
        self.add_hook('load',
605
 
                      'Invoked when a config store is loaded.'
606
 
                      ' The signature is (store).',
607
 
                      (2, 4))
608
 
        self.add_hook('save',
609
 
                      'Invoked when a config store is saved.'
610
 
                      ' The signature is (store).',
611
 
                      (2, 4))
612
 
        # The hooks for config options
613
 
        self.add_hook('get',
614
 
                      'Invoked when a config option is read.'
615
 
                      ' The signature is (stack, name, value).',
616
 
                      (2, 4))
617
 
        self.add_hook('set',
618
 
                      'Invoked when a config option is set.'
619
 
                      ' The signature is (stack, name, value).',
620
 
                      (2, 4))
621
 
        self.add_hook('remove',
622
 
                      'Invoked when a config option is removed.'
623
 
                      ' The signature is (stack, name).',
624
 
                      (2, 4))
625
 
ConfigHooks = _ConfigHooks()
626
 
 
627
 
 
628
 
class _OldConfigHooks(hooks.Hooks):
629
 
    """A dict mapping hook names and a list of callables for configs.
630
 
    """
631
 
 
632
 
    def __init__(self):
633
 
        """Create the default hooks.
634
 
 
635
 
        These are all empty initially, because by default nothing should get
636
 
        notified.
637
 
        """
638
 
        super(_OldConfigHooks, self).__init__('bzrlib.config', 'OldConfigHooks')
639
 
        self.add_hook('load',
640
 
                      'Invoked when a config store is loaded.'
641
 
                      ' The signature is (config).',
642
 
                      (2, 4))
643
 
        self.add_hook('save',
644
 
                      'Invoked when a config store is saved.'
645
 
                      ' The signature is (config).',
646
 
                      (2, 4))
647
 
        # The hooks for config options
648
 
        self.add_hook('get',
649
 
                      'Invoked when a config option is read.'
650
 
                      ' The signature is (config, name, value).',
651
 
                      (2, 4))
652
 
        self.add_hook('set',
653
 
                      'Invoked when a config option is set.'
654
 
                      ' The signature is (config, name, value).',
655
 
                      (2, 4))
656
 
        self.add_hook('remove',
657
 
                      'Invoked when a config option is removed.'
658
 
                      ' The signature is (config, name).',
659
 
                      (2, 4))
660
 
OldConfigHooks = _OldConfigHooks()
661
 
 
662
 
 
663
550
class IniBasedConfig(Config):
664
551
    """A configuration policy that draws from ini files."""
665
552
 
725
612
            self._parser = ConfigObj(co_input, encoding='utf-8')
726
613
        except configobj.ConfigObjError, e:
727
614
            raise errors.ParseConfigError(e.errors, e.config.filename)
728
 
        except UnicodeDecodeError:
729
 
            raise errors.ConfigContentError(self.file_name)
730
615
        # Make sure self.reload() will use the right file name
731
616
        self._parser.filename = self.file_name
732
 
        for hook in OldConfigHooks['load']:
733
 
            hook(self)
734
617
        return self._parser
735
618
 
736
619
    def reload(self):
739
622
            raise AssertionError('We need a file name to reload the config')
740
623
        if self._parser is not None:
741
624
            self._parser.reload()
742
 
        for hook in ConfigHooks['load']:
743
 
            hook(self)
744
625
 
745
626
    def _get_matching_sections(self):
746
627
        """Return an ordered list of (section_name, extra_path) pairs.
863
744
        """See Config.log_format."""
864
745
        return self._get_user_option('log_format')
865
746
 
866
 
    def _validate_signatures_in_log(self):
867
 
        """See Config.validate_signatures_in_log."""
868
 
        return self._get_user_option('validate_signatures_in_log')
869
 
 
870
 
    def _acceptable_keys(self):
871
 
        """See Config.acceptable_keys."""
872
 
        return self._get_user_option('acceptable_keys')
873
 
 
874
747
    def _post_commit(self):
875
748
        """See Config.post_commit."""
876
749
        return self._get_user_option('post_commit')
926
799
        except KeyError:
927
800
            raise errors.NoSuchConfigOption(option_name)
928
801
        self._write_config_file()
929
 
        for hook in OldConfigHooks['remove']:
930
 
            hook(self, option_name)
931
802
 
932
803
    def _write_config_file(self):
933
804
        if self.file_name is None:
939
810
        atomic_file.commit()
940
811
        atomic_file.close()
941
812
        osutils.copy_ownership_from_path(self.file_name)
942
 
        for hook in OldConfigHooks['save']:
943
 
            hook(self)
944
813
 
945
814
 
946
815
class LockableConfig(IniBasedConfig):
1039
908
        conf._create_from_string(str_or_unicode, save)
1040
909
        return conf
1041
910
 
1042
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1043
911
    def get_editor(self):
1044
912
        return self._get_user_option('editor')
1045
913
 
1074
942
        self.reload()
1075
943
        self._get_parser().setdefault(section, {})[option] = value
1076
944
        self._write_config_file()
1077
 
        for hook in OldConfigHooks['set']:
1078
 
            hook(self, option, value)
 
945
 
1079
946
 
1080
947
    def _get_sections(self, name=None):
1081
948
        """See IniBasedConfig._get_sections()."""
1111
978
        number of path components in the section name, section is the section
1112
979
        name and extra_path is the difference between location and the section
1113
980
        name.
1114
 
 
1115
 
    ``location`` will always be a local path and never a 'file://' url but the
1116
 
    section names themselves can be in either form.
1117
981
    """
1118
982
    location_parts = location.rstrip('/').split('/')
1119
983
 
1120
984
    for section in sections:
1121
 
        # location is a local path if possible, so we need to convert 'file://'
1122
 
        # urls in section names to local paths if necessary.
 
985
        # location is a local path if possible, so we need
 
986
        # to convert 'file://' urls to local paths if necessary.
 
987
 
 
988
        # FIXME: I don't think the above comment is still up to date,
 
989
        # LocationConfig is always instantiated with an url -- vila 2011-04-07
1123
990
 
1124
991
        # This also avoids having file:///path be a more exact
1125
992
        # match than '/path'.
1126
993
 
1127
 
        # FIXME: This still raises an issue if a user defines both file:///path
1128
 
        # *and* /path. Should we raise an error in this case -- vila 20110505
 
994
        # FIXME: Not sure about the above either, but since the path components
 
995
        # are compared in sync, adding two empty components (//) is likely to
 
996
        # trick the comparison and also trick the check on the number of
 
997
        # components, so we *should* take only the relevant part of the url. On
 
998
        # the other hand, this means 'file://' urls *can't* be used in sections
 
999
        # so more work is probably needed -- vila 2011-04-07
1129
1000
 
1130
1001
        if section.startswith('file://'):
1131
1002
            section_path = urlutils.local_path_from_url(section)
1277
1148
        # the allowed values of store match the config policies
1278
1149
        self._set_option_policy(location, option, store)
1279
1150
        self._write_config_file()
1280
 
        for hook in OldConfigHooks['set']:
1281
 
            hook(self, option, value)
1282
1151
 
1283
1152
 
1284
1153
class BranchConfig(Config):
1451
1320
        """See Config.log_format."""
1452
1321
        return self._get_best_value('_log_format')
1453
1322
 
1454
 
    def _validate_signatures_in_log(self):
1455
 
        """See Config.validate_signatures_in_log."""
1456
 
        return self._get_best_value('_validate_signatures_in_log')
1457
 
 
1458
 
    def _acceptable_keys(self):
1459
 
        """See Config.acceptable_keys."""
1460
 
        return self._get_best_value('_acceptable_keys')
1461
 
 
1462
1323
 
1463
1324
def ensure_config_dir_exists(path=None):
1464
1325
    """Make sure a configuration directory exists.
1620
1481
    try:
1621
1482
        w = pwd.getpwuid(uid)
1622
1483
    except KeyError:
1623
 
        trace.mutter('no passwd entry for uid %d?' % uid)
 
1484
        mutter('no passwd entry for uid %d?' % uid)
1624
1485
        return None, None
1625
1486
 
1626
1487
    # we try utf-8 first, because on many variants (like Linux),
1635
1496
            encoding = osutils.get_user_encoding()
1636
1497
            gecos = w.pw_gecos.decode(encoding)
1637
1498
        except UnicodeError, e:
1638
 
            trace.mutter("cannot decode passwd entry %s" % w)
 
1499
            mutter("cannot decode passwd entry %s" % w)
1639
1500
            return None, None
1640
1501
    try:
1641
1502
        username = w.pw_name.decode(encoding)
1642
1503
    except UnicodeError, e:
1643
 
        trace.mutter("cannot decode passwd entry %s" % w)
 
1504
        mutter("cannot decode passwd entry %s" % w)
1644
1505
        return None, None
1645
1506
 
1646
1507
    comma = gecos.find(',')
1748
1609
            self._config = ConfigObj(self._input, encoding='utf-8')
1749
1610
        except configobj.ConfigObjError, e:
1750
1611
            raise errors.ParseConfigError(e.errors, e.config.filename)
1751
 
        except UnicodeError:
1752
 
            raise errors.ConfigContentError(self._filename)
1753
1612
        return self._config
1754
1613
 
1755
1614
    def _save(self):
1772
1631
        section[option_name] = value
1773
1632
        self._save()
1774
1633
 
1775
 
    def get_credentials(self, scheme, host, port=None, user=None, path=None,
 
1634
    def get_credentials(self, scheme, host, port=None, user=None, path=None, 
1776
1635
                        realm=None):
1777
1636
        """Returns the matching credentials from authentication.conf file.
1778
1637
 
1946
1805
            if ask:
1947
1806
                if prompt is None:
1948
1807
                    # Create a default prompt suitable for most cases
1949
 
                    prompt = u'%s' % (scheme.upper(),) + u' %(host)s username'
 
1808
                    prompt = scheme.upper() + ' %(host)s username'
1950
1809
                # Special handling for optional fields in the prompt
1951
1810
                if port is not None:
1952
1811
                    prompt_host = '%s:%d' % (host, port)
1990
1849
        if password is None:
1991
1850
            if prompt is None:
1992
1851
                # Create a default prompt suitable for most cases
1993
 
                prompt = u'%s' % scheme.upper() + u' %(user)s@%(host)s password'
 
1852
                prompt = '%s' % scheme.upper() + ' %(user)s@%(host)s password'
1994
1853
            # Special handling for optional fields in the prompt
1995
1854
            if port is not None:
1996
1855
                prompt_host = '%s:%d' % (host, port)
2191
2050
                section_obj = configobj[section]
2192
2051
            except KeyError:
2193
2052
                return default
2194
 
        value = section_obj.get(name, default)
2195
 
        for hook in OldConfigHooks['get']:
2196
 
            hook(self, name, value)
2197
 
        return value
 
2053
        return section_obj.get(name, default)
2198
2054
 
2199
2055
    def set_option(self, value, name, section=None):
2200
2056
        """Set the value associated with a named option.
2208
2064
            configobj[name] = value
2209
2065
        else:
2210
2066
            configobj.setdefault(section, {})[name] = value
2211
 
        for hook in OldConfigHooks['set']:
2212
 
            hook(self, name, value)
2213
2067
        self._set_configobj(configobj)
2214
2068
 
2215
2069
    def remove_option(self, option_name, section_name=None):
2218
2072
            del configobj[option_name]
2219
2073
        else:
2220
2074
            del configobj[section_name][option_name]
2221
 
        for hook in OldConfigHooks['remove']:
2222
 
            hook(self, option_name)
2223
2075
        self._set_configobj(configobj)
2224
2076
 
2225
2077
    def _get_config_file(self):
2226
2078
        try:
2227
 
            f = StringIO(self._transport.get_bytes(self._filename))
2228
 
            for hook in OldConfigHooks['load']:
2229
 
                hook(self)
2230
 
            return f
 
2079
            return StringIO(self._transport.get_bytes(self._filename))
2231
2080
        except errors.NoSuchFile:
2232
2081
            return StringIO()
2233
2082
 
2234
 
    def _external_url(self):
2235
 
        return urlutils.join(self._transport.external_url(), self._filename)
2236
 
 
2237
2083
    def _get_configobj(self):
2238
2084
        f = self._get_config_file()
2239
2085
        try:
2240
 
            try:
2241
 
                conf = ConfigObj(f, encoding='utf-8')
2242
 
            except configobj.ConfigObjError, e:
2243
 
                raise errors.ParseConfigError(e.errors, self._external_url())
2244
 
            except UnicodeDecodeError:
2245
 
                raise errors.ConfigContentError(self._external_url())
 
2086
            return ConfigObj(f, encoding='utf-8')
2246
2087
        finally:
2247
2088
            f.close()
2248
 
        return conf
2249
2089
 
2250
2090
    def _set_configobj(self, configobj):
2251
2091
        out_file = StringIO()
2252
2092
        configobj.write(out_file)
2253
2093
        out_file.seek(0)
2254
2094
        self._transport.put_file(self._filename, out_file)
2255
 
        for hook in OldConfigHooks['save']:
2256
 
            hook(self)
2257
 
 
2258
 
 
2259
 
class Option(object):
2260
 
    """An option definition.
2261
 
 
2262
 
    The option *values* are stored in config files and found in sections.
2263
 
 
2264
 
    Here we define various properties about the option itself, its default
2265
 
    value, in which config files it can be stored, etc (TBC).
2266
 
    """
2267
 
 
2268
 
    def __init__(self, name, default=None):
2269
 
        self.name = name
2270
 
        self.default = default
2271
 
 
2272
 
    def get_default(self):
2273
 
        return self.default
2274
 
 
2275
 
 
2276
 
# Options registry
2277
 
 
2278
 
option_registry = registry.Registry()
2279
 
 
2280
 
 
2281
 
option_registry.register(
2282
 
    'editor', Option('editor'),
2283
 
    help='The command called to launch an editor to enter a message.')
2284
2095
 
2285
2096
 
2286
2097
class Section(object):
2287
 
    """A section defines a dict of option name => value.
 
2098
    """A section defines a dict of options.
2288
2099
 
2289
2100
    This is merely a read-only dict which can add some knowledge about the
2290
2101
    options. It is *not* a python dict object though and doesn't try to mimic
2347
2158
        """Loads the Store from persistent storage."""
2348
2159
        raise NotImplementedError(self.load)
2349
2160
 
2350
 
    def _load_from_string(self, bytes):
 
2161
    def _load_from_string(self, str_or_unicode):
2351
2162
        """Create a store from a string in configobj syntax.
2352
2163
 
2353
 
        :param bytes: A string representing the file content.
 
2164
        :param str_or_unicode: A string representing the file content. This will
 
2165
            be encoded to suit store needs internally.
 
2166
 
 
2167
        This is for tests and should not be used in production unless a
 
2168
        convincing use case can be demonstrated :)
2354
2169
        """
2355
2170
        raise NotImplementedError(self._load_from_string)
2356
2171
 
2357
 
    def unload(self):
2358
 
        """Unloads the Store.
2359
 
 
2360
 
        This should make is_loaded() return False. This is used when the caller
2361
 
        knows that the persistent storage has changed or may have change since
2362
 
        the last load.
2363
 
        """
2364
 
        raise NotImplementedError(self.unload)
2365
 
 
2366
2172
    def save(self):
2367
2173
        """Saves the Store to persistent storage."""
2368
2174
        raise NotImplementedError(self.save)
2416
2222
    def is_loaded(self):
2417
2223
        return self._config_obj != None
2418
2224
 
2419
 
    def unload(self):
2420
 
        self._config_obj = None
2421
 
 
2422
2225
    def load(self):
2423
2226
        """Load the store from the associated file."""
2424
2227
        if self.is_loaded():
2425
2228
            return
2426
2229
        content = self.transport.get_bytes(self.file_name)
2427
2230
        self._load_from_string(content)
2428
 
        for hook in ConfigHooks['load']:
2429
 
            hook(self)
2430
2231
 
2431
 
    def _load_from_string(self, bytes):
 
2232
    def _load_from_string(self, str_or_unicode):
2432
2233
        """Create a config store from a string.
2433
2234
 
2434
 
        :param bytes: A string representing the file content.
 
2235
        :param str_or_unicode: A string representing the file content. This will
 
2236
            be utf-8 encoded internally.
 
2237
 
 
2238
        This is for tests and should not be used in production unless a
 
2239
        convincing use case can be demonstrated :)
2435
2240
        """
2436
2241
        if self.is_loaded():
2437
2242
            raise AssertionError('Already loaded: %r' % (self._config_obj,))
2438
 
        co_input = StringIO(bytes)
 
2243
        co_input = StringIO(str_or_unicode.encode('utf-8'))
2439
2244
        try:
2440
2245
            # The config files are always stored utf8-encoded
2441
2246
            self._config_obj = ConfigObj(co_input, encoding='utf-8')
2442
2247
        except configobj.ConfigObjError, e:
2443
2248
            self._config_obj = None
2444
2249
            raise errors.ParseConfigError(e.errors, self.external_url())
2445
 
        except UnicodeDecodeError:
2446
 
            raise errors.ConfigContentError(self.external_url())
2447
2250
 
2448
2251
    def save(self):
2449
2252
        if not self.is_loaded():
2452
2255
        out = StringIO()
2453
2256
        self._config_obj.write(out)
2454
2257
        self.transport.put_bytes(self.file_name, out.getvalue())
2455
 
        for hook in ConfigHooks['save']:
2456
 
            hook(self)
2457
2258
 
2458
2259
    def external_url(self):
2459
2260
        # FIXME: external_url should really accepts an optional relpath
2469
2270
        :returns: An iterable of (name, dict).
2470
2271
        """
2471
2272
        # We need a loaded store
2472
 
        try:
2473
 
            self.load()
2474
 
        except errors.NoSuchFile:
2475
 
            # If the file doesn't exist, there is no sections
2476
 
            return
 
2273
        self.load()
2477
2274
        cobj = self._config_obj
2478
2275
        if cobj.scalars:
2479
2276
            yield self.readonly_section_class(None, cobj)
2535
2332
 
2536
2333
    @needs_write_lock
2537
2334
    def save(self):
2538
 
        # We need to be able to override the undecorated implementation
2539
 
        self.save_without_locking()
2540
 
 
2541
 
    def save_without_locking(self):
2542
2335
        super(LockableIniFileStore, self).save()
2543
2336
 
2544
2337
 
2571
2364
    def __init__(self, branch):
2572
2365
        super(BranchStore, self).__init__(branch.control_transport,
2573
2366
                                          'branch.conf')
2574
 
        self.branch = branch
2575
 
 
2576
 
    def lock_write(self, token=None):
2577
 
        return self.branch.lock_write(token)
2578
 
 
2579
 
    def unlock(self):
2580
 
        return self.branch.unlock()
2581
 
 
2582
 
    @needs_write_lock
2583
 
    def save(self):
2584
 
        # We need to be able to override the undecorated implementation
2585
 
        self.save_without_locking()
2586
 
 
2587
 
    def save_without_locking(self):
2588
 
        super(BranchStore, self).save()
2589
 
 
2590
2367
 
2591
2368
class SectionMatcher(object):
2592
2369
    """Select sections into a given Store.
2632
2409
 
2633
2410
    def __init__(self, store, location):
2634
2411
        super(LocationMatcher, self).__init__(store)
2635
 
        if location.startswith('file://'):
2636
 
            location = urlutils.local_path_from_url(location)
2637
2412
        self.location = location
2638
2413
 
2639
 
    def _get_matching_sections(self):
2640
 
        """Get all sections matching ``location``."""
2641
 
        # We slightly diverge from LocalConfig here by allowing the no-name
2642
 
        # section as the most generic one and the lower priority.
2643
 
        no_name_section = None
2644
 
        sections = []
2645
 
        # Filter out the no_name_section so _iter_for_location_by_parts can be
2646
 
        # used (it assumes all sections have a name).
2647
 
        for section in self.store.get_sections():
2648
 
            if section.id is None:
2649
 
                no_name_section = section
2650
 
            else:
2651
 
                sections.append(section)
2652
 
        # Unfortunately _iter_for_location_by_parts deals with section names so
2653
 
        # we have to resync.
 
2414
    def get_sections(self):
 
2415
        # Override the default implementation as we want to change the order
 
2416
 
 
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())
2654
2420
        filtered_sections = _iter_for_location_by_parts(
2655
2421
            [s.id for s in sections], self.location)
2656
2422
        iter_sections = iter(sections)
2657
2423
        matching_sections = []
2658
 
        if no_name_section is not None:
2659
 
            matching_sections.append(
2660
 
                LocationSection(no_name_section, 0, self.location))
2661
2424
        for section_id, extra_path, length in filtered_sections:
2662
2425
            # a section id is unique for a given store so it's safe to iterate
2663
2426
            # again
2665
2428
            if section_id == section.id:
2666
2429
                matching_sections.append(
2667
2430
                    LocationSection(section, length, extra_path))
2668
 
        return matching_sections
2669
 
 
2670
 
    def get_sections(self):
2671
 
        # Override the default implementation as we want to change the order
2672
 
        matching_sections = self._get_matching_sections()
2673
2431
        # We want the longest (aka more specific) locations first
2674
2432
        sections = sorted(matching_sections,
2675
2433
                          key=lambda section: (section.length, section.id),
2717
2475
        existence) require loading the store (even partially).
2718
2476
        """
2719
2477
        # FIXME: No caching of options nor sections yet -- vila 20110503
2720
 
        value = None
2721
 
        # Ensuring lazy loading is achieved by delaying section matching (which
2722
 
        # implies querying the persistent storage) until it can't be avoided
2723
 
        # anymore by using callables to describe (possibly empty) section
2724
 
        # lists.
 
2478
 
 
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.
2725
2482
        for section_or_callable in self.sections_def:
2726
2483
            # Each section can expand to multiple ones when a callable is used
2727
2484
            if callable(section_or_callable):
2731
2488
            for section in sections:
2732
2489
                value = section.get(name)
2733
2490
                if value is not None:
2734
 
                    break
2735
 
            if value is not None:
2736
 
                break
2737
 
        if value is None:
2738
 
            # If the option is registered, it may provide a default value
2739
 
            try:
2740
 
                opt = option_registry.get(name)
2741
 
            except KeyError:
2742
 
                # Not registered
2743
 
                opt = None
2744
 
            if opt is not None:
2745
 
                value = opt.get_default()
2746
 
        for hook in ConfigHooks['get']:
2747
 
            hook(self, name, value)
2748
 
        return value
 
2491
                    return value
 
2492
        # No definition was found
 
2493
        return None
2749
2494
 
2750
2495
    def _get_mutable_section(self):
2751
2496
        """Get the MutableSection for the Stack.
2753
2498
        This is where we guarantee that the mutable section is lazily loaded:
2754
2499
        this means we won't load the corresponding store before setting a value
2755
2500
        or deleting an option. In practice the store will often be loaded but
2756
 
        this allows helps catching some programming errors.
 
2501
        this allows catching some programming errors.
2757
2502
        """
2758
2503
        section = self.store.get_mutable_section(self.mutable_section_name)
2759
2504
        return section
2762
2507
        """Set a new value for the option."""
2763
2508
        section = self._get_mutable_section()
2764
2509
        section.set(name, value)
2765
 
        for hook in ConfigHooks['set']:
2766
 
            hook(self, name, value)
2767
2510
 
2768
2511
    def remove(self, name):
2769
2512
        """Remove an existing option."""
2770
2513
        section = self._get_mutable_section()
2771
2514
        section.remove(name)
2772
 
        for hook in ConfigHooks['remove']:
2773
 
            hook(self, name)
2774
2515
 
2775
2516
    def __repr__(self):
2776
2517
        # Mostly for debugging use
2777
2518
        return "<config.%s(%s)>" % (self.__class__.__name__, id(self))
2778
2519
 
2779
2520
 
2780
 
class _CompatibleStack(Stack):
2781
 
    """Place holder for compatibility with previous design.
2782
 
 
2783
 
    This is intended to ease the transition from the Config-based design to the
2784
 
    Stack-based design and should not be used nor relied upon by plugins.
2785
 
 
2786
 
    One assumption made here is that the daughter classes will all use Stores
2787
 
    derived from LockableIniFileStore).
2788
 
 
2789
 
    It implements set() by re-loading the store before applying the
2790
 
    modification and saving it.
2791
 
 
2792
 
    The long term plan being to implement a single write by store to save
2793
 
    all modifications, this class should not be used in the interim.
2794
 
    """
2795
 
 
2796
 
    def set(self, name, value):
2797
 
        # Force a reload
2798
 
        self.store.unload()
2799
 
        super(_CompatibleStack, self).set(name, value)
2800
 
        # Force a write to persistent storage
2801
 
        self.store.save()
2802
 
 
2803
 
 
2804
 
class GlobalStack(_CompatibleStack):
2805
 
 
2806
 
    def __init__(self):
2807
 
        # Get a GlobalStore
2808
 
        gstore = GlobalStore()
2809
 
        super(GlobalStack, self).__init__([gstore.get_sections], gstore)
2810
 
 
2811
 
 
2812
 
class LocationStack(_CompatibleStack):
2813
 
 
2814
 
    def __init__(self, location):
2815
 
        lstore = LocationStore()
2816
 
        matcher = LocationMatcher(lstore, location)
2817
 
        gstore = GlobalStore()
2818
 
        super(LocationStack, self).__init__(
2819
 
            [matcher.get_sections, gstore.get_sections], lstore)
2820
 
 
2821
 
class BranchStack(_CompatibleStack):
2822
 
 
2823
 
    def __init__(self, branch):
2824
 
        bstore = BranchStore(branch)
2825
 
        lstore = LocationStore()
2826
 
        matcher = LocationMatcher(lstore, branch.base)
2827
 
        gstore = GlobalStore()
2828
 
        super(BranchStack, self).__init__(
2829
 
            [matcher.get_sections, bstore.get_sections, gstore.get_sections],
2830
 
            bstore)
2831
 
        self.branch = branch
2832
 
 
2833
 
 
2834
2521
class cmd_config(commands.Command):
2835
2522
    __doc__ = """Display, set or remove a configuration option.
2836
2523
 
2863
2550
                        ' the configuration file'),
2864
2551
        ]
2865
2552
 
2866
 
    _see_also = ['configuration']
2867
 
 
2868
2553
    @commands.display_command
2869
2554
    def run(self, name=None, all=False, directory=None, scope=None,
2870
2555
            remove=False):
2944
2629
            raise errors.NoSuchConfigOption(name)
2945
2630
 
2946
2631
    def _show_matching_options(self, name, directory, scope):
2947
 
        name = lazy_regex.lazy_compile(name)
 
2632
        name = re.compile(name)
2948
2633
        # We want any error in the regexp to be raised *now* so we need to
2949
 
        # avoid the delay introduced by the lazy regexp.  But, we still do
2950
 
        # want the nicer errors raised by lazy_regex.
 
2634
        # avoid the delay introduced by the lazy regexp.
2951
2635
        name._compile_and_collapse()
2952
2636
        cur_conf_id = None
2953
2637
        cur_section = None
2997
2681
            raise errors.NoSuchConfig(scope)
2998
2682
        if not removed:
2999
2683
            raise errors.NoSuchConfigOption(name)
3000
 
 
3001
 
# Test registries
3002
 
#
3003
 
# We need adapters that can build a Store or a Stack in a test context. Test
3004
 
# classes, based on TestCaseWithTransport, can use the registry to parametrize
3005
 
# themselves. The builder will receive a test instance and should return a
3006
 
# ready-to-use store or stack.  Plugins that define new store/stacks can also
3007
 
# register themselves here to be tested against the tests defined in
3008
 
# bzrlib.tests.test_config. Note that the builder can be called multiple times
3009
 
# for the same tests.
3010
 
 
3011
 
# The registered object should be a callable receiving a test instance
3012
 
# parameter (inheriting from tests.TestCaseWithTransport) and returning a Store
3013
 
# object.
3014
 
test_store_builder_registry = registry.Registry()
3015
 
 
3016
 
# The registered object should be a callable receiving a test instance
3017
 
# parameter (inheriting from tests.TestCaseWithTransport) and returning a Stack
3018
 
# object.
3019
 
test_stack_builder_registry = registry.Registry()