~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Martin Pool
  • Date: 2011-06-28 13:55:39 UTC
  • mfrom: (5995 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5996.
  • Revision ID: mbp@canonical.com-20110628135539-6541falwx39fl46i
merge news

Show diffs side-by-side

added added

removed removed

Lines of Context:
65
65
import os
66
66
import string
67
67
import sys
68
 
import weakref
69
 
 
70
 
from bzrlib import commands
 
68
 
 
69
 
71
70
from bzrlib.decorators import needs_write_lock
72
71
from bzrlib.lazy_import import lazy_import
73
72
lazy_import(globals(), """
75
74
import re
76
75
from cStringIO import StringIO
77
76
 
78
 
import bzrlib
79
77
from bzrlib import (
80
78
    atomicfile,
81
79
    bzrdir,
95
93
from bzrlib.util.configobj import configobj
96
94
""")
97
95
from bzrlib import (
 
96
    commands,
 
97
    hooks,
98
98
    registry,
99
99
    )
100
100
from bzrlib.symbol_versioning import (
145
145
                                        interpolation=False,
146
146
                                        **kwargs)
147
147
 
148
 
 
149
148
    def get_bool(self, section, key):
150
149
        return self[section].as_bool(key)
151
150
 
159
158
        return self[section][name]
160
159
 
161
160
 
162
 
# FIXME: Until we can guarantee that each config file is loaded once and and
 
161
# FIXME: Until we can guarantee that each config file is loaded once and
163
162
# only once for a given bzrlib session, we don't want to re-read the file every
164
163
# time we query for an option so we cache the value (bad ! watch out for tests
165
164
# needing to restore the proper value).This shouldn't be part of 2.4.0 final,
370
369
                              % (option_name,))
371
370
            else:
372
371
                value = self._expand_options_in_string(value)
 
372
        for hook in OldConfigHooks['get']:
 
373
            hook(self, option_name, value)
373
374
        return value
374
375
 
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.
 
376
    def get_user_option_as_bool(self, option_name, expand=None, default=None):
 
377
        """Get a generic option as a boolean.
377
378
 
 
379
        :param expand: Allow expanding references to other config values.
 
380
        :param default: Default value if nothing is configured
378
381
        :return None if the option doesn't exist or its value can't be
379
382
            interpreted as a boolean. Returns True or False otherwise.
380
383
        """
381
384
        s = self.get_user_option(option_name, expand=expand)
382
385
        if s is None:
383
386
            # The option doesn't exist
384
 
            return None
 
387
            return default
385
388
        val = ui.bool_from_string(s)
386
389
        if val is None:
387
390
            # The value can't be interpreted as a boolean
492
495
        if policy is None:
493
496
            policy = self._get_signature_checking()
494
497
            if policy is not None:
 
498
                #this warning should go away once check_signatures is
 
499
                #implemented (if not before)
495
500
                trace.warning("Please use create_signatures,"
496
501
                              " not check_signatures to set signing policy.")
497
 
            if policy == CHECK_ALWAYS:
498
 
                return True
499
502
        elif policy == SIGN_ALWAYS:
500
503
            return True
501
504
        return False
544
547
        return tools
545
548
 
546
549
    def find_merge_tool(self, name):
547
 
        # We fake a defaults mechanism here by checking if the given name can 
 
550
        # We fake a defaults mechanism here by checking if the given name can
548
551
        # be found in the known_merge_tools if it's not found in the config.
549
552
        # This should be done through the proposed config defaults mechanism
550
553
        # when it becomes available in the future.
554
557
        return command_line
555
558
 
556
559
 
 
560
class _ConfigHooks(hooks.Hooks):
 
561
    """A dict mapping hook names and a list of callables for configs.
 
562
    """
 
563
 
 
564
    def __init__(self):
 
565
        """Create the default hooks.
 
566
 
 
567
        These are all empty initially, because by default nothing should get
 
568
        notified.
 
569
        """
 
570
        super(_ConfigHooks, self).__init__('bzrlib.config', 'ConfigHooks')
 
571
        self.add_hook('load',
 
572
                      'Invoked when a config store is loaded.'
 
573
                      ' The signature is (store).',
 
574
                      (2, 4))
 
575
        self.add_hook('save',
 
576
                      'Invoked when a config store is saved.'
 
577
                      ' The signature is (store).',
 
578
                      (2, 4))
 
579
        # The hooks for config options
 
580
        self.add_hook('get',
 
581
                      'Invoked when a config option is read.'
 
582
                      ' The signature is (stack, name, value).',
 
583
                      (2, 4))
 
584
        self.add_hook('set',
 
585
                      'Invoked when a config option is set.'
 
586
                      ' The signature is (stack, name, value).',
 
587
                      (2, 4))
 
588
        self.add_hook('remove',
 
589
                      'Invoked when a config option is removed.'
 
590
                      ' The signature is (stack, name).',
 
591
                      (2, 4))
 
592
ConfigHooks = _ConfigHooks()
 
593
 
 
594
 
 
595
class _OldConfigHooks(hooks.Hooks):
 
596
    """A dict mapping hook names and a list of callables for configs.
 
597
    """
 
598
 
 
599
    def __init__(self):
 
600
        """Create the default hooks.
 
601
 
 
602
        These are all empty initially, because by default nothing should get
 
603
        notified.
 
604
        """
 
605
        super(_OldConfigHooks, self).__init__('bzrlib.config', 'OldConfigHooks')
 
606
        self.add_hook('load',
 
607
                      'Invoked when a config store is loaded.'
 
608
                      ' The signature is (config).',
 
609
                      (2, 4))
 
610
        self.add_hook('save',
 
611
                      'Invoked when a config store is saved.'
 
612
                      ' The signature is (config).',
 
613
                      (2, 4))
 
614
        # The hooks for config options
 
615
        self.add_hook('get',
 
616
                      'Invoked when a config option is read.'
 
617
                      ' The signature is (config, name, value).',
 
618
                      (2, 4))
 
619
        self.add_hook('set',
 
620
                      'Invoked when a config option is set.'
 
621
                      ' The signature is (config, name, value).',
 
622
                      (2, 4))
 
623
        self.add_hook('remove',
 
624
                      'Invoked when a config option is removed.'
 
625
                      ' The signature is (config, name).',
 
626
                      (2, 4))
 
627
OldConfigHooks = _OldConfigHooks()
 
628
 
 
629
 
557
630
class IniBasedConfig(Config):
558
631
    """A configuration policy that draws from ini files."""
559
632
 
619
692
            self._parser = ConfigObj(co_input, encoding='utf-8')
620
693
        except configobj.ConfigObjError, e:
621
694
            raise errors.ParseConfigError(e.errors, e.config.filename)
 
695
        except UnicodeDecodeError:
 
696
            raise errors.ConfigContentError(self.file_name)
622
697
        # Make sure self.reload() will use the right file name
623
698
        self._parser.filename = self.file_name
 
699
        for hook in OldConfigHooks['load']:
 
700
            hook(self)
624
701
        return self._parser
625
702
 
626
703
    def reload(self):
629
706
            raise AssertionError('We need a file name to reload the config')
630
707
        if self._parser is not None:
631
708
            self._parser.reload()
 
709
        for hook in ConfigHooks['load']:
 
710
            hook(self)
632
711
 
633
712
    def _get_matching_sections(self):
634
713
        """Return an ordered list of (section_name, extra_path) pairs.
806
885
        except KeyError:
807
886
            raise errors.NoSuchConfigOption(option_name)
808
887
        self._write_config_file()
 
888
        for hook in OldConfigHooks['remove']:
 
889
            hook(self, option_name)
809
890
 
810
891
    def _write_config_file(self):
811
892
        if self.file_name is None:
817
898
        atomic_file.commit()
818
899
        atomic_file.close()
819
900
        osutils.copy_ownership_from_path(self.file_name)
 
901
        for hook in OldConfigHooks['save']:
 
902
            hook(self)
820
903
 
821
904
 
822
905
class LockableConfig(IniBasedConfig):
950
1033
        self.reload()
951
1034
        self._get_parser().setdefault(section, {})[option] = value
952
1035
        self._write_config_file()
953
 
 
 
1036
        for hook in OldConfigHooks['set']:
 
1037
            hook(self, option, value)
954
1038
 
955
1039
    def _get_sections(self, name=None):
956
1040
        """See IniBasedConfig._get_sections()."""
1152
1236
        # the allowed values of store match the config policies
1153
1237
        self._set_option_policy(location, option, store)
1154
1238
        self._write_config_file()
 
1239
        for hook in OldConfigHooks['set']:
 
1240
            hook(self, option, value)
1155
1241
 
1156
1242
 
1157
1243
class BranchConfig(Config):
1613
1699
            self._config = ConfigObj(self._input, encoding='utf-8')
1614
1700
        except configobj.ConfigObjError, e:
1615
1701
            raise errors.ParseConfigError(e.errors, e.config.filename)
 
1702
        except UnicodeError:
 
1703
            raise errors.ConfigContentError(self._filename)
1616
1704
        return self._config
1617
1705
 
1618
1706
    def _save(self):
1635
1723
        section[option_name] = value
1636
1724
        self._save()
1637
1725
 
1638
 
    def get_credentials(self, scheme, host, port=None, user=None, path=None, 
 
1726
    def get_credentials(self, scheme, host, port=None, user=None, path=None,
1639
1727
                        realm=None):
1640
1728
        """Returns the matching credentials from authentication.conf file.
1641
1729
 
2054
2142
                section_obj = configobj[section]
2055
2143
            except KeyError:
2056
2144
                return default
2057
 
        return section_obj.get(name, default)
 
2145
        value = section_obj.get(name, default)
 
2146
        for hook in OldConfigHooks['get']:
 
2147
            hook(self, name, value)
 
2148
        return value
2058
2149
 
2059
2150
    def set_option(self, value, name, section=None):
2060
2151
        """Set the value associated with a named option.
2068
2159
            configobj[name] = value
2069
2160
        else:
2070
2161
            configobj.setdefault(section, {})[name] = value
 
2162
        for hook in OldConfigHooks['set']:
 
2163
            hook(self, name, value)
2071
2164
        self._set_configobj(configobj)
2072
2165
 
2073
2166
    def remove_option(self, option_name, section_name=None):
2076
2169
            del configobj[option_name]
2077
2170
        else:
2078
2171
            del configobj[section_name][option_name]
 
2172
        for hook in OldConfigHooks['remove']:
 
2173
            hook(self, option_name)
2079
2174
        self._set_configobj(configobj)
2080
2175
 
2081
2176
    def _get_config_file(self):
2082
2177
        try:
2083
 
            return StringIO(self._transport.get_bytes(self._filename))
 
2178
            f = StringIO(self._transport.get_bytes(self._filename))
 
2179
            for hook in OldConfigHooks['load']:
 
2180
                hook(self)
 
2181
            return f
2084
2182
        except errors.NoSuchFile:
2085
2183
            return StringIO()
2086
2184
 
 
2185
    def _external_url(self):
 
2186
        return urlutils.join(self._transport.external_url(), self._filename)
 
2187
 
2087
2188
    def _get_configobj(self):
2088
2189
        f = self._get_config_file()
2089
2190
        try:
2090
 
            return ConfigObj(f, encoding='utf-8')
 
2191
            try:
 
2192
                conf = ConfigObj(f, encoding='utf-8')
 
2193
            except configobj.ConfigObjError, e:
 
2194
                raise errors.ParseConfigError(e.errors, self._external_url())
 
2195
            except UnicodeDecodeError:
 
2196
                raise errors.ConfigContentError(self._external_url())
2091
2197
        finally:
2092
2198
            f.close()
 
2199
        return conf
2093
2200
 
2094
2201
    def _set_configobj(self, configobj):
2095
2202
        out_file = StringIO()
2096
2203
        configobj.write(out_file)
2097
2204
        out_file.seek(0)
2098
2205
        self._transport.put_file(self._filename, out_file)
 
2206
        for hook in OldConfigHooks['save']:
 
2207
            hook(self)
2099
2208
 
2100
2209
 
2101
2210
class Option(object):
2189
2298
        """Loads the Store from persistent storage."""
2190
2299
        raise NotImplementedError(self.load)
2191
2300
 
2192
 
    def _load_from_string(self, str_or_unicode):
 
2301
    def _load_from_string(self, bytes):
2193
2302
        """Create a store from a string in configobj syntax.
2194
2303
 
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 :)
 
2304
        :param bytes: A string representing the file content.
2200
2305
        """
2201
2306
        raise NotImplementedError(self._load_from_string)
2202
2307
 
2271
2376
            return
2272
2377
        content = self.transport.get_bytes(self.file_name)
2273
2378
        self._load_from_string(content)
 
2379
        for hook in ConfigHooks['load']:
 
2380
            hook(self)
2274
2381
 
2275
 
    def _load_from_string(self, str_or_unicode):
 
2382
    def _load_from_string(self, bytes):
2276
2383
        """Create a config store from a string.
2277
2384
 
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 :)
 
2385
        :param bytes: A string representing the file content.
2283
2386
        """
2284
2387
        if self.is_loaded():
2285
2388
            raise AssertionError('Already loaded: %r' % (self._config_obj,))
2286
 
        co_input = StringIO(str_or_unicode.encode('utf-8'))
 
2389
        co_input = StringIO(bytes)
2287
2390
        try:
2288
2391
            # The config files are always stored utf8-encoded
2289
2392
            self._config_obj = ConfigObj(co_input, encoding='utf-8')
2290
2393
        except configobj.ConfigObjError, e:
2291
2394
            self._config_obj = None
2292
2395
            raise errors.ParseConfigError(e.errors, self.external_url())
 
2396
        except UnicodeDecodeError:
 
2397
            raise errors.ConfigContentError(self.external_url())
2293
2398
 
2294
2399
    def save(self):
2295
2400
        if not self.is_loaded():
2298
2403
        out = StringIO()
2299
2404
        self._config_obj.write(out)
2300
2405
        self.transport.put_bytes(self.file_name, out.getvalue())
 
2406
        for hook in ConfigHooks['save']:
 
2407
            hook(self)
2301
2408
 
2302
2409
    def external_url(self):
2303
2410
        # FIXME: external_url should really accepts an optional relpath
2587
2694
                opt = None
2588
2695
            if opt is not None:
2589
2696
                value = opt.get_default()
 
2697
        for hook in ConfigHooks['get']:
 
2698
            hook(self, name, value)
2590
2699
        return value
2591
2700
 
2592
2701
    def _get_mutable_section(self):
2604
2713
        """Set a new value for the option."""
2605
2714
        section = self._get_mutable_section()
2606
2715
        section.set(name, value)
 
2716
        for hook in ConfigHooks['set']:
 
2717
            hook(self, name, value)
2607
2718
 
2608
2719
    def remove(self, name):
2609
2720
        """Remove an existing option."""
2610
2721
        section = self._get_mutable_section()
2611
2722
        section.remove(name)
 
2723
        for hook in ConfigHooks['remove']:
 
2724
            hook(self, name)
2612
2725
 
2613
2726
    def __repr__(self):
2614
2727
        # Mostly for debugging use
2701
2814
                        ' the configuration file'),
2702
2815
        ]
2703
2816
 
 
2817
    _see_also = ['configuration']
 
2818
 
2704
2819
    @commands.display_command
2705
2820
    def run(self, name=None, all=False, directory=None, scope=None,
2706
2821
            remove=False):