~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: mbp at sourcefrog
  • Date: 2005-03-19 01:41:44 UTC
  • Revision ID: mbp@sourcefrog.net-20050319014144-5298a74caebaf378
fix local-time-offset calculation

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
 
from ConfigParser import ConfigParser
55
 
import os
56
 
from fnmatch import fnmatch
57
 
import errno
58
 
import re
59
 
 
60
 
import bzrlib
61
 
import bzrlib.errors as errors
62
 
 
63
 
 
64
 
CHECK_IF_POSSIBLE=0
65
 
CHECK_ALWAYS=1
66
 
CHECK_NEVER=2
67
 
 
68
 
 
69
 
class Config(object):
70
 
    """A configuration policy - what username, editor, gpg needs etc."""
71
 
 
72
 
    def get_editor(self):
73
 
        """Get the users pop up editor."""
74
 
        raise NotImplementedError
75
 
 
76
 
    def _get_signature_checking(self):
77
 
        """Template method to override signature checking policy."""
78
 
 
79
 
    def _get_user_option(self, option_name):
80
 
        """Template method to provide a user option."""
81
 
        return None
82
 
 
83
 
    def get_user_option(self, option_name):
84
 
        """Get a generic option - no special process, no default."""
85
 
        return self._get_user_option(option_name)
86
 
 
87
 
    def gpg_signing_command(self):
88
 
        """What program should be used to sign signatures?"""
89
 
        result = self._gpg_signing_command()
90
 
        if result is None:
91
 
            result = "gpg"
92
 
        return result
93
 
 
94
 
    def _gpg_signing_command(self):
95
 
        """See gpg_signing_command()."""
96
 
        return None
97
 
 
98
 
    def __init__(self):
99
 
        super(Config, self).__init__()
100
 
 
101
 
    def user_email(self):
102
 
        """Return just the email component of a username."""
103
 
        e = self.username()
104
 
        m = re.search(r'[\w+.-]+@[\w+.-]+', e)
105
 
        if not m:
106
 
            raise BzrError("%r doesn't seem to contain "
107
 
                           "a reasonable email address" % e)
108
 
        return m.group(0)
109
 
 
110
 
    def username(self):
111
 
        """Return email-style username.
112
 
    
113
 
        Something similar to 'Martin Pool <mbp@sourcefrog.net>'
114
 
        
115
 
        $BZREMAIL can be set to override this, then
116
 
        the concrete policy type is checked, and finally
117
 
        $EMAIL is examinged.
118
 
        but if none is found, a reasonable default is (hopefully)
119
 
        created.
120
 
    
121
 
        TODO: Check it's reasonably well-formed.
122
 
        """
123
 
        v = os.environ.get('BZREMAIL')
124
 
        if v:
125
 
            return v.decode(bzrlib.user_encoding)
126
 
    
127
 
        v = self._get_user_id()
128
 
        if v:
129
 
            return v
130
 
        
131
 
        v = os.environ.get('EMAIL')
132
 
        if v:
133
 
            return v.decode(bzrlib.user_encoding)
134
 
 
135
 
        name, email = _auto_user_id()
136
 
        if name:
137
 
            return '%s <%s>' % (name, email)
138
 
        else:
139
 
            return email
140
 
 
141
 
    def signature_checking(self):
142
 
        """What is the current policy for signature checking?."""
143
 
        policy = self._get_signature_checking()
144
 
        if policy is not None:
145
 
            return policy
146
 
        return CHECK_IF_POSSIBLE
147
 
 
148
 
    def signature_needed(self):
149
 
        """Is a signature needed when committing ?."""
150
 
        policy = self._get_signature_checking()
151
 
        if policy == CHECK_ALWAYS:
152
 
            return True
153
 
        return False
154
 
 
155
 
 
156
 
class IniBasedConfig(Config):
157
 
    """A configuration policy that draws from ini files."""
158
 
 
159
 
    def _get_parser(self, file=None):
160
 
        if self._parser is not None:
161
 
            return self._parser
162
 
        parser = ConfigParser()
163
 
        if file is not None:
164
 
            parser.readfp(file)
165
 
        else:
166
 
            parser.read([self._get_filename()])
167
 
        self._parser = parser
168
 
        return parser
169
 
 
170
 
    def _get_section(self):
171
 
        """Override this to define the section used by the config."""
172
 
        return "DEFAULT"
173
 
 
174
 
    def _get_signature_checking(self):
175
 
        """See Config._get_signature_checking."""
176
 
        section = self._get_section()
177
 
        if section is None:
178
 
            return None
179
 
        if self._get_parser().has_option(section, 'check_signatures'):
180
 
            return self._string_to_signature_policy(
181
 
                self._get_parser().get(section, 'check_signatures'))
182
 
 
183
 
    def _get_user_id(self):
184
 
        """Get the user id from the 'email' key in the current section."""
185
 
        section = self._get_section()
186
 
        if section is not None:
187
 
            if self._get_parser().has_option(section, 'email'):
188
 
                return self._get_parser().get(section, 'email')
189
 
 
190
 
    def _get_user_option(self, option_name):
191
 
        """See Config._get_user_option."""
192
 
        section = self._get_section()
193
 
        if section is not None:
194
 
            if self._get_parser().has_option(section, option_name):
195
 
                return self._get_parser().get(section, option_name)
196
 
 
197
 
    def _gpg_signing_command(self):
198
 
        """See Config.gpg_signing_command."""
199
 
        section = self._get_section()
200
 
        if section is not None:
201
 
            if self._get_parser().has_option(section, 'gpg_signing_command'):
202
 
                return self._get_parser().get(section, 'gpg_signing_command')
203
 
 
204
 
    def __init__(self, get_filename):
205
 
        super(IniBasedConfig, self).__init__()
206
 
        self._get_filename = get_filename
207
 
        self._parser = None
208
 
 
209
 
    def _string_to_signature_policy(self, signature_string):
210
 
        """Convert a string to a signing policy."""
211
 
        if signature_string.lower() == 'check-available':
212
 
            return CHECK_IF_POSSIBLE
213
 
        if signature_string.lower() == 'ignore':
214
 
            return CHECK_NEVER
215
 
        if signature_string.lower() == 'require':
216
 
            return CHECK_ALWAYS
217
 
        raise errors.BzrError("Invalid signatures policy '%s'"
218
 
                              % signature_string)
219
 
 
220
 
 
221
 
class GlobalConfig(IniBasedConfig):
222
 
    """The configuration that should be used for a specific location."""
223
 
 
224
 
    def get_editor(self):
225
 
        if self._get_parser().has_option(self._get_section(), 'editor'):
226
 
            return self._get_parser().get(self._get_section(), 'editor')
227
 
 
228
 
    def __init__(self):
229
 
        super(GlobalConfig, self).__init__(config_filename)
230
 
 
231
 
 
232
 
class LocationConfig(IniBasedConfig):
233
 
    """A configuration object that gives the policy for a location."""
234
 
 
235
 
    def __init__(self, location):
236
 
        super(LocationConfig, self).__init__(branches_config_filename)
237
 
        self._global_config = None
238
 
        self.location = location
239
 
 
240
 
    def _get_global_config(self):
241
 
        if self._global_config is None:
242
 
            self._global_config = GlobalConfig()
243
 
        return self._global_config
244
 
 
245
 
    def _get_section(self):
246
 
        """Get the section we should look in for config items.
247
 
 
248
 
        Returns None if none exists. 
249
 
        TODO: perhaps return a NullSection that thunks through to the 
250
 
              global config.
251
 
        """
252
 
        sections = self._get_parser().sections()
253
 
        location_names = self.location.split('/')
254
 
        if self.location.endswith('/'):
255
 
            del location_names[-1]
256
 
        matches=[]
257
 
        for section in sections:
258
 
            section_names = section.split('/')
259
 
            if section.endswith('/'):
260
 
                del section_names[-1]
261
 
            names = zip(location_names, section_names)
262
 
            matched = True
263
 
            for name in names:
264
 
                if not fnmatch(name[0], name[1]):
265
 
                    matched = False
266
 
                    break
267
 
            if not matched:
268
 
                continue
269
 
            # so, for the common prefix they matched.
270
 
            # if section is longer, no match.
271
 
            if len(section_names) > len(location_names):
272
 
                continue
273
 
            # if path is longer, and recurse is not true, no match
274
 
            if len(section_names) < len(location_names):
275
 
                if (self._get_parser().has_option(section, 'recurse')
276
 
                    and not self._get_parser().getboolean(section, 'recurse')):
277
 
                    continue
278
 
            matches.append((len(section_names), section))
279
 
        if not len(matches):
280
 
            return None
281
 
        matches.sort(reverse=True)
282
 
        return matches[0][1]
283
 
 
284
 
    def _gpg_signing_command(self):
285
 
        """See Config.gpg_signing_command."""
286
 
        command = super(LocationConfig, self)._gpg_signing_command()
287
 
        if command is not None:
288
 
            return command
289
 
        return self._get_global_config()._gpg_signing_command()
290
 
 
291
 
    def _get_user_id(self):
292
 
        user_id = super(LocationConfig, self)._get_user_id()
293
 
        if user_id is not None:
294
 
            return user_id
295
 
        return self._get_global_config()._get_user_id()
296
 
 
297
 
    def _get_user_option(self, option_name):
298
 
        """See Config._get_user_option."""
299
 
        option_value = super(LocationConfig, 
300
 
                             self)._get_user_option(option_name)
301
 
        if option_value is not None:
302
 
            return option_value
303
 
        return self._get_global_config()._get_user_option(option_name)
304
 
 
305
 
    def _get_signature_checking(self):
306
 
        """See Config._get_signature_checking."""
307
 
        check = super(LocationConfig, self)._get_signature_checking()
308
 
        if check is not None:
309
 
            return check
310
 
        return self._get_global_config()._get_signature_checking()
311
 
 
312
 
 
313
 
class BranchConfig(Config):
314
 
    """A configuration object giving the policy for a branch."""
315
 
 
316
 
    def _get_location_config(self):
317
 
        if self._location_config is None:
318
 
            self._location_config = LocationConfig(self.branch.base)
319
 
        return self._location_config
320
 
 
321
 
    def _get_user_id(self):
322
 
        """Return the full user id for the branch.
323
 
    
324
 
        e.g. "John Hacker <jhacker@foo.org>"
325
 
        This is looked up in the email controlfile for the branch.
326
 
        """
327
 
        try:
328
 
            return (self.branch.controlfile("email", "r") 
329
 
                    .read()
330
 
                    .decode(bzrlib.user_encoding)
331
 
                    .rstrip("\r\n"))
332
 
        except errors.NoSuchFile, e:
333
 
            pass
334
 
        
335
 
        return self._get_location_config()._get_user_id()
336
 
 
337
 
    def _get_signature_checking(self):
338
 
        """See Config._get_signature_checking."""
339
 
        return self._get_location_config()._get_signature_checking()
340
 
 
341
 
    def _get_user_option(self, option_name):
342
 
        """See Config._get_user_option."""
343
 
        return self._get_location_config()._get_user_option(option_name)
344
 
 
345
 
    def _gpg_signing_command(self):
346
 
        """See Config.gpg_signing_command."""
347
 
        return self._get_location_config()._gpg_signing_command()
348
 
        
349
 
    def __init__(self, branch):
350
 
        super(BranchConfig, self).__init__()
351
 
        self._location_config = None
352
 
        self.branch = branch
353
 
 
354
 
 
355
 
def config_dir():
356
 
    """Return per-user configuration directory.
357
 
 
358
 
    By default this is ~/.bazaar/
359
 
    
360
 
    TODO: Global option --config-dir to override this.
361
 
    """
362
 
    return os.path.join(os.path.expanduser("~"), ".bazaar")
363
 
 
364
 
 
365
 
def config_filename():
366
 
    """Return per-user configuration ini file filename."""
367
 
    return os.path.join(config_dir(), 'bazaar.conf')
368
 
 
369
 
 
370
 
def branches_config_filename():
371
 
    """Return per-user configuration ini file filename."""
372
 
    return os.path.join(config_dir(), 'branches.conf')
373
 
 
374
 
 
375
 
def _auto_user_id():
376
 
    """Calculate automatic user identification.
377
 
 
378
 
    Returns (realname, email).
379
 
 
380
 
    Only used when none is set in the environment or the id file.
381
 
 
382
 
    This previously used the FQDN as the default domain, but that can
383
 
    be very slow on machines where DNS is broken.  So now we simply
384
 
    use the hostname.
385
 
    """
386
 
    import socket
387
 
 
388
 
    # XXX: Any good way to get real user name on win32?
389
 
 
390
 
    try:
391
 
        import pwd
392
 
        uid = os.getuid()
393
 
        w = pwd.getpwuid(uid)
394
 
        gecos = w.pw_gecos.decode(bzrlib.user_encoding)
395
 
        username = w.pw_name.decode(bzrlib.user_encoding)
396
 
        comma = gecos.find(',')
397
 
        if comma == -1:
398
 
            realname = gecos
399
 
        else:
400
 
            realname = gecos[:comma]
401
 
        if not realname:
402
 
            realname = username
403
 
 
404
 
    except ImportError:
405
 
        import getpass
406
 
        realname = username = getpass.getuser().decode(bzrlib.user_encoding)
407
 
 
408
 
    return realname, (username + '@' + socket.gethostname())
409
 
 
410
 
 
411
 
def extract_email_address(e):
412
 
    """Return just the address part of an email string.
413
 
    
414
 
    That is just the user@domain part, nothing else. 
415
 
    This part is required to contain only ascii characters.
416
 
    If it can't be extracted, raises an error.
417
 
    
418
 
    >>> extract_email_address('Jane Tester <jane@test.com>')
419
 
    "jane@test.com"
420
 
    """
421
 
    m = re.search(r'[\w+.-]+@[\w+.-]+', e)
422
 
    if not m:
423
 
        raise BzrError("%r doesn't seem to contain "
424
 
                       "a reasonable email address" % e)
425
 
    return m.group(0)