~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Vincent Ladeuil
  • Date: 2011-06-16 05:21:36 UTC
  • mfrom: (5743.8.25 config-hooks)
  • mto: This revision was merged to the branch mainline in revision 5976.
  • Revision ID: v.ladeuil+lp@free.fr-20110616052136-g4d3yjrqo8knv3x2
Implement config hooks

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(), """
94
93
from bzrlib.util.configobj import configobj
95
94
""")
96
95
from bzrlib import (
 
96
    commands,
 
97
    hooks,
97
98
    registry,
98
99
    )
99
100
from bzrlib.symbol_versioning import (
157
158
        return self[section][name]
158
159
 
159
160
 
160
 
# 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
161
162
# only once for a given bzrlib session, we don't want to re-read the file every
162
163
# time we query for an option so we cache the value (bad ! watch out for tests
163
164
# needing to restore the proper value).This shouldn't be part of 2.4.0 final,
368
369
                              % (option_name,))
369
370
            else:
370
371
                value = self._expand_options_in_string(value)
 
372
        for hook in OldConfigHooks['get']:
 
373
            hook(self, option_name, value)
371
374
        return value
372
375
 
373
376
    def get_user_option_as_bool(self, option_name, expand=None, default=None):
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
 
621
694
            raise errors.ParseConfigError(e.errors, e.config.filename)
622
695
        # Make sure self.reload() will use the right file name
623
696
        self._parser.filename = self.file_name
 
697
        for hook in OldConfigHooks['load']:
 
698
            hook(self)
624
699
        return self._parser
625
700
 
626
701
    def reload(self):
629
704
            raise AssertionError('We need a file name to reload the config')
630
705
        if self._parser is not None:
631
706
            self._parser.reload()
 
707
        for hook in ConfigHooks['load']:
 
708
            hook(self)
632
709
 
633
710
    def _get_matching_sections(self):
634
711
        """Return an ordered list of (section_name, extra_path) pairs.
806
883
        except KeyError:
807
884
            raise errors.NoSuchConfigOption(option_name)
808
885
        self._write_config_file()
 
886
        for hook in OldConfigHooks['remove']:
 
887
            hook(self, option_name)
809
888
 
810
889
    def _write_config_file(self):
811
890
        if self.file_name is None:
817
896
        atomic_file.commit()
818
897
        atomic_file.close()
819
898
        osutils.copy_ownership_from_path(self.file_name)
 
899
        for hook in OldConfigHooks['save']:
 
900
            hook(self)
820
901
 
821
902
 
822
903
class LockableConfig(IniBasedConfig):
950
1031
        self.reload()
951
1032
        self._get_parser().setdefault(section, {})[option] = value
952
1033
        self._write_config_file()
953
 
 
 
1034
        for hook in OldConfigHooks['set']:
 
1035
            hook(self, option, value)
954
1036
 
955
1037
    def _get_sections(self, name=None):
956
1038
        """See IniBasedConfig._get_sections()."""
1152
1234
        # the allowed values of store match the config policies
1153
1235
        self._set_option_policy(location, option, store)
1154
1236
        self._write_config_file()
 
1237
        for hook in OldConfigHooks['set']:
 
1238
            hook(self, option, value)
1155
1239
 
1156
1240
 
1157
1241
class BranchConfig(Config):
1635
1719
        section[option_name] = value
1636
1720
        self._save()
1637
1721
 
1638
 
    def get_credentials(self, scheme, host, port=None, user=None, path=None, 
 
1722
    def get_credentials(self, scheme, host, port=None, user=None, path=None,
1639
1723
                        realm=None):
1640
1724
        """Returns the matching credentials from authentication.conf file.
1641
1725
 
2054
2138
                section_obj = configobj[section]
2055
2139
            except KeyError:
2056
2140
                return default
2057
 
        return section_obj.get(name, default)
 
2141
        value = section_obj.get(name, default)
 
2142
        for hook in OldConfigHooks['get']:
 
2143
            hook(self, name, value)
 
2144
        return value
2058
2145
 
2059
2146
    def set_option(self, value, name, section=None):
2060
2147
        """Set the value associated with a named option.
2068
2155
            configobj[name] = value
2069
2156
        else:
2070
2157
            configobj.setdefault(section, {})[name] = value
 
2158
        for hook in OldConfigHooks['set']:
 
2159
            hook(self, name, value)
2071
2160
        self._set_configobj(configobj)
2072
2161
 
2073
2162
    def remove_option(self, option_name, section_name=None):
2076
2165
            del configobj[option_name]
2077
2166
        else:
2078
2167
            del configobj[section_name][option_name]
 
2168
        for hook in OldConfigHooks['remove']:
 
2169
            hook(self, option_name)
2079
2170
        self._set_configobj(configobj)
2080
2171
 
2081
2172
    def _get_config_file(self):
2082
2173
        try:
2083
 
            return StringIO(self._transport.get_bytes(self._filename))
 
2174
            f = StringIO(self._transport.get_bytes(self._filename))
 
2175
            for hook in OldConfigHooks['load']:
 
2176
                hook(self)
 
2177
            return f
2084
2178
        except errors.NoSuchFile:
2085
2179
            return StringIO()
2086
2180
 
2096
2190
        configobj.write(out_file)
2097
2191
        out_file.seek(0)
2098
2192
        self._transport.put_file(self._filename, out_file)
 
2193
        for hook in OldConfigHooks['save']:
 
2194
            hook(self)
2099
2195
 
2100
2196
 
2101
2197
class Option(object):
2271
2367
            return
2272
2368
        content = self.transport.get_bytes(self.file_name)
2273
2369
        self._load_from_string(content)
 
2370
        for hook in ConfigHooks['load']:
 
2371
            hook(self)
2274
2372
 
2275
2373
    def _load_from_string(self, str_or_unicode):
2276
2374
        """Create a config store from a string.
2298
2396
        out = StringIO()
2299
2397
        self._config_obj.write(out)
2300
2398
        self.transport.put_bytes(self.file_name, out.getvalue())
 
2399
        for hook in ConfigHooks['save']:
 
2400
            hook(self)
2301
2401
 
2302
2402
    def external_url(self):
2303
2403
        # FIXME: external_url should really accepts an optional relpath
2587
2687
                opt = None
2588
2688
            if opt is not None:
2589
2689
                value = opt.get_default()
 
2690
        for hook in ConfigHooks['get']:
 
2691
            hook(self, name, value)
2590
2692
        return value
2591
2693
 
2592
2694
    def _get_mutable_section(self):
2604
2706
        """Set a new value for the option."""
2605
2707
        section = self._get_mutable_section()
2606
2708
        section.set(name, value)
 
2709
        for hook in ConfigHooks['set']:
 
2710
            hook(self, name, value)
2607
2711
 
2608
2712
    def remove(self, name):
2609
2713
        """Remove an existing option."""
2610
2714
        section = self._get_mutable_section()
2611
2715
        section.remove(name)
 
2716
        for hook in ConfigHooks['remove']:
 
2717
            hook(self, name)
2612
2718
 
2613
2719
    def __repr__(self):
2614
2720
        # Mostly for debugging use