~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-03-16 14:01:20 UTC
  • mfrom: (3280.2.5 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20080316140120-i3yq8yr1l66m11h7
Start 1.4 development

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2007 Canonical Ltd
2
2
#   Authors: Robert Collins <robert.collins@canonical.com>
3
3
#            and others
4
4
#
14
14
#
15
15
# You should have received a copy of the GNU General Public License
16
16
# along with this program; if not, write to the Free Software
17
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
18
 
19
19
"""Configuration that affects the behaviour of Bazaar.
20
20
 
37
37
[/home/robertc/source]
38
38
recurse=False|True(default)
39
39
email= as above
40
 
check_signatures= as above
 
40
check_signatures= as above 
41
41
create_signatures= as above.
42
42
 
43
43
explanation of options
45
45
editor - this option sets the pop up editor to use during commits.
46
46
email - this option sets the user id bzr will use when committing.
47
47
check_signatures - this option controls whether bzr will require good gpg
48
 
                   signatures, ignore them, or check them if they are
 
48
                   signatures, ignore them, or check them if they are 
49
49
                   present.
50
 
create_signatures - this option controls whether bzr will always create
 
50
create_signatures - this option controls whether bzr will always create 
51
51
                    gpg signatures, never create them, or create them if the
52
52
                    branch is configured to require them.
53
53
log_format - this option sets the default log format.  Possible values are
65
65
import os
66
66
import sys
67
67
 
68
 
from bzrlib.decorators import needs_write_lock
69
68
from bzrlib.lazy_import import lazy_import
70
69
lazy_import(globals(), """
71
70
import errno
75
74
 
76
75
import bzrlib
77
76
from bzrlib import (
78
 
    atomicfile,
79
77
    debug,
80
78
    errors,
81
 
    lockdir,
82
79
    mail_client,
83
80
    osutils,
84
 
    registry,
85
81
    symbol_versioning,
86
82
    trace,
87
 
    transport,
88
83
    ui,
89
84
    urlutils,
90
85
    win32utils,
126
121
STORE_BRANCH = 3
127
122
STORE_GLOBAL = 4
128
123
 
129
 
_ConfigObj = None
130
 
def ConfigObj(*args, **kwargs):
131
 
    global _ConfigObj
132
 
    if _ConfigObj is None:
133
 
        class ConfigObj(configobj.ConfigObj):
134
 
 
135
 
            def get_bool(self, section, key):
136
 
                return self[section].as_bool(key)
137
 
 
138
 
            def get_value(self, section, name):
139
 
                # Try [] for the old DEFAULT section.
140
 
                if section == "DEFAULT":
141
 
                    try:
142
 
                        return self[name]
143
 
                    except KeyError:
144
 
                        pass
145
 
                return self[section][name]
146
 
        _ConfigObj = ConfigObj
147
 
    return _ConfigObj(*args, **kwargs)
 
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]
148
138
 
149
139
 
150
140
class Config(object):
151
141
    """A configuration policy - what username, editor, gpg needs etc."""
152
142
 
153
 
    def __init__(self):
154
 
        super(Config, self).__init__()
155
 
 
156
143
    def get_editor(self):
157
144
        """Get the users pop up editor."""
158
145
        raise NotImplementedError
159
146
 
160
 
    def get_change_editor(self, old_tree, new_tree):
161
 
        from bzrlib import diff
162
 
        cmd = self._get_change_editor()
163
 
        if cmd is None:
164
 
            return None
165
 
        return diff.DiffFromTool.from_string(cmd, old_tree, new_tree,
166
 
                                             sys.stdout)
167
 
 
168
 
 
169
147
    def get_mail_client(self):
170
148
        """Get a mail client to use"""
171
149
        selected_client = self.get_user_option('mail_client')
172
 
        _registry = mail_client.mail_client_registry
173
150
        try:
174
 
            mail_client_class = _registry.get(selected_client)
 
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]
175
164
        except KeyError:
176
165
            raise errors.UnknownMailClient(selected_client)
177
166
        return mail_client_class(self)
190
179
        """Get a generic option - no special process, no default."""
191
180
        return self._get_user_option(option_name)
192
181
 
193
 
    def get_user_option_as_bool(self, option_name):
194
 
        """Get a generic option as a boolean - no special process, no default.
195
 
 
196
 
        :return None if the option doesn't exist or its value can't be
197
 
            interpreted as a boolean. Returns True or False otherwise.
198
 
        """
199
 
        s = self._get_user_option(option_name)
200
 
        if s is None:
201
 
            # The option doesn't exist
202
 
            return None
203
 
        val = ui.bool_from_string(s)
204
 
        if val is None:
205
 
            # The value can't be interpreted as a boolean
206
 
            trace.warning('Value "%s" is not a boolean for "%s"',
207
 
                          s, option_name)
208
 
        return val
209
 
 
210
 
    def get_user_option_as_list(self, option_name):
211
 
        """Get a generic option as a list - no special process, no default.
212
 
 
213
 
        :return None if the option doesn't exist. Returns the value as a list
214
 
            otherwise.
215
 
        """
216
 
        l = self._get_user_option(option_name)
217
 
        if isinstance(l, (str, unicode)):
218
 
            # A single value, most probably the user forgot the final ','
219
 
            l = [l]
220
 
        return l
221
 
 
222
182
    def gpg_signing_command(self):
223
183
        """What program should be used to sign signatures?"""
224
184
        result = self._gpg_signing_command()
241
201
        """See log_format()."""
242
202
        return None
243
203
 
 
204
    def __init__(self):
 
205
        super(Config, self).__init__()
 
206
 
244
207
    def post_commit(self):
245
208
        """An ordered list of python functions to call.
246
209
 
258
221
 
259
222
    def username(self):
260
223
        """Return email-style username.
261
 
 
 
224
    
262
225
        Something similar to 'Martin Pool <mbp@sourcefrog.net>'
263
 
 
264
 
        $BZR_EMAIL can be set to override this, then
 
226
        
 
227
        $BZR_EMAIL can be set to override this (as well as the
 
228
        deprecated $BZREMAIL), then
265
229
        the concrete policy type is checked, and finally
266
230
        $EMAIL is examined.
267
 
        If no username can be found, errors.NoWhoami exception is raised.
268
 
 
 
231
        If none is found, a reasonable default is (hopefully)
 
232
        created.
 
233
    
269
234
        TODO: Check it's reasonably well-formed.
270
235
        """
271
236
        v = os.environ.get('BZR_EMAIL')
272
237
        if v:
273
 
            return v.decode(osutils.get_user_encoding())
 
238
            return v.decode(bzrlib.user_encoding)
274
239
 
275
240
        v = self._get_user_id()
276
241
        if v:
278
243
 
279
244
        v = os.environ.get('EMAIL')
280
245
        if v:
281
 
            return v.decode(osutils.get_user_encoding())
282
 
 
283
 
        raise errors.NoWhoami()
284
 
 
285
 
    def ensure_username(self):
286
 
        """Raise errors.NoWhoami if username is not set.
287
 
 
288
 
        This method relies on the username() function raising the error.
289
 
        """
290
 
        self.username()
 
246
            return v.decode(bzrlib.user_encoding)
 
247
 
 
248
        name, email = _auto_user_id()
 
249
        if name:
 
250
            return '%s <%s>' % (name, email)
 
251
        else:
 
252
            return email
291
253
 
292
254
    def signature_checking(self):
293
255
        """What is the current policy for signature checking?."""
338
300
                path = 'bzr'
339
301
            return path
340
302
 
341
 
    def suppress_warning(self, warning):
342
 
        """Should the warning be suppressed or emitted.
343
 
 
344
 
        :param warning: The name of the warning being tested.
345
 
 
346
 
        :returns: True if the warning should be suppressed, False otherwise.
347
 
        """
348
 
        warnings = self.get_user_option_as_list('suppress_warnings')
349
 
        if warnings is None or warning not in warnings:
350
 
            return False
351
 
        else:
352
 
            return True
353
 
 
354
303
 
355
304
class IniBasedConfig(Config):
356
305
    """A configuration policy that draws from ini files."""
357
306
 
358
 
    def __init__(self, get_filename=symbol_versioning.DEPRECATED_PARAMETER,
359
 
                 file_name=None):
360
 
        """Base class for configuration files using an ini-like syntax.
361
 
 
362
 
        :param file_name: The configuration file path.
363
 
        """
364
 
        super(IniBasedConfig, self).__init__()
365
 
        self.file_name = file_name
366
 
        if symbol_versioning.deprecated_passed(get_filename):
367
 
            symbol_versioning.warn(
368
 
                'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
369
 
                ' Use file_name instead.',
370
 
                DeprecationWarning,
371
 
                stacklevel=2)
372
 
            if get_filename is not None:
373
 
                self.file_name = get_filename()
374
 
        else:
375
 
            self.file_name = file_name
376
 
        self._content = None
377
 
        self._parser = None
378
 
 
379
 
    @classmethod
380
 
    def from_string(cls, str_or_unicode, file_name=None, save=False):
381
 
        """Create a config object from a string.
382
 
 
383
 
        :param str_or_unicode: A string representing the file content. This will
384
 
            be utf-8 encoded.
385
 
 
386
 
        :param file_name: The configuration file path.
387
 
 
388
 
        :param _save: Whether the file should be saved upon creation.
389
 
        """
390
 
        conf = cls(file_name=file_name)
391
 
        conf._create_from_string(str_or_unicode, save)
392
 
        return conf
393
 
 
394
 
    def _create_from_string(self, str_or_unicode, save):
395
 
        self._content = StringIO(str_or_unicode.encode('utf-8'))
396
 
        # Some tests use in-memory configs, some other always need the config
397
 
        # file to exist on disk.
398
 
        if save:
399
 
            self._write_config_file()
400
 
 
401
 
    def _get_parser(self, file=symbol_versioning.DEPRECATED_PARAMETER):
 
307
    def _get_parser(self, file=None):
402
308
        if self._parser is not None:
403
309
            return self._parser
404
 
        if symbol_versioning.deprecated_passed(file):
405
 
            symbol_versioning.warn(
406
 
                'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
407
 
                ' Use IniBasedConfig(_content=xxx) instead.',
408
 
                DeprecationWarning,
409
 
                stacklevel=2)
410
 
        if self._content is not None:
411
 
            co_input = self._content
412
 
        elif self.file_name is None:
413
 
            raise AssertionError('We have no content to create the config')
 
310
        if file is None:
 
311
            input = self._get_filename()
414
312
        else:
415
 
            co_input = self.file_name
 
313
            input = file
416
314
        try:
417
 
            self._parser = ConfigObj(co_input, encoding='utf-8')
 
315
            self._parser = ConfigObj(input, encoding='utf-8')
418
316
        except configobj.ConfigObjError, e:
419
317
            raise errors.ParseConfigError(e.errors, e.config.filename)
420
 
        # Make sure self.reload() will use the right file name
421
 
        self._parser.filename = self.file_name
422
318
        return self._parser
423
319
 
424
 
    def reload(self):
425
 
        """Reload the config file from disk."""
426
 
        if self.file_name is None:
427
 
            raise AssertionError('We need a file name to reload the config')
428
 
        if self._parser is not None:
429
 
            self._parser.reload()
430
 
 
431
320
    def _get_matching_sections(self):
432
321
        """Return an ordered list of (section_name, extra_path) pairs.
433
322
 
448
337
        """Return the policy for the given (section, option_name) pair."""
449
338
        return POLICY_NONE
450
339
 
451
 
    def _get_change_editor(self):
452
 
        return self.get_user_option('change_editor')
453
 
 
454
340
    def _get_signature_checking(self):
455
341
        """See Config._get_signature_checking."""
456
342
        policy = self._get_user_option('check_signatures')
500
386
        """See Config.log_format."""
501
387
        return self._get_user_option('log_format')
502
388
 
 
389
    def __init__(self, get_filename):
 
390
        super(IniBasedConfig, self).__init__()
 
391
        self._get_filename = get_filename
 
392
        self._parser = None
 
393
        
503
394
    def _post_commit(self):
504
395
        """See Config.post_commit."""
505
396
        return self._get_user_option('post_commit')
528
419
 
529
420
    def _get_alias(self, value):
530
421
        try:
531
 
            return self._get_parser().get_value("ALIASES",
 
422
            return self._get_parser().get_value("ALIASES", 
532
423
                                                value)
533
424
        except KeyError:
534
425
            pass
536
427
    def _get_nickname(self):
537
428
        return self.get_user_option('nickname')
538
429
 
539
 
    def _write_config_file(self):
540
 
        if self.file_name is None:
541
 
            raise AssertionError('We cannot save, self.file_name is None')
542
 
        conf_dir = os.path.dirname(self.file_name)
543
 
        ensure_config_dir_exists(conf_dir)
544
 
        atomic_file = atomicfile.AtomicFile(self.file_name)
545
 
        self._get_parser().write(atomic_file)
546
 
        atomic_file.commit()
547
 
        atomic_file.close()
548
 
        osutils.copy_ownership_from_path(self.file_name)
549
 
 
550
 
 
551
 
class LockableConfig(IniBasedConfig):
552
 
    """A configuration needing explicit locking for access.
553
 
 
554
 
    If several processes try to write the config file, the accesses need to be
555
 
    serialized.
556
 
 
557
 
    Daughter classes should decorate all methods that update a config with the
558
 
    ``@needs_write_lock`` decorator (they call, directly or indirectly, the
559
 
    ``_write_config_file()`` method. These methods (typically ``set_option()``
560
 
    and variants must reload the config file from disk before calling
561
 
    ``_write_config_file()``), this can be achieved by calling the
562
 
    ``self.reload()`` method. Note that the lock scope should cover both the
563
 
    reading and the writing of the config file which is why the decorator can't
564
 
    be applied to ``_write_config_file()`` only.
565
 
 
566
 
    This should be enough to implement the following logic:
567
 
    - lock for exclusive write access,
568
 
    - reload the config file from disk,
569
 
    - set the new value
570
 
    - unlock
571
 
 
572
 
    This logic guarantees that a writer can update a value without erasing an
573
 
    update made by another writer.
574
 
    """
575
 
 
576
 
    lock_name = 'lock'
577
 
 
578
 
    def __init__(self, file_name):
579
 
        super(LockableConfig, self).__init__(file_name=file_name)
580
 
        self.dir = osutils.dirname(osutils.safe_unicode(self.file_name))
581
 
        self.transport = transport.get_transport(self.dir)
582
 
        self._lock = lockdir.LockDir(self.transport, 'lock')
583
 
 
584
 
    def _create_from_string(self, unicode_bytes, save):
585
 
        super(LockableConfig, self)._create_from_string(unicode_bytes, False)
586
 
        if save:
587
 
            # We need to handle the saving here (as opposed to IniBasedConfig)
588
 
            # to be able to lock
589
 
            self.lock_write()
590
 
            self._write_config_file()
591
 
            self.unlock()
592
 
 
593
 
    def lock_write(self, token=None):
594
 
        """Takes a write lock in the directory containing the config file.
595
 
 
596
 
        If the directory doesn't exist it is created.
597
 
        """
598
 
        ensure_config_dir_exists(self.dir)
599
 
        return self._lock.lock_write(token)
600
 
 
601
 
    def unlock(self):
602
 
        self._lock.unlock()
603
 
 
604
 
    def break_lock(self):
605
 
        self._lock.break_lock()
606
 
 
607
 
    def _write_config_file(self):
608
 
        if self._lock is None or not self._lock.is_held:
609
 
            # NB: if the following exception is raised it probably means a
610
 
            # missing @needs_write_lock decorator on one of the callers.
611
 
            raise errors.ObjectNotLocked(self)
612
 
        super(LockableConfig, self)._write_config_file()
613
 
 
614
 
 
615
 
class GlobalConfig(LockableConfig):
 
430
 
 
431
class GlobalConfig(IniBasedConfig):
616
432
    """The configuration that should be used for a specific location."""
617
433
 
618
 
    def __init__(self):
619
 
        super(GlobalConfig, self).__init__(file_name=config_filename())
620
 
 
621
 
    @classmethod
622
 
    def from_string(cls, str_or_unicode, save=False):
623
 
        """Create a config object from a string.
624
 
 
625
 
        :param str_or_unicode: A string representing the file content. This
626
 
            will be utf-8 encoded.
627
 
 
628
 
        :param save: Whether the file should be saved upon creation.
629
 
        """
630
 
        conf = cls()
631
 
        conf._create_from_string(str_or_unicode, save)
632
 
        return conf
633
 
 
634
434
    def get_editor(self):
635
435
        return self._get_user_option('editor')
636
436
 
637
 
    @needs_write_lock
 
437
    def __init__(self):
 
438
        super(GlobalConfig, self).__init__(config_filename)
 
439
 
638
440
    def set_user_option(self, option, value):
639
441
        """Save option and its value in the configuration."""
640
 
        self._set_option(option, value, 'DEFAULT')
641
 
 
642
 
    def get_aliases(self):
643
 
        """Return the aliases section."""
644
 
        if 'ALIASES' in self._get_parser():
645
 
            return self._get_parser()['ALIASES']
646
 
        else:
647
 
            return {}
648
 
 
649
 
    @needs_write_lock
650
 
    def set_alias(self, alias_name, alias_command):
651
 
        """Save the alias in the configuration."""
652
 
        self._set_option(alias_name, alias_command, 'ALIASES')
653
 
 
654
 
    @needs_write_lock
655
 
    def unset_alias(self, alias_name):
656
 
        """Unset an existing alias."""
657
 
        self.reload()
658
 
        aliases = self._get_parser().get('ALIASES')
659
 
        if not aliases or alias_name not in aliases:
660
 
            raise errors.NoSuchAlias(alias_name)
661
 
        del aliases[alias_name]
662
 
        self._write_config_file()
663
 
 
664
 
    def _set_option(self, option, value, section):
665
 
        self.reload()
666
 
        self._get_parser().setdefault(section, {})[option] = value
667
 
        self._write_config_file()
668
 
 
669
 
 
670
 
class LocationConfig(LockableConfig):
 
442
        # FIXME: RBC 20051029 This should refresh the parser and also take a
 
443
        # file lock on bazaar.conf.
 
444
        conf_dir = os.path.dirname(self._get_filename())
 
445
        ensure_config_dir_exists(conf_dir)
 
446
        if 'DEFAULT' not in self._get_parser():
 
447
            self._get_parser()['DEFAULT'] = {}
 
448
        self._get_parser()['DEFAULT'][option] = value
 
449
        f = open(self._get_filename(), 'wb')
 
450
        self._get_parser().write(f)
 
451
        f.close()
 
452
 
 
453
 
 
454
class LocationConfig(IniBasedConfig):
671
455
    """A configuration object that gives the policy for a location."""
672
456
 
673
457
    def __init__(self, location):
674
 
        super(LocationConfig, self).__init__(
675
 
            file_name=locations_config_filename())
 
458
        name_generator = locations_config_filename
 
459
        if (not os.path.exists(name_generator()) and
 
460
                os.path.exists(branches_config_filename())):
 
461
            if sys.platform == 'win32':
 
462
                trace.warning('Please rename %s to %s'
 
463
                              % (branches_config_filename(),
 
464
                                 locations_config_filename()))
 
465
            else:
 
466
                trace.warning('Please rename ~/.bazaar/branches.conf'
 
467
                              ' to ~/.bazaar/locations.conf')
 
468
            name_generator = branches_config_filename
 
469
        super(LocationConfig, self).__init__(name_generator)
676
470
        # local file locations are looked up by local path, rather than
677
471
        # by file url. This is because the config file is a user
678
472
        # file, and we would rather not expose the user to file urls.
680
474
            location = urlutils.local_path_from_url(location)
681
475
        self.location = location
682
476
 
683
 
    @classmethod
684
 
    def from_string(cls, str_or_unicode, location, save=False):
685
 
        """Create a config object from a string.
686
 
 
687
 
        :param str_or_unicode: A string representing the file content. This will
688
 
            be utf-8 encoded.
689
 
 
690
 
        :param location: The location url to filter the configuration.
691
 
 
692
 
        :param save: Whether the file should be saved upon creation.
693
 
        """
694
 
        conf = cls(location)
695
 
        conf._create_from_string(str_or_unicode, save)
696
 
        return conf
697
 
 
698
477
    def _get_matching_sections(self):
699
478
        """Return an ordered list of section names matching this location."""
700
479
        sections = self._get_parser()
788
567
            if policy_key in self._get_parser()[section]:
789
568
                del self._get_parser()[section][policy_key]
790
569
 
791
 
    @needs_write_lock
792
570
    def set_user_option(self, option, value, store=STORE_LOCATION):
793
571
        """Save option and its value in the configuration."""
794
 
        if store not in [STORE_LOCATION,
 
572
        assert store in [STORE_LOCATION,
795
573
                         STORE_LOCATION_NORECURSE,
796
 
                         STORE_LOCATION_APPENDPATH]:
797
 
            raise ValueError('bad storage policy %r for %r' %
798
 
                (store, option))
799
 
        self.reload()
 
574
                         STORE_LOCATION_APPENDPATH], 'bad storage policy'
 
575
        # FIXME: RBC 20051029 This should refresh the parser and also take a
 
576
        # file lock on locations.conf.
 
577
        conf_dir = os.path.dirname(self._get_filename())
 
578
        ensure_config_dir_exists(conf_dir)
800
579
        location = self.location
801
580
        if location.endswith('/'):
802
581
            location = location[:-1]
803
 
        parser = self._get_parser()
804
 
        if not location in parser and not location + '/' in parser:
805
 
            parser[location] = {}
806
 
        elif location + '/' in parser:
 
582
        if (not location in self._get_parser() and
 
583
            not location + '/' in self._get_parser()):
 
584
            self._get_parser()[location]={}
 
585
        elif location + '/' in self._get_parser():
807
586
            location = location + '/'
808
 
        parser[location][option]=value
 
587
        self._get_parser()[location][option]=value
809
588
        # the allowed values of store match the config policies
810
589
        self._set_option_policy(location, option, store)
811
 
        self._write_config_file()
 
590
        self._get_parser().write(file(self._get_filename(), 'wb'))
812
591
 
813
592
 
814
593
class BranchConfig(Config):
815
594
    """A configuration object giving the policy for a branch."""
816
595
 
817
 
    def __init__(self, branch):
818
 
        super(BranchConfig, self).__init__()
819
 
        self._location_config = None
820
 
        self._branch_data_config = None
821
 
        self._global_config = None
822
 
        self.branch = branch
823
 
        self.option_sources = (self._get_location_config,
824
 
                               self._get_branch_data_config,
825
 
                               self._get_global_config)
826
 
 
827
596
    def _get_branch_data_config(self):
828
597
        if self._branch_data_config is None:
829
598
            self._branch_data_config = TreeConfig(self.branch)
853
622
 
854
623
    def _get_safe_value(self, option_name):
855
624
        """This variant of get_best_value never returns untrusted values.
856
 
 
 
625
        
857
626
        It does not return values from the branch data, because the branch may
858
627
        not be controlled by the user.
859
628
 
868
637
 
869
638
    def _get_user_id(self):
870
639
        """Return the full user id for the branch.
871
 
 
872
 
        e.g. "John Hacker <jhacker@example.com>"
 
640
    
 
641
        e.g. "John Hacker <jhacker@foo.org>"
873
642
        This is looked up in the email controlfile for the branch.
874
643
        """
875
644
        try:
876
 
            return (self.branch._transport.get_bytes("email")
877
 
                    .decode(osutils.get_user_encoding())
 
645
            return (self.branch.control_files.get_utf8("email") 
 
646
                    .read()
 
647
                    .decode(bzrlib.user_encoding)
878
648
                    .rstrip("\r\n"))
879
649
        except errors.NoSuchFile, e:
880
650
            pass
881
 
 
 
651
        
882
652
        return self._get_best_value('_get_user_id')
883
653
 
884
 
    def _get_change_editor(self):
885
 
        return self._get_best_value('_get_change_editor')
886
 
 
887
654
    def _get_signature_checking(self):
888
655
        """See Config._get_signature_checking."""
889
656
        return self._get_best_value('_get_signature_checking')
923
690
                        trace.warning('Value "%s" is masked by "%s" from'
924
691
                                      ' branch.conf', value, mask_value)
925
692
 
 
693
 
926
694
    def _gpg_signing_command(self):
927
695
        """See Config.gpg_signing_command."""
928
696
        return self._get_safe_value('_gpg_signing_command')
 
697
        
 
698
    def __init__(self, branch):
 
699
        super(BranchConfig, self).__init__()
 
700
        self._location_config = None
 
701
        self._branch_data_config = None
 
702
        self._global_config = None
 
703
        self.branch = branch
 
704
        self.option_sources = (self._get_location_config, 
 
705
                               self._get_branch_data_config,
 
706
                               self._get_global_config)
929
707
 
930
708
    def _post_commit(self):
931
709
        """See Config.post_commit."""
965
743
            os.mkdir(parent_dir)
966
744
        trace.mutter('creating config directory: %r', path)
967
745
        os.mkdir(path)
968
 
        osutils.copy_ownership_from_path(path)
969
746
 
970
747
 
971
748
def config_dir():
972
749
    """Return per-user configuration directory.
973
750
 
974
751
    By default this is ~/.bazaar/
975
 
 
 
752
    
976
753
    TODO: Global option --config-dir to override this.
977
754
    """
978
755
    base = os.environ.get('BZR_HOME', None)
986
763
                                  ' or HOME set')
987
764
        return osutils.pathjoin(base, 'bazaar', '2.0')
988
765
    else:
 
766
        # cygwin, linux, and darwin all have a $HOME directory
989
767
        if base is None:
990
768
            base = os.path.expanduser("~")
991
769
        return osutils.pathjoin(base, ".bazaar")
996
774
    return osutils.pathjoin(config_dir(), 'bazaar.conf')
997
775
 
998
776
 
 
777
def branches_config_filename():
 
778
    """Return per-user configuration ini file filename."""
 
779
    return osutils.pathjoin(config_dir(), 'branches.conf')
 
780
 
 
781
 
999
782
def locations_config_filename():
1000
783
    """Return per-user configuration ini file filename."""
1001
784
    return osutils.pathjoin(config_dir(), 'locations.conf')
1011
794
    return osutils.pathjoin(config_dir(), 'ignore')
1012
795
 
1013
796
 
1014
 
def crash_dir():
1015
 
    """Return the directory name to store crash files.
1016
 
 
1017
 
    This doesn't implicitly create it.
1018
 
 
1019
 
    On Windows it's in the config directory; elsewhere it's /var/crash
1020
 
    which may be monitored by apport.  It can be overridden by
1021
 
    $APPORT_CRASH_DIR.
 
797
def _auto_user_id():
 
798
    """Calculate automatic user identification.
 
799
 
 
800
    Returns (realname, email).
 
801
 
 
802
    Only used when none is set in the environment or the id file.
 
803
 
 
804
    This previously used the FQDN as the default domain, but that can
 
805
    be very slow on machines where DNS is broken.  So now we simply
 
806
    use the hostname.
1022
807
    """
 
808
    import socket
 
809
 
1023
810
    if sys.platform == 'win32':
1024
 
        return osutils.pathjoin(config_dir(), 'Crash')
1025
 
    else:
1026
 
        # XXX: hardcoded in apport_python_hook.py; therefore here too -- mbp
1027
 
        # 2010-01-31
1028
 
        return os.environ.get('APPORT_CRASH_DIR', '/var/crash')
1029
 
 
1030
 
 
1031
 
def xdg_cache_dir():
1032
 
    # See http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
1033
 
    # Possibly this should be different on Windows?
1034
 
    e = os.environ.get('XDG_CACHE_DIR', None)
1035
 
    if e:
1036
 
        return e
1037
 
    else:
1038
 
        return os.path.expanduser('~/.cache')
 
811
        name = win32utils.get_user_name_unicode()
 
812
        if name is None:
 
813
            raise errors.BzrError("Cannot autodetect user name.\n"
 
814
                                  "Please, set your name with command like:\n"
 
815
                                  'bzr whoami "Your Name <name@domain.com>"')
 
816
        host = win32utils.get_host_name_unicode()
 
817
        if host is None:
 
818
            host = socket.gethostname()
 
819
        return name, (name + '@' + host)
 
820
 
 
821
    try:
 
822
        import pwd
 
823
        uid = os.getuid()
 
824
        w = pwd.getpwuid(uid)
 
825
 
 
826
        # we try utf-8 first, because on many variants (like Linux),
 
827
        # /etc/passwd "should" be in utf-8, and because it's unlikely to give
 
828
        # false positives.  (many users will have their user encoding set to
 
829
        # latin-1, which cannot raise UnicodeError.)
 
830
        try:
 
831
            gecos = w.pw_gecos.decode('utf-8')
 
832
            encoding = 'utf-8'
 
833
        except UnicodeError:
 
834
            try:
 
835
                gecos = w.pw_gecos.decode(bzrlib.user_encoding)
 
836
                encoding = bzrlib.user_encoding
 
837
            except UnicodeError:
 
838
                raise errors.BzrCommandError('Unable to determine your name.  '
 
839
                   'Use "bzr whoami" to set it.')
 
840
        try:
 
841
            username = w.pw_name.decode(encoding)
 
842
        except UnicodeError:
 
843
            raise errors.BzrCommandError('Unable to determine your name.  '
 
844
                'Use "bzr whoami" to set it.')
 
845
 
 
846
        comma = gecos.find(',')
 
847
        if comma == -1:
 
848
            realname = gecos
 
849
        else:
 
850
            realname = gecos[:comma]
 
851
        if not realname:
 
852
            realname = username
 
853
 
 
854
    except ImportError:
 
855
        import getpass
 
856
        try:
 
857
            realname = username = getpass.getuser().decode(bzrlib.user_encoding)
 
858
        except UnicodeDecodeError:
 
859
            raise errors.BzrError("Can't decode username as %s." % \
 
860
                    bzrlib.user_encoding)
 
861
 
 
862
    return realname, (username + '@' + socket.gethostname())
1039
863
 
1040
864
 
1041
865
def parse_username(username):
1050
874
def extract_email_address(e):
1051
875
    """Return just the address part of an email string.
1052
876
 
1053
 
    That is just the user@domain part, nothing else.
 
877
    That is just the user@domain part, nothing else. 
1054
878
    This part is required to contain only ascii characters.
1055
879
    If it can't be extracted, raises an error.
1056
880
 
1066
890
class TreeConfig(IniBasedConfig):
1067
891
    """Branch configuration data associated with its contents, not location"""
1068
892
 
1069
 
    # XXX: Really needs a better name, as this is not part of the tree! -- mbp 20080507
1070
 
 
1071
893
    def __init__(self, branch):
1072
 
        self._config = branch._get_config()
1073
894
        self.branch = branch
1074
895
 
1075
896
    def _get_parser(self, file=None):
1076
897
        if file is not None:
1077
898
            return IniBasedConfig._get_parser(file)
1078
 
        return self._config._get_configobj()
 
899
        return self._get_config()
 
900
 
 
901
    def _get_config(self):
 
902
        try:
 
903
            obj = ConfigObj(self.branch.control_files.get('branch.conf'),
 
904
                            encoding='utf-8')
 
905
        except errors.NoSuchFile:
 
906
            obj = ConfigObj(encoding='utf=8')
 
907
        return obj
1079
908
 
1080
909
    def get_option(self, name, section=None, default=None):
1081
910
        self.branch.lock_read()
1082
911
        try:
1083
 
            return self._config.get_option(name, section, default)
 
912
            obj = self._get_config()
 
913
            try:
 
914
                if section is not None:
 
915
                    obj = obj[section]
 
916
                result = obj[name]
 
917
            except KeyError:
 
918
                result = default
1084
919
        finally:
1085
920
            self.branch.unlock()
 
921
        return result
1086
922
 
1087
923
    def set_option(self, value, name, section=None):
1088
924
        """Set a per-branch configuration option"""
1089
925
        self.branch.lock_write()
1090
926
        try:
1091
 
            self._config.set_option(value, name, section)
 
927
            cfg_obj = self._get_config()
 
928
            if section is None:
 
929
                obj = cfg_obj
 
930
            else:
 
931
                try:
 
932
                    obj = cfg_obj[section]
 
933
                except KeyError:
 
934
                    cfg_obj[section] = {}
 
935
                    obj = cfg_obj[section]
 
936
            obj[name] = value
 
937
            out_file = StringIO()
 
938
            cfg_obj.write(out_file)
 
939
            out_file.seek(0)
 
940
            self.branch.control_files.put('branch.conf', out_file)
1092
941
        finally:
1093
942
            self.branch.unlock()
1094
943
 
1129
978
        """Save the config file, only tests should use it for now."""
1130
979
        conf_dir = os.path.dirname(self._filename)
1131
980
        ensure_config_dir_exists(conf_dir)
1132
 
        f = file(self._filename, 'wb')
1133
 
        try:
1134
 
            self._get_config().write(f)
1135
 
        finally:
1136
 
            f.close()
 
981
        self._get_config().write(file(self._filename, 'wb'))
1137
982
 
1138
983
    def _set_option(self, section_name, option_name, value):
1139
984
        """Set an authentication configuration option"""
1145
990
        section[option_name] = value
1146
991
        self._save()
1147
992
 
1148
 
    def get_credentials(self, scheme, host, port=None, user=None, path=None, 
1149
 
                        realm=None):
 
993
    def get_credentials(self, scheme, host, port=None, user=None, path=None):
1150
994
        """Returns the matching credentials from authentication.conf file.
1151
995
 
1152
996
        :param scheme: protocol
1158
1002
        :param user: login (optional)
1159
1003
 
1160
1004
        :param path: the absolute path on the server (optional)
1161
 
        
1162
 
        :param realm: the http authentication realm (optional)
1163
1005
 
1164
1006
        :return: A dict containing the matching credentials or None.
1165
1007
           This includes:
1166
1008
           - name: the section name of the credentials in the
1167
1009
             authentication.conf file,
1168
 
           - user: can't be different from the provided user if any,
1169
 
           - scheme: the server protocol,
1170
 
           - host: the server address,
1171
 
           - port: the server port (can be None),
1172
 
           - path: the absolute server path (can be None),
1173
 
           - realm: the http specific authentication realm (can be None),
 
1010
           - user: can't de different from the provided user if any,
1174
1011
           - password: the decoded password, could be None if the credential
1175
1012
             defines only the user
1176
1013
           - verify_certificates: https specific, True if the server
1178
1015
        """
1179
1016
        credentials = None
1180
1017
        for auth_def_name, auth_def in self._get_config().items():
1181
 
            if type(auth_def) is not configobj.Section:
1182
 
                raise ValueError("%s defined outside a section" % auth_def_name)
1183
 
 
1184
1018
            a_scheme, a_host, a_user, a_path = map(
1185
1019
                auth_def.get, ['scheme', 'host', 'user', 'path'])
1186
1020
 
1217
1051
            if a_user is None:
1218
1052
                # Can't find a user
1219
1053
                continue
1220
 
            # Prepare a credentials dictionary with additional keys
1221
 
            # for the credential providers
1222
1054
            credentials = dict(name=auth_def_name,
1223
 
                               user=a_user,
1224
 
                               scheme=a_scheme,
1225
 
                               host=host,
1226
 
                               port=port,
1227
 
                               path=path,
1228
 
                               realm=realm,
1229
 
                               password=auth_def.get('password', None),
 
1055
                               user=a_user, password=auth_def['password'],
1230
1056
                               verify_certificates=a_verify_certificates)
1231
 
            # Decode the password in the credentials (or get one)
1232
1057
            self.decode_password(credentials,
1233
1058
                                 auth_def.get('password_encoding', None))
1234
1059
            if 'auth' in debug.debug_flags:
1235
1060
                trace.mutter("Using authentication section: %r", auth_def_name)
1236
1061
            break
1237
1062
 
1238
 
        if credentials is None:
1239
 
            # No credentials were found in authentication.conf, try the fallback
1240
 
            # credentials stores.
1241
 
            credentials = credential_store_registry.get_fallback_credentials(
1242
 
                scheme, host, port, user, path, realm)
1243
 
 
1244
1063
        return credentials
1245
1064
 
1246
 
    def set_credentials(self, name, host, user, scheme=None, password=None,
1247
 
                        port=None, path=None, verify_certificates=None,
1248
 
                        realm=None):
1249
 
        """Set authentication credentials for a host.
1250
 
 
1251
 
        Any existing credentials with matching scheme, host, port and path
1252
 
        will be deleted, regardless of name.
1253
 
 
1254
 
        :param name: An arbitrary name to describe this set of credentials.
1255
 
        :param host: Name of the host that accepts these credentials.
1256
 
        :param user: The username portion of these credentials.
1257
 
        :param scheme: The URL scheme (e.g. ssh, http) the credentials apply
1258
 
            to.
1259
 
        :param password: Password portion of these credentials.
1260
 
        :param port: The IP port on the host that these credentials apply to.
1261
 
        :param path: A filesystem path on the host that these credentials
1262
 
            apply to.
1263
 
        :param verify_certificates: On https, verify server certificates if
1264
 
            True.
1265
 
        :param realm: The http authentication realm (optional).
1266
 
        """
1267
 
        values = {'host': host, 'user': user}
1268
 
        if password is not None:
1269
 
            values['password'] = password
1270
 
        if scheme is not None:
1271
 
            values['scheme'] = scheme
1272
 
        if port is not None:
1273
 
            values['port'] = '%d' % port
1274
 
        if path is not None:
1275
 
            values['path'] = path
1276
 
        if verify_certificates is not None:
1277
 
            values['verify_certificates'] = str(verify_certificates)
1278
 
        if realm is not None:
1279
 
            values['realm'] = realm
1280
 
        config = self._get_config()
1281
 
        for_deletion = []
1282
 
        for section, existing_values in config.items():
1283
 
            for key in ('scheme', 'host', 'port', 'path', 'realm'):
1284
 
                if existing_values.get(key) != values.get(key):
1285
 
                    break
1286
 
            else:
1287
 
                del config[section]
1288
 
        config.update({name: values})
1289
 
        self._save()
1290
 
 
1291
 
    def get_user(self, scheme, host, port=None, realm=None, path=None,
1292
 
                 prompt=None, ask=False, default=None):
 
1065
    def get_user(self, scheme, host, port=None,
 
1066
                 realm=None, path=None, prompt=None):
1293
1067
        """Get a user from authentication file.
1294
1068
 
1295
1069
        :param scheme: protocol
1302
1076
 
1303
1077
        :param path: the absolute path on the server (optional)
1304
1078
 
1305
 
        :param ask: Ask the user if there is no explicitly configured username 
1306
 
                    (optional)
1307
 
 
1308
 
        :param default: The username returned if none is defined (optional).
1309
 
 
1310
1079
        :return: The found user.
1311
1080
        """
1312
1081
        credentials = self.get_credentials(scheme, host, port, user=None,
1313
 
                                           path=path, realm=realm)
 
1082
                                           path=path)
1314
1083
        if credentials is not None:
1315
1084
            user = credentials['user']
1316
1085
        else:
1317
1086
            user = None
1318
 
        if user is None:
1319
 
            if ask:
1320
 
                if prompt is None:
1321
 
                    # Create a default prompt suitable for most cases
1322
 
                    prompt = scheme.upper() + ' %(host)s username'
1323
 
                # Special handling for optional fields in the prompt
1324
 
                if port is not None:
1325
 
                    prompt_host = '%s:%d' % (host, port)
1326
 
                else:
1327
 
                    prompt_host = host
1328
 
                user = ui.ui_factory.get_username(prompt, host=prompt_host)
1329
 
            else:
1330
 
                user = default
1331
1087
        return user
1332
1088
 
1333
1089
    def get_password(self, scheme, host, user, port=None,
1348
1104
 
1349
1105
        :return: The found password or the one entered by the user.
1350
1106
        """
1351
 
        credentials = self.get_credentials(scheme, host, port, user, path,
1352
 
                                           realm)
 
1107
        credentials = self.get_credentials(scheme, host, port, user, path)
1353
1108
        if credentials is not None:
1354
1109
            password = credentials['password']
1355
 
            if password is not None and scheme is 'ssh':
1356
 
                trace.warning('password ignored in section [%s],'
1357
 
                              ' use an ssh agent instead'
1358
 
                              % credentials['name'])
1359
 
                password = None
1360
1110
        else:
1361
1111
            password = None
1362
1112
        # Prompt user only if we could't find a password
1363
1113
        if password is None:
1364
1114
            if prompt is None:
1365
 
                # Create a default prompt suitable for most cases
 
1115
                # Create a default prompt suitable for most of the cases
1366
1116
                prompt = '%s' % scheme.upper() + ' %(user)s@%(host)s password'
1367
1117
            # Special handling for optional fields in the prompt
1368
1118
            if port is not None:
1374
1124
        return password
1375
1125
 
1376
1126
    def decode_password(self, credentials, encoding):
1377
 
        try:
1378
 
            cs = credential_store_registry.get_credential_store(encoding)
1379
 
        except KeyError:
1380
 
            raise ValueError('%r is not a known password_encoding' % encoding)
1381
 
        credentials['password'] = cs.decode_password(credentials)
1382
 
        return credentials
1383
 
 
1384
 
 
1385
 
class CredentialStoreRegistry(registry.Registry):
1386
 
    """A class that registers credential stores.
1387
 
 
1388
 
    A credential store provides access to credentials via the password_encoding
1389
 
    field in authentication.conf sections.
1390
 
 
1391
 
    Except for stores provided by bzr itself, most stores are expected to be
1392
 
    provided by plugins that will therefore use
1393
 
    register_lazy(password_encoding, module_name, member_name, help=help,
1394
 
    fallback=fallback) to install themselves.
1395
 
 
1396
 
    A fallback credential store is one that is queried if no credentials can be
1397
 
    found via authentication.conf.
1398
 
    """
1399
 
 
1400
 
    def get_credential_store(self, encoding=None):
1401
 
        cs = self.get(encoding)
1402
 
        if callable(cs):
1403
 
            cs = cs()
1404
 
        return cs
1405
 
 
1406
 
    def is_fallback(self, name):
1407
 
        """Check if the named credentials store should be used as fallback."""
1408
 
        return self.get_info(name)
1409
 
 
1410
 
    def get_fallback_credentials(self, scheme, host, port=None, user=None,
1411
 
                                 path=None, realm=None):
1412
 
        """Request credentials from all fallback credentials stores.
1413
 
 
1414
 
        The first credentials store that can provide credentials wins.
1415
 
        """
1416
 
        credentials = None
1417
 
        for name in self.keys():
1418
 
            if not self.is_fallback(name):
1419
 
                continue
1420
 
            cs = self.get_credential_store(name)
1421
 
            credentials = cs.get_credentials(scheme, host, port, user,
1422
 
                                             path, realm)
1423
 
            if credentials is not None:
1424
 
                # We found some credentials
1425
 
                break
1426
 
        return credentials
1427
 
 
1428
 
    def register(self, key, obj, help=None, override_existing=False,
1429
 
                 fallback=False):
1430
 
        """Register a new object to a name.
1431
 
 
1432
 
        :param key: This is the key to use to request the object later.
1433
 
        :param obj: The object to register.
1434
 
        :param help: Help text for this entry. This may be a string or
1435
 
                a callable. If it is a callable, it should take two
1436
 
                parameters (registry, key): this registry and the key that
1437
 
                the help was registered under.
1438
 
        :param override_existing: Raise KeyErorr if False and something has
1439
 
                already been registered for that key. If True, ignore if there
1440
 
                is an existing key (always register the new value).
1441
 
        :param fallback: Whether this credential store should be 
1442
 
                used as fallback.
1443
 
        """
1444
 
        return super(CredentialStoreRegistry,
1445
 
                     self).register(key, obj, help, info=fallback,
1446
 
                                    override_existing=override_existing)
1447
 
 
1448
 
    def register_lazy(self, key, module_name, member_name,
1449
 
                      help=None, override_existing=False,
1450
 
                      fallback=False):
1451
 
        """Register a new credential store to be loaded on request.
1452
 
 
1453
 
        :param module_name: The python path to the module. Such as 'os.path'.
1454
 
        :param member_name: The member of the module to return.  If empty or
1455
 
                None, get() will return the module itself.
1456
 
        :param help: Help text for this entry. This may be a string or
1457
 
                a callable.
1458
 
        :param override_existing: If True, replace the existing object
1459
 
                with the new one. If False, if there is already something
1460
 
                registered with the same key, raise a KeyError
1461
 
        :param fallback: Whether this credential store should be 
1462
 
                used as fallback.
1463
 
        """
1464
 
        return super(CredentialStoreRegistry, self).register_lazy(
1465
 
            key, module_name, member_name, help,
1466
 
            info=fallback, override_existing=override_existing)
1467
 
 
1468
 
 
1469
 
credential_store_registry = CredentialStoreRegistry()
1470
 
 
1471
 
 
1472
 
class CredentialStore(object):
1473
 
    """An abstract class to implement storage for credentials"""
1474
 
 
1475
 
    def decode_password(self, credentials):
1476
 
        """Returns a clear text password for the provided credentials."""
1477
 
        raise NotImplementedError(self.decode_password)
1478
 
 
1479
 
    def get_credentials(self, scheme, host, port=None, user=None, path=None,
1480
 
                        realm=None):
1481
 
        """Return the matching credentials from this credential store.
1482
 
 
1483
 
        This method is only called on fallback credential stores.
1484
 
        """
1485
 
        raise NotImplementedError(self.get_credentials)
1486
 
 
1487
 
 
1488
 
 
1489
 
class PlainTextCredentialStore(CredentialStore):
1490
 
    __doc__ = """Plain text credential store for the authentication.conf file"""
1491
 
 
1492
 
    def decode_password(self, credentials):
1493
 
        """See CredentialStore.decode_password."""
1494
 
        return credentials['password']
1495
 
 
1496
 
 
1497
 
credential_store_registry.register('plain', PlainTextCredentialStore,
1498
 
                                   help=PlainTextCredentialStore.__doc__)
1499
 
credential_store_registry.default_key = 'plain'
1500
 
 
1501
 
 
1502
 
class BzrDirConfig(object):
1503
 
 
1504
 
    def __init__(self, bzrdir):
1505
 
        self._bzrdir = bzrdir
1506
 
        self._config = bzrdir._get_config()
1507
 
 
1508
 
    def set_default_stack_on(self, value):
1509
 
        """Set the default stacking location.
1510
 
 
1511
 
        It may be set to a location, or None.
1512
 
 
1513
 
        This policy affects all branches contained by this bzrdir, except for
1514
 
        those under repositories.
1515
 
        """
1516
 
        if self._config is None:
1517
 
            raise errors.BzrError("Cannot set configuration in %s" % self._bzrdir)
1518
 
        if value is None:
1519
 
            self._config.set_option('', 'default_stack_on')
1520
 
        else:
1521
 
            self._config.set_option(value, 'default_stack_on')
1522
 
 
1523
 
    def get_default_stack_on(self):
1524
 
        """Return the default stacking location.
1525
 
 
1526
 
        This will either be a location, or None.
1527
 
 
1528
 
        This policy affects all branches contained by this bzrdir, except for
1529
 
        those under repositories.
1530
 
        """
1531
 
        if self._config is None:
1532
 
            return None
1533
 
        value = self._config.get_option('default_stack_on')
1534
 
        if value == '':
1535
 
            value = None
1536
 
        return value
1537
 
 
1538
 
 
1539
 
class TransportConfig(object):
1540
 
    """A Config that reads/writes a config file on a Transport.
1541
 
 
1542
 
    It is a low-level object that considers config data to be name/value pairs
1543
 
    that may be associated with a section.  Assigning meaning to the these
1544
 
    values is done at higher levels like TreeConfig.
1545
 
    """
1546
 
 
1547
 
    def __init__(self, transport, filename):
1548
 
        self._transport = transport
1549
 
        self._filename = filename
1550
 
 
1551
 
    def get_option(self, name, section=None, default=None):
1552
 
        """Return the value associated with a named option.
1553
 
 
1554
 
        :param name: The name of the value
1555
 
        :param section: The section the option is in (if any)
1556
 
        :param default: The value to return if the value is not set
1557
 
        :return: The value or default value
1558
 
        """
1559
 
        configobj = self._get_configobj()
1560
 
        if section is None:
1561
 
            section_obj = configobj
1562
 
        else:
1563
 
            try:
1564
 
                section_obj = configobj[section]
1565
 
            except KeyError:
1566
 
                return default
1567
 
        return section_obj.get(name, default)
1568
 
 
1569
 
    def set_option(self, value, name, section=None):
1570
 
        """Set the value associated with a named option.
1571
 
 
1572
 
        :param value: The value to set
1573
 
        :param name: The name of the value to set
1574
 
        :param section: The section the option is in (if any)
1575
 
        """
1576
 
        configobj = self._get_configobj()
1577
 
        if section is None:
1578
 
            configobj[name] = value
1579
 
        else:
1580
 
            configobj.setdefault(section, {})[name] = value
1581
 
        self._set_configobj(configobj)
1582
 
 
1583
 
    def _get_config_file(self):
1584
 
        try:
1585
 
            return StringIO(self._transport.get_bytes(self._filename))
1586
 
        except errors.NoSuchFile:
1587
 
            return StringIO()
1588
 
 
1589
 
    def _get_configobj(self):
1590
 
        f = self._get_config_file()
1591
 
        try:
1592
 
            return ConfigObj(f, encoding='utf-8')
1593
 
        finally:
1594
 
            f.close()
1595
 
 
1596
 
    def _set_configobj(self, configobj):
1597
 
        out_file = StringIO()
1598
 
        configobj.write(out_file)
1599
 
        out_file.seek(0)
1600
 
        self._transport.put_file(self._filename, out_file)
 
1127
        return credentials