~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Vincent Ladeuil
  • Date: 2011-06-27 15:42:09 UTC
  • mfrom: (5993 +trunk)
  • mto: (5993.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 5994.
  • Revision ID: v.ladeuil+lp@free.fr-20110627154209-azubuhbuxsz109hq
Merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
66
66
import string
67
67
import sys
68
68
 
69
 
from bzrlib import commands
 
69
 
70
70
from bzrlib.decorators import needs_write_lock
71
71
from bzrlib.lazy_import import lazy_import
72
72
lazy_import(globals(), """
74
74
import re
75
75
from cStringIO import StringIO
76
76
 
77
 
import bzrlib
78
77
from bzrlib import (
79
78
    atomicfile,
80
79
    bzrdir,
94
93
from bzrlib.util.configobj import configobj
95
94
""")
96
95
from bzrlib import (
 
96
    commands,
 
97
    hooks,
97
98
    registry,
98
99
    )
 
100
from bzrlib.symbol_versioning import (
 
101
    deprecated_in,
 
102
    deprecated_method,
 
103
    )
99
104
 
100
105
 
101
106
CHECK_IF_POSSIBLE=0
140
145
                                        interpolation=False,
141
146
                                        **kwargs)
142
147
 
143
 
 
144
148
    def get_bool(self, section, key):
145
149
        return self[section].as_bool(key)
146
150
 
154
158
        return self[section][name]
155
159
 
156
160
 
157
 
# 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
158
162
# only once for a given bzrlib session, we don't want to re-read the file every
159
163
# time we query for an option so we cache the value (bad ! watch out for tests
160
164
# needing to restore the proper value).This shouldn't be part of 2.4.0 final,
187
191
        """Returns a unique ID for the config."""
188
192
        raise NotImplementedError(self.config_id)
189
193
 
 
194
    @deprecated_method(deprecated_in((2, 4, 0)))
190
195
    def get_editor(self):
191
196
        """Get the users pop up editor."""
192
197
        raise NotImplementedError
199
204
        return diff.DiffFromTool.from_string(cmd, old_tree, new_tree,
200
205
                                             sys.stdout)
201
206
 
202
 
 
203
207
    def get_mail_client(self):
204
208
        """Get a mail client to use"""
205
209
        selected_client = self.get_user_option('mail_client')
365
369
                              % (option_name,))
366
370
            else:
367
371
                value = self._expand_options_in_string(value)
 
372
        for hook in OldConfigHooks['get']:
 
373
            hook(self, option_name, value)
368
374
        return value
369
375
 
370
 
    def get_user_option_as_bool(self, option_name, expand=None):
371
 
        """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.
372
378
 
 
379
        :param expand: Allow expanding references to other config values.
 
380
        :param default: Default value if nothing is configured
373
381
        :return None if the option doesn't exist or its value can't be
374
382
            interpreted as a boolean. Returns True or False otherwise.
375
383
        """
376
384
        s = self.get_user_option(option_name, expand=expand)
377
385
        if s is None:
378
386
            # The option doesn't exist
379
 
            return None
 
387
            return default
380
388
        val = ui.bool_from_string(s)
381
389
        if val is None:
382
390
            # The value can't be interpreted as a boolean
487
495
        if policy is None:
488
496
            policy = self._get_signature_checking()
489
497
            if policy is not None:
 
498
                #this warning should go away once check_signatures is
 
499
                #implemented (if not before)
490
500
                trace.warning("Please use create_signatures,"
491
501
                              " not check_signatures to set signing policy.")
492
 
            if policy == CHECK_ALWAYS:
493
 
                return True
494
502
        elif policy == SIGN_ALWAYS:
495
503
            return True
496
504
        return False
539
547
        return tools
540
548
 
541
549
    def find_merge_tool(self, name):
542
 
        # 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
543
551
        # be found in the known_merge_tools if it's not found in the config.
544
552
        # This should be done through the proposed config defaults mechanism
545
553
        # when it becomes available in the future.
549
557
        return command_line
550
558
 
551
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
 
552
630
class IniBasedConfig(Config):
553
631
    """A configuration policy that draws from ini files."""
554
632
 
614
692
            self._parser = ConfigObj(co_input, encoding='utf-8')
615
693
        except configobj.ConfigObjError, e:
616
694
            raise errors.ParseConfigError(e.errors, e.config.filename)
 
695
        except UnicodeDecodeError:
 
696
            raise errors.ConfigContentError(self.file_name)
617
697
        # Make sure self.reload() will use the right file name
618
698
        self._parser.filename = self.file_name
 
699
        for hook in OldConfigHooks['load']:
 
700
            hook(self)
619
701
        return self._parser
620
702
 
621
703
    def reload(self):
624
706
            raise AssertionError('We need a file name to reload the config')
625
707
        if self._parser is not None:
626
708
            self._parser.reload()
 
709
        for hook in ConfigHooks['load']:
 
710
            hook(self)
627
711
 
628
712
    def _get_matching_sections(self):
629
713
        """Return an ordered list of (section_name, extra_path) pairs.
801
885
        except KeyError:
802
886
            raise errors.NoSuchConfigOption(option_name)
803
887
        self._write_config_file()
 
888
        for hook in OldConfigHooks['remove']:
 
889
            hook(self, option_name)
804
890
 
805
891
    def _write_config_file(self):
806
892
        if self.file_name is None:
812
898
        atomic_file.commit()
813
899
        atomic_file.close()
814
900
        osutils.copy_ownership_from_path(self.file_name)
 
901
        for hook in OldConfigHooks['save']:
 
902
            hook(self)
815
903
 
816
904
 
817
905
class LockableConfig(IniBasedConfig):
910
998
        conf._create_from_string(str_or_unicode, save)
911
999
        return conf
912
1000
 
 
1001
    @deprecated_method(deprecated_in((2, 4, 0)))
913
1002
    def get_editor(self):
914
1003
        return self._get_user_option('editor')
915
1004
 
944
1033
        self.reload()
945
1034
        self._get_parser().setdefault(section, {})[option] = value
946
1035
        self._write_config_file()
947
 
 
 
1036
        for hook in OldConfigHooks['set']:
 
1037
            hook(self, option, value)
948
1038
 
949
1039
    def _get_sections(self, name=None):
950
1040
        """See IniBasedConfig._get_sections()."""
1146
1236
        # the allowed values of store match the config policies
1147
1237
        self._set_option_policy(location, option, store)
1148
1238
        self._write_config_file()
 
1239
        for hook in OldConfigHooks['set']:
 
1240
            hook(self, option, value)
1149
1241
 
1150
1242
 
1151
1243
class BranchConfig(Config):
1607
1699
            self._config = ConfigObj(self._input, encoding='utf-8')
1608
1700
        except configobj.ConfigObjError, e:
1609
1701
            raise errors.ParseConfigError(e.errors, e.config.filename)
 
1702
        except UnicodeError:
 
1703
            raise errors.ConfigContentError(self._filename)
1610
1704
        return self._config
1611
1705
 
1612
1706
    def _save(self):
1629
1723
        section[option_name] = value
1630
1724
        self._save()
1631
1725
 
1632
 
    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,
1633
1727
                        realm=None):
1634
1728
        """Returns the matching credentials from authentication.conf file.
1635
1729
 
2048
2142
                section_obj = configobj[section]
2049
2143
            except KeyError:
2050
2144
                return default
2051
 
        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
2052
2149
 
2053
2150
    def set_option(self, value, name, section=None):
2054
2151
        """Set the value associated with a named option.
2062
2159
            configobj[name] = value
2063
2160
        else:
2064
2161
            configobj.setdefault(section, {})[name] = value
 
2162
        for hook in OldConfigHooks['set']:
 
2163
            hook(self, name, value)
2065
2164
        self._set_configobj(configobj)
2066
2165
 
2067
2166
    def remove_option(self, option_name, section_name=None):
2070
2169
            del configobj[option_name]
2071
2170
        else:
2072
2171
            del configobj[section_name][option_name]
 
2172
        for hook in OldConfigHooks['remove']:
 
2173
            hook(self, option_name)
2073
2174
        self._set_configobj(configobj)
2074
2175
 
2075
2176
    def _get_config_file(self):
2076
2177
        try:
2077
 
            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
2078
2182
        except errors.NoSuchFile:
2079
2183
            return StringIO()
2080
2184
 
 
2185
    def _external_url(self):
 
2186
        return urlutils.join(self._transport.external_url(), self._filename)
 
2187
 
2081
2188
    def _get_configobj(self):
2082
2189
        f = self._get_config_file()
2083
2190
        try:
2084
 
            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())
2085
2197
        finally:
2086
2198
            f.close()
 
2199
        return conf
2087
2200
 
2088
2201
    def _set_configobj(self, configobj):
2089
2202
        out_file = StringIO()
2090
2203
        configobj.write(out_file)
2091
2204
        out_file.seek(0)
2092
2205
        self._transport.put_file(self._filename, out_file)
 
2206
        for hook in OldConfigHooks['save']:
 
2207
            hook(self)
 
2208
 
 
2209
 
 
2210
class Option(object):
 
2211
    """An option definition.
 
2212
 
 
2213
    The option *values* are stored in config files and found in sections.
 
2214
 
 
2215
    Here we define various properties about the option itself, its default
 
2216
    value, in which config files it can be stored, etc (TBC).
 
2217
    """
 
2218
 
 
2219
    def __init__(self, name, default=None):
 
2220
        self.name = name
 
2221
        self.default = default
 
2222
 
 
2223
    def get_default(self):
 
2224
        return self.default
 
2225
 
 
2226
 
 
2227
# Options registry
 
2228
 
 
2229
option_registry = registry.Registry()
 
2230
 
 
2231
 
 
2232
option_registry.register(
 
2233
    'editor', Option('editor'),
 
2234
    help='The command called to launch an editor to enter a message.')
2093
2235
 
2094
2236
 
2095
2237
class Section(object):
2096
 
    """A section defines a dict of options.
 
2238
    """A section defines a dict of option name => value.
2097
2239
 
2098
2240
    This is merely a read-only dict which can add some knowledge about the
2099
2241
    options. It is *not* a python dict object though and doesn't try to mimic
2156
2298
        """Loads the Store from persistent storage."""
2157
2299
        raise NotImplementedError(self.load)
2158
2300
 
2159
 
    def _load_from_string(self, str_or_unicode):
 
2301
    def _load_from_string(self, bytes):
2160
2302
        """Create a store from a string in configobj syntax.
2161
2303
 
2162
 
        :param str_or_unicode: A string representing the file content. This will
2163
 
            be encoded to suit store needs internally.
2164
 
 
2165
 
        This is for tests and should not be used in production unless a
2166
 
        convincing use case can be demonstrated :)
 
2304
        :param bytes: A string representing the file content.
2167
2305
        """
2168
2306
        raise NotImplementedError(self._load_from_string)
2169
2307
 
 
2308
    def unload(self):
 
2309
        """Unloads the Store.
 
2310
 
 
2311
        This should make is_loaded() return False. This is used when the caller
 
2312
        knows that the persistent storage has changed or may have change since
 
2313
        the last load.
 
2314
        """
 
2315
        raise NotImplementedError(self.unload)
 
2316
 
2170
2317
    def save(self):
2171
2318
        """Saves the Store to persistent storage."""
2172
2319
        raise NotImplementedError(self.save)
2220
2367
    def is_loaded(self):
2221
2368
        return self._config_obj != None
2222
2369
 
 
2370
    def unload(self):
 
2371
        self._config_obj = None
 
2372
 
2223
2373
    def load(self):
2224
2374
        """Load the store from the associated file."""
2225
2375
        if self.is_loaded():
2226
2376
            return
2227
2377
        content = self.transport.get_bytes(self.file_name)
2228
2378
        self._load_from_string(content)
 
2379
        for hook in ConfigHooks['load']:
 
2380
            hook(self)
2229
2381
 
2230
 
    def _load_from_string(self, str_or_unicode):
 
2382
    def _load_from_string(self, bytes):
2231
2383
        """Create a config store from a string.
2232
2384
 
2233
 
        :param str_or_unicode: A string representing the file content. This will
2234
 
            be utf-8 encoded internally.
2235
 
 
2236
 
        This is for tests and should not be used in production unless a
2237
 
        convincing use case can be demonstrated :)
 
2385
        :param bytes: A string representing the file content.
2238
2386
        """
2239
2387
        if self.is_loaded():
2240
2388
            raise AssertionError('Already loaded: %r' % (self._config_obj,))
2241
 
        co_input = StringIO(str_or_unicode.encode('utf-8'))
 
2389
        co_input = StringIO(bytes)
2242
2390
        try:
2243
2391
            # The config files are always stored utf8-encoded
2244
2392
            self._config_obj = ConfigObj(co_input, encoding='utf-8')
2245
2393
        except configobj.ConfigObjError, e:
2246
2394
            self._config_obj = None
2247
2395
            raise errors.ParseConfigError(e.errors, self.external_url())
 
2396
        except UnicodeDecodeError:
 
2397
            raise errors.ConfigContentError(self.external_url())
2248
2398
 
2249
2399
    def save(self):
2250
2400
        if not self.is_loaded():
2253
2403
        out = StringIO()
2254
2404
        self._config_obj.write(out)
2255
2405
        self.transport.put_bytes(self.file_name, out.getvalue())
 
2406
        for hook in ConfigHooks['save']:
 
2407
            hook(self)
2256
2408
 
2257
2409
    def external_url(self):
2258
2410
        # FIXME: external_url should really accepts an optional relpath
2334
2486
 
2335
2487
    @needs_write_lock
2336
2488
    def save(self):
 
2489
        # We need to be able to override the undecorated implementation
 
2490
        self.save_without_locking()
 
2491
 
 
2492
    def save_without_locking(self):
2337
2493
        super(LockableIniFileStore, self).save()
2338
2494
 
2339
2495
 
2366
2522
    def __init__(self, branch):
2367
2523
        super(BranchStore, self).__init__(branch.control_transport,
2368
2524
                                          'branch.conf')
 
2525
        self.branch = branch
 
2526
 
 
2527
    def lock_write(self, token=None):
 
2528
        return self.branch.lock_write(token)
 
2529
 
 
2530
    def unlock(self):
 
2531
        return self.branch.unlock()
 
2532
 
 
2533
    @needs_write_lock
 
2534
    def save(self):
 
2535
        # We need to be able to override the undecorated implementation
 
2536
        self.save_without_locking()
 
2537
 
 
2538
    def save_without_locking(self):
 
2539
        super(BranchStore, self).save()
 
2540
 
2369
2541
 
2370
2542
class SectionMatcher(object):
2371
2543
    """Select sections into a given Store.
2496
2668
        existence) require loading the store (even partially).
2497
2669
        """
2498
2670
        # FIXME: No caching of options nor sections yet -- vila 20110503
2499
 
 
 
2671
        value = None
2500
2672
        # Ensuring lazy loading is achieved by delaying section matching (which
2501
2673
        # implies querying the persistent storage) until it can't be avoided
2502
2674
        # anymore by using callables to describe (possibly empty) section
2510
2682
            for section in sections:
2511
2683
                value = section.get(name)
2512
2684
                if value is not None:
2513
 
                    return value
2514
 
        # No definition was found
2515
 
        return None
 
2685
                    break
 
2686
            if value is not None:
 
2687
                break
 
2688
        if value is None:
 
2689
            # If the option is registered, it may provide a default value
 
2690
            try:
 
2691
                opt = option_registry.get(name)
 
2692
            except KeyError:
 
2693
                # Not registered
 
2694
                opt = None
 
2695
            if opt is not None:
 
2696
                value = opt.get_default()
 
2697
        for hook in ConfigHooks['get']:
 
2698
            hook(self, name, value)
 
2699
        return value
2516
2700
 
2517
2701
    def _get_mutable_section(self):
2518
2702
        """Get the MutableSection for the Stack.
2529
2713
        """Set a new value for the option."""
2530
2714
        section = self._get_mutable_section()
2531
2715
        section.set(name, value)
 
2716
        for hook in ConfigHooks['set']:
 
2717
            hook(self, name, value)
2532
2718
 
2533
2719
    def remove(self, name):
2534
2720
        """Remove an existing option."""
2535
2721
        section = self._get_mutable_section()
2536
2722
        section.remove(name)
 
2723
        for hook in ConfigHooks['remove']:
 
2724
            hook(self, name)
2537
2725
 
2538
2726
    def __repr__(self):
2539
2727
        # Mostly for debugging use
2540
2728
        return "<config.%s(%s)>" % (self.__class__.__name__, id(self))
2541
2729
 
2542
2730
 
2543
 
class GlobalStack(Stack):
 
2731
class _CompatibleStack(Stack):
 
2732
    """Place holder for compatibility with previous design.
 
2733
 
 
2734
    This is intended to ease the transition from the Config-based design to the
 
2735
    Stack-based design and should not be used nor relied upon by plugins.
 
2736
 
 
2737
    One assumption made here is that the daughter classes will all use Stores
 
2738
    derived from LockableIniFileStore).
 
2739
 
 
2740
    It implements set() by re-loading the store before applying the
 
2741
    modification and saving it.
 
2742
 
 
2743
    The long term plan being to implement a single write by store to save
 
2744
    all modifications, this class should not be used in the interim.
 
2745
    """
 
2746
 
 
2747
    def set(self, name, value):
 
2748
        # Force a reload
 
2749
        self.store.unload()
 
2750
        super(_CompatibleStack, self).set(name, value)
 
2751
        # Force a write to persistent storage
 
2752
        self.store.save()
 
2753
 
 
2754
 
 
2755
class GlobalStack(_CompatibleStack):
2544
2756
 
2545
2757
    def __init__(self):
2546
2758
        # Get a GlobalStore
2548
2760
        super(GlobalStack, self).__init__([gstore.get_sections], gstore)
2549
2761
 
2550
2762
 
2551
 
class LocationStack(Stack):
 
2763
class LocationStack(_CompatibleStack):
2552
2764
 
2553
2765
    def __init__(self, location):
2554
2766
        lstore = LocationStore()
2557
2769
        super(LocationStack, self).__init__(
2558
2770
            [matcher.get_sections, gstore.get_sections], lstore)
2559
2771
 
2560
 
 
2561
 
class BranchStack(Stack):
 
2772
class BranchStack(_CompatibleStack):
2562
2773
 
2563
2774
    def __init__(self, branch):
2564
2775
        bstore = BranchStore(branch)
2568
2779
        super(BranchStack, self).__init__(
2569
2780
            [matcher.get_sections, bstore.get_sections, gstore.get_sections],
2570
2781
            bstore)
 
2782
        self.branch = branch
2571
2783
 
2572
2784
 
2573
2785
class cmd_config(commands.Command):
2602
2814
                        ' the configuration file'),
2603
2815
        ]
2604
2816
 
 
2817
    _see_also = ['configuration']
 
2818
 
2605
2819
    @commands.display_command
2606
2820
    def run(self, name=None, all=False, directory=None, scope=None,
2607
2821
            remove=False):
2733
2947
            raise errors.NoSuchConfig(scope)
2734
2948
        if not removed:
2735
2949
            raise errors.NoSuchConfigOption(name)
 
2950
 
 
2951
# Test registries
 
2952
#
 
2953
# We need adapters that can build a Store or a Stack in a test context. Test
 
2954
# classes, based on TestCaseWithTransport, can use the registry to parametrize
 
2955
# themselves. The builder will receive a test instance and should return a
 
2956
# ready-to-use store or stack.  Plugins that define new store/stacks can also
 
2957
# register themselves here to be tested against the tests defined in
 
2958
# bzrlib.tests.test_config. Note that the builder can be called multiple times
 
2959
# for the same tests.
 
2960
 
 
2961
# The registered object should be a callable receiving a test instance
 
2962
# parameter (inheriting from tests.TestCaseWithTransport) and returning a Store
 
2963
# object.
 
2964
test_store_builder_registry = registry.Registry()
 
2965
 
 
2966
# The registered object should be a callable receiving a test instance
 
2967
# parameter (inheriting from tests.TestCaseWithTransport) and returning a Stack
 
2968
# object.
 
2969
test_stack_builder_registry = registry.Registry()