~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Danny van Heumen
  • Date: 2010-03-09 16:38:10 UTC
  • mto: (4634.139.5 2.0)
  • mto: This revision was merged to the branch mainline in revision 5160.
  • Revision ID: danny@dannyvanheumen.nl-20100309163810-ujn8hcx08f75nlf1
Refined test to make use of locking hooks and also validate if lock is truly a checkout-lock.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2007, 2008 Canonical Ltd
2
2
#   Authors: Robert Collins <robert.collins@canonical.com>
3
3
#            and others
4
4
#
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
81
    registry,
85
82
    symbol_versioning,
86
83
    trace,
87
 
    transport,
88
84
    ui,
89
85
    urlutils,
90
86
    win32utils,
157
153
        """Get the users pop up editor."""
158
154
        raise NotImplementedError
159
155
 
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
156
    def get_mail_client(self):
170
157
        """Get a mail client to use"""
171
158
        selected_client = self.get_user_option('mail_client')
194
181
        """Get a generic option as a boolean - no special process, no default.
195
182
 
196
183
        :return None if the option doesn't exist or its value can't be
197
 
            interpreted as a boolean. Returns True or False otherwise.
 
184
            interpreted as a boolean. Returns True or False ortherwise.
198
185
        """
199
186
        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
 
187
        return ui.bool_from_string(s)
221
188
 
222
189
    def gpg_signing_command(self):
223
190
        """What program should be used to sign signatures?"""
261
228
 
262
229
        Something similar to 'Martin Pool <mbp@sourcefrog.net>'
263
230
 
264
 
        $BZR_EMAIL can be set to override this, then
 
231
        $BZR_EMAIL can be set to override this (as well as the
 
232
        deprecated $BZREMAIL), then
265
233
        the concrete policy type is checked, and finally
266
234
        $EMAIL is examined.
267
 
        If no username can be found, errors.NoWhoami exception is raised.
 
235
        If none is found, a reasonable default is (hopefully)
 
236
        created.
268
237
 
269
238
        TODO: Check it's reasonably well-formed.
270
239
        """
280
249
        if v:
281
250
            return v.decode(osutils.get_user_encoding())
282
251
 
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()
 
252
        name, email = _auto_user_id()
 
253
        if name:
 
254
            return '%s <%s>' % (name, email)
 
255
        else:
 
256
            return email
291
257
 
292
258
    def signature_checking(self):
293
259
        """What is the current policy for signature checking?."""
338
304
                path = 'bzr'
339
305
            return path
340
306
 
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
307
 
355
308
class IniBasedConfig(Config):
356
309
    """A configuration policy that draws from ini files."""
357
310
 
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
 
        """
 
311
    def __init__(self, get_filename):
364
312
        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
 
313
        self._get_filename = get_filename
377
314
        self._parser = None
378
315
 
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):
 
316
    def _get_parser(self, file=None):
402
317
        if self._parser is not None:
403
318
            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')
 
319
        if file is None:
 
320
            input = self._get_filename()
414
321
        else:
415
 
            co_input = self.file_name
 
322
            input = file
416
323
        try:
417
 
            self._parser = ConfigObj(co_input, encoding='utf-8')
 
324
            self._parser = ConfigObj(input, encoding='utf-8')
418
325
        except configobj.ConfigObjError, e:
419
326
            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
327
        return self._parser
423
328
 
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
329
    def _get_matching_sections(self):
432
330
        """Return an ordered list of (section_name, extra_path) pairs.
433
331
 
448
346
        """Return the policy for the given (section, option_name) pair."""
449
347
        return POLICY_NONE
450
348
 
451
 
    def _get_change_editor(self):
452
 
        return self.get_user_option('change_editor')
453
 
 
454
349
    def _get_signature_checking(self):
455
350
        """See Config._get_signature_checking."""
456
351
        policy = self._get_user_option('check_signatures')
536
431
    def _get_nickname(self):
537
432
        return self.get_user_option('nickname')
538
433
 
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):
 
434
 
 
435
class GlobalConfig(IniBasedConfig):
616
436
    """The configuration that should be used for a specific location."""
617
437
 
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
438
    def get_editor(self):
635
439
        return self._get_user_option('editor')
636
440
 
637
 
    @needs_write_lock
 
441
    def __init__(self):
 
442
        super(GlobalConfig, self).__init__(config_filename)
 
443
 
638
444
    def set_user_option(self, option, value):
639
445
        """Save option and its value in the configuration."""
640
446
        self._set_option(option, value, 'DEFAULT')
646
452
        else:
647
453
            return {}
648
454
 
649
 
    @needs_write_lock
650
455
    def set_alias(self, alias_name, alias_command):
651
456
        """Save the alias in the configuration."""
652
457
        self._set_option(alias_name, alias_command, 'ALIASES')
653
458
 
654
 
    @needs_write_lock
655
459
    def unset_alias(self, alias_name):
656
460
        """Unset an existing alias."""
657
 
        self.reload()
658
461
        aliases = self._get_parser().get('ALIASES')
659
462
        if not aliases or alias_name not in aliases:
660
463
            raise errors.NoSuchAlias(alias_name)
662
465
        self._write_config_file()
663
466
 
664
467
    def _set_option(self, option, value, section):
665
 
        self.reload()
 
468
        # FIXME: RBC 20051029 This should refresh the parser and also take a
 
469
        # file lock on bazaar.conf.
 
470
        conf_dir = os.path.dirname(self._get_filename())
 
471
        ensure_config_dir_exists(conf_dir)
666
472
        self._get_parser().setdefault(section, {})[option] = value
667
473
        self._write_config_file()
668
474
 
669
 
 
670
 
class LocationConfig(LockableConfig):
 
475
    def _write_config_file(self):
 
476
        f = open(self._get_filename(), 'wb')
 
477
        self._get_parser().write(f)
 
478
        f.close()
 
479
 
 
480
 
 
481
class LocationConfig(IniBasedConfig):
671
482
    """A configuration object that gives the policy for a location."""
672
483
 
673
484
    def __init__(self, location):
674
 
        super(LocationConfig, self).__init__(
675
 
            file_name=locations_config_filename())
 
485
        name_generator = locations_config_filename
 
486
        if (not os.path.exists(name_generator()) and
 
487
                os.path.exists(branches_config_filename())):
 
488
            if sys.platform == 'win32':
 
489
                trace.warning('Please rename %s to %s'
 
490
                              % (branches_config_filename(),
 
491
                                 locations_config_filename()))
 
492
            else:
 
493
                trace.warning('Please rename ~/.bazaar/branches.conf'
 
494
                              ' to ~/.bazaar/locations.conf')
 
495
            name_generator = branches_config_filename
 
496
        super(LocationConfig, self).__init__(name_generator)
676
497
        # local file locations are looked up by local path, rather than
677
498
        # by file url. This is because the config file is a user
678
499
        # file, and we would rather not expose the user to file urls.
680
501
            location = urlutils.local_path_from_url(location)
681
502
        self.location = location
682
503
 
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
504
    def _get_matching_sections(self):
699
505
        """Return an ordered list of section names matching this location."""
700
506
        sections = self._get_parser()
788
594
            if policy_key in self._get_parser()[section]:
789
595
                del self._get_parser()[section][policy_key]
790
596
 
791
 
    @needs_write_lock
792
597
    def set_user_option(self, option, value, store=STORE_LOCATION):
793
598
        """Save option and its value in the configuration."""
794
599
        if store not in [STORE_LOCATION,
796
601
                         STORE_LOCATION_APPENDPATH]:
797
602
            raise ValueError('bad storage policy %r for %r' %
798
603
                (store, option))
799
 
        self.reload()
 
604
        # FIXME: RBC 20051029 This should refresh the parser and also take a
 
605
        # file lock on locations.conf.
 
606
        conf_dir = os.path.dirname(self._get_filename())
 
607
        ensure_config_dir_exists(conf_dir)
800
608
        location = self.location
801
609
        if location.endswith('/'):
802
610
            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:
 
611
        if (not location in self._get_parser() and
 
612
            not location + '/' in self._get_parser()):
 
613
            self._get_parser()[location]={}
 
614
        elif location + '/' in self._get_parser():
807
615
            location = location + '/'
808
 
        parser[location][option]=value
 
616
        self._get_parser()[location][option]=value
809
617
        # the allowed values of store match the config policies
810
618
        self._set_option_policy(location, option, store)
811
 
        self._write_config_file()
 
619
        self._get_parser().write(file(self._get_filename(), 'wb'))
812
620
 
813
621
 
814
622
class BranchConfig(Config):
815
623
    """A configuration object giving the policy for a branch."""
816
624
 
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
625
    def _get_branch_data_config(self):
828
626
        if self._branch_data_config is None:
829
627
            self._branch_data_config = TreeConfig(self.branch)
881
679
 
882
680
        return self._get_best_value('_get_user_id')
883
681
 
884
 
    def _get_change_editor(self):
885
 
        return self._get_best_value('_get_change_editor')
886
 
 
887
682
    def _get_signature_checking(self):
888
683
        """See Config._get_signature_checking."""
889
684
        return self._get_best_value('_get_signature_checking')
927
722
        """See Config.gpg_signing_command."""
928
723
        return self._get_safe_value('_gpg_signing_command')
929
724
 
 
725
    def __init__(self, branch):
 
726
        super(BranchConfig, self).__init__()
 
727
        self._location_config = None
 
728
        self._branch_data_config = None
 
729
        self._global_config = None
 
730
        self.branch = branch
 
731
        self.option_sources = (self._get_location_config,
 
732
                               self._get_branch_data_config,
 
733
                               self._get_global_config)
 
734
 
930
735
    def _post_commit(self):
931
736
        """See Config.post_commit."""
932
737
        return self._get_safe_value('_post_commit')
965
770
            os.mkdir(parent_dir)
966
771
        trace.mutter('creating config directory: %r', path)
967
772
        os.mkdir(path)
968
 
        osutils.copy_ownership_from_path(path)
969
773
 
970
774
 
971
775
def config_dir():
986
790
                                  ' or HOME set')
987
791
        return osutils.pathjoin(base, 'bazaar', '2.0')
988
792
    else:
 
793
        # cygwin, linux, and darwin all have a $HOME directory
989
794
        if base is None:
990
795
            base = os.path.expanduser("~")
991
796
        return osutils.pathjoin(base, ".bazaar")
996
801
    return osutils.pathjoin(config_dir(), 'bazaar.conf')
997
802
 
998
803
 
 
804
def branches_config_filename():
 
805
    """Return per-user configuration ini file filename."""
 
806
    return osutils.pathjoin(config_dir(), 'branches.conf')
 
807
 
 
808
 
999
809
def locations_config_filename():
1000
810
    """Return per-user configuration ini file filename."""
1001
811
    return osutils.pathjoin(config_dir(), 'locations.conf')
1016
826
 
1017
827
    This doesn't implicitly create it.
1018
828
 
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.
 
829
    On Windows it's in the config directory; elsewhere in the XDG cache directory.
1022
830
    """
1023
831
    if sys.platform == 'win32':
1024
832
        return osutils.pathjoin(config_dir(), 'Crash')
1025
833
    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')
 
834
        return osutils.pathjoin(xdg_cache_dir(), 'crash')
1029
835
 
1030
836
 
1031
837
def xdg_cache_dir():
1038
844
        return os.path.expanduser('~/.cache')
1039
845
 
1040
846
 
 
847
def _auto_user_id():
 
848
    """Calculate automatic user identification.
 
849
 
 
850
    Returns (realname, email).
 
851
 
 
852
    Only used when none is set in the environment or the id file.
 
853
 
 
854
    This previously used the FQDN as the default domain, but that can
 
855
    be very slow on machines where DNS is broken.  So now we simply
 
856
    use the hostname.
 
857
    """
 
858
    import socket
 
859
 
 
860
    if sys.platform == 'win32':
 
861
        name = win32utils.get_user_name_unicode()
 
862
        if name is None:
 
863
            raise errors.BzrError("Cannot autodetect user name.\n"
 
864
                                  "Please, set your name with command like:\n"
 
865
                                  'bzr whoami "Your Name <name@domain.com>"')
 
866
        host = win32utils.get_host_name_unicode()
 
867
        if host is None:
 
868
            host = socket.gethostname()
 
869
        return name, (name + '@' + host)
 
870
 
 
871
    try:
 
872
        import pwd
 
873
        uid = os.getuid()
 
874
        try:
 
875
            w = pwd.getpwuid(uid)
 
876
        except KeyError:
 
877
            raise errors.BzrCommandError('Unable to determine your name.  '
 
878
                'Please use "bzr whoami" to set it.')
 
879
 
 
880
        # we try utf-8 first, because on many variants (like Linux),
 
881
        # /etc/passwd "should" be in utf-8, and because it's unlikely to give
 
882
        # false positives.  (many users will have their user encoding set to
 
883
        # latin-1, which cannot raise UnicodeError.)
 
884
        try:
 
885
            gecos = w.pw_gecos.decode('utf-8')
 
886
            encoding = 'utf-8'
 
887
        except UnicodeError:
 
888
            try:
 
889
                encoding = osutils.get_user_encoding()
 
890
                gecos = w.pw_gecos.decode(encoding)
 
891
            except UnicodeError:
 
892
                raise errors.BzrCommandError('Unable to determine your name.  '
 
893
                   'Use "bzr whoami" to set it.')
 
894
        try:
 
895
            username = w.pw_name.decode(encoding)
 
896
        except UnicodeError:
 
897
            raise errors.BzrCommandError('Unable to determine your name.  '
 
898
                'Use "bzr whoami" to set it.')
 
899
 
 
900
        comma = gecos.find(',')
 
901
        if comma == -1:
 
902
            realname = gecos
 
903
        else:
 
904
            realname = gecos[:comma]
 
905
        if not realname:
 
906
            realname = username
 
907
 
 
908
    except ImportError:
 
909
        import getpass
 
910
        try:
 
911
            user_encoding = osutils.get_user_encoding()
 
912
            realname = username = getpass.getuser().decode(user_encoding)
 
913
        except UnicodeDecodeError:
 
914
            raise errors.BzrError("Can't decode username as %s." % \
 
915
                    user_encoding)
 
916
 
 
917
    return realname, (username + '@' + socket.gethostname())
 
918
 
 
919
 
1041
920
def parse_username(username):
1042
921
    """Parse e-mail username and return a (name, address) tuple."""
1043
922
    match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
1129
1008
        """Save the config file, only tests should use it for now."""
1130
1009
        conf_dir = os.path.dirname(self._filename)
1131
1010
        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()
 
1011
        self._get_config().write(file(self._filename, 'wb'))
1137
1012
 
1138
1013
    def _set_option(self, section_name, option_name, value):
1139
1014
        """Set an authentication configuration option"""
1487
1362
 
1488
1363
 
1489
1364
class PlainTextCredentialStore(CredentialStore):
1490
 
    __doc__ = """Plain text credential store for the authentication.conf file"""
 
1365
    """Plain text credential store for the authentication.conf file."""
1491
1366
 
1492
1367
    def decode_password(self, credentials):
1493
1368
        """See CredentialStore.decode_password."""
1582
1457
 
1583
1458
    def _get_config_file(self):
1584
1459
        try:
1585
 
            return StringIO(self._transport.get_bytes(self._filename))
 
1460
            return self._transport.get(self._filename)
1586
1461
        except errors.NoSuchFile:
1587
1462
            return StringIO()
1588
1463
 
1589
1464
    def _get_configobj(self):
1590
 
        f = self._get_config_file()
1591
 
        try:
1592
 
            return ConfigObj(f, encoding='utf-8')
1593
 
        finally:
1594
 
            f.close()
 
1465
        return ConfigObj(self._get_config_file(), encoding='utf-8')
1595
1466
 
1596
1467
    def _set_configobj(self, configobj):
1597
1468
        out_file = StringIO()