~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

Handled simultaneous renames of parent and child better

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005 by Canonical Ltd
 
2
#   Authors: Robert Collins <robert.collins@canonical.com>
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
 
 
18
"""Configuration that affects the behaviour of Bazaar.
 
19
 
 
20
Currently this configuration resides in ~/.bazaar/bazaar.conf
 
21
and ~/.bazaar/branches.conf, which is written to by bzr.
 
22
 
 
23
In bazaar.conf the following options may be set:
 
24
[DEFAULT]
 
25
editor=name-of-program
 
26
email=Your Name <your@email.address>
 
27
check_signatures=require|ignore|check-available(default)
 
28
create_signatures=always|never|when-required(default)
 
29
gpg_signing_command=name-of-program
 
30
 
 
31
in branches.conf, you specify the url of a branch and options for it.
 
32
Wildcards may be used - * and ? as normal in shell completion. Options
 
33
set in both bazaar.conf and branches.conf are overriden by the branches.conf
 
34
setting.
 
35
[/home/robertc/source]
 
36
recurse=False|True(default)
 
37
email= as above
 
38
check_signatures= as abive 
 
39
create_signatures= as above.
 
40
 
 
41
explanation of options
 
42
----------------------
 
43
editor - this option sets the pop up editor to use during commits.
 
44
email - this option sets the user id bzr will use when committing.
 
45
check_signatures - this option controls whether bzr will require good gpg
 
46
                   signatures, ignore them, or check them if they are 
 
47
                   present.
 
48
create_signatures - this option controls whether bzr will always create 
 
49
                    gpg signatures, never create them, or create them if the
 
50
                    branch is configured to require them.
 
51
                    NB: This option is planned, but not implemented yet.
 
52
"""
 
53
 
 
54
 
 
55
import errno
 
56
import os
 
57
import sys
 
58
from fnmatch import fnmatch
 
59
import re
 
60
import shlex
 
61
 
 
62
import bzrlib
 
63
import bzrlib.errors as errors
 
64
from bzrlib.osutils import pathjoin
 
65
from bzrlib.trace import mutter
 
66
import bzrlib.util.configobj.configobj as configobj
 
67
from StringIO import StringIO
 
68
 
 
69
CHECK_IF_POSSIBLE=0
 
70
CHECK_ALWAYS=1
 
71
CHECK_NEVER=2
 
72
 
 
73
 
 
74
class ConfigObj(configobj.ConfigObj):
 
75
 
 
76
    def get_bool(self, section, key):
 
77
        val = self[section][key].lower()
 
78
        if val in ('1', 'yes', 'true', 'on'):
 
79
            return True
 
80
        elif val in ('0', 'no', 'false', 'off'):
 
81
            return False
 
82
        else:
 
83
            raise ValueError("Value %r is not boolean" % val)
 
84
 
 
85
    def get_value(self, section, name):
 
86
        # Try [] for the old DEFAULT section.
 
87
        if section == "DEFAULT":
 
88
            try:
 
89
                return self[name]
 
90
            except KeyError:
 
91
                pass
 
92
        return self[section][name]
 
93
 
 
94
 
 
95
class Config(object):
 
96
    """A configuration policy - what username, editor, gpg needs etc."""
 
97
 
 
98
    def get_editor(self):
 
99
        """Get the users pop up editor."""
 
100
        raise NotImplementedError
 
101
 
 
102
    def _get_signature_checking(self):
 
103
        """Template method to override signature checking policy."""
 
104
 
 
105
    def _get_user_option(self, option_name):
 
106
        """Template method to provide a user option."""
 
107
        return None
 
108
 
 
109
    def get_user_option(self, option_name):
 
110
        """Get a generic option - no special process, no default."""
 
111
        return self._get_user_option(option_name)
 
112
 
 
113
    def gpg_signing_command(self):
 
114
        """What program should be used to sign signatures?"""
 
115
        result = self._gpg_signing_command()
 
116
        if result is None:
 
117
            result = "gpg"
 
118
        return result
 
119
 
 
120
    def _gpg_signing_command(self):
 
121
        """See gpg_signing_command()."""
 
122
        return None
 
123
 
 
124
    def __init__(self):
 
125
        super(Config, self).__init__()
 
126
 
 
127
    def post_commit(self):
 
128
        """An ordered list of python functions to call.
 
129
 
 
130
        Each function takes branch, rev_id as parameters.
 
131
        """
 
132
        return self._post_commit()
 
133
 
 
134
    def _post_commit(self):
 
135
        """See Config.post_commit."""
 
136
        return None
 
137
 
 
138
    def user_email(self):
 
139
        """Return just the email component of a username."""
 
140
        return extract_email_address(self.username())
 
141
 
 
142
    def username(self):
 
143
        """Return email-style username.
 
144
    
 
145
        Something similar to 'Martin Pool <mbp@sourcefrog.net>'
 
146
        
 
147
        $BZREMAIL can be set to override this, then
 
148
        the concrete policy type is checked, and finally
 
149
        $EMAIL is examined.
 
150
        If none is found, a reasonable default is (hopefully)
 
151
        created.
 
152
    
 
153
        TODO: Check it's reasonably well-formed.
 
154
        """
 
155
        v = os.environ.get('BZREMAIL')
 
156
        if v:
 
157
            return v.decode(bzrlib.user_encoding)
 
158
    
 
159
        v = self._get_user_id()
 
160
        if v:
 
161
            return v
 
162
        
 
163
        v = os.environ.get('EMAIL')
 
164
        if v:
 
165
            return v.decode(bzrlib.user_encoding)
 
166
 
 
167
        name, email = _auto_user_id()
 
168
        if name:
 
169
            return '%s <%s>' % (name, email)
 
170
        else:
 
171
            return email
 
172
 
 
173
    def signature_checking(self):
 
174
        """What is the current policy for signature checking?."""
 
175
        policy = self._get_signature_checking()
 
176
        if policy is not None:
 
177
            return policy
 
178
        return CHECK_IF_POSSIBLE
 
179
 
 
180
    def signature_needed(self):
 
181
        """Is a signature needed when committing ?."""
 
182
        policy = self._get_signature_checking()
 
183
        if policy == CHECK_ALWAYS:
 
184
            return True
 
185
        return False
 
186
 
 
187
    def get_command_defaults(self, command_name):
 
188
        """Return the default options"""
 
189
        # All these template methods look like overengineering to me.  Is it
 
190
        # clear that we want to support a variety of configuration formats?
 
191
        return self._get_command_defaults(command_name)
 
192
 
 
193
    def _get_command_defaults(self, command_name):
 
194
        return []
 
195
 
 
196
 
 
197
class IniBasedConfig(Config):
 
198
    """A configuration policy that draws from ini files."""
 
199
 
 
200
    def _get_parser(self, file=None):
 
201
        if self._parser is not None:
 
202
            return self._parser
 
203
        if file is None:
 
204
            input = self._get_filename()
 
205
        else:
 
206
            input = file
 
207
        try:
 
208
            self._parser = ConfigObj(input)
 
209
        except configobj.ConfigObjError, e:
 
210
            raise errors.ParseConfigError(e.errors, e.config.filename)
 
211
        return self._parser
 
212
 
 
213
    def _get_section(self):
 
214
        """Override this to define the section used by the config."""
 
215
        return "DEFAULT"
 
216
 
 
217
    def _get_signature_checking(self):
 
218
        """See Config._get_signature_checking."""
 
219
        policy = self._get_user_option('check_signatures')
 
220
        if policy:
 
221
            return self._string_to_signature_policy(policy)
 
222
 
 
223
    def _get_user_id(self):
 
224
        """Get the user id from the 'email' key in the current section."""
 
225
        return self._get_user_option('email')
 
226
 
 
227
    def _get_user_option(self, option_name):
 
228
        """See Config._get_user_option."""
 
229
        try:
 
230
            return self._get_parser().get_value(self._get_section(),
 
231
                                                option_name)
 
232
        except KeyError:
 
233
            pass
 
234
 
 
235
    def _gpg_signing_command(self):
 
236
        """See Config.gpg_signing_command."""
 
237
        return self._get_user_option('gpg_signing_command')
 
238
 
 
239
    def __init__(self, get_filename):
 
240
        super(IniBasedConfig, self).__init__()
 
241
        self._get_filename = get_filename
 
242
        self._parser = None
 
243
        
 
244
    def _post_commit(self):
 
245
        """See Config.post_commit."""
 
246
        return self._get_user_option('post_commit')
 
247
 
 
248
    def _string_to_signature_policy(self, signature_string):
 
249
        """Convert a string to a signing policy."""
 
250
        if signature_string.lower() == 'check-available':
 
251
            return CHECK_IF_POSSIBLE
 
252
        if signature_string.lower() == 'ignore':
 
253
            return CHECK_NEVER
 
254
        if signature_string.lower() == 'require':
 
255
            return CHECK_ALWAYS
 
256
        raise errors.BzrError("Invalid signatures policy '%s'"
 
257
                              % signature_string)
 
258
 
 
259
    def _get_command_defaults(self, command_name):
 
260
        try:
 
261
            defaults = self._get_parser().get_value("COMMAND_DEFAULTS", 
 
262
                                                    command_name)
 
263
        except KeyError:
 
264
            return []
 
265
        try:
 
266
            return shlex.split(defaults)
 
267
        except ValueError, e:
 
268
            raise errors.CommandDefaultSyntax(command_name=command_name, 
 
269
                                              error=e)
 
270
 
 
271
class GlobalConfig(IniBasedConfig):
 
272
    """The configuration that should be used for a specific location."""
 
273
 
 
274
    def get_editor(self):
 
275
        return self._get_user_option('editor')
 
276
 
 
277
    def __init__(self):
 
278
        super(GlobalConfig, self).__init__(config_filename)
 
279
 
 
280
 
 
281
class LocationConfig(IniBasedConfig):
 
282
    """A configuration object that gives the policy for a location."""
 
283
 
 
284
    def __init__(self, location):
 
285
        super(LocationConfig, self).__init__(branches_config_filename)
 
286
        self._global_config = None
 
287
        self.location = location
 
288
 
 
289
    def _get_global_config(self):
 
290
        if self._global_config is None:
 
291
            self._global_config = GlobalConfig()
 
292
        return self._global_config
 
293
 
 
294
    def _get_section(self):
 
295
        """Get the section we should look in for config items.
 
296
 
 
297
        Returns None if none exists. 
 
298
        TODO: perhaps return a NullSection that thunks through to the 
 
299
              global config.
 
300
        """
 
301
        sections = self._get_parser()
 
302
        location_names = self.location.split('/')
 
303
        if self.location.endswith('/'):
 
304
            del location_names[-1]
 
305
        matches=[]
 
306
        for section in sections:
 
307
            section_names = section.split('/')
 
308
            if section.endswith('/'):
 
309
                del section_names[-1]
 
310
            names = zip(location_names, section_names)
 
311
            matched = True
 
312
            for name in names:
 
313
                if not fnmatch(name[0], name[1]):
 
314
                    matched = False
 
315
                    break
 
316
            if not matched:
 
317
                continue
 
318
            # so, for the common prefix they matched.
 
319
            # if section is longer, no match.
 
320
            if len(section_names) > len(location_names):
 
321
                continue
 
322
            # if path is longer, and recurse is not true, no match
 
323
            if len(section_names) < len(location_names):
 
324
                try:
 
325
                    if not self._get_parser().get_bool(section, 'recurse'):
 
326
                        continue
 
327
                except KeyError:
 
328
                    pass
 
329
            matches.append((len(section_names), section))
 
330
        if not len(matches):
 
331
            return None
 
332
        matches.sort(reverse=True)
 
333
        return matches[0][1]
 
334
 
 
335
    def _gpg_signing_command(self):
 
336
        """See Config.gpg_signing_command."""
 
337
        command = super(LocationConfig, self)._gpg_signing_command()
 
338
        if command is not None:
 
339
            return command
 
340
        return self._get_global_config()._gpg_signing_command()
 
341
 
 
342
    def _get_user_id(self):
 
343
        user_id = super(LocationConfig, self)._get_user_id()
 
344
        if user_id is not None:
 
345
            return user_id
 
346
        return self._get_global_config()._get_user_id()
 
347
 
 
348
    def _get_user_option(self, option_name):
 
349
        """See Config._get_user_option."""
 
350
        option_value = super(LocationConfig, 
 
351
                             self)._get_user_option(option_name)
 
352
        if option_value is not None:
 
353
            return option_value
 
354
        return self._get_global_config()._get_user_option(option_name)
 
355
 
 
356
    def _get_signature_checking(self):
 
357
        """See Config._get_signature_checking."""
 
358
        check = super(LocationConfig, self)._get_signature_checking()
 
359
        if check is not None:
 
360
            return check
 
361
        return self._get_global_config()._get_signature_checking()
 
362
 
 
363
    def _post_commit(self):
 
364
        """See Config.post_commit."""
 
365
        hook = self._get_user_option('post_commit')
 
366
        if hook is not None:
 
367
            return hook
 
368
        return self._get_global_config()._post_commit()
 
369
 
 
370
    def set_user_option(self, option, value):
 
371
        """Save option and its value in the configuration."""
 
372
        # FIXME: RBC 20051029 This should refresh the parser and also take a
 
373
        # file lock on branches.conf.
 
374
        conf_dir = os.path.dirname(self._get_filename())
 
375
        ensure_config_dir_exists(conf_dir)
 
376
        location = self.location
 
377
        if location.endswith('/'):
 
378
            location = location[:-1]
 
379
        if (not location in self._get_parser() and
 
380
            not location + '/' in self._get_parser()):
 
381
            self._get_parser()[location]={}
 
382
        elif location + '/' in self._get_parser():
 
383
            location = location + '/'
 
384
        self._get_parser()[location][option]=value
 
385
        self._get_parser().write()
 
386
 
 
387
 
 
388
class BranchConfig(Config):
 
389
    """A configuration object giving the policy for a branch."""
 
390
 
 
391
    def _get_location_config(self):
 
392
        if self._location_config is None:
 
393
            self._location_config = LocationConfig(self.branch.base)
 
394
        return self._location_config
 
395
 
 
396
    def _get_user_id(self):
 
397
        """Return the full user id for the branch.
 
398
    
 
399
        e.g. "John Hacker <jhacker@foo.org>"
 
400
        This is looked up in the email controlfile for the branch.
 
401
        """
 
402
        try:
 
403
            return (self.branch.control_files.get_utf8("email") 
 
404
                    .read()
 
405
                    .decode(bzrlib.user_encoding)
 
406
                    .rstrip("\r\n"))
 
407
        except errors.NoSuchFile, e:
 
408
            pass
 
409
        
 
410
        return self._get_location_config()._get_user_id()
 
411
 
 
412
    def _get_signature_checking(self):
 
413
        """See Config._get_signature_checking."""
 
414
        return self._get_location_config()._get_signature_checking()
 
415
 
 
416
    def _get_user_option(self, option_name):
 
417
        """See Config._get_user_option."""
 
418
        return self._get_location_config()._get_user_option(option_name)
 
419
 
 
420
    def _gpg_signing_command(self):
 
421
        """See Config.gpg_signing_command."""
 
422
        return self._get_location_config()._gpg_signing_command()
 
423
        
 
424
    def __init__(self, branch):
 
425
        super(BranchConfig, self).__init__()
 
426
        self._location_config = None
 
427
        self.branch = branch
 
428
 
 
429
    def _post_commit(self):
 
430
        """See Config.post_commit."""
 
431
        return self._get_location_config()._post_commit()
 
432
 
 
433
 
 
434
def ensure_config_dir_exists(path=None):
 
435
    """Make sure a configuration directory exists.
 
436
    This makes sure that the directory exists.
 
437
    On windows, since configuration directories are 2 levels deep,
 
438
    it makes sure both the directory and the parent directory exists.
 
439
    """
 
440
    if path is None:
 
441
        path = config_dir()
 
442
    if not os.path.isdir(path):
 
443
        if sys.platform == 'win32':
 
444
            parent_dir = os.path.dirname(path)
 
445
            if not os.path.isdir(parent_dir):
 
446
                mutter('creating config parent directory: %r', parent_dir)
 
447
            os.mkdir(parent_dir)
 
448
        mutter('creating config directory: %r', path)
 
449
        os.mkdir(path)
 
450
 
 
451
 
 
452
def config_dir():
 
453
    """Return per-user configuration directory.
 
454
 
 
455
    By default this is ~/.bazaar/
 
456
    
 
457
    TODO: Global option --config-dir to override this.
 
458
    """
 
459
    base = os.environ.get('BZR_HOME', None)
 
460
    if sys.platform == 'win32':
 
461
        if base is None:
 
462
            base = os.environ.get('APPDATA', None)
 
463
        if base is None:
 
464
            base = os.environ.get('HOME', None)
 
465
        if base is None:
 
466
            raise BzrError('You must have one of BZR_HOME, APPDATA, or HOME set')
 
467
        return pathjoin(base, 'bazaar', '2.0')
 
468
    else:
 
469
        # cygwin, linux, and darwin all have a $HOME directory
 
470
        if base is None:
 
471
            base = os.path.expanduser("~")
 
472
        return pathjoin(base, ".bazaar")
 
473
 
 
474
 
 
475
def config_filename():
 
476
    """Return per-user configuration ini file filename."""
 
477
    return pathjoin(config_dir(), 'bazaar.conf')
 
478
 
 
479
 
 
480
def branches_config_filename():
 
481
    """Return per-user configuration ini file filename."""
 
482
    return pathjoin(config_dir(), 'branches.conf')
 
483
 
 
484
 
 
485
def _auto_user_id():
 
486
    """Calculate automatic user identification.
 
487
 
 
488
    Returns (realname, email).
 
489
 
 
490
    Only used when none is set in the environment or the id file.
 
491
 
 
492
    This previously used the FQDN as the default domain, but that can
 
493
    be very slow on machines where DNS is broken.  So now we simply
 
494
    use the hostname.
 
495
    """
 
496
    import socket
 
497
 
 
498
    # XXX: Any good way to get real user name on win32?
 
499
 
 
500
    try:
 
501
        import pwd
 
502
        uid = os.getuid()
 
503
        w = pwd.getpwuid(uid)
 
504
        gecos = w.pw_gecos.decode(bzrlib.user_encoding)
 
505
        username = w.pw_name.decode(bzrlib.user_encoding)
 
506
        comma = gecos.find(',')
 
507
        if comma == -1:
 
508
            realname = gecos
 
509
        else:
 
510
            realname = gecos[:comma]
 
511
        if not realname:
 
512
            realname = username
 
513
 
 
514
    except ImportError:
 
515
        import getpass
 
516
        realname = username = getpass.getuser().decode(bzrlib.user_encoding)
 
517
 
 
518
    return realname, (username + '@' + socket.gethostname())
 
519
 
 
520
 
 
521
def extract_email_address(e):
 
522
    """Return just the address part of an email string.
 
523
    
 
524
    That is just the user@domain part, nothing else. 
 
525
    This part is required to contain only ascii characters.
 
526
    If it can't be extracted, raises an error.
 
527
    
 
528
    >>> extract_email_address('Jane Tester <jane@test.com>')
 
529
    "jane@test.com"
 
530
    """
 
531
    m = re.search(r'[\w+.-]+@[\w+.-]+', e)
 
532
    if not m:
 
533
        raise errors.BzrError("%r doesn't seem to contain "
 
534
                              "a reasonable email address" % e)
 
535
    return m.group(0)
 
536
 
 
537
class TreeConfig(object):
 
538
    """Branch configuration data associated with its contents, not location"""
 
539
    def __init__(self, branch):
 
540
        self.branch = branch
 
541
 
 
542
    def _get_config(self):
 
543
        try:
 
544
            obj = ConfigObj(self.branch.control_files.get('branch.conf'
 
545
                        ).readlines())
 
546
            obj.decode('UTF-8')
 
547
        except errors.NoSuchFile:
 
548
            obj = ConfigObj()
 
549
        return obj
 
550
 
 
551
    def get_option(self, name, section=None, default=None):
 
552
        self.branch.lock_read()
 
553
        try:
 
554
            obj = self._get_config()
 
555
            try:
 
556
                if section is not None:
 
557
                    obj[section]
 
558
                result = obj[name]
 
559
            except KeyError:
 
560
                result = default
 
561
        finally:
 
562
            self.branch.unlock()
 
563
        return result
 
564
 
 
565
    def set_option(self, value, name, section=None):
 
566
        """Set a per-branch configuration option"""
 
567
        self.branch.lock_write()
 
568
        try:
 
569
            cfg_obj = self._get_config()
 
570
            if section is None:
 
571
                obj = cfg_obj
 
572
            else:
 
573
                try:
 
574
                    obj = cfg_obj[section]
 
575
                except KeyError:
 
576
                    cfg_obj[section] = {}
 
577
                    obj = cfg_obj[section]
 
578
            obj[name] = value
 
579
            cfg_obj.encode('UTF-8')
 
580
            out_file = StringIO(''.join([l+'\n' for l in cfg_obj.write()]))
 
581
            out_file.seek(0)
 
582
            self.branch.control_files.put('branch.conf', out_file)
 
583
        finally:
 
584
            self.branch.unlock()