~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: mbp at sourcefrog
  • Date: 2005-04-11 02:53:57 UTC
  • Revision ID: mbp@sourcefrog.net-20050411025357-af577721308648ae
- remove profiler temporary file when done

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