~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: John Arbash Meinel
  • Date: 2008-07-22 21:47:13 UTC
  • mto: (3697.7.4 1.7)
  • mto: This revision was merged to the branch mainline in revision 3748.
  • Revision ID: john@arbash-meinel.com-20080722214713-xpdvsv1aixxq05tn
Test the case where BASE is missing a file that is present in THIS, OTHER and all LCAs.
Also include both the case where the file is modified, and where it isn't.

Show diffs side-by-side

added added

removed removed

Lines of Context:
70
70
import errno
71
71
from fnmatch import fnmatch
72
72
import re
73
 
from StringIO import StringIO
 
73
from cStringIO import StringIO
74
74
 
75
75
import bzrlib
76
76
from bzrlib import (
 
77
    debug,
77
78
    errors,
 
79
    mail_client,
78
80
    osutils,
79
81
    symbol_versioning,
 
82
    trace,
 
83
    ui,
80
84
    urlutils,
81
85
    win32utils,
82
86
    )
83
 
import bzrlib.util.configobj.configobj as configobj
 
87
from bzrlib.util.configobj import configobj
84
88
""")
85
89
 
86
 
from bzrlib.trace import mutter, warning
87
 
 
88
90
 
89
91
CHECK_IF_POSSIBLE=0
90
92
CHECK_ALWAYS=1
142
144
        """Get the users pop up editor."""
143
145
        raise NotImplementedError
144
146
 
 
147
    def get_mail_client(self):
 
148
        """Get a mail client to use"""
 
149
        selected_client = self.get_user_option('mail_client')
 
150
        try:
 
151
            mail_client_class = {
 
152
                None: mail_client.DefaultMail,
 
153
                # Specific clients
 
154
                'emacsclient': mail_client.EmacsMail,
 
155
                'evolution': mail_client.Evolution,
 
156
                'kmail': mail_client.KMail,
 
157
                'mutt': mail_client.Mutt,
 
158
                'thunderbird': mail_client.Thunderbird,
 
159
                # Generic options
 
160
                'default': mail_client.DefaultMail,
 
161
                'editor': mail_client.Editor,
 
162
                'mapi': mail_client.MAPIClient,
 
163
                'xdg-email': mail_client.XDGEmail,
 
164
            }[selected_client]
 
165
        except KeyError:
 
166
            raise errors.UnknownMailClient(selected_client)
 
167
        return mail_client_class(self)
 
168
 
145
169
    def _get_signature_checking(self):
146
170
        """Template method to override signature checking policy."""
147
171
 
213
237
        v = os.environ.get('BZR_EMAIL')
214
238
        if v:
215
239
            return v.decode(bzrlib.user_encoding)
216
 
        v = os.environ.get('BZREMAIL')
217
 
        if v:
218
 
            warning('BZREMAIL is deprecated in favor of BZR_EMAIL. Please update your configuration.')
219
 
            return v.decode(bzrlib.user_encoding)
220
 
    
 
240
 
221
241
        v = self._get_user_id()
222
242
        if v:
223
243
            return v
224
 
        
 
244
 
225
245
        v = os.environ.get('EMAIL')
226
246
        if v:
227
247
            return v.decode(bzrlib.user_encoding)
252
272
        if policy is None:
253
273
            policy = self._get_signature_checking()
254
274
            if policy is not None:
255
 
                warning("Please use create_signatures, not check_signatures "
256
 
                        "to set signing policy.")
 
275
                trace.warning("Please use create_signatures,"
 
276
                              " not check_signatures to set signing policy.")
257
277
            if policy == CHECK_ALWAYS:
258
278
                return True
259
279
        elif policy == SIGN_ALWAYS:
272
292
    def _get_nickname(self):
273
293
        return None
274
294
 
 
295
    def get_bzr_remote_path(self):
 
296
        try:
 
297
            return os.environ['BZR_REMOTE_PATH']
 
298
        except KeyError:
 
299
            path = self.get_user_option("bzr_remote_path")
 
300
            if path is None:
 
301
                path = 'bzr'
 
302
            return path
 
303
 
275
304
 
276
305
class IniBasedConfig(Config):
277
306
    """A configuration policy that draws from ini files."""
411
440
 
412
441
    def set_user_option(self, option, value):
413
442
        """Save option and its value in the configuration."""
 
443
        self._set_option(option, value, 'DEFAULT')
 
444
 
 
445
    def get_aliases(self):
 
446
        """Return the aliases section."""
 
447
        if 'ALIASES' in self._get_parser():
 
448
            return self._get_parser()['ALIASES']
 
449
        else:
 
450
            return {}
 
451
 
 
452
    def set_alias(self, alias_name, alias_command):
 
453
        """Save the alias in the configuration."""
 
454
        self._set_option(alias_name, alias_command, 'ALIASES')
 
455
 
 
456
    def unset_alias(self, alias_name):
 
457
        """Unset an existing alias."""
 
458
        aliases = self._get_parser().get('ALIASES')
 
459
        if not aliases or alias_name not in aliases:
 
460
            raise errors.NoSuchAlias(alias_name)
 
461
        del aliases[alias_name]
 
462
        self._write_config_file()
 
463
 
 
464
    def _set_option(self, option, value, section):
414
465
        # FIXME: RBC 20051029 This should refresh the parser and also take a
415
466
        # file lock on bazaar.conf.
416
467
        conf_dir = os.path.dirname(self._get_filename())
417
468
        ensure_config_dir_exists(conf_dir)
418
 
        if 'DEFAULT' not in self._get_parser():
419
 
            self._get_parser()['DEFAULT'] = {}
420
 
        self._get_parser()['DEFAULT'][option] = value
 
469
        self._get_parser().setdefault(section, {})[option] = value
 
470
        self._write_config_file()
 
471
 
 
472
    def _write_config_file(self):
421
473
        f = open(self._get_filename(), 'wb')
422
474
        self._get_parser().write(f)
423
475
        f.close()
428
480
 
429
481
    def __init__(self, location):
430
482
        name_generator = locations_config_filename
431
 
        if (not os.path.exists(name_generator()) and 
 
483
        if (not os.path.exists(name_generator()) and
432
484
                os.path.exists(branches_config_filename())):
433
485
            if sys.platform == 'win32':
434
 
                warning('Please rename %s to %s' 
435
 
                         % (branches_config_filename(),
436
 
                            locations_config_filename()))
 
486
                trace.warning('Please rename %s to %s'
 
487
                              % (branches_config_filename(),
 
488
                                 locations_config_filename()))
437
489
            else:
438
 
                warning('Please rename ~/.bazaar/branches.conf'
439
 
                        ' to ~/.bazaar/locations.conf')
 
490
                trace.warning('Please rename ~/.bazaar/branches.conf'
 
491
                              ' to ~/.bazaar/locations.conf')
440
492
            name_generator = branches_config_filename
441
493
        super(LocationConfig, self).__init__(name_generator)
442
494
        # local file locations are looked up by local path, rather than
541
593
 
542
594
    def set_user_option(self, option, value, store=STORE_LOCATION):
543
595
        """Save option and its value in the configuration."""
544
 
        assert store in [STORE_LOCATION,
 
596
        if store not in [STORE_LOCATION,
545
597
                         STORE_LOCATION_NORECURSE,
546
 
                         STORE_LOCATION_APPENDPATH], 'bad storage policy'
 
598
                         STORE_LOCATION_APPENDPATH]:
 
599
            raise ValueError('bad storage policy %r for %r' %
 
600
                (store, option))
547
601
        # FIXME: RBC 20051029 This should refresh the parser and also take a
548
602
        # file lock on locations.conf.
549
603
        conf_dir = os.path.dirname(self._get_filename())
610
664
    def _get_user_id(self):
611
665
        """Return the full user id for the branch.
612
666
    
613
 
        e.g. "John Hacker <jhacker@foo.org>"
 
667
        e.g. "John Hacker <jhacker@example.com>"
614
668
        This is looked up in the email controlfile for the branch.
615
669
        """
616
670
        try:
617
 
            return (self.branch.control_files.get_utf8("email") 
618
 
                    .read()
 
671
            return (self.branch._transport.get_bytes("email")
619
672
                    .decode(bzrlib.user_encoding)
620
673
                    .rstrip("\r\n"))
621
674
        except errors.NoSuchFile, e:
639
692
                return value
640
693
        return None
641
694
 
642
 
    def set_user_option(self, name, value, store=STORE_BRANCH):
 
695
    def set_user_option(self, name, value, store=STORE_BRANCH,
 
696
        warn_masked=False):
643
697
        if store == STORE_BRANCH:
644
698
            self._get_branch_data_config().set_option(value, name)
645
699
        elif store == STORE_GLOBAL:
646
700
            self._get_global_config().set_user_option(name, value)
647
701
        else:
648
702
            self._get_location_config().set_user_option(name, value, store)
 
703
        if not warn_masked:
 
704
            return
 
705
        if store in (STORE_GLOBAL, STORE_BRANCH):
 
706
            mask_value = self._get_location_config().get_user_option(name)
 
707
            if mask_value is not None:
 
708
                trace.warning('Value "%s" is masked by "%s" from'
 
709
                              ' locations.conf', value, mask_value)
 
710
            else:
 
711
                if store == STORE_GLOBAL:
 
712
                    branch_config = self._get_branch_data_config()
 
713
                    mask_value = branch_config.get_user_option(name)
 
714
                    if mask_value is not None:
 
715
                        trace.warning('Value "%s" is masked by "%s" from'
 
716
                                      ' branch.conf', value, mask_value)
 
717
 
649
718
 
650
719
    def _gpg_signing_command(self):
651
720
        """See Config.gpg_signing_command."""
695
764
        if sys.platform == 'win32':
696
765
            parent_dir = os.path.dirname(path)
697
766
            if not os.path.isdir(parent_dir):
698
 
                mutter('creating config parent directory: %r', parent_dir)
 
767
                trace.mutter('creating config parent directory: %r', parent_dir)
699
768
            os.mkdir(parent_dir)
700
 
        mutter('creating config directory: %r', path)
 
769
        trace.mutter('creating config directory: %r', path)
701
770
        os.mkdir(path)
702
771
 
703
772
 
715
784
        if base is None:
716
785
            base = os.environ.get('HOME', None)
717
786
        if base is None:
718
 
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA, or HOME set')
 
787
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA,'
 
788
                                  ' or HOME set')
719
789
        return osutils.pathjoin(base, 'bazaar', '2.0')
720
790
    else:
721
791
        # cygwin, linux, and darwin all have a $HOME directory
739
809
    return osutils.pathjoin(config_dir(), 'locations.conf')
740
810
 
741
811
 
 
812
def authentication_config_filename():
 
813
    """Return per-user authentication ini file filename."""
 
814
    return osutils.pathjoin(config_dir(), 'authentication.conf')
 
815
 
 
816
 
742
817
def user_ignore_config_filename():
743
818
    """Return the user default ignore filename"""
744
819
    return osutils.pathjoin(config_dir(), 'ignore')
812
887
    return realname, (username + '@' + socket.gethostname())
813
888
 
814
889
 
 
890
def parse_username(username):
 
891
    """Parse e-mail username and return a (name, address) tuple."""
 
892
    match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
 
893
    if match is None:
 
894
        return (username, '')
 
895
    else:
 
896
        return (match.group(1), match.group(2))
 
897
 
 
898
 
815
899
def extract_email_address(e):
816
900
    """Return just the address part of an email string.
817
 
    
 
901
 
818
902
    That is just the user@domain part, nothing else. 
819
903
    This part is required to contain only ascii characters.
820
904
    If it can't be extracted, raises an error.
821
 
    
 
905
 
822
906
    >>> extract_email_address('Jane Tester <jane@test.com>')
823
907
    "jane@test.com"
824
908
    """
825
 
    m = re.search(r'[\w+.-]+@[\w+.-]+', e)
826
 
    if not m:
 
909
    name, email = parse_username(e)
 
910
    if not email:
827
911
        raise errors.NoEmailInUsername(e)
828
 
    return m.group(0)
 
912
    return email
829
913
 
830
914
 
831
915
class TreeConfig(IniBasedConfig):
832
916
    """Branch configuration data associated with its contents, not location"""
 
917
 
 
918
    # XXX: Really needs a better name, as this is not part of the tree! -- mbp 20080507
 
919
 
833
920
    def __init__(self, branch):
 
921
        # XXX: Really this should be asking the branch for its configuration
 
922
        # data, rather than relying on a Transport, so that it can work 
 
923
        # more cleanly with a RemoteBranch that has no transport.
 
924
        self._config = TransportConfig(branch._transport, 'branch.conf')
834
925
        self.branch = branch
835
926
 
836
927
    def _get_parser(self, file=None):
837
928
        if file is not None:
838
929
            return IniBasedConfig._get_parser(file)
839
 
        return self._get_config()
840
 
 
841
 
    def _get_config(self):
842
 
        try:
843
 
            obj = ConfigObj(self.branch.control_files.get('branch.conf'),
844
 
                            encoding='utf-8')
845
 
        except errors.NoSuchFile:
846
 
            obj = ConfigObj(encoding='utf=8')
847
 
        return obj
 
930
        return self._config._get_configobj()
848
931
 
849
932
    def get_option(self, name, section=None, default=None):
850
933
        self.branch.lock_read()
851
934
        try:
852
 
            obj = self._get_config()
853
 
            try:
854
 
                if section is not None:
855
 
                    obj[section]
856
 
                result = obj[name]
857
 
            except KeyError:
858
 
                result = default
 
935
            return self._config.get_option(name, section, default)
859
936
        finally:
860
937
            self.branch.unlock()
861
938
        return result
864
941
        """Set a per-branch configuration option"""
865
942
        self.branch.lock_write()
866
943
        try:
867
 
            cfg_obj = self._get_config()
868
 
            if section is None:
869
 
                obj = cfg_obj
870
 
            else:
871
 
                try:
872
 
                    obj = cfg_obj[section]
873
 
                except KeyError:
874
 
                    cfg_obj[section] = {}
875
 
                    obj = cfg_obj[section]
876
 
            obj[name] = value
877
 
            out_file = StringIO()
878
 
            cfg_obj.write(out_file)
879
 
            out_file.seek(0)
880
 
            self.branch.control_files.put('branch.conf', out_file)
 
944
            self._config.set_option(value, name, section)
881
945
        finally:
882
946
            self.branch.unlock()
 
947
 
 
948
 
 
949
class AuthenticationConfig(object):
 
950
    """The authentication configuration file based on a ini file.
 
951
 
 
952
    Implements the authentication.conf file described in
 
953
    doc/developers/authentication-ring.txt.
 
954
    """
 
955
 
 
956
    def __init__(self, _file=None):
 
957
        self._config = None # The ConfigObj
 
958
        if _file is None:
 
959
            self._filename = authentication_config_filename()
 
960
            self._input = self._filename = authentication_config_filename()
 
961
        else:
 
962
            # Tests can provide a string as _file
 
963
            self._filename = None
 
964
            self._input = _file
 
965
 
 
966
    def _get_config(self):
 
967
        if self._config is not None:
 
968
            return self._config
 
969
        try:
 
970
            # FIXME: Should we validate something here ? Includes: empty
 
971
            # sections are useless, at least one of
 
972
            # user/password/password_encoding should be defined, etc.
 
973
 
 
974
            # Note: the encoding below declares that the file itself is utf-8
 
975
            # encoded, but the values in the ConfigObj are always Unicode.
 
976
            self._config = ConfigObj(self._input, encoding='utf-8')
 
977
        except configobj.ConfigObjError, e:
 
978
            raise errors.ParseConfigError(e.errors, e.config.filename)
 
979
        return self._config
 
980
 
 
981
    def _save(self):
 
982
        """Save the config file, only tests should use it for now."""
 
983
        conf_dir = os.path.dirname(self._filename)
 
984
        ensure_config_dir_exists(conf_dir)
 
985
        self._get_config().write(file(self._filename, 'wb'))
 
986
 
 
987
    def _set_option(self, section_name, option_name, value):
 
988
        """Set an authentication configuration option"""
 
989
        conf = self._get_config()
 
990
        section = conf.get(section_name)
 
991
        if section is None:
 
992
            conf[section] = {}
 
993
            section = conf[section]
 
994
        section[option_name] = value
 
995
        self._save()
 
996
 
 
997
    def get_credentials(self, scheme, host, port=None, user=None, path=None):
 
998
        """Returns the matching credentials from authentication.conf file.
 
999
 
 
1000
        :param scheme: protocol
 
1001
 
 
1002
        :param host: the server address
 
1003
 
 
1004
        :param port: the associated port (optional)
 
1005
 
 
1006
        :param user: login (optional)
 
1007
 
 
1008
        :param path: the absolute path on the server (optional)
 
1009
 
 
1010
        :return: A dict containing the matching credentials or None.
 
1011
           This includes:
 
1012
           - name: the section name of the credentials in the
 
1013
             authentication.conf file,
 
1014
           - user: can't de different from the provided user if any,
 
1015
           - password: the decoded password, could be None if the credential
 
1016
             defines only the user
 
1017
           - verify_certificates: https specific, True if the server
 
1018
             certificate should be verified, False otherwise.
 
1019
        """
 
1020
        credentials = None
 
1021
        for auth_def_name, auth_def in self._get_config().items():
 
1022
            if type(auth_def) is not configobj.Section:
 
1023
                raise ValueError("%s defined outside a section" % auth_def_name)
 
1024
 
 
1025
            a_scheme, a_host, a_user, a_path = map(
 
1026
                auth_def.get, ['scheme', 'host', 'user', 'path'])
 
1027
 
 
1028
            try:
 
1029
                a_port = auth_def.as_int('port')
 
1030
            except KeyError:
 
1031
                a_port = None
 
1032
            except ValueError:
 
1033
                raise ValueError("'port' not numeric in %s" % auth_def_name)
 
1034
            try:
 
1035
                a_verify_certificates = auth_def.as_bool('verify_certificates')
 
1036
            except KeyError:
 
1037
                a_verify_certificates = True
 
1038
            except ValueError:
 
1039
                raise ValueError(
 
1040
                    "'verify_certificates' not boolean in %s" % auth_def_name)
 
1041
 
 
1042
            # Attempt matching
 
1043
            if a_scheme is not None and scheme != a_scheme:
 
1044
                continue
 
1045
            if a_host is not None:
 
1046
                if not (host == a_host
 
1047
                        or (a_host.startswith('.') and host.endswith(a_host))):
 
1048
                    continue
 
1049
            if a_port is not None and port != a_port:
 
1050
                continue
 
1051
            if (a_path is not None and path is not None
 
1052
                and not path.startswith(a_path)):
 
1053
                continue
 
1054
            if (a_user is not None and user is not None
 
1055
                and a_user != user):
 
1056
                # Never contradict the caller about the user to be used
 
1057
                continue
 
1058
            if a_user is None:
 
1059
                # Can't find a user
 
1060
                continue
 
1061
            credentials = dict(name=auth_def_name,
 
1062
                               user=a_user,
 
1063
                               password=auth_def.get('password', None),
 
1064
                               verify_certificates=a_verify_certificates)
 
1065
            self.decode_password(credentials,
 
1066
                                 auth_def.get('password_encoding', None))
 
1067
            if 'auth' in debug.debug_flags:
 
1068
                trace.mutter("Using authentication section: %r", auth_def_name)
 
1069
            break
 
1070
 
 
1071
        return credentials
 
1072
 
 
1073
    def get_user(self, scheme, host, port=None,
 
1074
                 realm=None, path=None, prompt=None):
 
1075
        """Get a user from authentication file.
 
1076
 
 
1077
        :param scheme: protocol
 
1078
 
 
1079
        :param host: the server address
 
1080
 
 
1081
        :param port: the associated port (optional)
 
1082
 
 
1083
        :param realm: the realm sent by the server (optional)
 
1084
 
 
1085
        :param path: the absolute path on the server (optional)
 
1086
 
 
1087
        :return: The found user.
 
1088
        """
 
1089
        credentials = self.get_credentials(scheme, host, port, user=None,
 
1090
                                           path=path)
 
1091
        if credentials is not None:
 
1092
            user = credentials['user']
 
1093
        else:
 
1094
            user = None
 
1095
        return user
 
1096
 
 
1097
    def get_password(self, scheme, host, user, port=None,
 
1098
                     realm=None, path=None, prompt=None):
 
1099
        """Get a password from authentication file or prompt the user for one.
 
1100
 
 
1101
        :param scheme: protocol
 
1102
 
 
1103
        :param host: the server address
 
1104
 
 
1105
        :param port: the associated port (optional)
 
1106
 
 
1107
        :param user: login
 
1108
 
 
1109
        :param realm: the realm sent by the server (optional)
 
1110
 
 
1111
        :param path: the absolute path on the server (optional)
 
1112
 
 
1113
        :return: The found password or the one entered by the user.
 
1114
        """
 
1115
        credentials = self.get_credentials(scheme, host, port, user, path)
 
1116
        if credentials is not None:
 
1117
            password = credentials['password']
 
1118
            if password is not None and scheme is 'ssh':
 
1119
                trace.warning('password ignored in section [%s],'
 
1120
                              ' use an ssh agent instead'
 
1121
                              % credentials['name'])
 
1122
                password = None
 
1123
        else:
 
1124
            password = None
 
1125
        # Prompt user only if we could't find a password
 
1126
        if password is None:
 
1127
            if prompt is None:
 
1128
                # Create a default prompt suitable for most cases
 
1129
                prompt = '%s' % scheme.upper() + ' %(user)s@%(host)s password'
 
1130
            # Special handling for optional fields in the prompt
 
1131
            if port is not None:
 
1132
                prompt_host = '%s:%d' % (host, port)
 
1133
            else:
 
1134
                prompt_host = host
 
1135
            password = ui.ui_factory.get_password(prompt,
 
1136
                                                  host=prompt_host, user=user)
 
1137
        return password
 
1138
 
 
1139
    def decode_password(self, credentials, encoding):
 
1140
        return credentials
 
1141
 
 
1142
 
 
1143
class BzrDirConfig(object):
 
1144
 
 
1145
    def __init__(self, transport):
 
1146
        self._config = TransportConfig(transport, 'control.conf')
 
1147
 
 
1148
    def set_default_stack_on(self, value):
 
1149
        """Set the default stacking location.
 
1150
 
 
1151
        It may be set to a location, or None.
 
1152
 
 
1153
        This policy affects all branches contained by this bzrdir, except for
 
1154
        those under repositories.
 
1155
        """
 
1156
        if value is None:
 
1157
            self._config.set_option('', 'default_stack_on')
 
1158
        else:
 
1159
            self._config.set_option(value, 'default_stack_on')
 
1160
 
 
1161
    def get_default_stack_on(self):
 
1162
        """Return the default stacking location.
 
1163
 
 
1164
        This will either be a location, or None.
 
1165
 
 
1166
        This policy affects all branches contained by this bzrdir, except for
 
1167
        those under repositories.
 
1168
        """
 
1169
        value = self._config.get_option('default_stack_on')
 
1170
        if value == '':
 
1171
            value = None
 
1172
        return value
 
1173
 
 
1174
 
 
1175
class TransportConfig(object):
 
1176
    """A Config that reads/writes a config file on a Transport.
 
1177
 
 
1178
    It is a low-level object that considers config data to be name/value pairs
 
1179
    that may be associated with a section.  Assigning meaning to the these
 
1180
    values is done at higher levels like TreeConfig.
 
1181
    """
 
1182
 
 
1183
    def __init__(self, transport, filename):
 
1184
        self._transport = transport
 
1185
        self._filename = filename
 
1186
 
 
1187
    def get_option(self, name, section=None, default=None):
 
1188
        """Return the value associated with a named option.
 
1189
 
 
1190
        :param name: The name of the value
 
1191
        :param section: The section the option is in (if any)
 
1192
        :param default: The value to return if the value is not set
 
1193
        :return: The value or default value
 
1194
        """
 
1195
        configobj = self._get_configobj()
 
1196
        if section is None:
 
1197
            section_obj = configobj
 
1198
        else:
 
1199
            try:
 
1200
                section_obj = configobj[section]
 
1201
            except KeyError:
 
1202
                return default
 
1203
        return section_obj.get(name, default)
 
1204
 
 
1205
    def set_option(self, value, name, section=None):
 
1206
        """Set the value associated with a named option.
 
1207
 
 
1208
        :param value: The value to set
 
1209
        :param name: The name of the value to set
 
1210
        :param section: The section the option is in (if any)
 
1211
        """
 
1212
        configobj = self._get_configobj()
 
1213
        if section is None:
 
1214
            configobj[name] = value
 
1215
        else:
 
1216
            configobj.setdefault(section, {})[name] = value
 
1217
        self._set_configobj(configobj)
 
1218
 
 
1219
    def _get_configobj(self):
 
1220
        try:
 
1221
            return ConfigObj(self._transport.get(self._filename),
 
1222
                             encoding='utf-8')
 
1223
        except errors.NoSuchFile:
 
1224
            return ConfigObj(encoding='utf-8')
 
1225
 
 
1226
    def _set_configobj(self, configobj):
 
1227
        out_file = StringIO()
 
1228
        configobj.write(out_file)
 
1229
        out_file.seek(0)
 
1230
        self._transport.put_file(self._filename, out_file)