~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Aaron Bentley
  • Date: 2007-02-06 14:52:16 UTC
  • mfrom: (2266 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2268.
  • Revision ID: abentley@panoramicfeedback.com-20070206145216-fcpi8o3ufvuzwbp9
Merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2007 Canonical Ltd
 
1
# Copyright (C) 2005 Canonical Ltd
2
2
#   Authors: Robert Collins <robert.collins@canonical.com>
3
 
#            and others
4
3
#
5
4
# This program is free software; you can redistribute it and/or modify
6
5
# it under the terms of the GNU General Public License as published by
70
69
import errno
71
70
from fnmatch import fnmatch
72
71
import re
73
 
from cStringIO import StringIO
 
72
from StringIO import StringIO
74
73
 
75
74
import bzrlib
76
75
from bzrlib import (
77
 
    debug,
78
76
    errors,
79
 
    mail_client,
80
77
    osutils,
81
78
    symbol_versioning,
82
 
    trace,
83
 
    ui,
84
79
    urlutils,
85
80
    win32utils,
86
81
    )
87
 
from bzrlib.util.configobj import configobj
 
82
import bzrlib.util.configobj.configobj as configobj
88
83
""")
89
84
 
 
85
from bzrlib.trace import mutter, warning
 
86
 
90
87
 
91
88
CHECK_IF_POSSIBLE=0
92
89
CHECK_ALWAYS=1
144
141
        """Get the users pop up editor."""
145
142
        raise NotImplementedError
146
143
 
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
 
 
169
144
    def _get_signature_checking(self):
170
145
        """Template method to override signature checking policy."""
171
146
 
237
212
        v = os.environ.get('BZR_EMAIL')
238
213
        if v:
239
214
            return v.decode(bzrlib.user_encoding)
240
 
 
 
215
        v = os.environ.get('BZREMAIL')
 
216
        if v:
 
217
            warning('BZREMAIL is deprecated in favor of BZR_EMAIL. Please update your configuration.')
 
218
            return v.decode(bzrlib.user_encoding)
 
219
    
241
220
        v = self._get_user_id()
242
221
        if v:
243
222
            return v
244
 
 
 
223
        
245
224
        v = os.environ.get('EMAIL')
246
225
        if v:
247
226
            return v.decode(bzrlib.user_encoding)
272
251
        if policy is None:
273
252
            policy = self._get_signature_checking()
274
253
            if policy is not None:
275
 
                trace.warning("Please use create_signatures,"
276
 
                              " not check_signatures to set signing policy.")
 
254
                warning("Please use create_signatures, not check_signatures "
 
255
                        "to set signing policy.")
277
256
            if policy == CHECK_ALWAYS:
278
257
                return True
279
258
        elif policy == SIGN_ALWAYS:
292
271
    def _get_nickname(self):
293
272
        return None
294
273
 
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
 
 
304
274
 
305
275
class IniBasedConfig(Config):
306
276
    """A configuration policy that draws from ini files."""
440
410
 
441
411
    def set_user_option(self, option, value):
442
412
        """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):
465
413
        # FIXME: RBC 20051029 This should refresh the parser and also take a
466
414
        # file lock on bazaar.conf.
467
415
        conf_dir = os.path.dirname(self._get_filename())
468
416
        ensure_config_dir_exists(conf_dir)
469
 
        self._get_parser().setdefault(section, {})[option] = value
470
 
        self._write_config_file()
471
 
 
472
 
    def _write_config_file(self):
 
417
        if 'DEFAULT' not in self._get_parser():
 
418
            self._get_parser()['DEFAULT'] = {}
 
419
        self._get_parser()['DEFAULT'][option] = value
473
420
        f = open(self._get_filename(), 'wb')
474
421
        self._get_parser().write(f)
475
422
        f.close()
480
427
 
481
428
    def __init__(self, location):
482
429
        name_generator = locations_config_filename
483
 
        if (not os.path.exists(name_generator()) and
 
430
        if (not os.path.exists(name_generator()) and 
484
431
                os.path.exists(branches_config_filename())):
485
432
            if sys.platform == 'win32':
486
 
                trace.warning('Please rename %s to %s'
487
 
                              % (branches_config_filename(),
488
 
                                 locations_config_filename()))
 
433
                warning('Please rename %s to %s' 
 
434
                         % (branches_config_filename(),
 
435
                            locations_config_filename()))
489
436
            else:
490
 
                trace.warning('Please rename ~/.bazaar/branches.conf'
491
 
                              ' to ~/.bazaar/locations.conf')
 
437
                warning('Please rename ~/.bazaar/branches.conf'
 
438
                        ' to ~/.bazaar/locations.conf')
492
439
            name_generator = branches_config_filename
493
440
        super(LocationConfig, self).__init__(name_generator)
494
441
        # local file locations are looked up by local path, rather than
593
540
 
594
541
    def set_user_option(self, option, value, store=STORE_LOCATION):
595
542
        """Save option and its value in the configuration."""
596
 
        if store not in [STORE_LOCATION,
 
543
        assert store in [STORE_LOCATION,
597
544
                         STORE_LOCATION_NORECURSE,
598
 
                         STORE_LOCATION_APPENDPATH]:
599
 
            raise ValueError('bad storage policy %r for %r' %
600
 
                (store, option))
 
545
                         STORE_LOCATION_APPENDPATH], 'bad storage policy'
601
546
        # FIXME: RBC 20051029 This should refresh the parser and also take a
602
547
        # file lock on locations.conf.
603
548
        conf_dir = os.path.dirname(self._get_filename())
664
609
    def _get_user_id(self):
665
610
        """Return the full user id for the branch.
666
611
    
667
 
        e.g. "John Hacker <jhacker@example.com>"
 
612
        e.g. "John Hacker <jhacker@foo.org>"
668
613
        This is looked up in the email controlfile for the branch.
669
614
        """
670
615
        try:
671
 
            return (self.branch._transport.get_bytes("email")
 
616
            return (self.branch.control_files.get_utf8("email") 
 
617
                    .read()
672
618
                    .decode(bzrlib.user_encoding)
673
619
                    .rstrip("\r\n"))
674
620
        except errors.NoSuchFile, e:
692
638
                return value
693
639
        return None
694
640
 
695
 
    def set_user_option(self, name, value, store=STORE_BRANCH,
696
 
        warn_masked=False):
 
641
    def set_user_option(self, name, value, store=STORE_BRANCH):
697
642
        if store == STORE_BRANCH:
698
643
            self._get_branch_data_config().set_option(value, name)
699
644
        elif store == STORE_GLOBAL:
700
645
            self._get_global_config().set_user_option(name, value)
701
646
        else:
702
647
            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
 
 
718
648
 
719
649
    def _gpg_signing_command(self):
720
650
        """See Config.gpg_signing_command."""
764
694
        if sys.platform == 'win32':
765
695
            parent_dir = os.path.dirname(path)
766
696
            if not os.path.isdir(parent_dir):
767
 
                trace.mutter('creating config parent directory: %r', parent_dir)
 
697
                mutter('creating config parent directory: %r', parent_dir)
768
698
            os.mkdir(parent_dir)
769
 
        trace.mutter('creating config directory: %r', path)
 
699
        mutter('creating config directory: %r', path)
770
700
        os.mkdir(path)
771
701
 
772
702
 
784
714
        if base is None:
785
715
            base = os.environ.get('HOME', None)
786
716
        if base is None:
787
 
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA,'
788
 
                                  ' or HOME set')
 
717
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA, or HOME set')
789
718
        return osutils.pathjoin(base, 'bazaar', '2.0')
790
719
    else:
791
720
        # cygwin, linux, and darwin all have a $HOME directory
809
738
    return osutils.pathjoin(config_dir(), 'locations.conf')
810
739
 
811
740
 
812
 
def authentication_config_filename():
813
 
    """Return per-user authentication ini file filename."""
814
 
    return osutils.pathjoin(config_dir(), 'authentication.conf')
815
 
 
816
 
 
817
741
def user_ignore_config_filename():
818
742
    """Return the user default ignore filename"""
819
743
    return osutils.pathjoin(config_dir(), 'ignore')
887
811
    return realname, (username + '@' + socket.gethostname())
888
812
 
889
813
 
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
 
 
899
814
def extract_email_address(e):
900
815
    """Return just the address part of an email string.
901
 
 
 
816
    
902
817
    That is just the user@domain part, nothing else. 
903
818
    This part is required to contain only ascii characters.
904
819
    If it can't be extracted, raises an error.
905
 
 
 
820
    
906
821
    >>> extract_email_address('Jane Tester <jane@test.com>')
907
822
    "jane@test.com"
908
823
    """
909
 
    name, email = parse_username(e)
910
 
    if not email:
 
824
    m = re.search(r'[\w+.-]+@[\w+.-]+', e)
 
825
    if not m:
911
826
        raise errors.NoEmailInUsername(e)
912
 
    return email
 
827
    return m.group(0)
913
828
 
914
829
 
915
830
class TreeConfig(IniBasedConfig):
916
831
    """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
 
 
920
832
    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')
925
833
        self.branch = branch
926
834
 
927
835
    def _get_parser(self, file=None):
928
836
        if file is not None:
929
837
            return IniBasedConfig._get_parser(file)
930
 
        return self._config._get_configobj()
 
838
        return self._get_config()
 
839
 
 
840
    def _get_config(self):
 
841
        try:
 
842
            obj = ConfigObj(self.branch.control_files.get('branch.conf'), 
 
843
                            encoding='utf-8')
 
844
        except errors.NoSuchFile:
 
845
            obj = ConfigObj(encoding='utf=8')
 
846
        return obj
931
847
 
932
848
    def get_option(self, name, section=None, default=None):
933
849
        self.branch.lock_read()
934
850
        try:
935
 
            return self._config.get_option(name, section, default)
 
851
            obj = self._get_config()
 
852
            try:
 
853
                if section is not None:
 
854
                    obj[section]
 
855
                result = obj[name]
 
856
            except KeyError:
 
857
                result = default
936
858
        finally:
937
859
            self.branch.unlock()
938
860
        return result
941
863
        """Set a per-branch configuration option"""
942
864
        self.branch.lock_write()
943
865
        try:
944
 
            self._config.set_option(value, name, section)
 
866
            cfg_obj = self._get_config()
 
867
            if section is None:
 
868
                obj = cfg_obj
 
869
            else:
 
870
                try:
 
871
                    obj = cfg_obj[section]
 
872
                except KeyError:
 
873
                    cfg_obj[section] = {}
 
874
                    obj = cfg_obj[section]
 
875
            obj[name] = value
 
876
            out_file = StringIO()
 
877
            cfg_obj.write(out_file)
 
878
            out_file.seek(0)
 
879
            self.branch.control_files.put('branch.conf', out_file)
945
880
        finally:
946
881
            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)