/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Vincent Ladeuil
  • Date: 2012-01-18 14:09:19 UTC
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120118140919-rlvdrhpc0nq1lbwi
Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2014, 2016 Canonical Ltd
 
1
# Copyright (C) 2005-2012 Canonical Ltd
2
2
#   Authors: Robert Collins <robert.collins@canonical.com>
3
3
#            and others
4
4
#
55
55
                   turns on create_signatures.
56
56
create_signatures - this option controls whether bzr will always create
57
57
                    gpg signatures or not on commits.  There is an unused
58
 
                    option which in future is expected to work if
 
58
                    option which in future is expected to work if               
59
59
                    branch settings require signatures.
60
60
log_format - this option sets the default log format.  Possible values are
61
61
             long, short, line, or a plugin can register new formats.
73
73
"""
74
74
 
75
75
from __future__ import absolute_import
76
 
from cStringIO import StringIO
 
76
 
77
77
import os
78
78
import sys
79
79
 
81
81
from bzrlib.decorators import needs_write_lock
82
82
from bzrlib.lazy_import import lazy_import
83
83
lazy_import(globals(), """
84
 
import base64
85
84
import fnmatch
86
85
import re
 
86
from cStringIO import StringIO
87
87
 
88
88
from bzrlib import (
89
89
    atomicfile,
90
90
    controldir,
91
91
    debug,
92
 
    directory_service,
93
92
    errors,
94
93
    lazy_regex,
95
94
    library_state,
96
95
    lockdir,
 
96
    mail_client,
97
97
    mergetools,
98
98
    osutils,
99
99
    symbol_versioning,
197
197
        return self[section][name]
198
198
 
199
199
 
 
200
# FIXME: Until we can guarantee that each config file is loaded once and
 
201
# only once for a given bzrlib session, we don't want to re-read the file every
 
202
# time we query for an option so we cache the value (bad ! watch out for tests
 
203
# needing to restore the proper value). -- vila 20110219
 
204
_expand_default_value = None
 
205
def _get_expand_default_value():
 
206
    global _expand_default_value
 
207
    if _expand_default_value is not None:
 
208
        return _expand_default_value
 
209
    conf = GlobalConfig()
 
210
    # Note that we must not use None for the expand value below or we'll run
 
211
    # into infinite recursion. Using False really would be quite silly ;)
 
212
    expand = conf.get_user_option_as_bool('bzr.config.expand', expand=True)
 
213
    if expand is None:
 
214
        # This is an opt-in feature, you *really* need to clearly say you want
 
215
        # to activate it !
 
216
        expand = False
 
217
    _expand_default_value = expand
 
218
    return expand
 
219
 
 
220
 
200
221
class Config(object):
201
222
    """A configuration policy - what username, editor, gpg needs etc."""
202
223
 
207
228
        """Returns a unique ID for the config."""
208
229
        raise NotImplementedError(self.config_id)
209
230
 
 
231
    @deprecated_method(deprecated_in((2, 4, 0)))
 
232
    def get_editor(self):
 
233
        """Get the users pop up editor."""
 
234
        raise NotImplementedError
 
235
 
210
236
    def get_change_editor(self, old_tree, new_tree):
211
237
        from bzrlib import diff
212
238
        cmd = self._get_change_editor()
215
241
        return diff.DiffFromTool.from_string(cmd, old_tree, new_tree,
216
242
                                             sys.stdout)
217
243
 
 
244
    def get_mail_client(self):
 
245
        """Get a mail client to use"""
 
246
        selected_client = self.get_user_option('mail_client')
 
247
        _registry = mail_client.mail_client_registry
 
248
        try:
 
249
            mail_client_class = _registry.get(selected_client)
 
250
        except KeyError:
 
251
            raise errors.UnknownMailClient(selected_client)
 
252
        return mail_client_class(self)
 
253
 
218
254
    def _get_signature_checking(self):
219
255
        """Template method to override signature checking policy."""
220
256
 
349
385
        """Template method to provide a user option."""
350
386
        return None
351
387
 
352
 
    def get_user_option(self, option_name, expand=True):
 
388
    def get_user_option(self, option_name, expand=None):
353
389
        """Get a generic option - no special process, no default.
354
390
 
355
391
        :param option_name: The queried option.
358
394
 
359
395
        :returns: The value of the option.
360
396
        """
 
397
        if expand is None:
 
398
            expand = _get_expand_default_value()
361
399
        value = self._get_user_option(option_name)
362
400
        if expand:
363
401
            if isinstance(value, list):
611
649
        for (oname, value, section, conf_id, parser) in self._get_options():
612
650
            if oname.startswith('bzr.mergetool.'):
613
651
                tool_name = oname[len('bzr.mergetool.'):]
614
 
                tools[tool_name] = self.get_user_option(oname, False)
 
652
                tools[tool_name] = self.get_user_option(oname)
615
653
        trace.mutter('loaded merge tools: %r' % tools)
616
654
        return tools
617
655
 
1053
1091
        conf._create_from_string(str_or_unicode, save)
1054
1092
        return conf
1055
1093
 
 
1094
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1095
    def get_editor(self):
 
1096
        return self._get_user_option('editor')
 
1097
 
1056
1098
    @needs_write_lock
1057
1099
    def set_user_option(self, option, value):
1058
1100
        """Save option and its value in the configuration."""
1441
1483
        value = self._get_explicit_nickname()
1442
1484
        if value is not None:
1443
1485
            return value
1444
 
        if self.branch.name:
1445
 
            return self.branch.name
1446
1486
        return urlutils.unescape(self.branch.base.split('/')[-2])
1447
1487
 
1448
1488
    def has_explicit_nickname(self):
1485
1525
 
1486
1526
 
1487
1527
def config_dir():
1488
 
    """Return per-user configuration directory as unicode string
 
1528
    """Return per-user configuration directory.
1489
1529
 
1490
1530
    By default this is %APPDATA%/bazaar/2.0 on Windows, ~/.bazaar on Mac OS X
1491
 
    and Linux.  On Mac OS X and Linux, if there is a $XDG_CONFIG_HOME/bazaar directory,
 
1531
    and Linux.  On Linux, if there is a $XDG_CONFIG_HOME/bazaar directory,
1492
1532
    that will be used instead.
1493
1533
 
1494
1534
    TODO: Global option --config-dir to override this.
1495
1535
    """
1496
 
    base = osutils.path_from_environ('BZR_HOME')
 
1536
    base = os.environ.get('BZR_HOME', None)
1497
1537
    if sys.platform == 'win32':
1498
 
        if base is None:
1499
 
            base = win32utils.get_appdata_location()
1500
 
        if base is None:
1501
 
            base = win32utils.get_home_location()
1502
 
        # GZ 2012-02-01: Really the two level subdirs only make sense inside
1503
 
        #                APPDATA, but hard to move. See bug 348640 for more.
 
1538
        # environ variables on Windows are in user encoding/mbcs. So decode
 
1539
        # before using one
 
1540
        if base is not None:
 
1541
            base = base.decode('mbcs')
 
1542
        if base is None:
 
1543
            base = win32utils.get_appdata_location_unicode()
 
1544
        if base is None:
 
1545
            base = os.environ.get('HOME', None)
 
1546
            if base is not None:
 
1547
                base = base.decode('mbcs')
 
1548
        if base is None:
 
1549
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA,'
 
1550
                                  ' or HOME set')
1504
1551
        return osutils.pathjoin(base, 'bazaar', '2.0')
1505
 
    if base is None:
1506
 
        xdg_dir = osutils.path_from_environ('XDG_CONFIG_HOME')
1507
 
        if xdg_dir is None:
1508
 
            xdg_dir = osutils.pathjoin(osutils._get_home_dir(), ".config")
1509
 
        xdg_dir = osutils.pathjoin(xdg_dir, 'bazaar')
1510
 
        if osutils.isdir(xdg_dir):
1511
 
            trace.mutter(
1512
 
                "Using configuration in XDG directory %s." % xdg_dir)
1513
 
            return xdg_dir
1514
 
        base = osutils._get_home_dir()
1515
 
    return osutils.pathjoin(base, ".bazaar")
 
1552
    else:
 
1553
        if base is not None:
 
1554
            base = base.decode(osutils._fs_enc)
 
1555
    if sys.platform == 'darwin':
 
1556
        if base is None:
 
1557
            # this takes into account $HOME
 
1558
            base = os.path.expanduser("~")
 
1559
        return osutils.pathjoin(base, '.bazaar')
 
1560
    else:
 
1561
        if base is None:
 
1562
            xdg_dir = os.environ.get('XDG_CONFIG_HOME', None)
 
1563
            if xdg_dir is None:
 
1564
                xdg_dir = osutils.pathjoin(os.path.expanduser("~"), ".config")
 
1565
            xdg_dir = osutils.pathjoin(xdg_dir, 'bazaar')
 
1566
            if osutils.isdir(xdg_dir):
 
1567
                trace.mutter(
 
1568
                    "Using configuration in XDG directory %s." % xdg_dir)
 
1569
                return xdg_dir
 
1570
            base = os.path.expanduser("~")
 
1571
        return osutils.pathjoin(base, ".bazaar")
1516
1572
 
1517
1573
 
1518
1574
def config_filename():
1555
1611
def xdg_cache_dir():
1556
1612
    # See http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
1557
1613
    # Possibly this should be different on Windows?
1558
 
    e = os.environ.get('XDG_CACHE_HOME', None)
 
1614
    e = os.environ.get('XDG_CACHE_DIR', None)
1559
1615
    if e:
1560
1616
        return e
1561
1617
    else:
1562
1618
        return os.path.expanduser('~/.cache')
1563
1619
 
1564
1620
 
1565
 
def _get_default_mail_domain(mailname_file='/etc/mailname'):
 
1621
def _get_default_mail_domain():
1566
1622
    """If possible, return the assumed default email domain.
1567
1623
 
1568
1624
    :returns: string mail domain, or None.
1571
1627
        # No implementation yet; patches welcome
1572
1628
        return None
1573
1629
    try:
1574
 
        f = open(mailname_file)
 
1630
        f = open('/etc/mailname')
1575
1631
    except (IOError, OSError), e:
1576
1632
        return None
1577
1633
    try:
1578
 
        domain = f.readline().strip()
 
1634
        domain = f.read().strip()
1579
1635
        return domain
1580
1636
    finally:
1581
1637
        f.close()
2131
2187
credential_store_registry.default_key = 'plain'
2132
2188
 
2133
2189
 
2134
 
class Base64CredentialStore(CredentialStore):
2135
 
    __doc__ = """Base64 credential store for the authentication.conf file"""
2136
 
 
2137
 
    def decode_password(self, credentials):
2138
 
        """See CredentialStore.decode_password."""
2139
 
        # GZ 2012-07-28: Will raise binascii.Error if password is not base64,
2140
 
        #                should probably propogate as something more useful.
2141
 
        return base64.decodestring(credentials['password'])
2142
 
 
2143
 
credential_store_registry.register('base64', Base64CredentialStore,
2144
 
                                   help=Base64CredentialStore.__doc__)
2145
 
 
2146
 
 
2147
2190
class BzrDirConfig(object):
2148
2191
 
2149
2192
    def __init__(self, bzrdir):
2155
2198
 
2156
2199
        It may be set to a location, or None.
2157
2200
 
2158
 
        This policy affects all branches contained by this control dir, except
2159
 
        for those under repositories.
 
2201
        This policy affects all branches contained by this bzrdir, except for
 
2202
        those under repositories.
2160
2203
        """
2161
2204
        if self._config is None:
2162
 
            raise errors.BzrError("Cannot set configuration in %s"
2163
 
                                  % self._bzrdir)
 
2205
            raise errors.BzrError("Cannot set configuration in %s" % self._bzrdir)
2164
2206
        if value is None:
2165
2207
            self._config.set_option('', 'default_stack_on')
2166
2208
        else:
2171
2213
 
2172
2214
        This will either be a location, or None.
2173
2215
 
2174
 
        This policy affects all branches contained by this control dir, except
2175
 
        for those under repositories.
 
2216
        This policy affects all branches contained by this bzrdir, except for
 
2217
        those under repositories.
2176
2218
        """
2177
2219
        if self._config is None:
2178
2220
            return None
2314
2356
        :param help: a doc string to explain the option to the user.
2315
2357
 
2316
2358
        :param from_unicode: a callable to convert the unicode string
2317
 
            representing the option value in a store or its default value.
 
2359
            representing the option value in a store. This is not called for
 
2360
            the default value.
2318
2361
 
2319
2362
        :param invalid: the action to be taken when an invalid value is
2320
2363
            encountered in a store. This is called only when from_unicode is
2354
2397
            raise AssertionError('%r is not supported as a default value'
2355
2398
                                 % (default,))
2356
2399
        self.default_from_env = default_from_env
2357
 
        self._help = help
 
2400
        self.help = help
2358
2401
        self.from_unicode = from_unicode
2359
2402
        self.unquote = unquote
2360
2403
        if invalid and invalid not in ('warning', 'error'):
2361
2404
            raise AssertionError("%s not supported for 'invalid'" % (invalid,))
2362
2405
        self.invalid = invalid
2363
2406
 
2364
 
    @property
2365
 
    def help(self):
2366
 
        return self._help
2367
 
 
2368
2407
    def convert_from_unicode(self, store, unicode_value):
2369
2408
        if self.unquote and store is not None and unicode_value is not None:
2370
2409
            unicode_value = store.unquote(unicode_value)
2411
2450
                value = self.default()
2412
2451
                if not isinstance(value, unicode):
2413
2452
                    raise AssertionError(
2414
 
                        "Callable default value for '%s' should be unicode"
2415
 
                        % (self.name))
 
2453
                    'Callable default values should be unicode')
2416
2454
            else:
2417
2455
                value = self.default
2418
2456
        return value
2419
2457
 
2420
 
    def get_help_topic(self):
2421
 
        return self.name
2422
 
 
2423
2458
    def get_help_text(self, additional_see_also=None, plain=True):
2424
2459
        result = self.help
2425
2460
        from bzrlib import help_topics
2471
2506
    return float(unicode_str)
2472
2507
 
2473
2508
 
2474
 
# Use an empty dict to initialize an empty configobj avoiding all parsing and
2475
 
# encoding checks
 
2509
# Use a an empty dict to initialize an empty configobj avoiding all
 
2510
# parsing and encoding checks
2476
2511
_list_converter_config = configobj.ConfigObj(
2477
2512
    {}, encoding='utf-8', list_values=True, interpolation=False)
2478
2513
 
2514
2549
        return l
2515
2550
 
2516
2551
 
2517
 
class RegistryOption(Option):
2518
 
    """Option for a choice from a registry."""
2519
 
 
2520
 
    def __init__(self, name, registry, default_from_env=None,
2521
 
                 help=None, invalid=None):
2522
 
        """A registry based Option definition.
2523
 
 
2524
 
        This overrides the base class so the conversion from a unicode string
2525
 
        can take quoting into account.
2526
 
        """
2527
 
        super(RegistryOption, self).__init__(
2528
 
            name, default=lambda: unicode(registry.default_key),
2529
 
            default_from_env=default_from_env,
2530
 
            from_unicode=self.from_unicode, help=help,
2531
 
            invalid=invalid, unquote=False)
2532
 
        self.registry = registry
2533
 
 
2534
 
    def from_unicode(self, unicode_str):
2535
 
        if not isinstance(unicode_str, basestring):
2536
 
            raise TypeError
2537
 
        try:
2538
 
            return self.registry.get(unicode_str)
2539
 
        except KeyError:
2540
 
            raise ValueError(
2541
 
                "Invalid value %s for %s."
2542
 
                "See help for a list of possible values." % (unicode_str,
2543
 
                    self.name))
2544
 
 
2545
 
    @property
2546
 
    def help(self):
2547
 
        ret = [self._help, "\n\nThe following values are supported:\n"]
2548
 
        for key in self.registry.keys():
2549
 
            ret.append(" %s - %s\n" % (key, self.registry.get_help(key)))
2550
 
        return "".join(ret)
2551
 
 
2552
 
 
2553
 
_option_ref_re = lazy_regex.lazy_compile('({[^\d\W](?:\.\w|-\w|\w)*})')
2554
 
"""Describes an expandable option reference.
2555
 
 
2556
 
We want to match the most embedded reference first.
2557
 
 
2558
 
I.e. for '{{foo}}' we will get '{foo}',
2559
 
for '{bar{baz}}' we will get '{baz}'
2560
 
"""
2561
 
 
2562
 
def iter_option_refs(string):
2563
 
    # Split isolate refs so every other chunk is a ref
2564
 
    is_ref = False
2565
 
    for chunk  in _option_ref_re.split(string):
2566
 
        yield is_ref, chunk
2567
 
        is_ref = not is_ref
2568
 
 
2569
 
 
2570
2552
class OptionRegistry(registry.Registry):
2571
2553
    """Register config options by their name.
2572
2554
 
2574
2556
    some information from the option object itself.
2575
2557
    """
2576
2558
 
2577
 
    def _check_option_name(self, option_name):
2578
 
        """Ensures an option name is valid.
2579
 
 
2580
 
        :param option_name: The name to validate.
2581
 
        """
2582
 
        if _option_ref_re.match('{%s}' % option_name) is None:
2583
 
            raise errors.IllegalOptionName(option_name)
2584
 
 
2585
2559
    def register(self, option):
2586
2560
        """Register a new option to its name.
2587
2561
 
2588
2562
        :param option: The option to register. Its name is used as the key.
2589
2563
        """
2590
 
        self._check_option_name(option.name)
2591
2564
        super(OptionRegistry, self).register(option.name, option,
2592
2565
                                             help=option.help)
2593
2566
 
2602
2575
        :param member_name: the member of the module to return.  If empty or 
2603
2576
                None, get() will return the module itself.
2604
2577
        """
2605
 
        self._check_option_name(key)
2606
2578
        super(OptionRegistry, self).register_lazy(key,
2607
2579
                                                  module_name, member_name)
2608
2580
 
2672
2644
           help="""\
2673
2645
Whether revisions associated with tags should be fetched.
2674
2646
"""))
2675
 
option_registry.register_lazy(
2676
 
    'bzr.transform.orphan_policy', 'bzrlib.transform', 'opt_transform_orphan')
2677
2647
option_registry.register(
2678
2648
    Option('bzr.workingtree.worth_saving_limit', default=10,
2679
2649
           from_unicode=int_from_store,  invalid='warning',
2687
2657
a file has been touched.
2688
2658
'''))
2689
2659
option_registry.register(
2690
 
    Option('bugtracker', default=None,
2691
 
           help='''\
2692
 
Default bug tracker to use.
2693
 
 
2694
 
This bug tracker will be used for example when marking bugs
2695
 
as fixed using ``bzr commit --fixes``, if no explicit
2696
 
bug tracker was specified.
2697
 
'''))
2698
 
option_registry.register(
2699
2660
    Option('check_signatures', default=CHECK_IF_POSSIBLE,
2700
2661
           from_unicode=signature_policy_from_unicode,
2701
2662
           help='''\
2803
2764
Standard log formats are ``long``, ``short`` and ``line``. Additional formats
2804
2765
may be provided by plugins.
2805
2766
'''))
2806
 
option_registry.register_lazy('mail_client', 'bzrlib.mail_client',
2807
 
    'opt_mail_client')
2808
2767
option_registry.register(
2809
2768
    Option('output_encoding',
2810
2769
           help= 'Unicode encoding for output'
2827
2786
 
2828
2787
Each function takes branch, rev_id as parameters.
2829
2788
'''))
2830
 
option_registry.register_lazy('progress_bar', 'bzrlib.ui.text',
2831
 
                              'opt_progress_bar')
2832
2789
option_registry.register(
2833
2790
    Option('public_branch',
2834
2791
           default=None,
2909
2866
option_registry.register(
2910
2867
    Option('submit_to',
2911
2868
           help='''Where submissions from this branch are mailed to.'''))
2912
 
option_registry.register(
2913
 
    ListOption('suppress_warnings',
2914
 
           default=[],
2915
 
           help="List of warning classes to suppress."))
2916
 
option_registry.register(
2917
 
    Option('validate_signatures_in_log', default=False,
2918
 
           from_unicode=bool_from_store, invalid='warning',
2919
 
           help='''Whether to validate signatures in bzr log.'''))
2920
 
option_registry.register_lazy('ssl.ca_certs',
2921
 
    'bzrlib.transport.http._urllib2_wrappers', 'opt_ssl_ca_certs')
2922
 
 
2923
 
option_registry.register_lazy('ssl.cert_reqs',
2924
 
    'bzrlib.transport.http._urllib2_wrappers', 'opt_ssl_cert_reqs')
2925
2869
 
2926
2870
 
2927
2871
class Section(object):
2971
2915
        self.options[name] = value
2972
2916
 
2973
2917
    def remove(self, name):
2974
 
        if name not in self.orig and name in self.options:
 
2918
        if name not in self.orig:
2975
2919
            self.orig[name] = self.get(name, None)
2976
2920
        del self.options[name]
2977
2921
 
2999
2943
            # Report concurrent updates in an ad-hoc way. This should only
3000
2944
            # occurs when different processes try to update the same option
3001
2945
            # which is not supported (as in: the config framework is not meant
3002
 
            # to be used as a sharing mechanism).
 
2946
            # to be used a sharing mechanism).
3003
2947
            if expected != reloaded:
3004
2948
                if actual is _DeletedOption:
3005
2949
                    actual = '<DELETED>'
3025
2969
    mutable_section_class = MutableSection
3026
2970
 
3027
2971
    def __init__(self):
3028
 
        # Which sections need to be saved (by section id). We use a dict here
3029
 
        # so the dirty sections can be shared by multiple callers.
3030
 
        self.dirty_sections = {}
 
2972
        # Which sections need to be saved
 
2973
        self.dirty_sections = []
3031
2974
 
3032
2975
    def is_loaded(self):
3033
2976
        """Returns True if the Store has been loaded.
3076
3019
        raise NotImplementedError(self.save)
3077
3020
 
3078
3021
    def _need_saving(self):
3079
 
        for s in self.dirty_sections.values():
 
3022
        for s in self.dirty_sections:
3080
3023
            if s.orig:
3081
3024
                # At least one dirty section contains a modification
3082
3025
                return True
3096
3039
        # get_mutable_section() call below.
3097
3040
        self.unload()
3098
3041
        # Apply the changes from the preserved dirty sections
3099
 
        for section_id, dirty in dirty_sections.iteritems():
3100
 
            clean = self.get_mutable_section(section_id)
 
3042
        for dirty in dirty_sections:
 
3043
            clean = self.get_mutable_section(dirty.id)
3101
3044
            clean.apply_changes(dirty, self)
3102
3045
        # Everything is clean now
3103
 
        self.dirty_sections = {}
 
3046
        self.dirty_sections = []
3104
3047
 
3105
3048
    def save_changes(self):
3106
3049
        """Saves the Store to persistent storage if changes occurred.
3171
3114
class IniFileStore(Store):
3172
3115
    """A config Store using ConfigObj for storage.
3173
3116
 
 
3117
    :ivar transport: The transport object where the config file is located.
 
3118
 
 
3119
    :ivar file_name: The config file basename in the transport directory.
 
3120
 
3174
3121
    :ivar _config_obj: Private member to hold the ConfigObj instance used to
3175
3122
        serialize/deserialize the config file.
3176
3123
    """
3186
3133
 
3187
3134
    def unload(self):
3188
3135
        self._config_obj = None
3189
 
        self.dirty_sections = {}
 
3136
        self.dirty_sections = []
3190
3137
 
3191
3138
    def _load_content(self):
3192
3139
        """Load the config file bytes.
3240
3187
        if not self._need_saving():
3241
3188
            return
3242
3189
        # Preserve the current version
3243
 
        dirty_sections = dict(self.dirty_sections.items())
 
3190
        current = self._config_obj
 
3191
        dirty_sections = list(self.dirty_sections)
3244
3192
        self.apply_changes(dirty_sections)
3245
3193
        # Save to the persistent storage
3246
3194
        self.save()
3281
3229
        except errors.NoSuchFile:
3282
3230
            # The file doesn't exist, let's pretend it was empty
3283
3231
            self._load_from_string('')
3284
 
        if section_id in self.dirty_sections:
3285
 
            # We already created a mutable section for this id
3286
 
            return self.dirty_sections[section_id]
3287
3232
        if section_id is None:
3288
3233
            section = self._config_obj
3289
3234
        else:
3290
3235
            section = self._config_obj.setdefault(section_id, {})
3291
3236
        mutable_section = self.mutable_section_class(section_id, section)
3292
3237
        # All mutable sections can become dirty
3293
 
        self.dirty_sections[section_id] = mutable_section
 
3238
        self.dirty_sections.append(mutable_section)
3294
3239
        return mutable_section
3295
3240
 
3296
3241
    def quote(self, value):
3308
3253
            value = self._config_obj._unquote(value)
3309
3254
        return value
3310
3255
 
3311
 
    def external_url(self):
3312
 
        # Since an IniFileStore can be used without a file (at least in tests),
3313
 
        # it's better to provide something than raising a NotImplementedError.
3314
 
        # All daughter classes are supposed to provide an implementation
3315
 
        # anyway.
3316
 
        return 'In-Process Store, no URL'
3317
 
 
3318
3256
 
3319
3257
class TransportIniFileStore(IniFileStore):
3320
3258
    """IniFileStore that loads files from a transport.
3321
 
 
3322
 
    :ivar transport: The transport object where the config file is located.
3323
 
 
3324
 
    :ivar file_name: The config file basename in the transport directory.
3325
3259
    """
3326
3260
 
3327
3261
    def __init__(self, transport, file_name):
3411
3345
# on the relevant parts of the API that needs testing -- vila 20110503 (based
3412
3346
# on a poolie's remark)
3413
3347
class GlobalStore(LockableIniFileStore):
3414
 
    """A config store for global options.
3415
 
 
3416
 
    There is a single GlobalStore for a given process.
3417
 
    """
3418
3348
 
3419
3349
    def __init__(self, possible_transports=None):
3420
3350
        t = transport.get_transport_from_path(
3424
3354
 
3425
3355
 
3426
3356
class LocationStore(LockableIniFileStore):
3427
 
    """A config store for options specific to a location.
3428
 
 
3429
 
    There is a single LocationStore for a given process.
3430
 
    """
3431
3357
 
3432
3358
    def __init__(self, possible_transports=None):
3433
3359
        t = transport.get_transport_from_path(
3437
3363
 
3438
3364
 
3439
3365
class BranchStore(TransportIniFileStore):
3440
 
    """A config store for branch options.
3441
 
 
3442
 
    There is a single BranchStore for a given branch.
3443
 
    """
3444
3366
 
3445
3367
    def __init__(self, branch):
3446
3368
        super(BranchStore, self).__init__(branch.control_transport,
3448
3370
        self.branch = branch
3449
3371
        self.id = 'branch'
3450
3372
 
 
3373
    # FIXME: This is very handy to detect which callers forgot to lock the
 
3374
    # branch but break many bt.test_config tests. Either these tests should
 
3375
    # parametrized differently or better ways to achieve the branch locking
 
3376
    # should be found that don't require this.
 
3377
    def xget_mutable_section(self, section_id=None):
 
3378
        if self.branch.peek_lock_mode() != 'w':
 
3379
            from bzrlib import debug ; debug.set_trace()
 
3380
            raise AssertionError('The branch for %s is not write-locked'
 
3381
                                 % self.external_url())
 
3382
        return super(BranchStore, self).get_mutable_section(section_id)
 
3383
 
3451
3384
 
3452
3385
class ControlStore(LockableIniFileStore):
3453
3386
 
3499
3432
 
3500
3433
class LocationSection(Section):
3501
3434
 
3502
 
    def __init__(self, section, extra_path, branch_name=None):
 
3435
    def __init__(self, section, length, extra_path):
3503
3436
        super(LocationSection, self).__init__(section.id, section.options)
 
3437
        self.length = length
3504
3438
        self.extra_path = extra_path
3505
 
        if branch_name is None:
3506
 
            branch_name = ''
3507
3439
        self.locals = {'relpath': extra_path,
3508
 
                       'basename': urlutils.basename(extra_path),
3509
 
                       'branchname': branch_name}
 
3440
                       'basename': urlutils.basename(extra_path)}
3510
3441
 
3511
3442
    def get(self, name, default=None, expand=True):
3512
3443
        value = super(LocationSection, self).get(name, default)
3531
3462
        return value
3532
3463
 
3533
3464
 
3534
 
class StartingPathMatcher(SectionMatcher):
3535
 
    """Select sections for a given location respecting the Store order."""
3536
 
 
3537
 
    # FIXME: Both local paths and urls can be used for section names as well as
3538
 
    # ``location`` to stay consistent with ``LocationMatcher`` which itself
3539
 
    # inherited the fuzziness from the previous ``LocationConfig``
3540
 
    # implementation. We probably need to revisit which encoding is allowed for
3541
 
    # both ``location`` and section names and how we normalize
3542
 
    # them. http://pad.lv/85479, http://pad.lv/437009 and http://359320 are
3543
 
    # related too. -- vila 2012-01-04
3544
 
 
3545
 
    def __init__(self, store, location):
3546
 
        super(StartingPathMatcher, self).__init__(store)
3547
 
        if location.startswith('file://'):
3548
 
            location = urlutils.local_path_from_url(location)
3549
 
        self.location = location
3550
 
 
3551
 
    def get_sections(self):
3552
 
        """Get all sections matching ``location`` in the store.
3553
 
 
3554
 
        The most generic sections are described first in the store, then more
3555
 
        specific ones can be provided for reduced scopes.
3556
 
 
3557
 
        The returned section are therefore returned in the reversed order so
3558
 
        the most specific ones can be found first.
3559
 
        """
3560
 
        location_parts = self.location.rstrip('/').split('/')
3561
 
        store = self.store
3562
 
        # Later sections are more specific, they should be returned first
3563
 
        for _, section in reversed(list(store.get_sections())):
3564
 
            if section.id is None:
3565
 
                # The no-name section is always included if present
3566
 
                yield store, LocationSection(section, self.location)
3567
 
                continue
3568
 
            section_path = section.id
3569
 
            if section_path.startswith('file://'):
3570
 
                # the location is already a local path or URL, convert the
3571
 
                # section id to the same format
3572
 
                section_path = urlutils.local_path_from_url(section_path)
3573
 
            if (self.location.startswith(section_path)
3574
 
                or fnmatch.fnmatch(self.location, section_path)):
3575
 
                section_parts = section_path.rstrip('/').split('/')
3576
 
                extra_path = '/'.join(location_parts[len(section_parts):])
3577
 
                yield store, LocationSection(section, extra_path)
3578
 
 
3579
 
 
3580
3465
class LocationMatcher(SectionMatcher):
3581
3466
 
3582
3467
    def __init__(self, store, location):
3583
3468
        super(LocationMatcher, self).__init__(store)
3584
 
        url, params = urlutils.split_segment_parameters(location)
3585
3469
        if location.startswith('file://'):
3586
3470
            location = urlutils.local_path_from_url(location)
3587
3471
        self.location = location
3588
 
        branch_name = params.get('branch')
3589
 
        if branch_name is None:
3590
 
            self.branch_name = urlutils.basename(self.location)
3591
 
        else:
3592
 
            self.branch_name = urlutils.unescape(branch_name)
3593
3472
 
3594
3473
    def _get_matching_sections(self):
3595
3474
        """Get all sections matching ``location``."""
3612
3491
        matching_sections = []
3613
3492
        if no_name_section is not None:
3614
3493
            matching_sections.append(
3615
 
                (0, LocationSection(no_name_section, self.location)))
 
3494
                LocationSection(no_name_section, 0, self.location))
3616
3495
        for section_id, extra_path, length in filtered_sections:
3617
3496
            # a section id is unique for a given store so it's safe to take the
3618
3497
            # first matching section while iterating. Also, all filtered
3621
3500
            while True:
3622
3501
                section = iter_all_sections.next()
3623
3502
                if section_id == section.id:
3624
 
                    section = LocationSection(section, extra_path,
3625
 
                                              self.branch_name)
3626
 
                    matching_sections.append((length, section))
 
3503
                    matching_sections.append(
 
3504
                        LocationSection(section, length, extra_path))
3627
3505
                    break
3628
3506
        return matching_sections
3629
3507
 
3632
3510
        matching_sections = self._get_matching_sections()
3633
3511
        # We want the longest (aka more specific) locations first
3634
3512
        sections = sorted(matching_sections,
3635
 
                          key=lambda (length, section): (length, section.id),
 
3513
                          key=lambda section: (section.length, section.id),
3636
3514
                          reverse=True)
3637
3515
        # Sections mentioning 'ignore_parents' restrict the selection
3638
 
        for _, section in sections:
 
3516
        for section in sections:
3639
3517
            # FIXME: We really want to use as_bool below -- vila 2011-04-07
3640
3518
            ignore = section.get('ignore_parents', None)
3641
3519
            if ignore is not None:
3646
3524
            yield self.store, section
3647
3525
 
3648
3526
 
3649
 
# FIXME: _shared_stores should be an attribute of a library state once a
3650
 
# library_state object is always available.
3651
 
_shared_stores = {}
3652
 
_shared_stores_at_exit_installed = False
 
3527
_option_ref_re = lazy_regex.lazy_compile('({[^{}\n]+})')
 
3528
"""Describes an expandable option reference.
 
3529
 
 
3530
We want to match the most embedded reference first.
 
3531
 
 
3532
I.e. for '{{foo}}' we will get '{foo}',
 
3533
for '{bar{baz}}' we will get '{baz}'
 
3534
"""
 
3535
 
 
3536
def iter_option_refs(string):
 
3537
    # Split isolate refs so every other chunk is a ref
 
3538
    is_ref = False
 
3539
    for chunk  in _option_ref_re.split(string):
 
3540
        yield is_ref, chunk
 
3541
        is_ref = not is_ref
 
3542
 
3653
3543
 
3654
3544
class Stack(object):
3655
3545
    """A stack of configurations where an option can be defined"""
3672
3562
        self.store = store
3673
3563
        self.mutable_section_id = mutable_section_id
3674
3564
 
3675
 
    def iter_sections(self):
3676
 
        """Iterate all the defined sections."""
3677
 
        # Ensuring lazy loading is achieved by delaying section matching (which
3678
 
        # implies querying the persistent storage) until it can't be avoided
3679
 
        # anymore by using callables to describe (possibly empty) section
3680
 
        # lists.
3681
 
        for sections in self.sections_def:
3682
 
            for store, section in sections():
3683
 
                yield store, section
3684
 
 
3685
 
    def get(self, name, expand=True, convert=True):
 
3565
    def get(self, name, expand=None):
3686
3566
        """Return the *first* option value found in the sections.
3687
3567
 
3688
3568
        This is where we guarantee that sections coming from Store are loaded
3695
3575
 
3696
3576
        :param expand: Whether options references should be expanded.
3697
3577
 
3698
 
        :param convert: Whether the option value should be converted from
3699
 
            unicode (do nothing for non-registered options).
3700
 
 
3701
3578
        :returns: The value of the option.
3702
3579
        """
3703
3580
        # FIXME: No caching of options nor sections yet -- vila 20110503
 
3581
        if expand is None:
 
3582
            expand = _get_expand_default_value()
3704
3583
        value = None
3705
3584
        found_store = None # Where the option value has been found
3706
3585
        # If the option is registered, it may provide additional info about
3724
3603
                                      % (name, type(val)))
3725
3604
                if opt is None:
3726
3605
                    val = found_store.unquote(val)
3727
 
                elif convert:
 
3606
                else:
3728
3607
                    val = opt.convert_from_unicode(found_store, val)
3729
3608
            return val
3730
3609
 
3734
3613
            value = opt.get_override()
3735
3614
            value = expand_and_convert(value)
3736
3615
        if value is None:
3737
 
            for store, section in self.iter_sections():
3738
 
                value = section.get(name)
 
3616
            # Ensuring lazy loading is achieved by delaying section matching
 
3617
            # (which implies querying the persistent storage) until it can't be
 
3618
            # avoided anymore by using callables to describe (possibly empty)
 
3619
            # section lists.
 
3620
            for sections in self.sections_def:
 
3621
                for store, section in sections():
 
3622
                    value = section.get(name)
 
3623
                    if value is not None:
 
3624
                        found_store = store
 
3625
                        break
3739
3626
                if value is not None:
3740
 
                    found_store = store
3741
3627
                    break
3742
3628
            value = expand_and_convert(value)
3743
3629
            if opt is not None and value is None:
3809
3695
            # anything else
3810
3696
            value = env[name]
3811
3697
        else:
3812
 
            value = self.get(name, expand=False, convert=False)
 
3698
            value = self.get(name, expand=False)
3813
3699
            value = self._expand_options_in_string(value, env, _refs)
3814
3700
        return value
3815
3701
 
3844
3730
        return "<config.%s(%s)>" % (self.__class__.__name__, id(self))
3845
3731
 
3846
3732
    def _get_overrides(self):
3847
 
        # FIXME: Hack around library_state.initialize never called
 
3733
        # Hack around library_state.initialize never called
3848
3734
        if bzrlib.global_state is not None:
3849
3735
            return bzrlib.global_state.cmdline_overrides.get_sections()
3850
3736
        return []
3851
3737
 
3852
 
    def get_shared_store(self, store, state=None):
3853
 
        """Get a known shared store.
3854
 
 
3855
 
        Store urls uniquely identify them and are used to ensure a single copy
3856
 
        is shared across all users.
3857
 
 
3858
 
        :param store: The store known to the caller.
3859
 
 
3860
 
        :param state: The library state where the known stores are kept.
3861
 
 
3862
 
        :returns: The store received if it's not a known one, an already known
3863
 
            otherwise.
3864
 
        """
3865
 
        if state is None:
3866
 
            state = bzrlib.global_state
3867
 
        if state is None:
3868
 
            global _shared_stores_at_exit_installed
3869
 
            stores = _shared_stores
3870
 
            def save_config_changes():
3871
 
                for k, store in stores.iteritems():
3872
 
                    store.save_changes()
3873
 
            if not _shared_stores_at_exit_installed:
3874
 
                # FIXME: Ugly hack waiting for library_state to always be
3875
 
                # available. -- vila 20120731
3876
 
                import atexit
3877
 
                atexit.register(save_config_changes)
3878
 
                _shared_stores_at_exit_installed = True
3879
 
        else:
3880
 
            stores = state.config_stores
3881
 
        url = store.external_url()
3882
 
        try:
3883
 
            return stores[url]
3884
 
        except KeyError:
3885
 
            stores[url] = store
3886
 
            return store
3887
 
 
3888
3738
 
3889
3739
class MemoryStack(Stack):
3890
3740
    """A configuration stack defined from a string.
3940
3790
        self.store.save()
3941
3791
 
3942
3792
 
3943
 
class GlobalStack(Stack):
 
3793
class GlobalStack(_CompatibleStack):
3944
3794
    """Global options only stack.
3945
3795
 
3946
3796
    The following sections are queried:
3954
3804
    """
3955
3805
 
3956
3806
    def __init__(self):
3957
 
        gstore = self.get_shared_store(GlobalStore())
 
3807
        gstore = GlobalStore()
3958
3808
        super(GlobalStack, self).__init__(
3959
3809
            [self._get_overrides,
3960
3810
             NameMatcher(gstore, 'DEFAULT').get_sections],
3961
3811
            gstore, mutable_section_id='DEFAULT')
3962
3812
 
3963
3813
 
3964
 
class LocationStack(Stack):
 
3814
class LocationStack(_CompatibleStack):
3965
3815
    """Per-location options falling back to global options stack.
3966
3816
 
3967
3817
 
3983
3833
        """Make a new stack for a location and global configuration.
3984
3834
 
3985
3835
        :param location: A URL prefix to """
3986
 
        lstore = self.get_shared_store(LocationStore())
 
3836
        lstore = LocationStore()
3987
3837
        if location.startswith('file://'):
3988
3838
            location = urlutils.local_path_from_url(location)
3989
 
        gstore = self.get_shared_store(GlobalStore())
 
3839
        gstore = GlobalStore()
3990
3840
        super(LocationStack, self).__init__(
3991
3841
            [self._get_overrides,
3992
3842
             LocationMatcher(lstore, location).get_sections,
4014
3864
    """
4015
3865
 
4016
3866
    def __init__(self, branch):
4017
 
        lstore = self.get_shared_store(LocationStore())
 
3867
        lstore = LocationStore()
4018
3868
        bstore = branch._get_config_store()
4019
 
        gstore = self.get_shared_store(GlobalStore())
 
3869
        gstore = GlobalStore()
4020
3870
        super(BranchStack, self).__init__(
4021
3871
            [self._get_overrides,
4022
3872
             LocationMatcher(lstore, branch.base).get_sections,
4034
3884
    @needs_write_lock
4035
3885
    def set(self, name, value):
4036
3886
        super(BranchStack, self).set(name, value)
4037
 
        # Unlocking the branch will trigger a store.save_changes() so the last
4038
 
        # unlock saves all the changes.
 
3887
        # Force a write to persistent storage
 
3888
        self.store.save_changes()
4039
3889
 
4040
3890
    @needs_write_lock
4041
3891
    def remove(self, name):
4042
3892
        super(BranchStack, self).remove(name)
4043
 
        # Unlocking the branch will trigger a store.save_changes() so the last
4044
 
        # unlock saves all the changes.
4045
 
 
4046
 
 
4047
 
class RemoteControlStack(Stack):
 
3893
        # Force a write to persistent storage
 
3894
        self.store.save_changes()
 
3895
 
 
3896
 
 
3897
class RemoteControlStack(_CompatibleStack):
4048
3898
    """Remote control-only options stack."""
4049
3899
 
4050
3900
    # FIXME 2011-11-22 JRV This should probably be renamed to avoid confusion
4092
3942
        self.store.save_changes()
4093
3943
 
4094
3944
 
 
3945
# Use a an empty dict to initialize an empty configobj avoiding all
 
3946
# parsing and encoding checks
 
3947
_quoting_config = configobj.ConfigObj(
 
3948
    {}, encoding='utf-8', interpolation=False, list_values=True)
 
3949
 
4095
3950
class cmd_config(commands.Command):
4096
3951
    __doc__ = """Display, set or remove a configuration option.
4097
3952
 
4098
 
    Display the active value for option NAME.
 
3953
    Display the active value for a given option.
4099
3954
 
4100
3955
    If --all is specified, NAME is interpreted as a regular expression and all
4101
 
    matching options are displayed mentioning their scope and without resolving
4102
 
    option references in the value). The active value that bzr will take into
4103
 
    account is the first one displayed for each option.
4104
 
 
4105
 
    If NAME is not given, --all .* is implied (all options are displayed for the
4106
 
    current scope).
4107
 
 
4108
 
    Setting a value is achieved by using NAME=value without spaces. The value
 
3956
    matching options are displayed mentioning their scope. The active value
 
3957
    that bzr will take into account is the first one displayed for each option.
 
3958
 
 
3959
    If no NAME is given, --all .* is implied.
 
3960
 
 
3961
    Setting a value is achieved by using name=value without spaces. The value
4109
3962
    is set in the most relevant scope and can be checked by displaying the
4110
3963
    option again.
4111
 
 
4112
 
    Removing a value is achieved by using --remove NAME.
4113
3964
    """
4114
3965
 
4115
3966
    takes_args = ['name?']
4136
3987
            remove=False):
4137
3988
        if directory is None:
4138
3989
            directory = '.'
4139
 
        directory = directory_service.directories.dereference(directory)
4140
3990
        directory = urlutils.normalize_url(directory)
4141
3991
        if remove and all:
4142
3992
            raise errors.BzrError(
4200
4050
            except errors.NotBranchError:
4201
4051
                return LocationStack(directory)
4202
4052
 
4203
 
    def _quote_multiline(self, value):
4204
 
        if '\n' in value:
4205
 
            value = '"""' + value + '"""'
4206
 
        return value
4207
 
 
4208
4053
    def _show_value(self, name, directory, scope):
4209
4054
        conf = self._get_stack(directory, scope)
4210
 
        value = conf.get(name, expand=True, convert=False)
 
4055
        value = conf.get(name, expand=True)
4211
4056
        if value is not None:
4212
4057
            # Quote the value appropriately
4213
 
            value = self._quote_multiline(value)
 
4058
            value = _quoting_config._quote(value)
4214
4059
            self.outf.write('%s\n' % (value,))
4215
4060
        else:
4216
4061
            raise errors.NoSuchConfigOption(name)
4224
4069
        cur_store_id = None
4225
4070
        cur_section = None
4226
4071
        conf = self._get_stack(directory, scope)
4227
 
        for store, section in conf.iter_sections():
4228
 
            for oname in section.iter_option_names():
4229
 
                if name.search(oname):
4230
 
                    if cur_store_id != store.id:
4231
 
                        # Explain where the options are defined
4232
 
                        self.outf.write('%s:\n' % (store.id,))
4233
 
                        cur_store_id = store.id
4234
 
                        cur_section = None
4235
 
                    if (section.id is not None and cur_section != section.id):
4236
 
                        # Display the section id as it appears in the store
4237
 
                        # (None doesn't appear by definition)
4238
 
                        self.outf.write('  [%s]\n' % (section.id,))
4239
 
                        cur_section = section.id
4240
 
                    value = section.get(oname, expand=False)
4241
 
                    # Quote the value appropriately
4242
 
                    value = self._quote_multiline(value)
4243
 
                    self.outf.write('  %s = %s\n' % (oname, value))
 
4072
        for sections in conf.sections_def:
 
4073
            for store, section in sections():
 
4074
                for oname in section.iter_option_names():
 
4075
                    if name.search(oname):
 
4076
                        if cur_store_id != store.id:
 
4077
                            # Explain where the options are defined
 
4078
                            self.outf.write('%s:\n' % (store.id,))
 
4079
                            cur_store_id = store.id
 
4080
                            cur_section = None
 
4081
                        if (section.id is not None
 
4082
                            and cur_section != section.id):
 
4083
                            # Display the section id as it appears in the store
 
4084
                            # (None doesn't appear by definition)
 
4085
                            self.outf.write('  [%s]\n' % (section.id,))
 
4086
                            cur_section = section.id
 
4087
                        value = section.get(oname, expand=False)
 
4088
                        # Since we don't use the stack, we need to restore a
 
4089
                        # proper quoting.
 
4090
                        try:
 
4091
                            opt = option_registry.get(oname)
 
4092
                            value = opt.convert_from_unicode(store, value)
 
4093
                        except KeyError:
 
4094
                            value = store.unquote(value)
 
4095
                        value = _quoting_config._quote(value)
 
4096
                        self.outf.write('  %s = %s\n' % (oname, value))
4244
4097
 
4245
4098
    def _set_config_option(self, name, value, directory, scope):
4246
4099
        conf = self._get_stack(directory, scope, write_access=True)
4247
4100
        conf.set(name, value)
4248
 
        # Explicitly save the changes
4249
 
        conf.store.save_changes()
4250
4101
 
4251
4102
    def _remove_config_option(self, name, directory, scope):
4252
4103
        if name is None:
4255
4106
        conf = self._get_stack(directory, scope, write_access=True)
4256
4107
        try:
4257
4108
            conf.remove(name)
4258
 
            # Explicitly save the changes
4259
 
            conf.store.save_changes()
4260
4109
        except KeyError:
4261
4110
            raise errors.NoSuchConfigOption(name)
4262
4111