/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 breezy/config.py

  • Committer: Breezy landing bot
  • Author(s): Colin Watson
  • Date: 2020-11-16 21:47:08 UTC
  • mfrom: (7521.1.1 remove-lp-workaround)
  • Revision ID: breezy.the.bot@gmail.com-20201116214708-jos209mgxi41oy15
Remove breezy.git workaround for bazaar.launchpad.net.

Merged from https://code.launchpad.net/~cjwatson/brz/remove-lp-workaround/+merge/393710

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
# along with this program; if not, write to the Free Software
17
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
 
19
 
"""Configuration that affects the behaviour of Bazaar.
20
 
 
21
 
Currently this configuration resides in ~/.bazaar/bazaar.conf
22
 
and ~/.bazaar/locations.conf, which is written to by bzr.
23
 
 
24
 
In bazaar.conf the following options may be set:
 
19
"""Configuration that affects the behaviour of Breezy.
 
20
 
 
21
Currently this configuration resides in ~/.config/breezy/breezy.conf
 
22
and ~/.config/breezy/locations.conf, which is written to by brz.
 
23
 
 
24
If the first location doesn't exist, then brz falls back to reading
 
25
Bazaar configuration files in ~/.bazaar or ~/.config/bazaar.
 
26
 
 
27
In breezy.conf the following options may be set:
25
28
[DEFAULT]
26
29
editor=name-of-program
27
30
email=Your Name <your@email.address>
28
31
check_signatures=require|ignore|check-available(default)
29
32
create_signatures=always|never|when-required(default)
30
 
gpg_signing_command=name-of-program
31
33
log_format=name-of-format
32
34
validate_signatures_in_log=true|false(default)
33
35
acceptable_keys=pattern1,pattern2
35
37
 
36
38
in locations.conf, you specify the url of a branch and options for it.
37
39
Wildcards may be used - * and ? as normal in shell completion. Options
38
 
set in both bazaar.conf and locations.conf are overridden by the locations.conf
 
40
set in both breezy.conf and locations.conf are overridden by the locations.conf
39
41
setting.
40
42
[/home/robertc/source]
41
43
recurse=False|True(default)
48
50
explanation of options
49
51
----------------------
50
52
editor - this option sets the pop up editor to use during commits.
51
 
email - this option sets the user id bzr will use when committing.
52
 
check_signatures - this option will control whether bzr will require good gpg
 
53
email - this option sets the user id brz will use when committing.
 
54
check_signatures - this option will control whether brz will require good gpg
53
55
                   signatures, ignore them, or check them if they are
54
 
                   present.  Currently it is unused except that check_signatures
55
 
                   turns on create_signatures.
56
 
create_signatures - this option controls whether bzr will always create
 
56
                   present.  Currently it is unused except that
 
57
                   check_signatures turns on create_signatures.
 
58
create_signatures - this option controls whether brz will always create
57
59
                    gpg signatures or not on commits.  There is an unused
58
60
                    option which in future is expected to work if
59
61
                    branch settings require signatures.
63
65
acceptable_keys - comma separated list of key patterns acceptable for
64
66
                  verify-signatures command
65
67
 
66
 
In bazaar.conf you can also define aliases in the ALIASES sections, example
 
68
In breezy.conf you can also define aliases in the ALIASES sections, example
67
69
 
68
70
[ALIASES]
69
71
lastlog=log --line -r-10..-1
72
74
up=pull
73
75
"""
74
76
 
75
 
from __future__ import absolute_import
76
 
from cStringIO import StringIO
77
77
import os
78
78
import sys
79
79
 
80
 
import bzrlib
81
 
from bzrlib.decorators import needs_write_lock
82
 
from bzrlib.lazy_import import lazy_import
 
80
import configobj
 
81
from io import BytesIO
 
82
 
 
83
import breezy
 
84
from .lazy_import import lazy_import
83
85
lazy_import(globals(), """
84
86
import base64
 
87
import errno
85
88
import fnmatch
86
89
import re
 
90
import stat
87
91
 
88
 
from bzrlib import (
89
 
    atomicfile,
 
92
from breezy import (
 
93
    cmdline,
90
94
    controldir,
91
95
    debug,
92
96
    directory_service,
93
 
    errors,
94
 
    lazy_regex,
95
 
    library_state,
 
97
    lock,
96
98
    lockdir,
97
99
    mergetools,
98
100
    osutils,
99
 
    symbol_versioning,
100
101
    trace,
101
102
    transport,
102
103
    ui,
103
104
    urlutils,
104
105
    win32utils,
105
106
    )
106
 
from bzrlib.i18n import gettext
107
 
from bzrlib.util.configobj import configobj
 
107
from breezy.i18n import gettext
108
108
""")
109
 
from bzrlib import (
 
109
from . import (
110
110
    commands,
 
111
    bedding,
 
112
    errors,
111
113
    hooks,
112
114
    lazy_regex,
113
115
    registry,
114
116
    )
115
 
from bzrlib.symbol_versioning import (
116
 
    deprecated_in,
117
 
    deprecated_method,
118
 
    )
119
 
 
120
 
 
121
 
CHECK_IF_POSSIBLE=0
122
 
CHECK_ALWAYS=1
123
 
CHECK_NEVER=2
124
 
 
125
 
 
126
 
SIGN_WHEN_REQUIRED=0
127
 
SIGN_ALWAYS=1
128
 
SIGN_NEVER=2
 
117
 
 
118
 
 
119
CHECK_IF_POSSIBLE = 0
 
120
CHECK_ALWAYS = 1
 
121
CHECK_NEVER = 2
 
122
 
 
123
 
 
124
SIGN_WHEN_REQUIRED = 0
 
125
SIGN_ALWAYS = 1
 
126
SIGN_NEVER = 2
129
127
 
130
128
 
131
129
POLICY_NONE = 0
152
150
STORE_GLOBAL = 4
153
151
 
154
152
 
 
153
class OptionExpansionLoop(errors.BzrError):
 
154
 
 
155
    _fmt = 'Loop involving %(refs)r while expanding "%(string)s".'
 
156
 
 
157
    def __init__(self, string, refs):
 
158
        self.string = string
 
159
        self.refs = '->'.join(refs)
 
160
 
 
161
 
 
162
class ExpandingUnknownOption(errors.BzrError):
 
163
 
 
164
    _fmt = 'Option "%(name)s" is not defined while expanding "%(string)s".'
 
165
 
 
166
    def __init__(self, name, string):
 
167
        self.name = name
 
168
        self.string = string
 
169
 
 
170
 
 
171
class IllegalOptionName(errors.BzrError):
 
172
 
 
173
    _fmt = 'Option "%(name)s" is not allowed.'
 
174
 
 
175
    def __init__(self, name):
 
176
        self.name = name
 
177
 
 
178
 
 
179
class ConfigContentError(errors.BzrError):
 
180
 
 
181
    _fmt = "Config file %(filename)s is not UTF-8 encoded\n"
 
182
 
 
183
    def __init__(self, filename):
 
184
        self.filename = filename
 
185
 
 
186
 
 
187
class ParseConfigError(errors.BzrError):
 
188
 
 
189
    _fmt = "Error(s) parsing config file %(filename)s:\n%(errors)s"
 
190
 
 
191
    def __init__(self, errors, filename):
 
192
        self.filename = filename
 
193
        self.errors = '\n'.join(e.msg for e in errors)
 
194
 
 
195
 
 
196
class ConfigOptionValueError(errors.BzrError):
 
197
 
 
198
    _fmt = ('Bad value "%(value)s" for option "%(name)s".\n'
 
199
            'See ``brz help %(name)s``')
 
200
 
 
201
    def __init__(self, name, value):
 
202
        errors.BzrError.__init__(self, name=name, value=value)
 
203
 
 
204
 
 
205
class NoEmailInUsername(errors.BzrError):
 
206
 
 
207
    _fmt = "%(username)r does not seem to contain a reasonable email address"
 
208
 
 
209
    def __init__(self, username):
 
210
        self.username = username
 
211
 
 
212
 
 
213
class NoSuchConfig(errors.BzrError):
 
214
 
 
215
    _fmt = ('The "%(config_id)s" configuration does not exist.')
 
216
 
 
217
    def __init__(self, config_id):
 
218
        errors.BzrError.__init__(self, config_id=config_id)
 
219
 
 
220
 
 
221
class NoSuchConfigOption(errors.BzrError):
 
222
 
 
223
    _fmt = ('The "%(option_name)s" configuration option does not exist.')
 
224
 
 
225
    def __init__(self, option_name):
 
226
        errors.BzrError.__init__(self, option_name=option_name)
 
227
 
 
228
 
155
229
def signature_policy_from_unicode(signature_string):
156
230
    """Convert a string to a signing policy."""
157
231
    if signature_string.lower() == 'check-available':
176
250
                     % signature_string)
177
251
 
178
252
 
 
253
def _has_triplequote_bug():
 
254
    """True if triple quote logic is reversed, see lp:710410."""
 
255
    conf = configobj.ConfigObj()
 
256
    quote = getattr(conf, "_get_triple_quote", None)
 
257
    if quote and quote('"""') != "'''":
 
258
        return True
 
259
    return False
 
260
 
 
261
 
179
262
class ConfigObj(configobj.ConfigObj):
180
263
 
181
264
    def __init__(self, infile=None, **kwargs):
184
267
                                        interpolation=False,
185
268
                                        **kwargs)
186
269
 
 
270
    if _has_triplequote_bug():
 
271
        def _get_triple_quote(self, value):
 
272
            quot = super(ConfigObj, self)._get_triple_quote(value)
 
273
            if quot == configobj.tdquot:
 
274
                return configobj.tsquot
 
275
            return configobj.tdquot
 
276
 
187
277
    def get_bool(self, section, key):
188
278
        return self[section].as_bool(key)
189
279
 
208
298
        raise NotImplementedError(self.config_id)
209
299
 
210
300
    def get_change_editor(self, old_tree, new_tree):
211
 
        from bzrlib import diff
 
301
        from breezy import diff
212
302
        cmd = self._get_change_editor()
213
303
        if cmd is None:
214
304
            return None
 
305
        cmd = cmd.replace('@old_path', '{old_path}')
 
306
        cmd = cmd.replace('@new_path', '{new_path}')
 
307
        cmd = cmdline.split(cmd)
 
308
        if '{old_path}' not in cmd:
 
309
            cmd.extend(['{old_path}', '{new_path}'])
215
310
        return diff.DiffFromTool.from_string(cmd, old_tree, new_tree,
216
311
                                             sys.stdout)
217
312
 
304
399
                else:
305
400
                    name = chunk[1:-1]
306
401
                    if name in _ref_stack:
307
 
                        raise errors.OptionExpansionLoop(string, _ref_stack)
 
402
                        raise OptionExpansionLoop(string, _ref_stack)
308
403
                    _ref_stack.append(name)
309
404
                    value = self._expand_option(name, env, _ref_stack)
310
405
                    if value is None:
311
 
                        raise errors.ExpandingUnknownOption(name, string)
 
406
                        raise ExpandingUnknownOption(name, string)
312
407
                    if isinstance(value, list):
313
408
                        list_value = True
314
409
                        chunks.extend(value)
334
429
            value = env[name]
335
430
        else:
336
431
            # FIXME: This is a limited implementation, what we really need is a
337
 
            # way to query the bzr config for the value of an option,
 
432
            # way to query the brz config for the value of an option,
338
433
            # respecting the scope rules (That is, once we implement fallback
339
434
            # configs, getting the option value should restart from the top
340
435
            # config, not the current one) -- vila 20101222
398
493
            otherwise.
399
494
        """
400
495
        l = self.get_user_option(option_name, expand=expand)
401
 
        if isinstance(l, (str, unicode)):
 
496
        if isinstance(l, str):
402
497
            # A single value, most probably the user forgot (or didn't care to
403
498
            # add) the final ','
404
499
            l = [l]
405
500
        return l
406
501
 
407
 
    @deprecated_method(deprecated_in((2, 5, 0)))
408
 
    def get_user_option_as_int_from_SI(self, option_name, default=None):
409
 
        """Get a generic option from a human readable size in SI units, e.g 10MB
410
 
 
411
 
        Accepted suffixes are K,M,G. It is case-insensitive and may be followed
412
 
        by a trailing b (i.e. Kb, MB). This is intended to be practical and not
413
 
        pedantic.
414
 
 
415
 
        :return Integer, expanded to its base-10 value if a proper SI unit is 
416
 
            found. If the option doesn't exist, or isn't a value in 
417
 
            SI units, return default (which defaults to None)
418
 
        """
419
 
        val = self.get_user_option(option_name)
420
 
        if isinstance(val, list):
421
 
            val = val[0]
422
 
        if val is None:
423
 
            val = default
424
 
        else:
425
 
            p = re.compile("^(\d+)([kmg])*b*$", re.IGNORECASE)
426
 
            try:
427
 
                m = p.match(val)
428
 
                if m is not None:
429
 
                    val = int(m.group(1))
430
 
                    if m.group(2) is not None:
431
 
                        if m.group(2).lower() == 'k':
432
 
                            val *= 10**3
433
 
                        elif m.group(2).lower() == 'm':
434
 
                            val *= 10**6
435
 
                        elif m.group(2).lower() == 'g':
436
 
                            val *= 10**9
437
 
                else:
438
 
                    ui.ui_factory.show_warning(gettext('Invalid config value for "{0}" '
439
 
                                               ' value {1!r} is not an SI unit.').format(
440
 
                                                option_name, val))
441
 
                    val = default
442
 
            except TypeError:
443
 
                val = default
444
 
        return val
445
 
 
446
 
    @deprecated_method(deprecated_in((2, 5, 0)))
447
 
    def gpg_signing_command(self):
448
 
        """What program should be used to sign signatures?"""
449
 
        result = self._gpg_signing_command()
450
 
        if result is None:
451
 
            result = "gpg"
452
 
        return result
453
 
 
454
 
    def _gpg_signing_command(self):
455
 
        """See gpg_signing_command()."""
456
 
        return None
457
 
 
458
 
    @deprecated_method(deprecated_in((2, 5, 0)))
459
 
    def log_format(self):
460
 
        """What log format should be used"""
461
 
        result = self._log_format()
462
 
        if result is None:
463
 
            result = "long"
464
 
        return result
465
 
 
466
502
    def _log_format(self):
467
503
        """See log_format()."""
468
504
        return None
480
516
        """See validate_signatures_in_log()."""
481
517
        return None
482
518
 
483
 
    @deprecated_method(deprecated_in((2, 5, 0)))
484
 
    def acceptable_keys(self):
485
 
        """Comma separated list of key patterns acceptable to 
486
 
        verify-signatures command"""
487
 
        result = self._acceptable_keys()
488
 
        return result
489
 
 
490
 
    def _acceptable_keys(self):
491
 
        """See acceptable_keys()."""
492
 
        return None
493
 
 
494
 
    @deprecated_method(deprecated_in((2, 5, 0)))
495
 
    def post_commit(self):
496
 
        """An ordered list of python functions to call.
497
 
 
498
 
        Each function takes branch, rev_id as parameters.
499
 
        """
500
 
        return self._post_commit()
501
 
 
502
519
    def _post_commit(self):
503
520
        """See Config.post_commit."""
504
521
        return None
512
529
 
513
530
        Something similar to 'Martin Pool <mbp@sourcefrog.net>'
514
531
 
515
 
        $BZR_EMAIL can be set to override this, then
 
532
        $BRZ_EMAIL or $BZR_EMAIL can be set to override this, then
516
533
        the concrete policy type is checked, and finally
517
534
        $EMAIL is examined.
518
 
        If no username can be found, errors.NoWhoami exception is raised.
 
535
        If no username can be found, NoWhoami exception is raised.
519
536
        """
520
 
        v = os.environ.get('BZR_EMAIL')
 
537
        v = os.environ.get('BRZ_EMAIL') or os.environ.get('BZR_EMAIL')
521
538
        if v:
522
 
            return v.decode(osutils.get_user_encoding())
 
539
            return v
523
540
        v = self._get_user_id()
524
541
        if v:
525
542
            return v
526
 
        return default_email()
527
 
 
528
 
    def ensure_username(self):
529
 
        """Raise errors.NoWhoami if username is not set.
530
 
 
531
 
        This method relies on the username() function raising the error.
532
 
        """
533
 
        self.username()
534
 
 
535
 
    @deprecated_method(deprecated_in((2, 5, 0)))
536
 
    def signature_checking(self):
537
 
        """What is the current policy for signature checking?."""
538
 
        policy = self._get_signature_checking()
539
 
        if policy is not None:
540
 
            return policy
541
 
        return CHECK_IF_POSSIBLE
542
 
 
543
 
    @deprecated_method(deprecated_in((2, 5, 0)))
544
 
    def signing_policy(self):
545
 
        """What is the current policy for signature checking?."""
546
 
        policy = self._get_signing_policy()
547
 
        if policy is not None:
548
 
            return policy
549
 
        return SIGN_WHEN_REQUIRED
550
 
 
551
 
    @deprecated_method(deprecated_in((2, 5, 0)))
552
 
    def signature_needed(self):
553
 
        """Is a signature needed when committing ?."""
554
 
        policy = self._get_signing_policy()
555
 
        if policy is None:
556
 
            policy = self._get_signature_checking()
557
 
            if policy is not None:
558
 
                #this warning should go away once check_signatures is
559
 
                #implemented (if not before)
560
 
                trace.warning("Please use create_signatures,"
561
 
                              " not check_signatures to set signing policy.")
562
 
        elif policy == SIGN_ALWAYS:
563
 
            return True
564
 
        return False
565
 
 
566
 
    @deprecated_method(deprecated_in((2, 5, 0)))
567
 
    def gpg_signing_key(self):
568
 
        """GPG user-id to sign commits"""
569
 
        key = self.get_user_option('gpg_signing_key')
570
 
        if key == "default" or key == None:
571
 
            return self.user_email()
572
 
        else:
573
 
            return key
 
543
        return bedding.default_email()
574
544
 
575
545
    def get_alias(self, value):
576
546
        return self._get_alias(value)
621
591
        # This should be done through the proposed config defaults mechanism
622
592
        # when it becomes available in the future.
623
593
        command_line = (self.get_user_option('bzr.mergetool.%s' % name,
624
 
                                             expand=False)
625
 
                        or mergetools.known_merge_tools.get(name, None))
 
594
                                             expand=False) or
 
595
                        mergetools.known_merge_tools.get(name, None))
626
596
        return command_line
627
597
 
628
598
 
636
606
        These are all empty initially, because by default nothing should get
637
607
        notified.
638
608
        """
639
 
        super(_ConfigHooks, self).__init__('bzrlib.config', 'ConfigHooks')
 
609
        super(_ConfigHooks, self).__init__('breezy.config', 'ConfigHooks')
640
610
        self.add_hook('load',
641
611
                      'Invoked when a config store is loaded.'
642
612
                      ' The signature is (store).',
658
628
                      'Invoked when a config option is removed.'
659
629
                      ' The signature is (stack, name).',
660
630
                      (2, 4))
 
631
 
 
632
 
661
633
ConfigHooks = _ConfigHooks()
662
634
 
663
635
 
671
643
        These are all empty initially, because by default nothing should get
672
644
        notified.
673
645
        """
674
 
        super(_OldConfigHooks, self).__init__('bzrlib.config', 'OldConfigHooks')
 
646
        super(_OldConfigHooks, self).__init__(
 
647
            'breezy.config', 'OldConfigHooks')
675
648
        self.add_hook('load',
676
649
                      'Invoked when a config store is loaded.'
677
650
                      ' The signature is (config).',
693
666
                      'Invoked when a config option is removed.'
694
667
                      ' The signature is (config, name).',
695
668
                      (2, 4))
 
669
 
 
670
 
696
671
OldConfigHooks = _OldConfigHooks()
697
672
 
698
673
 
699
674
class IniBasedConfig(Config):
700
675
    """A configuration policy that draws from ini files."""
701
676
 
702
 
    def __init__(self, get_filename=symbol_versioning.DEPRECATED_PARAMETER,
703
 
                 file_name=None):
 
677
    def __init__(self, file_name=None):
704
678
        """Base class for configuration files using an ini-like syntax.
705
679
 
706
680
        :param file_name: The configuration file path.
707
681
        """
708
682
        super(IniBasedConfig, self).__init__()
709
683
        self.file_name = file_name
710
 
        if symbol_versioning.deprecated_passed(get_filename):
711
 
            symbol_versioning.warn(
712
 
                'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
713
 
                ' Use file_name instead.',
714
 
                DeprecationWarning,
715
 
                stacklevel=2)
716
 
            if get_filename is not None:
717
 
                self.file_name = get_filename()
718
 
        else:
719
 
            self.file_name = file_name
 
684
        self.file_name = file_name
720
685
        self._content = None
721
686
        self._parser = None
722
687
 
724
689
    def from_string(cls, str_or_unicode, file_name=None, save=False):
725
690
        """Create a config object from a string.
726
691
 
727
 
        :param str_or_unicode: A string representing the file content. This will
728
 
            be utf-8 encoded.
 
692
        :param str_or_unicode: A string representing the file content. This
 
693
            will be utf-8 encoded.
729
694
 
730
695
        :param file_name: The configuration file path.
731
696
 
736
701
        return conf
737
702
 
738
703
    def _create_from_string(self, str_or_unicode, save):
739
 
        self._content = StringIO(str_or_unicode.encode('utf-8'))
 
704
        if isinstance(str_or_unicode, str):
 
705
            str_or_unicode = str_or_unicode.encode('utf-8')
 
706
        self._content = BytesIO(str_or_unicode)
740
707
        # Some tests use in-memory configs, some other always need the config
741
708
        # file to exist on disk.
742
709
        if save:
743
710
            self._write_config_file()
744
711
 
745
 
    def _get_parser(self, file=symbol_versioning.DEPRECATED_PARAMETER):
 
712
    def _get_parser(self):
746
713
        if self._parser is not None:
747
714
            return self._parser
748
 
        if symbol_versioning.deprecated_passed(file):
749
 
            symbol_versioning.warn(
750
 
                'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
751
 
                ' Use IniBasedConfig(_content=xxx) instead.',
752
 
                DeprecationWarning,
753
 
                stacklevel=2)
754
715
        if self._content is not None:
755
716
            co_input = self._content
756
717
        elif self.file_name is None:
759
720
            co_input = self.file_name
760
721
        try:
761
722
            self._parser = ConfigObj(co_input, encoding='utf-8')
762
 
        except configobj.ConfigObjError, e:
763
 
            raise errors.ParseConfigError(e.errors, e.config.filename)
 
723
        except configobj.ConfigObjError as e:
 
724
            raise ParseConfigError(e.errors, e.config.filename)
764
725
        except UnicodeDecodeError:
765
 
            raise errors.ConfigContentError(self.file_name)
 
726
            raise ConfigContentError(self.file_name)
766
727
        # Make sure self.reload() will use the right file name
767
728
        self._parser.filename = self.file_name
768
729
        for hook in OldConfigHooks['load']:
824
785
            which sections should be searched. This is a list of (name,
825
786
            configobj) tuples.
826
787
        """
827
 
        opts = []
828
788
        if sections is None:
829
789
            parser = self._get_parser()
830
790
            sections = []
848
808
        return POLICY_NONE
849
809
 
850
810
    def _get_change_editor(self):
851
 
        return self.get_user_option('change_editor')
 
811
        return self.get_user_option('change_editor', expand=False)
852
812
 
853
813
    def _get_signature_checking(self):
854
814
        """See Config._get_signature_checking."""
891
851
        else:
892
852
            return None
893
853
 
894
 
    def _gpg_signing_command(self):
895
 
        """See Config.gpg_signing_command."""
896
 
        return self._get_user_option('gpg_signing_command')
897
 
 
898
854
    def _log_format(self):
899
855
        """See Config.log_format."""
900
856
        return self._get_user_option('log_format')
938
894
        try:
939
895
            del section[option_name]
940
896
        except KeyError:
941
 
            raise errors.NoSuchConfigOption(option_name)
 
897
            raise NoSuchConfigOption(option_name)
942
898
        self._write_config_file()
943
899
        for hook in OldConfigHooks['remove']:
944
900
            hook(self, option_name)
946
902
    def _write_config_file(self):
947
903
        if self.file_name is None:
948
904
            raise AssertionError('We cannot save, self.file_name is None')
 
905
        from . import atomicfile
949
906
        conf_dir = os.path.dirname(self.file_name)
950
 
        ensure_config_dir_exists(conf_dir)
951
 
        atomic_file = atomicfile.AtomicFile(self.file_name)
952
 
        self._get_parser().write(atomic_file)
953
 
        atomic_file.commit()
954
 
        atomic_file.close()
 
907
        bedding.ensure_config_dir_exists(conf_dir)
 
908
        with atomicfile.AtomicFile(self.file_name) as atomic_file:
 
909
            self._get_parser().write(atomic_file)
955
910
        osutils.copy_ownership_from_path(self.file_name)
956
911
        for hook in OldConfigHooks['save']:
957
912
            hook(self)
963
918
    If several processes try to write the config file, the accesses need to be
964
919
    serialized.
965
920
 
966
 
    Daughter classes should decorate all methods that update a config with the
967
 
    ``@needs_write_lock`` decorator (they call, directly or indirectly, the
 
921
    Daughter classes should use the self.lock_write() decorator method when
 
922
    they upate a config (they call, directly or indirectly, the
968
923
    ``_write_config_file()`` method. These methods (typically ``set_option()``
969
924
    and variants must reload the config file from disk before calling
970
925
    ``_write_config_file()``), this can be achieved by calling the
1009
964
 
1010
965
        If the directory doesn't exist it is created.
1011
966
        """
1012
 
        ensure_config_dir_exists(self.dir)
1013
 
        return self._lock.lock_write(token)
 
967
        bedding.ensure_config_dir_exists(self.dir)
 
968
        token = self._lock.lock_write(token)
 
969
        return lock.LogicalLockResult(self.unlock, token)
1014
970
 
1015
971
    def unlock(self):
1016
972
        self._lock.unlock()
1018
974
    def break_lock(self):
1019
975
        self._lock.break_lock()
1020
976
 
1021
 
    @needs_write_lock
1022
977
    def remove_user_option(self, option_name, section_name=None):
1023
 
        super(LockableConfig, self).remove_user_option(option_name,
1024
 
                                                       section_name)
 
978
        with self.lock_write():
 
979
            super(LockableConfig, self).remove_user_option(
 
980
                option_name, section_name)
1025
981
 
1026
982
    def _write_config_file(self):
1027
983
        if self._lock is None or not self._lock.is_held:
1028
984
            # NB: if the following exception is raised it probably means a
1029
 
            # missing @needs_write_lock decorator on one of the callers.
 
985
            # missing call to lock_write() by one of the callers.
1030
986
            raise errors.ObjectNotLocked(self)
1031
987
        super(LockableConfig, self)._write_config_file()
1032
988
 
1035
991
    """The configuration that should be used for a specific location."""
1036
992
 
1037
993
    def __init__(self):
1038
 
        super(GlobalConfig, self).__init__(file_name=config_filename())
 
994
        super(GlobalConfig, self).__init__(file_name=bedding.config_path())
1039
995
 
1040
996
    def config_id(self):
1041
 
        return 'bazaar'
 
997
        return 'breezy'
1042
998
 
1043
999
    @classmethod
1044
1000
    def from_string(cls, str_or_unicode, save=False):
1053
1009
        conf._create_from_string(str_or_unicode, save)
1054
1010
        return conf
1055
1011
 
1056
 
    @needs_write_lock
1057
1012
    def set_user_option(self, option, value):
1058
1013
        """Save option and its value in the configuration."""
1059
 
        self._set_option(option, value, 'DEFAULT')
 
1014
        with self.lock_write():
 
1015
            self._set_option(option, value, 'DEFAULT')
1060
1016
 
1061
1017
    def get_aliases(self):
1062
1018
        """Return the aliases section."""
1065
1021
        else:
1066
1022
            return {}
1067
1023
 
1068
 
    @needs_write_lock
1069
1024
    def set_alias(self, alias_name, alias_command):
1070
1025
        """Save the alias in the configuration."""
1071
 
        self._set_option(alias_name, alias_command, 'ALIASES')
 
1026
        with self.lock_write():
 
1027
            self._set_option(alias_name, alias_command, 'ALIASES')
1072
1028
 
1073
 
    @needs_write_lock
1074
1029
    def unset_alias(self, alias_name):
1075
1030
        """Unset an existing alias."""
1076
 
        self.reload()
1077
 
        aliases = self._get_parser().get('ALIASES')
1078
 
        if not aliases or alias_name not in aliases:
1079
 
            raise errors.NoSuchAlias(alias_name)
1080
 
        del aliases[alias_name]
1081
 
        self._write_config_file()
 
1031
        with self.lock_write():
 
1032
            self.reload()
 
1033
            aliases = self._get_parser().get('ALIASES')
 
1034
            if not aliases or alias_name not in aliases:
 
1035
                raise errors.NoSuchAlias(alias_name)
 
1036
            del aliases[alias_name]
 
1037
            self._write_config_file()
1082
1038
 
1083
1039
    def _set_option(self, option, value, section):
1084
1040
        self.reload()
1097
1053
            # doesn't exist yet. So we force DEFAULT when yielding
1098
1054
            name = 'DEFAULT'
1099
1055
            if 'DEFAULT' not in parser:
1100
 
               parser['DEFAULT']= {}
 
1056
                parser['DEFAULT'] = {}
1101
1057
        yield (name, parser[name], self.config_id())
1102
1058
 
1103
 
    @needs_write_lock
1104
1059
    def remove_user_option(self, option_name, section_name=None):
1105
1060
        if section_name is None:
1106
1061
            # We need to force the default section.
1107
1062
            section_name = 'DEFAULT'
1108
 
        # We need to avoid the LockableConfig implementation or we'll lock
1109
 
        # twice
1110
 
        super(LockableConfig, self).remove_user_option(option_name,
1111
 
                                                       section_name)
 
1063
        with self.lock_write():
 
1064
            # We need to avoid the LockableConfig implementation or we'll lock
 
1065
            # twice
 
1066
            super(LockableConfig, self).remove_user_option(
 
1067
                option_name, section_name)
 
1068
 
1112
1069
 
1113
1070
def _iter_for_location_by_parts(sections, location):
1114
1071
    """Keep only the sessions matching the specified location.
1150
1107
        else:
1151
1108
            # Rely on zip truncating in length to the length of the shortest
1152
1109
            # argument sequence.
1153
 
            names = zip(location_parts, section_parts)
1154
 
            for name in names:
 
1110
            for name in zip(location_parts, section_parts):
1155
1111
                if not fnmatch.fnmatch(name[0], name[1]):
1156
1112
                    matched = False
1157
1113
                    break
1167
1123
 
1168
1124
    def __init__(self, location):
1169
1125
        super(LocationConfig, self).__init__(
1170
 
            file_name=locations_config_filename())
 
1126
            file_name=bedding.locations_config_path())
1171
1127
        # local file locations are looked up by local path, rather than
1172
1128
        # by file url. This is because the config file is a user
1173
1129
        # file, and we would rather not expose the user to file urls.
1195
1151
 
1196
1152
    def _get_matching_sections(self):
1197
1153
        """Return an ordered list of section names matching this location."""
1198
 
        matches = list(_iter_for_location_by_parts(self._get_parser(),
1199
 
                                                   self.location))
1200
1154
        # put the longest (aka more specific) locations first
1201
 
        matches.sort(
1202
 
            key=lambda (section, extra_path, length): (length, section),
 
1155
        matches = sorted(
 
1156
            _iter_for_location_by_parts(self._get_parser(), self.location),
 
1157
            key=lambda match: (match[2], match[0]),
1203
1158
            reverse=True)
1204
1159
        for (section, extra_path, length) in matches:
1205
1160
            yield section, extra_path
1238
1193
 
1239
1194
    def _set_option_policy(self, section, option_name, option_policy):
1240
1195
        """Set the policy for the given option name in the given section."""
1241
 
        # The old recurse=False option affects all options in the
1242
 
        # section.  To handle multiple policies in the section, we
1243
 
        # need to convert it to a policy_norecurse key.
1244
 
        try:
1245
 
            recurse = self._get_parser()[section].as_bool('recurse')
1246
 
        except KeyError:
1247
 
            pass
1248
 
        else:
1249
 
            symbol_versioning.warn(
1250
 
                'The recurse option is deprecated as of 0.14.  '
1251
 
                'The section "%s" has been converted to use policies.'
1252
 
                % section,
1253
 
                DeprecationWarning)
1254
 
            del self._get_parser()[section]['recurse']
1255
 
            if not recurse:
1256
 
                for key in self._get_parser()[section].keys():
1257
 
                    if not key.endswith(':policy'):
1258
 
                        self._get_parser()[section][key +
1259
 
                                                    ':policy'] = 'norecurse'
1260
 
 
1261
1196
        policy_key = option_name + ':policy'
1262
1197
        policy_name = _policy_name[option_policy]
1263
1198
        if policy_name is not None:
1266
1201
            if policy_key in self._get_parser()[section]:
1267
1202
                del self._get_parser()[section][policy_key]
1268
1203
 
1269
 
    @needs_write_lock
1270
1204
    def set_user_option(self, option, value, store=STORE_LOCATION):
1271
1205
        """Save option and its value in the configuration."""
1272
1206
        if store not in [STORE_LOCATION,
1273
1207
                         STORE_LOCATION_NORECURSE,
1274
1208
                         STORE_LOCATION_APPENDPATH]:
1275
1209
            raise ValueError('bad storage policy %r for %r' %
1276
 
                (store, option))
1277
 
        self.reload()
1278
 
        location = self.location
1279
 
        if location.endswith('/'):
1280
 
            location = location[:-1]
1281
 
        parser = self._get_parser()
1282
 
        if not location in parser and not location + '/' in parser:
1283
 
            parser[location] = {}
1284
 
        elif location + '/' in parser:
1285
 
            location = location + '/'
1286
 
        parser[location][option]=value
1287
 
        # the allowed values of store match the config policies
1288
 
        self._set_option_policy(location, option, store)
1289
 
        self._write_config_file()
1290
 
        for hook in OldConfigHooks['set']:
1291
 
            hook(self, option, value)
 
1210
                             (store, option))
 
1211
        with self.lock_write():
 
1212
            self.reload()
 
1213
            location = self.location
 
1214
            if location.endswith('/'):
 
1215
                location = location[:-1]
 
1216
            parser = self._get_parser()
 
1217
            if location not in parser and not location + '/' in parser:
 
1218
                parser[location] = {}
 
1219
            elif location + '/' in parser:
 
1220
                location = location + '/'
 
1221
            parser[location][option] = value
 
1222
            # the allowed values of store match the config policies
 
1223
            self._set_option_policy(location, option, store)
 
1224
            self._write_config_file()
 
1225
            for hook in OldConfigHooks['set']:
 
1226
                hook(self, option, value)
1292
1227
 
1293
1228
 
1294
1229
class BranchConfig(Config):
1384
1319
                yield section
1385
1320
 
1386
1321
    def _get_options(self, sections=None):
1387
 
        opts = []
1388
1322
        # First the locations options
1389
1323
        for option in self._get_location_config()._get_options():
1390
1324
            yield option
1404
1338
            yield option
1405
1339
 
1406
1340
    def set_user_option(self, name, value, store=STORE_BRANCH,
1407
 
        warn_masked=False):
 
1341
                        warn_masked=False):
1408
1342
        if store == STORE_BRANCH:
1409
1343
            self._get_branch_data_config().set_option(value, name)
1410
1344
        elif store == STORE_GLOBAL:
1429
1363
    def remove_user_option(self, option_name, section_name=None):
1430
1364
        self._get_branch_data_config().remove_option(option_name, section_name)
1431
1365
 
1432
 
    def _gpg_signing_command(self):
1433
 
        """See Config.gpg_signing_command."""
1434
 
        return self._get_safe_value('_gpg_signing_command')
1435
 
 
1436
1366
    def _post_commit(self):
1437
1367
        """See Config.post_commit."""
1438
1368
        return self._get_safe_value('_post_commit')
1465
1395
        return self._get_best_value('_acceptable_keys')
1466
1396
 
1467
1397
 
1468
 
def ensure_config_dir_exists(path=None):
1469
 
    """Make sure a configuration directory exists.
1470
 
    This makes sure that the directory exists.
1471
 
    On windows, since configuration directories are 2 levels deep,
1472
 
    it makes sure both the directory and the parent directory exists.
1473
 
    """
1474
 
    if path is None:
1475
 
        path = config_dir()
1476
 
    if not os.path.isdir(path):
1477
 
        if sys.platform == 'win32':
1478
 
            parent_dir = os.path.dirname(path)
1479
 
            if not os.path.isdir(parent_dir):
1480
 
                trace.mutter('creating config parent directory: %r', parent_dir)
1481
 
                os.mkdir(parent_dir)
1482
 
        trace.mutter('creating config directory: %r', path)
1483
 
        os.mkdir(path)
1484
 
        osutils.copy_ownership_from_path(path)
1485
 
 
1486
 
 
1487
 
def config_dir():
1488
 
    """Return per-user configuration directory as unicode string
1489
 
 
1490
 
    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,
1492
 
    that will be used instead.
1493
 
 
1494
 
    TODO: Global option --config-dir to override this.
1495
 
    """
1496
 
    base = osutils.path_from_environ('BZR_HOME')
1497
 
    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.
1504
 
        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")
1516
 
 
1517
 
 
1518
 
def config_filename():
1519
 
    """Return per-user configuration ini file filename."""
1520
 
    return osutils.pathjoin(config_dir(), 'bazaar.conf')
1521
 
 
1522
 
 
1523
 
def locations_config_filename():
1524
 
    """Return per-user configuration ini file filename."""
1525
 
    return osutils.pathjoin(config_dir(), 'locations.conf')
1526
 
 
1527
 
 
1528
 
def authentication_config_filename():
1529
 
    """Return per-user authentication ini file filename."""
1530
 
    return osutils.pathjoin(config_dir(), 'authentication.conf')
1531
 
 
1532
 
 
1533
 
def user_ignore_config_filename():
1534
 
    """Return the user default ignore filename"""
1535
 
    return osutils.pathjoin(config_dir(), 'ignore')
1536
 
 
1537
 
 
1538
 
def crash_dir():
1539
 
    """Return the directory name to store crash files.
1540
 
 
1541
 
    This doesn't implicitly create it.
1542
 
 
1543
 
    On Windows it's in the config directory; elsewhere it's /var/crash
1544
 
    which may be monitored by apport.  It can be overridden by
1545
 
    $APPORT_CRASH_DIR.
1546
 
    """
1547
 
    if sys.platform == 'win32':
1548
 
        return osutils.pathjoin(config_dir(), 'Crash')
1549
 
    else:
1550
 
        # XXX: hardcoded in apport_python_hook.py; therefore here too -- mbp
1551
 
        # 2010-01-31
1552
 
        return os.environ.get('APPORT_CRASH_DIR', '/var/crash')
1553
 
 
1554
 
 
1555
 
def xdg_cache_dir():
1556
 
    # See http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
1557
 
    # Possibly this should be different on Windows?
1558
 
    e = os.environ.get('XDG_CACHE_HOME', None)
1559
 
    if e:
1560
 
        return e
1561
 
    else:
1562
 
        return os.path.expanduser('~/.cache')
1563
 
 
1564
 
 
1565
 
def _get_default_mail_domain(mailname_file='/etc/mailname'):
1566
 
    """If possible, return the assumed default email domain.
1567
 
 
1568
 
    :returns: string mail domain, or None.
1569
 
    """
1570
 
    if sys.platform == 'win32':
1571
 
        # No implementation yet; patches welcome
1572
 
        return None
1573
 
    try:
1574
 
        f = open(mailname_file)
1575
 
    except (IOError, OSError), e:
1576
 
        return None
1577
 
    try:
1578
 
        domain = f.readline().strip()
1579
 
        return domain
1580
 
    finally:
1581
 
        f.close()
1582
 
 
1583
 
 
1584
 
def default_email():
1585
 
    v = os.environ.get('BZR_EMAIL')
1586
 
    if v:
1587
 
        return v.decode(osutils.get_user_encoding())
1588
 
    v = os.environ.get('EMAIL')
1589
 
    if v:
1590
 
        return v.decode(osutils.get_user_encoding())
1591
 
    name, email = _auto_user_id()
1592
 
    if name and email:
1593
 
        return u'%s <%s>' % (name, email)
1594
 
    elif email:
1595
 
        return email
1596
 
    raise errors.NoWhoami()
1597
 
 
1598
 
 
1599
 
def _auto_user_id():
1600
 
    """Calculate automatic user identification.
1601
 
 
1602
 
    :returns: (realname, email), either of which may be None if they can't be
1603
 
    determined.
1604
 
 
1605
 
    Only used when none is set in the environment or the id file.
1606
 
 
1607
 
    This only returns an email address if we can be fairly sure the 
1608
 
    address is reasonable, ie if /etc/mailname is set on unix.
1609
 
 
1610
 
    This doesn't use the FQDN as the default domain because that may be 
1611
 
    slow, and it doesn't use the hostname alone because that's not normally 
1612
 
    a reasonable address.
1613
 
    """
1614
 
    if sys.platform == 'win32':
1615
 
        # No implementation to reliably determine Windows default mail
1616
 
        # address; please add one.
1617
 
        return None, None
1618
 
 
1619
 
    default_mail_domain = _get_default_mail_domain()
1620
 
    if not default_mail_domain:
1621
 
        return None, None
1622
 
 
1623
 
    import pwd
1624
 
    uid = os.getuid()
1625
 
    try:
1626
 
        w = pwd.getpwuid(uid)
1627
 
    except KeyError:
1628
 
        trace.mutter('no passwd entry for uid %d?' % uid)
1629
 
        return None, None
1630
 
 
1631
 
    # we try utf-8 first, because on many variants (like Linux),
1632
 
    # /etc/passwd "should" be in utf-8, and because it's unlikely to give
1633
 
    # false positives.  (many users will have their user encoding set to
1634
 
    # latin-1, which cannot raise UnicodeError.)
1635
 
    try:
1636
 
        gecos = w.pw_gecos.decode('utf-8')
1637
 
        encoding = 'utf-8'
1638
 
    except UnicodeError:
1639
 
        try:
1640
 
            encoding = osutils.get_user_encoding()
1641
 
            gecos = w.pw_gecos.decode(encoding)
1642
 
        except UnicodeError, e:
1643
 
            trace.mutter("cannot decode passwd entry %s" % w)
1644
 
            return None, None
1645
 
    try:
1646
 
        username = w.pw_name.decode(encoding)
1647
 
    except UnicodeError, e:
1648
 
        trace.mutter("cannot decode passwd entry %s" % w)
1649
 
        return None, None
1650
 
 
1651
 
    comma = gecos.find(',')
1652
 
    if comma == -1:
1653
 
        realname = gecos
1654
 
    else:
1655
 
        realname = gecos[:comma]
1656
 
 
1657
 
    return realname, (username + '@' + default_mail_domain)
1658
 
 
1659
 
 
1660
1398
def parse_username(username):
1661
1399
    """Parse e-mail username and return a (name, address) tuple."""
1662
1400
    match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
1663
1401
    if match is None:
1664
1402
        return (username, '')
1665
 
    else:
1666
 
        return (match.group(1), match.group(2))
 
1403
    return (match.group(1), match.group(2))
1667
1404
 
1668
1405
 
1669
1406
def extract_email_address(e):
1678
1415
    """
1679
1416
    name, email = parse_username(e)
1680
1417
    if not email:
1681
 
        raise errors.NoEmailInUsername(e)
 
1418
        raise NoEmailInUsername(e)
1682
1419
    return email
1683
1420
 
1684
1421
 
1685
1422
class TreeConfig(IniBasedConfig):
1686
1423
    """Branch configuration data associated with its contents, not location"""
1687
1424
 
1688
 
    # XXX: Really needs a better name, as this is not part of the tree! -- mbp 20080507
 
1425
    # XXX: Really needs a better name, as this is not part of the tree!
 
1426
    # -- mbp 20080507
1689
1427
 
1690
1428
    def __init__(self, branch):
1691
1429
        self._config = branch._get_config()
1697
1435
        return self._config._get_configobj()
1698
1436
 
1699
1437
    def get_option(self, name, section=None, default=None):
1700
 
        self.branch.lock_read()
1701
 
        try:
 
1438
        with self.branch.lock_read():
1702
1439
            return self._config.get_option(name, section, default)
1703
 
        finally:
1704
 
            self.branch.unlock()
1705
1440
 
1706
1441
    def set_option(self, value, name, section=None):
1707
1442
        """Set a per-branch configuration option"""
1708
1443
        # FIXME: We shouldn't need to lock explicitly here but rather rely on
1709
1444
        # higher levels providing the right lock -- vila 20101004
1710
 
        self.branch.lock_write()
1711
 
        try:
 
1445
        with self.branch.lock_write():
1712
1446
            self._config.set_option(value, name, section)
1713
 
        finally:
1714
 
            self.branch.unlock()
1715
1447
 
1716
1448
    def remove_option(self, option_name, section_name=None):
1717
1449
        # FIXME: We shouldn't need to lock explicitly here but rather rely on
1718
1450
        # higher levels providing the right lock -- vila 20101004
1719
 
        self.branch.lock_write()
1720
 
        try:
 
1451
        with self.branch.lock_write():
1721
1452
            self._config.remove_option(option_name, section_name)
1722
 
        finally:
1723
 
            self.branch.unlock()
 
1453
 
 
1454
 
 
1455
_authentication_config_permission_errors = set()
1724
1456
 
1725
1457
 
1726
1458
class AuthenticationConfig(object):
1731
1463
    """
1732
1464
 
1733
1465
    def __init__(self, _file=None):
1734
 
        self._config = None # The ConfigObj
 
1466
        self._config = None  # The ConfigObj
1735
1467
        if _file is None:
1736
 
            self._filename = authentication_config_filename()
1737
 
            self._input = self._filename = authentication_config_filename()
 
1468
            self._input = self._filename = bedding.authentication_config_path()
 
1469
            self._check_permissions()
1738
1470
        else:
1739
1471
            # Tests can provide a string as _file
1740
1472
            self._filename = None
1751
1483
            # Note: the encoding below declares that the file itself is utf-8
1752
1484
            # encoded, but the values in the ConfigObj are always Unicode.
1753
1485
            self._config = ConfigObj(self._input, encoding='utf-8')
1754
 
        except configobj.ConfigObjError, e:
1755
 
            raise errors.ParseConfigError(e.errors, e.config.filename)
 
1486
        except configobj.ConfigObjError as e:
 
1487
            raise ParseConfigError(e.errors, e.config.filename)
1756
1488
        except UnicodeError:
1757
 
            raise errors.ConfigContentError(self._filename)
 
1489
            raise ConfigContentError(self._filename)
1758
1490
        return self._config
1759
1491
 
 
1492
    def _check_permissions(self):
 
1493
        """Check permission of auth file are user read/write able only."""
 
1494
        try:
 
1495
            st = os.stat(self._filename)
 
1496
        except OSError as e:
 
1497
            if e.errno != errno.ENOENT:
 
1498
                trace.mutter('Unable to stat %r: %r', self._filename, e)
 
1499
            return
 
1500
        mode = stat.S_IMODE(st.st_mode)
 
1501
        if ((stat.S_IXOTH | stat.S_IWOTH | stat.S_IROTH | stat.S_IXGRP
 
1502
             | stat.S_IWGRP | stat.S_IRGRP) & mode):
 
1503
            # Only warn once
 
1504
            if (self._filename not in _authentication_config_permission_errors and
 
1505
                not GlobalConfig().suppress_warning(
 
1506
                    'insecure_permissions')):
 
1507
                trace.warning("The file '%s' has insecure "
 
1508
                              "file permissions. Saved passwords may be accessible "
 
1509
                              "by other users.", self._filename)
 
1510
                _authentication_config_permission_errors.add(self._filename)
 
1511
 
1760
1512
    def _save(self):
1761
1513
        """Save the config file, only tests should use it for now."""
1762
1514
        conf_dir = os.path.dirname(self._filename)
1763
 
        ensure_config_dir_exists(conf_dir)
1764
 
        f = file(self._filename, 'wb')
 
1515
        bedding.ensure_config_dir_exists(conf_dir)
 
1516
        fd = os.open(self._filename, os.O_RDWR | os.O_CREAT, 0o600)
1765
1517
        try:
 
1518
            f = os.fdopen(fd, 'wb')
1766
1519
            self._get_config().write(f)
1767
1520
        finally:
1768
1521
            f.close()
1809
1562
             certificate should be verified, False otherwise.
1810
1563
        """
1811
1564
        credentials = None
1812
 
        for auth_def_name, auth_def in self._get_config().items():
1813
 
            if type(auth_def) is not configobj.Section:
1814
 
                raise ValueError("%s defined outside a section" % auth_def_name)
 
1565
        for auth_def_name, auth_def in self._get_config().iteritems():
 
1566
            if not isinstance(auth_def, configobj.Section):
 
1567
                raise ValueError("%s defined outside a section" %
 
1568
                                 auth_def_name)
1815
1569
 
1816
1570
            a_scheme, a_host, a_user, a_path = map(
1817
1571
                auth_def.get, ['scheme', 'host', 'user', 'path'])
1834
1588
            if a_scheme is not None and scheme != a_scheme:
1835
1589
                continue
1836
1590
            if a_host is not None:
1837
 
                if not (host == a_host
1838
 
                        or (a_host.startswith('.') and host.endswith(a_host))):
 
1591
                if not (host == a_host or
 
1592
                        (a_host.startswith('.') and host.endswith(a_host))):
1839
1593
                    continue
1840
1594
            if a_port is not None and port != a_port:
1841
1595
                continue
1842
 
            if (a_path is not None and path is not None
1843
 
                and not path.startswith(a_path)):
 
1596
            if (a_path is not None and path is not None and
 
1597
                    not path.startswith(a_path)):
1844
1598
                continue
1845
 
            if (a_user is not None and user is not None
1846
 
                and a_user != user):
 
1599
            if (a_user is not None and user is not None and
 
1600
                    a_user != user):
1847
1601
                # Never contradict the caller about the user to be used
1848
1602
                continue
1849
1603
            if a_user is None:
1910
1664
        if realm is not None:
1911
1665
            values['realm'] = realm
1912
1666
        config = self._get_config()
1913
 
        for_deletion = []
1914
 
        for section, existing_values in config.items():
 
1667
        for section, existing_values in config.iteritems():
1915
1668
            for key in ('scheme', 'host', 'port', 'path', 'realm'):
1916
1669
                if existing_values.get(key) != values.get(key):
1917
1670
                    break
1934
1687
 
1935
1688
        :param path: the absolute path on the server (optional)
1936
1689
 
1937
 
        :param ask: Ask the user if there is no explicitly configured username 
 
1690
        :param ask: Ask the user if there is no explicitly configured username
1938
1691
                    (optional)
1939
1692
 
1940
1693
        :param default: The username returned if none is defined (optional).
1984
1737
                                           realm)
1985
1738
        if credentials is not None:
1986
1739
            password = credentials['password']
1987
 
            if password is not None and scheme is 'ssh':
 
1740
            if password is not None and scheme == 'ssh':
1988
1741
                trace.warning('password ignored in section [%s],'
1989
1742
                              ' use an ssh agent instead'
1990
1743
                              % credentials['name'])
1995
1748
        if password is None:
1996
1749
            if prompt is None:
1997
1750
                # Create a default prompt suitable for most cases
1998
 
                prompt = u'%s' % scheme.upper() + u' %(user)s@%(host)s password'
 
1751
                prompt = (u'%s' %
 
1752
                          scheme.upper() + u' %(user)s@%(host)s password')
1999
1753
            # Special handling for optional fields in the prompt
2000
1754
            if port is not None:
2001
1755
                prompt_host = '%s:%d' % (host, port)
2020
1774
    A credential store provides access to credentials via the password_encoding
2021
1775
    field in authentication.conf sections.
2022
1776
 
2023
 
    Except for stores provided by bzr itself, most stores are expected to be
 
1777
    Except for stores provided by brz itself, most stores are expected to be
2024
1778
    provided by plugins that will therefore use
2025
1779
    register_lazy(password_encoding, module_name, member_name, help=help,
2026
1780
    fallback=fallback) to install themselves.
2070
1824
        :param override_existing: Raise KeyErorr if False and something has
2071
1825
                already been registered for that key. If True, ignore if there
2072
1826
                is an existing key (always register the new value).
2073
 
        :param fallback: Whether this credential store should be 
 
1827
        :param fallback: Whether this credential store should be
2074
1828
                used as fallback.
2075
1829
        """
2076
1830
        return super(CredentialStoreRegistry,
2090
1844
        :param override_existing: If True, replace the existing object
2091
1845
                with the new one. If False, if there is already something
2092
1846
                registered with the same key, raise a KeyError
2093
 
        :param fallback: Whether this credential store should be 
 
1847
        :param fallback: Whether this credential store should be
2094
1848
                used as fallback.
2095
1849
        """
2096
1850
        return super(CredentialStoreRegistry, self).register_lazy(
2117
1871
        raise NotImplementedError(self.get_credentials)
2118
1872
 
2119
1873
 
2120
 
 
2121
1874
class PlainTextCredentialStore(CredentialStore):
2122
1875
    __doc__ = """Plain text credential store for the authentication.conf file"""
2123
1876
 
2138
1891
        """See CredentialStore.decode_password."""
2139
1892
        # GZ 2012-07-28: Will raise binascii.Error if password is not base64,
2140
1893
        #                should probably propogate as something more useful.
2141
 
        return base64.decodestring(credentials['password'])
 
1894
        return base64.standard_b64decode(credentials['password'])
 
1895
 
2142
1896
 
2143
1897
credential_store_registry.register('base64', Base64CredentialStore,
2144
1898
                                   help=Base64CredentialStore.__doc__)
2243
1997
 
2244
1998
    def _get_config_file(self):
2245
1999
        try:
2246
 
            f = StringIO(self._transport.get_bytes(self._filename))
 
2000
            f = BytesIO(self._transport.get_bytes(self._filename))
2247
2001
            for hook in OldConfigHooks['load']:
2248
2002
                hook(self)
2249
2003
            return f
2250
2004
        except errors.NoSuchFile:
2251
 
            return StringIO()
2252
 
        except errors.PermissionDenied, e:
2253
 
            trace.warning("Permission denied while trying to open "
2254
 
                "configuration file %s.", urlutils.unescape_for_display(
2255
 
                urlutils.join(self._transport.base, self._filename), "utf-8"))
2256
 
            return StringIO()
 
2005
            return BytesIO()
 
2006
        except errors.PermissionDenied:
 
2007
            trace.warning(
 
2008
                "Permission denied while trying to open "
 
2009
                "configuration file %s.",
 
2010
                urlutils.unescape_for_display(
 
2011
                    urlutils.join(self._transport.base, self._filename),
 
2012
                    "utf-8"))
 
2013
            return BytesIO()
2257
2014
 
2258
2015
    def _external_url(self):
2259
2016
        return urlutils.join(self._transport.external_url(), self._filename)
2263
2020
        try:
2264
2021
            try:
2265
2022
                conf = ConfigObj(f, encoding='utf-8')
2266
 
            except configobj.ConfigObjError, e:
2267
 
                raise errors.ParseConfigError(e.errors, self._external_url())
 
2023
            except configobj.ConfigObjError as e:
 
2024
                raise ParseConfigError(e.errors, self._external_url())
2268
2025
            except UnicodeDecodeError:
2269
 
                raise errors.ConfigContentError(self._external_url())
 
2026
                raise ConfigContentError(self._external_url())
2270
2027
        finally:
2271
2028
            f.close()
2272
2029
        return conf
2273
2030
 
2274
2031
    def _set_configobj(self, configobj):
2275
 
        out_file = StringIO()
 
2032
        out_file = BytesIO()
2276
2033
        configobj.write(out_file)
2277
2034
        out_file.seek(0)
2278
2035
        self._transport.put_file(self._filename, out_file)
2344
2101
                raise AssertionError(
2345
2102
                    'Only empty lists are supported as default values')
2346
2103
            self.default = u','
2347
 
        elif isinstance(default, (str, unicode, bool, int, float)):
 
2104
        elif isinstance(default, (bytes, str, bool, int, float)):
2348
2105
            # Rely on python to convert strings, booleans and integers
2349
2106
            self.default = u'%s' % (default,)
2350
2107
        elif callable(default):
2382
2139
                trace.warning('Value "%s" is not valid for "%s"',
2383
2140
                              unicode_value, self.name)
2384
2141
            elif self.invalid == 'error':
2385
 
                raise errors.ConfigOptionValueError(self.name, unicode_value)
 
2142
                raise ConfigOptionValueError(self.name, unicode_value)
2386
2143
        return converted
2387
2144
 
2388
2145
    def get_override(self):
2390
2147
        for var in self.override_from_env:
2391
2148
            try:
2392
2149
                # If the env variable is defined, its value takes precedence
2393
 
                value = os.environ[var].decode(osutils.get_user_encoding())
 
2150
                value = os.environ[var]
2394
2151
                break
2395
2152
            except KeyError:
2396
2153
                continue
2401
2158
        for var in self.default_from_env:
2402
2159
            try:
2403
2160
                # If the env variable is defined, its value is the default one
2404
 
                value = os.environ[var].decode(osutils.get_user_encoding())
 
2161
                value = os.environ[var]
2405
2162
                break
2406
2163
            except KeyError:
2407
2164
                continue
2409
2166
            # Otherwise, fallback to the value defined at registration
2410
2167
            if callable(self.default):
2411
2168
                value = self.default()
2412
 
                if not isinstance(value, unicode):
 
2169
                if not isinstance(value, str):
2413
2170
                    raise AssertionError(
2414
2171
                        "Callable default value for '%s' should be unicode"
2415
2172
                        % (self.name))
2422
2179
 
2423
2180
    def get_help_text(self, additional_see_also=None, plain=True):
2424
2181
        result = self.help
2425
 
        from bzrlib import help_topics
 
2182
        from breezy import help_topics
2426
2183
        result += help_topics._format_see_also(additional_see_also)
2427
2184
        if plain:
2428
2185
            result = help_topics.help_as_plain_text(result)
2441
2198
 
2442
2199
_unit_suffixes = dict(K=10**3, M=10**6, G=10**9)
2443
2200
 
 
2201
 
2444
2202
def int_SI_from_store(unicode_str):
2445
2203
    """Convert a human readable size in SI units, e.g 10MB into an integer.
2446
2204
 
2448
2206
    by a trailing b (i.e. Kb, MB). This is intended to be practical and not
2449
2207
    pedantic.
2450
2208
 
2451
 
    :return Integer, expanded to its base-10 value if a proper SI unit is 
 
2209
    :return Integer, expanded to its base-10 value if a proper SI unit is
2452
2210
        found, None otherwise.
2453
2211
    """
2454
 
    regexp = "^(\d+)(([" + ''.join(_unit_suffixes) + "])b?)?$"
 
2212
    regexp = "^(\\d+)(([" + ''.join(_unit_suffixes) + "])b?)?$"
2455
2213
    p = re.compile(regexp, re.IGNORECASE)
2456
2214
    m = p.match(unicode_str)
2457
2215
    val = None
2462
2220
            try:
2463
2221
                coeff = _unit_suffixes[unit.upper()]
2464
2222
            except KeyError:
2465
 
                raise ValueError(gettext('{0} is not an SI unit.').format(unit))
 
2223
                raise ValueError(
 
2224
                    gettext('{0} is not an SI unit.').format(unit))
2466
2225
            val *= coeff
2467
2226
    return val
2468
2227
 
2492
2251
            invalid=invalid, unquote=False)
2493
2252
 
2494
2253
    def from_unicode(self, unicode_str):
2495
 
        if not isinstance(unicode_str, basestring):
 
2254
        if not isinstance(unicode_str, str):
2496
2255
            raise TypeError
2497
2256
        # Now inject our string directly as unicode. All callers got their
2498
2257
        # value from configobj, so values that need to be quoted are already
2500
2259
        _list_converter_config.reset()
2501
2260
        _list_converter_config._parse([u"list=%s" % (unicode_str,)])
2502
2261
        maybe_list = _list_converter_config['list']
2503
 
        if isinstance(maybe_list, basestring):
 
2262
        if isinstance(maybe_list, str):
2504
2263
            if maybe_list:
2505
2264
                # A single value, most probably the user forgot (or didn't care
2506
2265
                # to add) the final ','
2525
2284
        can take quoting into account.
2526
2285
        """
2527
2286
        super(RegistryOption, self).__init__(
2528
 
            name, default=lambda: unicode(registry.default_key),
 
2287
            name, default=lambda: registry.default_key,
2529
2288
            default_from_env=default_from_env,
2530
2289
            from_unicode=self.from_unicode, help=help,
2531
2290
            invalid=invalid, unquote=False)
2532
2291
        self.registry = registry
2533
2292
 
2534
2293
    def from_unicode(self, unicode_str):
2535
 
        if not isinstance(unicode_str, basestring):
 
2294
        if not isinstance(unicode_str, str):
2536
2295
            raise TypeError
2537
2296
        try:
2538
2297
            return self.registry.get(unicode_str)
2540
2299
            raise ValueError(
2541
2300
                "Invalid value %s for %s."
2542
2301
                "See help for a list of possible values." % (unicode_str,
2543
 
                    self.name))
 
2302
                                                             self.name))
2544
2303
 
2545
2304
    @property
2546
2305
    def help(self):
2550
2309
        return "".join(ret)
2551
2310
 
2552
2311
 
2553
 
_option_ref_re = lazy_regex.lazy_compile('({[^\d\W](?:\.\w|-\w|\w)*})')
 
2312
_option_ref_re = lazy_regex.lazy_compile('({[^\\d\\W](?:\\.\\w|-\\w|\\w)*})')
2554
2313
"""Describes an expandable option reference.
2555
2314
 
2556
2315
We want to match the most embedded reference first.
2559
2318
for '{bar{baz}}' we will get '{baz}'
2560
2319
"""
2561
2320
 
 
2321
 
2562
2322
def iter_option_refs(string):
2563
2323
    # Split isolate refs so every other chunk is a ref
2564
2324
    is_ref = False
2565
 
    for chunk  in _option_ref_re.split(string):
 
2325
    for chunk in _option_ref_re.split(string):
2566
2326
        yield is_ref, chunk
2567
2327
        is_ref = not is_ref
2568
2328
 
2580
2340
        :param option_name: The name to validate.
2581
2341
        """
2582
2342
        if _option_ref_re.match('{%s}' % option_name) is None:
2583
 
            raise errors.IllegalOptionName(option_name)
 
2343
            raise IllegalOptionName(option_name)
2584
2344
 
2585
2345
    def register(self, option):
2586
2346
        """Register a new option to its name.
2599
2359
 
2600
2360
        :param module_name: the python path to the module. Such as 'os.path'.
2601
2361
 
2602
 
        :param member_name: the member of the module to return.  If empty or 
 
2362
        :param member_name: the member of the module to return.  If empty or
2603
2363
                None, get() will return the module itself.
2604
2364
        """
2605
2365
        self._check_option_name(key)
2631
2391
'''))
2632
2392
option_registry.register(
2633
2393
    ListOption('acceptable_keys',
2634
 
           default=None,
2635
 
           help="""\
 
2394
               default=None,
 
2395
               help="""\
2636
2396
List of GPG key patterns which are acceptable for verification.
2637
2397
"""))
2638
2398
option_registry.register(
2668
2428
See also: bound.
2669
2429
"""))
2670
2430
option_registry.register(
2671
 
    Option('branch.fetch_tags', default=False,  from_unicode=bool_from_store,
 
2431
    Option('branch.fetch_tags', default=False, from_unicode=bool_from_store,
2672
2432
           help="""\
2673
2433
Whether revisions associated with tags should be fetched.
2674
2434
"""))
2675
2435
option_registry.register_lazy(
2676
 
    'bzr.transform.orphan_policy', 'bzrlib.transform', 'opt_transform_orphan')
 
2436
    'transform.orphan_policy', 'breezy.transform', 'opt_transform_orphan')
2677
2437
option_registry.register(
2678
2438
    Option('bzr.workingtree.worth_saving_limit', default=10,
2679
 
           from_unicode=int_from_store,  invalid='warning',
 
2439
           from_unicode=int_from_store, invalid='warning',
2680
2440
           help='''\
2681
2441
How many changes before saving the dirstate.
2682
2442
 
2696
2456
bug tracker was specified.
2697
2457
'''))
2698
2458
option_registry.register(
 
2459
    Option('calculate_revnos', default=True,
 
2460
           from_unicode=bool_from_store,
 
2461
           help='''\
 
2462
Calculate revision numbers if they are not known.
 
2463
 
 
2464
Always show revision numbers, even for branch formats that don't store them
 
2465
natively (such as Git). Calculating the revision number requires traversing
 
2466
the left hand ancestry of the branch and can be slow on very large branches.
 
2467
'''))
 
2468
option_registry.register(
2699
2469
    Option('check_signatures', default=CHECK_IF_POSSIBLE,
2700
2470
           from_unicode=signature_policy_from_unicode,
2701
2471
           help='''\
2736
2506
'''))
2737
2507
option_registry.register(
2738
2508
    ListOption('debug_flags', default=[],
2739
 
           help='Debug flags to activate.'))
 
2509
               help='Debug flags to activate.'))
2740
2510
option_registry.register(
2741
2511
    Option('default_format', default='2a',
2742
2512
           help='Format used when creating branches.'))
2743
2513
option_registry.register(
2744
 
    Option('dpush_strict', default=None,
2745
 
           from_unicode=bool_from_store,
2746
 
           help='''\
2747
 
The default value for ``dpush --strict``.
2748
 
 
2749
 
If present, defines the ``--strict`` option default value for checking
2750
 
uncommitted changes before pushing into a different VCS without any
2751
 
custom bzr metadata.
2752
 
'''))
2753
 
option_registry.register(
2754
2514
    Option('editor',
2755
2515
           help='The command called to launch an editor to enter a message.'))
2756
2516
option_registry.register(
2757
 
    Option('email', override_from_env=['BZR_EMAIL'], default=default_email,
2758
 
           help='The users identity'))
2759
 
option_registry.register(
2760
 
    Option('gpg_signing_command',
2761
 
           default='gpg',
2762
 
           help="""\
2763
 
Program to use use for creating signatures.
2764
 
 
2765
 
This should support at least the -u and --clearsign options.
2766
 
"""))
 
2517
    Option('email', override_from_env=['BRZ_EMAIL', 'BZR_EMAIL'],
 
2518
           default=bedding.default_email, help='The users identity'))
2767
2519
option_registry.register(
2768
2520
    Option('gpg_signing_key',
2769
2521
           default=None,
2773
2525
This defaults to the first key associated with the users email.
2774
2526
"""))
2775
2527
option_registry.register(
2776
 
    Option('ignore_missing_extensions', default=False,
2777
 
           from_unicode=bool_from_store,
2778
 
           help='''\
2779
 
Control the missing extensions warning display.
2780
 
 
2781
 
The warning will not be emitted if set to True.
2782
 
'''))
2783
 
option_registry.register(
2784
2528
    Option('language',
2785
2529
           help='Language to translate messages into.'))
2786
2530
option_registry.register(
2787
 
    Option('locks.steal_dead', default=False, from_unicode=bool_from_store,
 
2531
    Option('locks.steal_dead', default=True, from_unicode=bool_from_store,
2788
2532
           help='''\
2789
2533
Steal locks that appears to be dead.
2790
2534
 
2797
2541
'''))
2798
2542
option_registry.register(
2799
2543
    Option('log_format', default='long',
2800
 
           help= '''\
 
2544
           help='''\
2801
2545
Log format to use when displaying revisions.
2802
2546
 
2803
2547
Standard log formats are ``long``, ``short`` and ``line``. Additional formats
2804
2548
may be provided by plugins.
2805
2549
'''))
2806
 
option_registry.register_lazy('mail_client', 'bzrlib.mail_client',
2807
 
    'opt_mail_client')
 
2550
option_registry.register_lazy('mail_client', 'breezy.mail_client',
 
2551
                              'opt_mail_client')
2808
2552
option_registry.register(
2809
2553
    Option('output_encoding',
2810
 
           help= 'Unicode encoding for output'
 
2554
           help='Unicode encoding for output'
2811
2555
           ' (terminal encoding if not specified).'))
2812
2556
option_registry.register(
2813
2557
    Option('parent_location',
2827
2571
 
2828
2572
Each function takes branch, rev_id as parameters.
2829
2573
'''))
2830
 
option_registry.register_lazy('progress_bar', 'bzrlib.ui.text',
 
2574
option_registry.register_lazy('progress_bar', 'breezy.ui.text',
2831
2575
                              'opt_progress_bar')
2832
2576
option_registry.register(
2833
2577
    Option('public_branch',
2866
2610
lost if the machine crashes.  See also dirstate.fdatasync.
2867
2611
'''))
2868
2612
option_registry.register_lazy('smtp_server',
2869
 
    'bzrlib.smtp_connection', 'smtp_server')
 
2613
                              'breezy.smtp_connection', 'smtp_server')
2870
2614
option_registry.register_lazy('smtp_password',
2871
 
    'bzrlib.smtp_connection', 'smtp_password')
 
2615
                              'breezy.smtp_connection', 'smtp_password')
2872
2616
option_registry.register_lazy('smtp_username',
2873
 
    'bzrlib.smtp_connection', 'smtp_username')
 
2617
                              'breezy.smtp_connection', 'smtp_username')
2874
2618
option_registry.register(
2875
2619
    Option('selftest.timeout',
2876
 
        default='600',
2877
 
        from_unicode=int_from_store,
2878
 
        help='Abort selftest if one test takes longer than this many seconds',
2879
 
        ))
 
2620
           default='600',
 
2621
           from_unicode=int_from_store,
 
2622
           help='Abort selftest if one test takes longer than this many seconds',
 
2623
           ))
2880
2624
 
2881
2625
option_registry.register(
2882
2626
    Option('send_strict', default=None,
2894
2638
           help="If we wait for a new request from a client for more than"
2895
2639
                " X seconds, consider the client idle, and hangup."))
2896
2640
option_registry.register(
 
2641
    Option('ssh',
 
2642
           default=None, override_from_env=['BRZ_SSH'],
 
2643
           help='SSH vendor to use.'))
 
2644
option_registry.register(
2897
2645
    Option('stacked_on_location',
2898
2646
           default=None,
2899
2647
           help="""The location where this branch is stacked on."""))
2911
2659
           help='''Where submissions from this branch are mailed to.'''))
2912
2660
option_registry.register(
2913
2661
    ListOption('suppress_warnings',
2914
 
           default=[],
2915
 
           help="List of warning classes to suppress."))
 
2662
               default=[],
 
2663
               help="List of warning classes to suppress."))
2916
2664
option_registry.register(
2917
2665
    Option('validate_signatures_in_log', default=False,
2918
2666
           from_unicode=bool_from_store, invalid='warning',
2919
 
           help='''Whether to validate signatures in bzr log.'''))
 
2667
           help='''Whether to validate signatures in brz log.'''))
2920
2668
option_registry.register_lazy('ssl.ca_certs',
2921
 
    'bzrlib.transport.http._urllib2_wrappers', 'opt_ssl_ca_certs')
 
2669
                              'breezy.transport.http', 'opt_ssl_ca_certs')
2922
2670
 
2923
2671
option_registry.register_lazy('ssl.cert_reqs',
2924
 
    'bzrlib.transport.http._urllib2_wrappers', 'opt_ssl_cert_reqs')
 
2672
                              'breezy.transport.http', 'opt_ssl_cert_reqs')
2925
2673
 
2926
2674
 
2927
2675
class Section(object):
2941
2689
        return self.options.get(name, default)
2942
2690
 
2943
2691
    def iter_option_names(self):
2944
 
        for k in self.options.iterkeys():
 
2692
        for k in self.options.keys():
2945
2693
            yield k
2946
2694
 
2947
2695
    def __repr__(self):
2988
2736
 
2989
2737
        :param store: the store containing the section
2990
2738
        """
2991
 
        for k, expected in dirty.orig.iteritems():
 
2739
        for k, expected in dirty.orig.items():
2992
2740
            actual = dirty.get(k, _DeletedOption)
2993
2741
            reloaded = self.get(k, _NewlyCreatedOption)
2994
2742
            if actual is _DeletedOption:
3010
2758
                # Someone changed the value since we get it from the persistent
3011
2759
                # storage.
3012
2760
                trace.warning(gettext(
3013
 
                        "Option {0} in section {1} of {2} was changed"
3014
 
                        " from {3} to {4}. The {5} value will be saved.".format(
3015
 
                            k, self.id, store.external_url(), expected,
3016
 
                            reloaded, actual)))
 
2761
                    "Option {0} in section {1} of {2} was changed"
 
2762
                    " from {3} to {4}. The {5} value will be saved.".format(
 
2763
                        k, self.id, store.external_url(), expected,
 
2764
                        reloaded, actual)))
3017
2765
        # No need to keep track of these changes
3018
2766
        self.reset_changes()
3019
2767
 
3096
2844
        # get_mutable_section() call below.
3097
2845
        self.unload()
3098
2846
        # Apply the changes from the preserved dirty sections
3099
 
        for section_id, dirty in dirty_sections.iteritems():
 
2847
        for section_id, dirty in dirty_sections.items():
3100
2848
            clean = self.get_mutable_section(section_id)
3101
2849
            clean.apply_changes(dirty, self)
3102
2850
        # Everything is clean now
3154
2902
            try:
3155
2903
                name, value = over.split('=', 1)
3156
2904
            except ValueError:
3157
 
                raise errors.BzrCommandError(
 
2905
                raise errors.CommandError(
3158
2906
                    gettext("Invalid '%s', should be of the form 'name=value'")
3159
2907
                    % (over,))
3160
2908
            self.options[name] = value
3165
2913
        return 'cmdline'
3166
2914
 
3167
2915
    def get_sections(self):
3168
 
        yield self,  self.readonly_section_class(None, self.options)
 
2916
        yield self, self.readonly_section_class(None, self.options)
3169
2917
 
3170
2918
 
3171
2919
class IniFileStore(Store):
3182
2930
        self._config_obj = None
3183
2931
 
3184
2932
    def is_loaded(self):
3185
 
        return self._config_obj != None
 
2933
        return self._config_obj is not None
3186
2934
 
3187
2935
    def unload(self):
3188
2936
        self._config_obj = None
3222
2970
        """
3223
2971
        if self.is_loaded():
3224
2972
            raise AssertionError('Already loaded: %r' % (self._config_obj,))
3225
 
        co_input = StringIO(bytes)
 
2973
        co_input = BytesIO(bytes)
3226
2974
        try:
3227
2975
            # The config files are always stored utf8-encoded
3228
2976
            self._config_obj = ConfigObj(co_input, encoding='utf-8',
3229
2977
                                         list_values=False)
3230
 
        except configobj.ConfigObjError, e:
 
2978
        except configobj.ConfigObjError as e:
3231
2979
            self._config_obj = None
3232
 
            raise errors.ParseConfigError(e.errors, self.external_url())
 
2980
            raise ParseConfigError(e.errors, self.external_url())
3233
2981
        except UnicodeDecodeError:
3234
 
            raise errors.ConfigContentError(self.external_url())
 
2982
            raise ConfigContentError(self.external_url())
3235
2983
 
3236
2984
    def save_changes(self):
3237
2985
        if not self.is_loaded():
3240
2988
        if not self._need_saving():
3241
2989
            return
3242
2990
        # Preserve the current version
3243
 
        dirty_sections = dict(self.dirty_sections.items())
 
2991
        dirty_sections = self.dirty_sections.copy()
3244
2992
        self.apply_changes(dirty_sections)
3245
2993
        # Save to the persistent storage
3246
2994
        self.save()
3249
2997
        if not self.is_loaded():
3250
2998
            # Nothing to save
3251
2999
            return
3252
 
        out = StringIO()
 
3000
        out = BytesIO()
3253
3001
        self._config_obj.write(out)
3254
3002
        self._save_content(out.getvalue())
3255
3003
        for hook in ConfigHooks['save']:
3280
3028
            self.load()
3281
3029
        except errors.NoSuchFile:
3282
3030
            # The file doesn't exist, let's pretend it was empty
3283
 
            self._load_from_string('')
 
3031
            self._load_from_string(b'')
3284
3032
        if section_id in self.dirty_sections:
3285
3033
            # We already created a mutable section for this id
3286
3034
            return self.dirty_sections[section_id]
3302
3050
            self._config_obj.list_values = False
3303
3051
 
3304
3052
    def unquote(self, value):
3305
 
        if value and isinstance(value, basestring):
 
3053
        if value and isinstance(value, str):
3306
3054
            # _unquote doesn't handle None nor empty strings nor anything that
3307
3055
            # is not a string, really.
3308
3056
            value = self._config_obj._unquote(value)
3351
3099
        # The following will do in the interim but maybe we don't want to
3352
3100
        # expose a path here but rather a config ID and its associated
3353
3101
        # object </hand wawe>.
3354
 
        return urlutils.join(self.transport.external_url(), self.file_name)
 
3102
        return urlutils.join(
 
3103
            self.transport.external_url(), urlutils.escape(self.file_name))
3355
3104
 
3356
3105
 
3357
3106
# Note that LockableConfigObjStore inherits from ConfigObjStore because we need
3385
3134
        # ensure_config_dir_exists does. It should if the transport is local
3386
3135
        # -- vila 2011-04-06
3387
3136
        self.transport.create_prefix()
3388
 
        return self._lock.lock_write(token)
 
3137
        token = self._lock.lock_write(token)
 
3138
        return lock.LogicalLockResult(self.unlock, token)
3389
3139
 
3390
3140
    def unlock(self):
3391
3141
        self._lock.unlock()
3393
3143
    def break_lock(self):
3394
3144
        self._lock.break_lock()
3395
3145
 
3396
 
    @needs_write_lock
3397
3146
    def save(self):
3398
 
        # We need to be able to override the undecorated implementation
3399
 
        self.save_without_locking()
 
3147
        with self.lock_write():
 
3148
            # We need to be able to override the undecorated implementation
 
3149
            self.save_without_locking()
3400
3150
 
3401
3151
    def save_without_locking(self):
3402
3152
        super(LockableIniFileStore, self).save()
3403
3153
 
3404
3154
 
3405
 
# FIXME: global, bazaar, shouldn't that be 'user' instead or even
 
3155
# FIXME: global, breezy, shouldn't that be 'user' instead or even
3406
3156
# 'user_defaults' as opposed to 'user_overrides', 'system_defaults'
3407
3157
# (/etc/bzr/bazaar.conf) and 'system_overrides' ? -- vila 2011-04-05
3408
3158
 
3417
3167
    """
3418
3168
 
3419
3169
    def __init__(self, possible_transports=None):
 
3170
        path, kind = bedding._config_dir()
3420
3171
        t = transport.get_transport_from_path(
3421
 
            config_dir(), possible_transports=possible_transports)
3422
 
        super(GlobalStore, self).__init__(t, 'bazaar.conf')
3423
 
        self.id = 'bazaar'
 
3172
            path, possible_transports=possible_transports)
 
3173
        super(GlobalStore, self).__init__(t, kind + '.conf')
 
3174
        self.id = 'breezy'
3424
3175
 
3425
3176
 
3426
3177
class LocationStore(LockableIniFileStore):
3431
3182
 
3432
3183
    def __init__(self, possible_transports=None):
3433
3184
        t = transport.get_transport_from_path(
3434
 
            config_dir(), possible_transports=possible_transports)
 
3185
            bedding.config_dir(), possible_transports=possible_transports)
3435
3186
        super(LocationStore, self).__init__(t, 'locations.conf')
3436
3187
        self.id = 'locations'
3437
3188
 
3453
3204
 
3454
3205
    def __init__(self, bzrdir):
3455
3206
        super(ControlStore, self).__init__(bzrdir.transport,
3456
 
                                          'control.conf',
 
3207
                                           'control.conf',
3457
3208
                                           lock_dir_name='branch_lock')
3458
3209
        self.id = 'control'
3459
3210
 
3570
3321
                # the location is already a local path or URL, convert the
3571
3322
                # section id to the same format
3572
3323
                section_path = urlutils.local_path_from_url(section_path)
3573
 
            if (self.location.startswith(section_path)
3574
 
                or fnmatch.fnmatch(self.location, section_path)):
 
3324
            if (self.location.startswith(section_path) or
 
3325
                    fnmatch.fnmatch(self.location, section_path)):
3575
3326
                section_parts = section_path.rstrip('/').split('/')
3576
3327
                extra_path = '/'.join(location_parts[len(section_parts):])
3577
3328
                yield store, LocationSection(section, extra_path)
3619
3370
            # sections are part of 'all_sections' and will always be found
3620
3371
            # there.
3621
3372
            while True:
3622
 
                section = iter_all_sections.next()
 
3373
                section = next(iter_all_sections)
3623
3374
                if section_id == section.id:
3624
3375
                    section = LocationSection(section, extra_path,
3625
3376
                                              self.branch_name)
3629
3380
 
3630
3381
    def get_sections(self):
3631
3382
        # Override the default implementation as we want to change the order
3632
 
        matching_sections = self._get_matching_sections()
3633
3383
        # We want the longest (aka more specific) locations first
3634
 
        sections = sorted(matching_sections,
3635
 
                          key=lambda (length, section): (length, section.id),
 
3384
        sections = sorted(self._get_matching_sections(),
 
3385
                          key=lambda match: (match[0], match[1].id),
3636
3386
                          reverse=True)
3637
3387
        # Sections mentioning 'ignore_parents' restrict the selection
3638
3388
        for _, section in sections:
3651
3401
_shared_stores = {}
3652
3402
_shared_stores_at_exit_installed = False
3653
3403
 
 
3404
 
3654
3405
class Stack(object):
3655
3406
    """A stack of configurations where an option can be defined"""
3656
3407
 
3702
3453
        """
3703
3454
        # FIXME: No caching of options nor sections yet -- vila 20110503
3704
3455
        value = None
3705
 
        found_store = None # Where the option value has been found
 
3456
        found_store = None  # Where the option value has been found
3706
3457
        # If the option is registered, it may provide additional info about
3707
3458
        # value handling
3708
3459
        try:
3716
3467
            # None or ends up being None during expansion or conversion.
3717
3468
            if val is not None:
3718
3469
                if expand:
3719
 
                    if isinstance(val, basestring):
 
3470
                    if isinstance(val, str):
3720
3471
                        val = self._expand_options_in_string(val)
3721
3472
                    else:
3722
3473
                        trace.warning('Cannot expand "%s":'
3793
3544
                    expanded = True
3794
3545
                    name = chunk[1:-1]
3795
3546
                    if name in _refs:
3796
 
                        raise errors.OptionExpansionLoop(string, _refs)
 
3547
                        raise OptionExpansionLoop(string, _refs)
3797
3548
                    _refs.append(name)
3798
3549
                    value = self._expand_option(name, env, _refs)
3799
3550
                    if value is None:
3800
 
                        raise errors.ExpandingUnknownOption(name, string)
 
3551
                        raise ExpandingUnknownOption(name, string)
3801
3552
                    chunks.append(value)
3802
3553
                    _refs.pop()
3803
3554
            result = ''.join(chunks)
3844
3595
        return "<config.%s(%s)>" % (self.__class__.__name__, id(self))
3845
3596
 
3846
3597
    def _get_overrides(self):
3847
 
        # FIXME: Hack around library_state.initialize never called
3848
 
        if bzrlib.global_state is not None:
3849
 
            return bzrlib.global_state.cmdline_overrides.get_sections()
 
3598
        if breezy._global_state is not None:
 
3599
            # TODO(jelmer): Urgh, this is circular so we can't call breezy.get_global_state()
 
3600
            return breezy._global_state.cmdline_overrides.get_sections()
3850
3601
        return []
3851
3602
 
3852
3603
    def get_shared_store(self, store, state=None):
3863
3614
            otherwise.
3864
3615
        """
3865
3616
        if state is None:
3866
 
            state = bzrlib.global_state
 
3617
            # TODO(jelmer): Urgh, this is circular so we can't call breezy.get_global_state()
 
3618
            state = breezy._global_state
3867
3619
        if state is None:
3868
3620
            global _shared_stores_at_exit_installed
3869
3621
            stores = _shared_stores
 
3622
 
3870
3623
            def save_config_changes():
3871
 
                for k, store in stores.iteritems():
 
3624
                for k, store in stores.items():
3872
3625
                    store.save_changes()
3873
3626
            if not _shared_stores_at_exit_installed:
3874
3627
                # FIXME: Ugly hack waiting for library_state to always be
4031
3784
    def unlock(self):
4032
3785
        return self.branch.unlock()
4033
3786
 
4034
 
    @needs_write_lock
4035
3787
    def set(self, name, value):
4036
 
        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.
 
3788
        with self.lock_write():
 
3789
            super(BranchStack, self).set(name, value)
 
3790
            # Unlocking the branch will trigger a store.save_changes() so the
 
3791
            # last unlock saves all the changes.
4039
3792
 
4040
 
    @needs_write_lock
4041
3793
    def remove(self, name):
4042
 
        super(BranchStack, self).remove(name)
4043
 
        # Unlocking the branch will trigger a store.save_changes() so the last
4044
 
        # unlock saves all the changes.
 
3794
        with self.lock_write():
 
3795
            super(BranchStack, self).remove(name)
 
3796
            # Unlocking the branch will trigger a store.save_changes() so the
 
3797
            # last unlock saves all the changes.
4045
3798
 
4046
3799
 
4047
3800
class RemoteControlStack(Stack):
4056
3809
        super(RemoteControlStack, self).__init__(
4057
3810
            [NameMatcher(cstore, None).get_sections],
4058
3811
            cstore)
4059
 
        self.bzrdir = bzrdir
 
3812
        self.controldir = bzrdir
4060
3813
 
4061
3814
 
4062
3815
class BranchOnlyStack(Stack):
4079
3832
    def unlock(self):
4080
3833
        return self.branch.unlock()
4081
3834
 
4082
 
    @needs_write_lock
4083
3835
    def set(self, name, value):
4084
 
        super(BranchOnlyStack, self).set(name, value)
4085
 
        # Force a write to persistent storage
4086
 
        self.store.save_changes()
 
3836
        with self.lock_write():
 
3837
            super(BranchOnlyStack, self).set(name, value)
 
3838
            # Force a write to persistent storage
 
3839
            self.store.save_changes()
4087
3840
 
4088
 
    @needs_write_lock
4089
3841
    def remove(self, name):
4090
 
        super(BranchOnlyStack, self).remove(name)
4091
 
        # Force a write to persistent storage
4092
 
        self.store.save_changes()
 
3842
        with self.lock_write():
 
3843
            super(BranchOnlyStack, self).remove(name)
 
3844
            # Force a write to persistent storage
 
3845
            self.store.save_changes()
4093
3846
 
4094
3847
 
4095
3848
class cmd_config(commands.Command):
4121
3874
        # http://pad.lv/788991 -- vila 20101115
4122
3875
        commands.Option('scope', help='Reduce the scope to the specified'
4123
3876
                        ' configuration file.',
4124
 
                        type=unicode),
 
3877
                        type=str),
4125
3878
        commands.Option('all',
4126
 
            help='Display all the defined values for the matching options.',
4127
 
            ),
 
3879
                        help='Display all the defined values for the matching options.',
 
3880
                        ),
4128
3881
        commands.Option('remove', help='Remove the option from'
4129
3882
                        ' the configuration file.'),
4130
3883
        ]
4177
3930
        # reduced to the plugin-specific store), related to
4178
3931
        # http://pad.lv/788991 -- vila 2011-11-15
4179
3932
        if scope is not None:
4180
 
            if scope == 'bazaar':
 
3933
            if scope == 'breezy':
4181
3934
                return GlobalStack()
4182
3935
            elif scope == 'locations':
4183
3936
                return LocationStack(directory)
4188
3941
                if write_access:
4189
3942
                    self.add_cleanup(br.lock_write().unlock)
4190
3943
                return br.get_config_stack()
4191
 
            raise errors.NoSuchConfig(scope)
 
3944
            raise NoSuchConfig(scope)
4192
3945
        else:
4193
3946
            try:
4194
3947
                (_, br, _) = (
4213
3966
            value = self._quote_multiline(value)
4214
3967
            self.outf.write('%s\n' % (value,))
4215
3968
        else:
4216
 
            raise errors.NoSuchConfigOption(name)
 
3969
            raise NoSuchConfigOption(name)
4217
3970
 
4218
3971
    def _show_matching_options(self, name, directory, scope):
4219
3972
        name = lazy_regex.lazy_compile(name)
4250
4003
 
4251
4004
    def _remove_config_option(self, name, directory, scope):
4252
4005
        if name is None:
4253
 
            raise errors.BzrCommandError(
 
4006
            raise errors.CommandError(
4254
4007
                '--remove expects an option to remove.')
4255
4008
        conf = self._get_stack(directory, scope, write_access=True)
4256
4009
        try:
4258
4011
            # Explicitly save the changes
4259
4012
            conf.store.save_changes()
4260
4013
        except KeyError:
4261
 
            raise errors.NoSuchConfigOption(name)
 
4014
            raise NoSuchConfigOption(name)
4262
4015
 
4263
4016
 
4264
4017
# Test registries
4268
4021
# themselves. The builder will receive a test instance and should return a
4269
4022
# ready-to-use store or stack.  Plugins that define new store/stacks can also
4270
4023
# register themselves here to be tested against the tests defined in
4271
 
# bzrlib.tests.test_config. Note that the builder can be called multiple times
 
4024
# breezy.tests.test_config. Note that the builder can be called multiple times
4272
4025
# for the same test.
4273
4026
 
4274
4027
# The registered object should be a callable receiving a test instance