~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Martin Pool
  • Date: 2007-07-11 04:15:48 UTC
  • mto: This revision was merged to the branch mainline in revision 2605.
  • Revision ID: mbp@sourcefrog.net-20070711041548-n1om2ptyj2j01r6l
Clean up old/unused global options

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2007 Canonical Ltd
 
2
#   Authors: Robert Collins <robert.collins@canonical.com>
 
3
#            and others
 
4
#
 
5
# This program is free software; you can redistribute it and/or modify
 
6
# it under the terms of the GNU General Public License as published by
 
7
# the Free Software Foundation; either version 2 of the License, or
 
8
# (at your option) any later version.
 
9
#
 
10
# This program is distributed in the hope that it will be useful,
 
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
# GNU General Public License for more details.
 
14
#
 
15
# You should have received a copy of the GNU General Public License
 
16
# along with this program; if not, write to the Free Software
 
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
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:
 
25
[DEFAULT]
 
26
editor=name-of-program
 
27
email=Your Name <your@email.address>
 
28
check_signatures=require|ignore|check-available(default)
 
29
create_signatures=always|never|when-required(default)
 
30
gpg_signing_command=name-of-program
 
31
log_format=name-of-format
 
32
 
 
33
in locations.conf, you specify the url of a branch and options for it.
 
34
Wildcards may be used - * and ? as normal in shell completion. Options
 
35
set in both bazaar.conf and locations.conf are overridden by the locations.conf
 
36
setting.
 
37
[/home/robertc/source]
 
38
recurse=False|True(default)
 
39
email= as above
 
40
check_signatures= as above 
 
41
create_signatures= as above.
 
42
 
 
43
explanation of options
 
44
----------------------
 
45
editor - this option sets the pop up editor to use during commits.
 
46
email - this option sets the user id bzr will use when committing.
 
47
check_signatures - this option controls whether bzr will require good gpg
 
48
                   signatures, ignore them, or check them if they are 
 
49
                   present.
 
50
create_signatures - this option controls whether bzr will always create 
 
51
                    gpg signatures, never create them, or create them if the
 
52
                    branch is configured to require them.
 
53
log_format - this option sets the default log format.  Possible values are
 
54
             long, short, line, or a plugin can register new formats.
 
55
 
 
56
In bazaar.conf you can also define aliases in the ALIASES sections, example
 
57
 
 
58
[ALIASES]
 
59
lastlog=log --line -r-10..-1
 
60
ll=log --line -r-10..-1
 
61
h=help
 
62
up=pull
 
63
"""
 
64
 
 
65
import os
 
66
import sys
 
67
 
 
68
from bzrlib.lazy_import import lazy_import
 
69
lazy_import(globals(), """
 
70
import errno
 
71
from fnmatch import fnmatch
 
72
import re
 
73
from StringIO import StringIO
 
74
 
 
75
import bzrlib
 
76
from bzrlib import (
 
77
    errors,
 
78
    osutils,
 
79
    symbol_versioning,
 
80
    trace,
 
81
    urlutils,
 
82
    win32utils,
 
83
    )
 
84
import bzrlib.util.configobj.configobj as configobj
 
85
""")
 
86
 
 
87
from bzrlib.trace import mutter, warning
 
88
 
 
89
 
 
90
CHECK_IF_POSSIBLE=0
 
91
CHECK_ALWAYS=1
 
92
CHECK_NEVER=2
 
93
 
 
94
 
 
95
SIGN_WHEN_REQUIRED=0
 
96
SIGN_ALWAYS=1
 
97
SIGN_NEVER=2
 
98
 
 
99
 
 
100
POLICY_NONE = 0
 
101
POLICY_NORECURSE = 1
 
102
POLICY_APPENDPATH = 2
 
103
 
 
104
_policy_name = {
 
105
    POLICY_NONE: None,
 
106
    POLICY_NORECURSE: 'norecurse',
 
107
    POLICY_APPENDPATH: 'appendpath',
 
108
    }
 
109
_policy_value = {
 
110
    None: POLICY_NONE,
 
111
    'none': POLICY_NONE,
 
112
    'norecurse': POLICY_NORECURSE,
 
113
    'appendpath': POLICY_APPENDPATH,
 
114
    }
 
115
 
 
116
 
 
117
STORE_LOCATION = POLICY_NONE
 
118
STORE_LOCATION_NORECURSE = POLICY_NORECURSE
 
119
STORE_LOCATION_APPENDPATH = POLICY_APPENDPATH
 
120
STORE_BRANCH = 3
 
121
STORE_GLOBAL = 4
 
122
 
 
123
 
 
124
class ConfigObj(configobj.ConfigObj):
 
125
 
 
126
    def get_bool(self, section, key):
 
127
        return self[section].as_bool(key)
 
128
 
 
129
    def get_value(self, section, name):
 
130
        # Try [] for the old DEFAULT section.
 
131
        if section == "DEFAULT":
 
132
            try:
 
133
                return self[name]
 
134
            except KeyError:
 
135
                pass
 
136
        return self[section][name]
 
137
 
 
138
 
 
139
class Config(object):
 
140
    """A configuration policy - what username, editor, gpg needs etc."""
 
141
 
 
142
    def get_editor(self):
 
143
        """Get the users pop up editor."""
 
144
        raise NotImplementedError
 
145
 
 
146
    def _get_signature_checking(self):
 
147
        """Template method to override signature checking policy."""
 
148
 
 
149
    def _get_signing_policy(self):
 
150
        """Template method to override signature creation policy."""
 
151
 
 
152
    def _get_user_option(self, option_name):
 
153
        """Template method to provide a user option."""
 
154
        return None
 
155
 
 
156
    def get_user_option(self, option_name):
 
157
        """Get a generic option - no special process, no default."""
 
158
        return self._get_user_option(option_name)
 
159
 
 
160
    def gpg_signing_command(self):
 
161
        """What program should be used to sign signatures?"""
 
162
        result = self._gpg_signing_command()
 
163
        if result is None:
 
164
            result = "gpg"
 
165
        return result
 
166
 
 
167
    def _gpg_signing_command(self):
 
168
        """See gpg_signing_command()."""
 
169
        return None
 
170
 
 
171
    def log_format(self):
 
172
        """What log format should be used"""
 
173
        result = self._log_format()
 
174
        if result is None:
 
175
            result = "long"
 
176
        return result
 
177
 
 
178
    def _log_format(self):
 
179
        """See log_format()."""
 
180
        return None
 
181
 
 
182
    def __init__(self):
 
183
        super(Config, self).__init__()
 
184
 
 
185
    def post_commit(self):
 
186
        """An ordered list of python functions to call.
 
187
 
 
188
        Each function takes branch, rev_id as parameters.
 
189
        """
 
190
        return self._post_commit()
 
191
 
 
192
    def _post_commit(self):
 
193
        """See Config.post_commit."""
 
194
        return None
 
195
 
 
196
    def user_email(self):
 
197
        """Return just the email component of a username."""
 
198
        return extract_email_address(self.username())
 
199
 
 
200
    def username(self):
 
201
        """Return email-style username.
 
202
    
 
203
        Something similar to 'Martin Pool <mbp@sourcefrog.net>'
 
204
        
 
205
        $BZR_EMAIL can be set to override this (as well as the
 
206
        deprecated $BZREMAIL), then
 
207
        the concrete policy type is checked, and finally
 
208
        $EMAIL is examined.
 
209
        If none is found, a reasonable default is (hopefully)
 
210
        created.
 
211
    
 
212
        TODO: Check it's reasonably well-formed.
 
213
        """
 
214
        v = os.environ.get('BZR_EMAIL')
 
215
        if v:
 
216
            return v.decode(bzrlib.user_encoding)
 
217
        v = os.environ.get('BZREMAIL')
 
218
        if v:
 
219
            warning('BZREMAIL is deprecated in favor of BZR_EMAIL. Please update your configuration.')
 
220
            return v.decode(bzrlib.user_encoding)
 
221
    
 
222
        v = self._get_user_id()
 
223
        if v:
 
224
            return v
 
225
        
 
226
        v = os.environ.get('EMAIL')
 
227
        if v:
 
228
            return v.decode(bzrlib.user_encoding)
 
229
 
 
230
        name, email = _auto_user_id()
 
231
        if name:
 
232
            return '%s <%s>' % (name, email)
 
233
        else:
 
234
            return email
 
235
 
 
236
    def signature_checking(self):
 
237
        """What is the current policy for signature checking?."""
 
238
        policy = self._get_signature_checking()
 
239
        if policy is not None:
 
240
            return policy
 
241
        return CHECK_IF_POSSIBLE
 
242
 
 
243
    def signing_policy(self):
 
244
        """What is the current policy for signature checking?."""
 
245
        policy = self._get_signing_policy()
 
246
        if policy is not None:
 
247
            return policy
 
248
        return SIGN_WHEN_REQUIRED
 
249
 
 
250
    def signature_needed(self):
 
251
        """Is a signature needed when committing ?."""
 
252
        policy = self._get_signing_policy()
 
253
        if policy is None:
 
254
            policy = self._get_signature_checking()
 
255
            if policy is not None:
 
256
                warning("Please use create_signatures, not check_signatures "
 
257
                        "to set signing policy.")
 
258
            if policy == CHECK_ALWAYS:
 
259
                return True
 
260
        elif policy == SIGN_ALWAYS:
 
261
            return True
 
262
        return False
 
263
 
 
264
    def get_alias(self, value):
 
265
        return self._get_alias(value)
 
266
 
 
267
    def _get_alias(self, value):
 
268
        pass
 
269
 
 
270
    def get_nickname(self):
 
271
        return self._get_nickname()
 
272
 
 
273
    def _get_nickname(self):
 
274
        return None
 
275
 
 
276
 
 
277
class IniBasedConfig(Config):
 
278
    """A configuration policy that draws from ini files."""
 
279
 
 
280
    def _get_parser(self, file=None):
 
281
        if self._parser is not None:
 
282
            return self._parser
 
283
        if file is None:
 
284
            input = self._get_filename()
 
285
        else:
 
286
            input = file
 
287
        try:
 
288
            self._parser = ConfigObj(input, encoding='utf-8')
 
289
        except configobj.ConfigObjError, e:
 
290
            raise errors.ParseConfigError(e.errors, e.config.filename)
 
291
        return self._parser
 
292
 
 
293
    def _get_matching_sections(self):
 
294
        """Return an ordered list of (section_name, extra_path) pairs.
 
295
 
 
296
        If the section contains inherited configuration, extra_path is
 
297
        a string containing the additional path components.
 
298
        """
 
299
        section = self._get_section()
 
300
        if section is not None:
 
301
            return [(section, '')]
 
302
        else:
 
303
            return []
 
304
 
 
305
    def _get_section(self):
 
306
        """Override this to define the section used by the config."""
 
307
        return "DEFAULT"
 
308
 
 
309
    def _get_option_policy(self, section, option_name):
 
310
        """Return the policy for the given (section, option_name) pair."""
 
311
        return POLICY_NONE
 
312
 
 
313
    def _get_signature_checking(self):
 
314
        """See Config._get_signature_checking."""
 
315
        policy = self._get_user_option('check_signatures')
 
316
        if policy:
 
317
            return self._string_to_signature_policy(policy)
 
318
 
 
319
    def _get_signing_policy(self):
 
320
        """See Config._get_signing_policy"""
 
321
        policy = self._get_user_option('create_signatures')
 
322
        if policy:
 
323
            return self._string_to_signing_policy(policy)
 
324
 
 
325
    def _get_user_id(self):
 
326
        """Get the user id from the 'email' key in the current section."""
 
327
        return self._get_user_option('email')
 
328
 
 
329
    def _get_user_option(self, option_name):
 
330
        """See Config._get_user_option."""
 
331
        for (section, extra_path) in self._get_matching_sections():
 
332
            try:
 
333
                value = self._get_parser().get_value(section, option_name)
 
334
            except KeyError:
 
335
                continue
 
336
            policy = self._get_option_policy(section, option_name)
 
337
            if policy == POLICY_NONE:
 
338
                return value
 
339
            elif policy == POLICY_NORECURSE:
 
340
                # norecurse items only apply to the exact path
 
341
                if extra_path:
 
342
                    continue
 
343
                else:
 
344
                    return value
 
345
            elif policy == POLICY_APPENDPATH:
 
346
                if extra_path:
 
347
                    value = urlutils.join(value, extra_path)
 
348
                return value
 
349
            else:
 
350
                raise AssertionError('Unexpected config policy %r' % policy)
 
351
        else:
 
352
            return None
 
353
 
 
354
    def _gpg_signing_command(self):
 
355
        """See Config.gpg_signing_command."""
 
356
        return self._get_user_option('gpg_signing_command')
 
357
 
 
358
    def _log_format(self):
 
359
        """See Config.log_format."""
 
360
        return self._get_user_option('log_format')
 
361
 
 
362
    def __init__(self, get_filename):
 
363
        super(IniBasedConfig, self).__init__()
 
364
        self._get_filename = get_filename
 
365
        self._parser = None
 
366
        
 
367
    def _post_commit(self):
 
368
        """See Config.post_commit."""
 
369
        return self._get_user_option('post_commit')
 
370
 
 
371
    def _string_to_signature_policy(self, signature_string):
 
372
        """Convert a string to a signing policy."""
 
373
        if signature_string.lower() == 'check-available':
 
374
            return CHECK_IF_POSSIBLE
 
375
        if signature_string.lower() == 'ignore':
 
376
            return CHECK_NEVER
 
377
        if signature_string.lower() == 'require':
 
378
            return CHECK_ALWAYS
 
379
        raise errors.BzrError("Invalid signatures policy '%s'"
 
380
                              % signature_string)
 
381
 
 
382
    def _string_to_signing_policy(self, signature_string):
 
383
        """Convert a string to a signing policy."""
 
384
        if signature_string.lower() == 'when-required':
 
385
            return SIGN_WHEN_REQUIRED
 
386
        if signature_string.lower() == 'never':
 
387
            return SIGN_NEVER
 
388
        if signature_string.lower() == 'always':
 
389
            return SIGN_ALWAYS
 
390
        raise errors.BzrError("Invalid signing policy '%s'"
 
391
                              % signature_string)
 
392
 
 
393
    def _get_alias(self, value):
 
394
        try:
 
395
            return self._get_parser().get_value("ALIASES", 
 
396
                                                value)
 
397
        except KeyError:
 
398
            pass
 
399
 
 
400
    def _get_nickname(self):
 
401
        return self.get_user_option('nickname')
 
402
 
 
403
 
 
404
class GlobalConfig(IniBasedConfig):
 
405
    """The configuration that should be used for a specific location."""
 
406
 
 
407
    def get_editor(self):
 
408
        return self._get_user_option('editor')
 
409
 
 
410
    def __init__(self):
 
411
        super(GlobalConfig, self).__init__(config_filename)
 
412
 
 
413
    def set_user_option(self, option, value):
 
414
        """Save option and its value in the configuration."""
 
415
        # FIXME: RBC 20051029 This should refresh the parser and also take a
 
416
        # file lock on bazaar.conf.
 
417
        conf_dir = os.path.dirname(self._get_filename())
 
418
        ensure_config_dir_exists(conf_dir)
 
419
        if 'DEFAULT' not in self._get_parser():
 
420
            self._get_parser()['DEFAULT'] = {}
 
421
        self._get_parser()['DEFAULT'][option] = value
 
422
        f = open(self._get_filename(), 'wb')
 
423
        self._get_parser().write(f)
 
424
        f.close()
 
425
 
 
426
 
 
427
class LocationConfig(IniBasedConfig):
 
428
    """A configuration object that gives the policy for a location."""
 
429
 
 
430
    def __init__(self, location):
 
431
        name_generator = locations_config_filename
 
432
        if (not os.path.exists(name_generator()) and 
 
433
                os.path.exists(branches_config_filename())):
 
434
            if sys.platform == 'win32':
 
435
                warning('Please rename %s to %s' 
 
436
                         % (branches_config_filename(),
 
437
                            locations_config_filename()))
 
438
            else:
 
439
                warning('Please rename ~/.bazaar/branches.conf'
 
440
                        ' to ~/.bazaar/locations.conf')
 
441
            name_generator = branches_config_filename
 
442
        super(LocationConfig, self).__init__(name_generator)
 
443
        # local file locations are looked up by local path, rather than
 
444
        # by file url. This is because the config file is a user
 
445
        # file, and we would rather not expose the user to file urls.
 
446
        if location.startswith('file://'):
 
447
            location = urlutils.local_path_from_url(location)
 
448
        self.location = location
 
449
 
 
450
    def _get_matching_sections(self):
 
451
        """Return an ordered list of section names matching this location."""
 
452
        sections = self._get_parser()
 
453
        location_names = self.location.split('/')
 
454
        if self.location.endswith('/'):
 
455
            del location_names[-1]
 
456
        matches=[]
 
457
        for section in sections:
 
458
            # location is a local path if possible, so we need
 
459
            # to convert 'file://' urls to local paths if necessary.
 
460
            # This also avoids having file:///path be a more exact
 
461
            # match than '/path'.
 
462
            if section.startswith('file://'):
 
463
                section_path = urlutils.local_path_from_url(section)
 
464
            else:
 
465
                section_path = section
 
466
            section_names = section_path.split('/')
 
467
            if section.endswith('/'):
 
468
                del section_names[-1]
 
469
            names = zip(location_names, section_names)
 
470
            matched = True
 
471
            for name in names:
 
472
                if not fnmatch(name[0], name[1]):
 
473
                    matched = False
 
474
                    break
 
475
            if not matched:
 
476
                continue
 
477
            # so, for the common prefix they matched.
 
478
            # if section is longer, no match.
 
479
            if len(section_names) > len(location_names):
 
480
                continue
 
481
            matches.append((len(section_names), section,
 
482
                            '/'.join(location_names[len(section_names):])))
 
483
        matches.sort(reverse=True)
 
484
        sections = []
 
485
        for (length, section, extra_path) in matches:
 
486
            sections.append((section, extra_path))
 
487
            # should we stop looking for parent configs here?
 
488
            try:
 
489
                if self._get_parser()[section].as_bool('ignore_parents'):
 
490
                    break
 
491
            except KeyError:
 
492
                pass
 
493
        return sections
 
494
 
 
495
    def _get_option_policy(self, section, option_name):
 
496
        """Return the policy for the given (section, option_name) pair."""
 
497
        # check for the old 'recurse=False' flag
 
498
        try:
 
499
            recurse = self._get_parser()[section].as_bool('recurse')
 
500
        except KeyError:
 
501
            recurse = True
 
502
        if not recurse:
 
503
            return POLICY_NORECURSE
 
504
 
 
505
        policy_key = option_name + ':policy'
 
506
        try:
 
507
            policy_name = self._get_parser()[section][policy_key]
 
508
        except KeyError:
 
509
            policy_name = None
 
510
 
 
511
        return _policy_value[policy_name]
 
512
 
 
513
    def _set_option_policy(self, section, option_name, option_policy):
 
514
        """Set the policy for the given option name in the given section."""
 
515
        # The old recurse=False option affects all options in the
 
516
        # section.  To handle multiple policies in the section, we
 
517
        # need to convert it to a policy_norecurse key.
 
518
        try:
 
519
            recurse = self._get_parser()[section].as_bool('recurse')
 
520
        except KeyError:
 
521
            pass
 
522
        else:
 
523
            symbol_versioning.warn(
 
524
                'The recurse option is deprecated as of 0.14.  '
 
525
                'The section "%s" has been converted to use policies.'
 
526
                % section,
 
527
                DeprecationWarning)
 
528
            del self._get_parser()[section]['recurse']
 
529
            if not recurse:
 
530
                for key in self._get_parser()[section].keys():
 
531
                    if not key.endswith(':policy'):
 
532
                        self._get_parser()[section][key +
 
533
                                                    ':policy'] = 'norecurse'
 
534
 
 
535
        policy_key = option_name + ':policy'
 
536
        policy_name = _policy_name[option_policy]
 
537
        if policy_name is not None:
 
538
            self._get_parser()[section][policy_key] = policy_name
 
539
        else:
 
540
            if policy_key in self._get_parser()[section]:
 
541
                del self._get_parser()[section][policy_key]
 
542
 
 
543
    def set_user_option(self, option, value, store=STORE_LOCATION):
 
544
        """Save option and its value in the configuration."""
 
545
        assert store in [STORE_LOCATION,
 
546
                         STORE_LOCATION_NORECURSE,
 
547
                         STORE_LOCATION_APPENDPATH], 'bad storage policy'
 
548
        # FIXME: RBC 20051029 This should refresh the parser and also take a
 
549
        # file lock on locations.conf.
 
550
        conf_dir = os.path.dirname(self._get_filename())
 
551
        ensure_config_dir_exists(conf_dir)
 
552
        location = self.location
 
553
        if location.endswith('/'):
 
554
            location = location[:-1]
 
555
        if (not location in self._get_parser() and
 
556
            not location + '/' in self._get_parser()):
 
557
            self._get_parser()[location]={}
 
558
        elif location + '/' in self._get_parser():
 
559
            location = location + '/'
 
560
        self._get_parser()[location][option]=value
 
561
        # the allowed values of store match the config policies
 
562
        self._set_option_policy(location, option, store)
 
563
        self._get_parser().write(file(self._get_filename(), 'wb'))
 
564
 
 
565
 
 
566
class BranchConfig(Config):
 
567
    """A configuration object giving the policy for a branch."""
 
568
 
 
569
    def _get_branch_data_config(self):
 
570
        if self._branch_data_config is None:
 
571
            self._branch_data_config = TreeConfig(self.branch)
 
572
        return self._branch_data_config
 
573
 
 
574
    def _get_location_config(self):
 
575
        if self._location_config is None:
 
576
            self._location_config = LocationConfig(self.branch.base)
 
577
        return self._location_config
 
578
 
 
579
    def _get_global_config(self):
 
580
        if self._global_config is None:
 
581
            self._global_config = GlobalConfig()
 
582
        return self._global_config
 
583
 
 
584
    def _get_best_value(self, option_name):
 
585
        """This returns a user option from local, tree or global config.
 
586
 
 
587
        They are tried in that order.  Use get_safe_value if trusted values
 
588
        are necessary.
 
589
        """
 
590
        for source in self.option_sources:
 
591
            value = getattr(source(), option_name)()
 
592
            if value is not None:
 
593
                return value
 
594
        return None
 
595
 
 
596
    def _get_safe_value(self, option_name):
 
597
        """This variant of get_best_value never returns untrusted values.
 
598
        
 
599
        It does not return values from the branch data, because the branch may
 
600
        not be controlled by the user.
 
601
 
 
602
        We may wish to allow locations.conf to control whether branches are
 
603
        trusted in the future.
 
604
        """
 
605
        for source in (self._get_location_config, self._get_global_config):
 
606
            value = getattr(source(), option_name)()
 
607
            if value is not None:
 
608
                return value
 
609
        return None
 
610
 
 
611
    def _get_user_id(self):
 
612
        """Return the full user id for the branch.
 
613
    
 
614
        e.g. "John Hacker <jhacker@foo.org>"
 
615
        This is looked up in the email controlfile for the branch.
 
616
        """
 
617
        try:
 
618
            return (self.branch.control_files.get_utf8("email") 
 
619
                    .read()
 
620
                    .decode(bzrlib.user_encoding)
 
621
                    .rstrip("\r\n"))
 
622
        except errors.NoSuchFile, e:
 
623
            pass
 
624
        
 
625
        return self._get_best_value('_get_user_id')
 
626
 
 
627
    def _get_signature_checking(self):
 
628
        """See Config._get_signature_checking."""
 
629
        return self._get_best_value('_get_signature_checking')
 
630
 
 
631
    def _get_signing_policy(self):
 
632
        """See Config._get_signing_policy."""
 
633
        return self._get_best_value('_get_signing_policy')
 
634
 
 
635
    def _get_user_option(self, option_name):
 
636
        """See Config._get_user_option."""
 
637
        for source in self.option_sources:
 
638
            value = source()._get_user_option(option_name)
 
639
            if value is not None:
 
640
                return value
 
641
        return None
 
642
 
 
643
    def set_user_option(self, name, value, store=STORE_BRANCH,
 
644
        warn_masked=False):
 
645
        if store == STORE_BRANCH:
 
646
            self._get_branch_data_config().set_option(value, name)
 
647
        elif store == STORE_GLOBAL:
 
648
            self._get_global_config().set_user_option(name, value)
 
649
        else:
 
650
            self._get_location_config().set_user_option(name, value, store)
 
651
        if not warn_masked:
 
652
            return
 
653
        if store in (STORE_GLOBAL, STORE_BRANCH):
 
654
            mask_value = self._get_location_config().get_user_option(name)
 
655
            if mask_value is not None:
 
656
                trace.warning('Value "%s" is masked by "%s" from'
 
657
                              ' locations.conf', value, mask_value)
 
658
            else:
 
659
                if store == STORE_GLOBAL:
 
660
                    branch_config = self._get_branch_data_config()
 
661
                    mask_value = branch_config.get_user_option(name)
 
662
                    if mask_value is not None:
 
663
                        trace.warning('Value "%s" is masked by "%s" from'
 
664
                                      ' branch.conf', value, mask_value)
 
665
 
 
666
 
 
667
    def _gpg_signing_command(self):
 
668
        """See Config.gpg_signing_command."""
 
669
        return self._get_safe_value('_gpg_signing_command')
 
670
        
 
671
    def __init__(self, branch):
 
672
        super(BranchConfig, self).__init__()
 
673
        self._location_config = None
 
674
        self._branch_data_config = None
 
675
        self._global_config = None
 
676
        self.branch = branch
 
677
        self.option_sources = (self._get_location_config, 
 
678
                               self._get_branch_data_config,
 
679
                               self._get_global_config)
 
680
 
 
681
    def _post_commit(self):
 
682
        """See Config.post_commit."""
 
683
        return self._get_safe_value('_post_commit')
 
684
 
 
685
    def _get_nickname(self):
 
686
        value = self._get_explicit_nickname()
 
687
        if value is not None:
 
688
            return value
 
689
        return urlutils.unescape(self.branch.base.split('/')[-2])
 
690
 
 
691
    def has_explicit_nickname(self):
 
692
        """Return true if a nickname has been explicitly assigned."""
 
693
        return self._get_explicit_nickname() is not None
 
694
 
 
695
    def _get_explicit_nickname(self):
 
696
        return self._get_best_value('_get_nickname')
 
697
 
 
698
    def _log_format(self):
 
699
        """See Config.log_format."""
 
700
        return self._get_best_value('_log_format')
 
701
 
 
702
 
 
703
def ensure_config_dir_exists(path=None):
 
704
    """Make sure a configuration directory exists.
 
705
    This makes sure that the directory exists.
 
706
    On windows, since configuration directories are 2 levels deep,
 
707
    it makes sure both the directory and the parent directory exists.
 
708
    """
 
709
    if path is None:
 
710
        path = config_dir()
 
711
    if not os.path.isdir(path):
 
712
        if sys.platform == 'win32':
 
713
            parent_dir = os.path.dirname(path)
 
714
            if not os.path.isdir(parent_dir):
 
715
                mutter('creating config parent directory: %r', parent_dir)
 
716
            os.mkdir(parent_dir)
 
717
        mutter('creating config directory: %r', path)
 
718
        os.mkdir(path)
 
719
 
 
720
 
 
721
def config_dir():
 
722
    """Return per-user configuration directory.
 
723
 
 
724
    By default this is ~/.bazaar/
 
725
    
 
726
    TODO: Global option --config-dir to override this.
 
727
    """
 
728
    base = os.environ.get('BZR_HOME', None)
 
729
    if sys.platform == 'win32':
 
730
        if base is None:
 
731
            base = win32utils.get_appdata_location_unicode()
 
732
        if base is None:
 
733
            base = os.environ.get('HOME', None)
 
734
        if base is None:
 
735
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA, or HOME set')
 
736
        return osutils.pathjoin(base, 'bazaar', '2.0')
 
737
    else:
 
738
        # cygwin, linux, and darwin all have a $HOME directory
 
739
        if base is None:
 
740
            base = os.path.expanduser("~")
 
741
        return osutils.pathjoin(base, ".bazaar")
 
742
 
 
743
 
 
744
def config_filename():
 
745
    """Return per-user configuration ini file filename."""
 
746
    return osutils.pathjoin(config_dir(), 'bazaar.conf')
 
747
 
 
748
 
 
749
def branches_config_filename():
 
750
    """Return per-user configuration ini file filename."""
 
751
    return osutils.pathjoin(config_dir(), 'branches.conf')
 
752
 
 
753
 
 
754
def locations_config_filename():
 
755
    """Return per-user configuration ini file filename."""
 
756
    return osutils.pathjoin(config_dir(), 'locations.conf')
 
757
 
 
758
 
 
759
def user_ignore_config_filename():
 
760
    """Return the user default ignore filename"""
 
761
    return osutils.pathjoin(config_dir(), 'ignore')
 
762
 
 
763
 
 
764
def _auto_user_id():
 
765
    """Calculate automatic user identification.
 
766
 
 
767
    Returns (realname, email).
 
768
 
 
769
    Only used when none is set in the environment or the id file.
 
770
 
 
771
    This previously used the FQDN as the default domain, but that can
 
772
    be very slow on machines where DNS is broken.  So now we simply
 
773
    use the hostname.
 
774
    """
 
775
    import socket
 
776
 
 
777
    if sys.platform == 'win32':
 
778
        name = win32utils.get_user_name_unicode()
 
779
        if name is None:
 
780
            raise errors.BzrError("Cannot autodetect user name.\n"
 
781
                                  "Please, set your name with command like:\n"
 
782
                                  'bzr whoami "Your Name <name@domain.com>"')
 
783
        host = win32utils.get_host_name_unicode()
 
784
        if host is None:
 
785
            host = socket.gethostname()
 
786
        return name, (name + '@' + host)
 
787
 
 
788
    try:
 
789
        import pwd
 
790
        uid = os.getuid()
 
791
        w = pwd.getpwuid(uid)
 
792
 
 
793
        # we try utf-8 first, because on many variants (like Linux),
 
794
        # /etc/passwd "should" be in utf-8, and because it's unlikely to give
 
795
        # false positives.  (many users will have their user encoding set to
 
796
        # latin-1, which cannot raise UnicodeError.)
 
797
        try:
 
798
            gecos = w.pw_gecos.decode('utf-8')
 
799
            encoding = 'utf-8'
 
800
        except UnicodeError:
 
801
            try:
 
802
                gecos = w.pw_gecos.decode(bzrlib.user_encoding)
 
803
                encoding = bzrlib.user_encoding
 
804
            except UnicodeError:
 
805
                raise errors.BzrCommandError('Unable to determine your name.  '
 
806
                   'Use "bzr whoami" to set it.')
 
807
        try:
 
808
            username = w.pw_name.decode(encoding)
 
809
        except UnicodeError:
 
810
            raise errors.BzrCommandError('Unable to determine your name.  '
 
811
                'Use "bzr whoami" to set it.')
 
812
 
 
813
        comma = gecos.find(',')
 
814
        if comma == -1:
 
815
            realname = gecos
 
816
        else:
 
817
            realname = gecos[:comma]
 
818
        if not realname:
 
819
            realname = username
 
820
 
 
821
    except ImportError:
 
822
        import getpass
 
823
        try:
 
824
            realname = username = getpass.getuser().decode(bzrlib.user_encoding)
 
825
        except UnicodeDecodeError:
 
826
            raise errors.BzrError("Can't decode username as %s." % \
 
827
                    bzrlib.user_encoding)
 
828
 
 
829
    return realname, (username + '@' + socket.gethostname())
 
830
 
 
831
 
 
832
def extract_email_address(e):
 
833
    """Return just the address part of an email string.
 
834
    
 
835
    That is just the user@domain part, nothing else. 
 
836
    This part is required to contain only ascii characters.
 
837
    If it can't be extracted, raises an error.
 
838
    
 
839
    >>> extract_email_address('Jane Tester <jane@test.com>')
 
840
    "jane@test.com"
 
841
    """
 
842
    m = re.search(r'[\w+.-]+@[\w+.-]+', e)
 
843
    if not m:
 
844
        raise errors.NoEmailInUsername(e)
 
845
    return m.group(0)
 
846
 
 
847
 
 
848
class TreeConfig(IniBasedConfig):
 
849
    """Branch configuration data associated with its contents, not location"""
 
850
    def __init__(self, branch):
 
851
        self.branch = branch
 
852
 
 
853
    def _get_parser(self, file=None):
 
854
        if file is not None:
 
855
            return IniBasedConfig._get_parser(file)
 
856
        return self._get_config()
 
857
 
 
858
    def _get_config(self):
 
859
        try:
 
860
            obj = ConfigObj(self.branch.control_files.get('branch.conf'),
 
861
                            encoding='utf-8')
 
862
        except errors.NoSuchFile:
 
863
            obj = ConfigObj(encoding='utf=8')
 
864
        return obj
 
865
 
 
866
    def get_option(self, name, section=None, default=None):
 
867
        self.branch.lock_read()
 
868
        try:
 
869
            obj = self._get_config()
 
870
            try:
 
871
                if section is not None:
 
872
                    obj = obj[section]
 
873
                result = obj[name]
 
874
            except KeyError:
 
875
                result = default
 
876
        finally:
 
877
            self.branch.unlock()
 
878
        return result
 
879
 
 
880
    def set_option(self, value, name, section=None):
 
881
        """Set a per-branch configuration option"""
 
882
        self.branch.lock_write()
 
883
        try:
 
884
            cfg_obj = self._get_config()
 
885
            if section is None:
 
886
                obj = cfg_obj
 
887
            else:
 
888
                try:
 
889
                    obj = cfg_obj[section]
 
890
                except KeyError:
 
891
                    cfg_obj[section] = {}
 
892
                    obj = cfg_obj[section]
 
893
            obj[name] = value
 
894
            out_file = StringIO()
 
895
            cfg_obj.write(out_file)
 
896
            out_file.seek(0)
 
897
            self.branch.control_files.put('branch.conf', out_file)
 
898
        finally:
 
899
            self.branch.unlock()