~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

Move all features to bzrlib.tests.features in 2.5

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
32
34
 
33
35
in locations.conf, you specify the url of a branch and options for it.
34
36
Wildcards may be used - * and ? as normal in shell completion. Options
39
41
email= as above
40
42
check_signatures= as above
41
43
create_signatures= as above.
 
44
validate_signatures_in_log=as above
 
45
acceptable_keys=as above
42
46
 
43
47
explanation of options
44
48
----------------------
45
49
editor - this option sets the pop up editor to use during commits.
46
50
email - this option sets the user id bzr will use when committing.
47
 
check_signatures - this option controls whether bzr will require good gpg
 
51
check_signatures - this option will control whether bzr will require good gpg
48
52
                   signatures, ignore them, or check them if they are
49
 
                   present.
 
53
                   present.  Currently it is unused except that check_signatures
 
54
                   turns on create_signatures.
50
55
create_signatures - this option controls whether bzr will always create
51
 
                    gpg signatures, never create them, or create them if the
52
 
                    branch is configured to require them.
 
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.
53
59
log_format - this option sets the default log format.  Possible values are
54
60
             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
55
64
 
56
65
In bazaar.conf you can also define aliases in the ALIASES sections, example
57
66
 
65
74
import os
66
75
import string
67
76
import sys
68
 
import weakref
69
 
 
70
 
from bzrlib import commands
 
77
 
 
78
 
71
79
from bzrlib.decorators import needs_write_lock
72
80
from bzrlib.lazy_import import lazy_import
73
81
lazy_import(globals(), """
75
83
import re
76
84
from cStringIO import StringIO
77
85
 
78
 
import bzrlib
79
86
from bzrlib import (
80
87
    atomicfile,
81
88
    bzrdir,
82
89
    debug,
83
90
    errors,
 
91
    lazy_regex,
84
92
    lockdir,
85
93
    mail_client,
86
94
    mergetools,
95
103
from bzrlib.util.configobj import configobj
96
104
""")
97
105
from bzrlib import (
 
106
    commands,
 
107
    hooks,
98
108
    registry,
99
109
    )
100
110
from bzrlib.symbol_versioning import (
145
155
                                        interpolation=False,
146
156
                                        **kwargs)
147
157
 
148
 
 
149
158
    def get_bool(self, section, key):
150
159
        return self[section].as_bool(key)
151
160
 
159
168
        return self[section][name]
160
169
 
161
170
 
162
 
# FIXME: Until we can guarantee that each config file is loaded once and and
 
171
# FIXME: Until we can guarantee that each config file is loaded once and
163
172
# only once for a given bzrlib session, we don't want to re-read the file every
164
173
# time we query for an option so we cache the value (bad ! watch out for tests
165
174
# needing to restore the proper value).This shouldn't be part of 2.4.0 final,
370
379
                              % (option_name,))
371
380
            else:
372
381
                value = self._expand_options_in_string(value)
 
382
        for hook in OldConfigHooks['get']:
 
383
            hook(self, option_name, value)
373
384
        return value
374
385
 
375
 
    def get_user_option_as_bool(self, option_name, expand=None):
376
 
        """Get a generic option as a boolean - no special process, no default.
 
386
    def get_user_option_as_bool(self, option_name, expand=None, default=None):
 
387
        """Get a generic option as a boolean.
377
388
 
 
389
        :param expand: Allow expanding references to other config values.
 
390
        :param default: Default value if nothing is configured
378
391
        :return None if the option doesn't exist or its value can't be
379
392
            interpreted as a boolean. Returns True or False otherwise.
380
393
        """
381
394
        s = self.get_user_option(option_name, expand=expand)
382
395
        if s is None:
383
396
            # The option doesn't exist
384
 
            return None
 
397
            return default
385
398
        val = ui.bool_from_string(s)
386
399
        if val is None:
387
400
            # The value can't be interpreted as a boolean
424
437
        """See log_format()."""
425
438
        return None
426
439
 
 
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
 
427
463
    def post_commit(self):
428
464
        """An ordered list of python functions to call.
429
465
 
492
528
        if policy is None:
493
529
            policy = self._get_signature_checking()
494
530
            if policy is not None:
 
531
                #this warning should go away once check_signatures is
 
532
                #implemented (if not before)
495
533
                trace.warning("Please use create_signatures,"
496
534
                              " not check_signatures to set signing policy.")
497
 
            if policy == CHECK_ALWAYS:
498
 
                return True
499
535
        elif policy == SIGN_ALWAYS:
500
536
            return True
501
537
        return False
544
580
        return tools
545
581
 
546
582
    def find_merge_tool(self, name):
547
 
        # We fake a defaults mechanism here by checking if the given name can 
 
583
        # We fake a defaults mechanism here by checking if the given name can
548
584
        # be found in the known_merge_tools if it's not found in the config.
549
585
        # This should be done through the proposed config defaults mechanism
550
586
        # when it becomes available in the future.
554
590
        return command_line
555
591
 
556
592
 
 
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
 
557
663
class IniBasedConfig(Config):
558
664
    """A configuration policy that draws from ini files."""
559
665
 
619
725
            self._parser = ConfigObj(co_input, encoding='utf-8')
620
726
        except configobj.ConfigObjError, e:
621
727
            raise errors.ParseConfigError(e.errors, e.config.filename)
 
728
        except UnicodeDecodeError:
 
729
            raise errors.ConfigContentError(self.file_name)
622
730
        # Make sure self.reload() will use the right file name
623
731
        self._parser.filename = self.file_name
 
732
        for hook in OldConfigHooks['load']:
 
733
            hook(self)
624
734
        return self._parser
625
735
 
626
736
    def reload(self):
629
739
            raise AssertionError('We need a file name to reload the config')
630
740
        if self._parser is not None:
631
741
            self._parser.reload()
 
742
        for hook in ConfigHooks['load']:
 
743
            hook(self)
632
744
 
633
745
    def _get_matching_sections(self):
634
746
        """Return an ordered list of (section_name, extra_path) pairs.
751
863
        """See Config.log_format."""
752
864
        return self._get_user_option('log_format')
753
865
 
 
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
 
754
874
    def _post_commit(self):
755
875
        """See Config.post_commit."""
756
876
        return self._get_user_option('post_commit')
806
926
        except KeyError:
807
927
            raise errors.NoSuchConfigOption(option_name)
808
928
        self._write_config_file()
 
929
        for hook in OldConfigHooks['remove']:
 
930
            hook(self, option_name)
809
931
 
810
932
    def _write_config_file(self):
811
933
        if self.file_name is None:
817
939
        atomic_file.commit()
818
940
        atomic_file.close()
819
941
        osutils.copy_ownership_from_path(self.file_name)
 
942
        for hook in OldConfigHooks['save']:
 
943
            hook(self)
820
944
 
821
945
 
822
946
class LockableConfig(IniBasedConfig):
950
1074
        self.reload()
951
1075
        self._get_parser().setdefault(section, {})[option] = value
952
1076
        self._write_config_file()
953
 
 
 
1077
        for hook in OldConfigHooks['set']:
 
1078
            hook(self, option, value)
954
1079
 
955
1080
    def _get_sections(self, name=None):
956
1081
        """See IniBasedConfig._get_sections()."""
1152
1277
        # the allowed values of store match the config policies
1153
1278
        self._set_option_policy(location, option, store)
1154
1279
        self._write_config_file()
 
1280
        for hook in OldConfigHooks['set']:
 
1281
            hook(self, option, value)
1155
1282
 
1156
1283
 
1157
1284
class BranchConfig(Config):
1324
1451
        """See Config.log_format."""
1325
1452
        return self._get_best_value('_log_format')
1326
1453
 
 
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
 
1327
1462
 
1328
1463
def ensure_config_dir_exists(path=None):
1329
1464
    """Make sure a configuration directory exists.
1613
1748
            self._config = ConfigObj(self._input, encoding='utf-8')
1614
1749
        except configobj.ConfigObjError, e:
1615
1750
            raise errors.ParseConfigError(e.errors, e.config.filename)
 
1751
        except UnicodeError:
 
1752
            raise errors.ConfigContentError(self._filename)
1616
1753
        return self._config
1617
1754
 
1618
1755
    def _save(self):
1635
1772
        section[option_name] = value
1636
1773
        self._save()
1637
1774
 
1638
 
    def get_credentials(self, scheme, host, port=None, user=None, path=None, 
 
1775
    def get_credentials(self, scheme, host, port=None, user=None, path=None,
1639
1776
                        realm=None):
1640
1777
        """Returns the matching credentials from authentication.conf file.
1641
1778
 
2054
2191
                section_obj = configobj[section]
2055
2192
            except KeyError:
2056
2193
                return default
2057
 
        return section_obj.get(name, default)
 
2194
        value = section_obj.get(name, default)
 
2195
        for hook in OldConfigHooks['get']:
 
2196
            hook(self, name, value)
 
2197
        return value
2058
2198
 
2059
2199
    def set_option(self, value, name, section=None):
2060
2200
        """Set the value associated with a named option.
2068
2208
            configobj[name] = value
2069
2209
        else:
2070
2210
            configobj.setdefault(section, {})[name] = value
 
2211
        for hook in OldConfigHooks['set']:
 
2212
            hook(self, name, value)
2071
2213
        self._set_configobj(configobj)
2072
2214
 
2073
2215
    def remove_option(self, option_name, section_name=None):
2076
2218
            del configobj[option_name]
2077
2219
        else:
2078
2220
            del configobj[section_name][option_name]
 
2221
        for hook in OldConfigHooks['remove']:
 
2222
            hook(self, option_name)
2079
2223
        self._set_configobj(configobj)
2080
2224
 
2081
2225
    def _get_config_file(self):
2082
2226
        try:
2083
 
            return StringIO(self._transport.get_bytes(self._filename))
 
2227
            f = StringIO(self._transport.get_bytes(self._filename))
 
2228
            for hook in OldConfigHooks['load']:
 
2229
                hook(self)
 
2230
            return f
2084
2231
        except errors.NoSuchFile:
2085
2232
            return StringIO()
2086
2233
 
 
2234
    def _external_url(self):
 
2235
        return urlutils.join(self._transport.external_url(), self._filename)
 
2236
 
2087
2237
    def _get_configobj(self):
2088
2238
        f = self._get_config_file()
2089
2239
        try:
2090
 
            return ConfigObj(f, encoding='utf-8')
 
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())
2091
2246
        finally:
2092
2247
            f.close()
 
2248
        return conf
2093
2249
 
2094
2250
    def _set_configobj(self, configobj):
2095
2251
        out_file = StringIO()
2096
2252
        configobj.write(out_file)
2097
2253
        out_file.seek(0)
2098
2254
        self._transport.put_file(self._filename, out_file)
 
2255
        for hook in OldConfigHooks['save']:
 
2256
            hook(self)
2099
2257
 
2100
2258
 
2101
2259
class Option(object):
2189
2347
        """Loads the Store from persistent storage."""
2190
2348
        raise NotImplementedError(self.load)
2191
2349
 
2192
 
    def _load_from_string(self, str_or_unicode):
 
2350
    def _load_from_string(self, bytes):
2193
2351
        """Create a store from a string in configobj syntax.
2194
2352
 
2195
 
        :param str_or_unicode: A string representing the file content. This will
2196
 
            be encoded to suit store needs internally.
2197
 
 
2198
 
        This is for tests and should not be used in production unless a
2199
 
        convincing use case can be demonstrated :)
 
2353
        :param bytes: A string representing the file content.
2200
2354
        """
2201
2355
        raise NotImplementedError(self._load_from_string)
2202
2356
 
2271
2425
            return
2272
2426
        content = self.transport.get_bytes(self.file_name)
2273
2427
        self._load_from_string(content)
 
2428
        for hook in ConfigHooks['load']:
 
2429
            hook(self)
2274
2430
 
2275
 
    def _load_from_string(self, str_or_unicode):
 
2431
    def _load_from_string(self, bytes):
2276
2432
        """Create a config store from a string.
2277
2433
 
2278
 
        :param str_or_unicode: A string representing the file content. This will
2279
 
            be utf-8 encoded internally.
2280
 
 
2281
 
        This is for tests and should not be used in production unless a
2282
 
        convincing use case can be demonstrated :)
 
2434
        :param bytes: A string representing the file content.
2283
2435
        """
2284
2436
        if self.is_loaded():
2285
2437
            raise AssertionError('Already loaded: %r' % (self._config_obj,))
2286
 
        co_input = StringIO(str_or_unicode.encode('utf-8'))
 
2438
        co_input = StringIO(bytes)
2287
2439
        try:
2288
2440
            # The config files are always stored utf8-encoded
2289
2441
            self._config_obj = ConfigObj(co_input, encoding='utf-8')
2290
2442
        except configobj.ConfigObjError, e:
2291
2443
            self._config_obj = None
2292
2444
            raise errors.ParseConfigError(e.errors, self.external_url())
 
2445
        except UnicodeDecodeError:
 
2446
            raise errors.ConfigContentError(self.external_url())
2293
2447
 
2294
2448
    def save(self):
2295
2449
        if not self.is_loaded():
2298
2452
        out = StringIO()
2299
2453
        self._config_obj.write(out)
2300
2454
        self.transport.put_bytes(self.file_name, out.getvalue())
 
2455
        for hook in ConfigHooks['save']:
 
2456
            hook(self)
2301
2457
 
2302
2458
    def external_url(self):
2303
2459
        # FIXME: external_url should really accepts an optional relpath
2587
2743
                opt = None
2588
2744
            if opt is not None:
2589
2745
                value = opt.get_default()
 
2746
        for hook in ConfigHooks['get']:
 
2747
            hook(self, name, value)
2590
2748
        return value
2591
2749
 
2592
2750
    def _get_mutable_section(self):
2604
2762
        """Set a new value for the option."""
2605
2763
        section = self._get_mutable_section()
2606
2764
        section.set(name, value)
 
2765
        for hook in ConfigHooks['set']:
 
2766
            hook(self, name, value)
2607
2767
 
2608
2768
    def remove(self, name):
2609
2769
        """Remove an existing option."""
2610
2770
        section = self._get_mutable_section()
2611
2771
        section.remove(name)
 
2772
        for hook in ConfigHooks['remove']:
 
2773
            hook(self, name)
2612
2774
 
2613
2775
    def __repr__(self):
2614
2776
        # Mostly for debugging use
2701
2863
                        ' the configuration file'),
2702
2864
        ]
2703
2865
 
 
2866
    _see_also = ['configuration']
 
2867
 
2704
2868
    @commands.display_command
2705
2869
    def run(self, name=None, all=False, directory=None, scope=None,
2706
2870
            remove=False):
2780
2944
            raise errors.NoSuchConfigOption(name)
2781
2945
 
2782
2946
    def _show_matching_options(self, name, directory, scope):
2783
 
        name = re.compile(name)
 
2947
        name = lazy_regex.lazy_compile(name)
2784
2948
        # We want any error in the regexp to be raised *now* so we need to
2785
 
        # avoid the delay introduced by the lazy regexp.
 
2949
        # avoid the delay introduced by the lazy regexp.  But, we still do
 
2950
        # want the nicer errors raised by lazy_regex.
2786
2951
        name._compile_and_collapse()
2787
2952
        cur_conf_id = None
2788
2953
        cur_section = None