~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: John Arbash Meinel
  • Author(s): Mark Hammond
  • Date: 2008-09-09 17:02:21 UTC
  • mto: This revision was merged to the branch mainline in revision 3697.
  • Revision ID: john@arbash-meinel.com-20080909170221-svim3jw2mrz0amp3
An updated transparent icon for bzr.

Show diffs side-by-side

added added

removed removed

Lines of Context:
84
84
    urlutils,
85
85
    win32utils,
86
86
    )
87
 
import bzrlib.util.configobj.configobj as configobj
 
87
from bzrlib.util.configobj import configobj
88
88
""")
89
89
 
90
90
 
147
147
    def get_mail_client(self):
148
148
        """Get a mail client to use"""
149
149
        selected_client = self.get_user_option('mail_client')
 
150
        _registry = mail_client.mail_client_registry
150
151
        try:
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]
 
152
            mail_client_class = _registry.get(selected_client)
164
153
        except KeyError:
165
154
            raise errors.UnknownMailClient(selected_client)
166
155
        return mail_client_class(self)
439
428
 
440
429
    def set_user_option(self, option, value):
441
430
        """Save option and its value in the configuration."""
 
431
        self._set_option(option, value, 'DEFAULT')
 
432
 
 
433
    def get_aliases(self):
 
434
        """Return the aliases section."""
 
435
        if 'ALIASES' in self._get_parser():
 
436
            return self._get_parser()['ALIASES']
 
437
        else:
 
438
            return {}
 
439
 
 
440
    def set_alias(self, alias_name, alias_command):
 
441
        """Save the alias in the configuration."""
 
442
        self._set_option(alias_name, alias_command, 'ALIASES')
 
443
 
 
444
    def unset_alias(self, alias_name):
 
445
        """Unset an existing alias."""
 
446
        aliases = self._get_parser().get('ALIASES')
 
447
        if not aliases or alias_name not in aliases:
 
448
            raise errors.NoSuchAlias(alias_name)
 
449
        del aliases[alias_name]
 
450
        self._write_config_file()
 
451
 
 
452
    def _set_option(self, option, value, section):
442
453
        # FIXME: RBC 20051029 This should refresh the parser and also take a
443
454
        # file lock on bazaar.conf.
444
455
        conf_dir = os.path.dirname(self._get_filename())
445
456
        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
 
457
        self._get_parser().setdefault(section, {})[option] = value
 
458
        self._write_config_file()
 
459
 
 
460
    def _write_config_file(self):
449
461
        f = open(self._get_filename(), 'wb')
450
462
        self._get_parser().write(f)
451
463
        f.close()
569
581
 
570
582
    def set_user_option(self, option, value, store=STORE_LOCATION):
571
583
        """Save option and its value in the configuration."""
572
 
        assert store in [STORE_LOCATION,
 
584
        if store not in [STORE_LOCATION,
573
585
                         STORE_LOCATION_NORECURSE,
574
 
                         STORE_LOCATION_APPENDPATH], 'bad storage policy'
 
586
                         STORE_LOCATION_APPENDPATH]:
 
587
            raise ValueError('bad storage policy %r for %r' %
 
588
                (store, option))
575
589
        # FIXME: RBC 20051029 This should refresh the parser and also take a
576
590
        # file lock on locations.conf.
577
591
        conf_dir = os.path.dirname(self._get_filename())
638
652
    def _get_user_id(self):
639
653
        """Return the full user id for the branch.
640
654
    
641
 
        e.g. "John Hacker <jhacker@foo.org>"
 
655
        e.g. "John Hacker <jhacker@example.com>"
642
656
        This is looked up in the email controlfile for the branch.
643
657
        """
644
658
        try:
645
 
            return (self.branch.control_files.get_utf8("email") 
646
 
                    .read()
 
659
            return (self.branch._transport.get_bytes("email")
647
660
                    .decode(bzrlib.user_encoding)
648
661
                    .rstrip("\r\n"))
649
662
        except errors.NoSuchFile, e:
759
772
        if base is None:
760
773
            base = os.environ.get('HOME', None)
761
774
        if base is None:
762
 
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA, or HOME set')
 
775
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA,'
 
776
                                  ' or HOME set')
763
777
        return osutils.pathjoin(base, 'bazaar', '2.0')
764
778
    else:
765
779
        # cygwin, linux, and darwin all have a $HOME directory
861
875
    return realname, (username + '@' + socket.gethostname())
862
876
 
863
877
 
 
878
def parse_username(username):
 
879
    """Parse e-mail username and return a (name, address) tuple."""
 
880
    match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
 
881
    if match is None:
 
882
        return (username, '')
 
883
    else:
 
884
        return (match.group(1), match.group(2))
 
885
 
 
886
 
864
887
def extract_email_address(e):
865
888
    """Return just the address part of an email string.
866
 
    
 
889
 
867
890
    That is just the user@domain part, nothing else. 
868
891
    This part is required to contain only ascii characters.
869
892
    If it can't be extracted, raises an error.
870
 
    
 
893
 
871
894
    >>> extract_email_address('Jane Tester <jane@test.com>')
872
895
    "jane@test.com"
873
896
    """
874
 
    m = re.search(r'[\w+.-]+@[\w+.-]+', e)
875
 
    if not m:
 
897
    name, email = parse_username(e)
 
898
    if not email:
876
899
        raise errors.NoEmailInUsername(e)
877
 
    return m.group(0)
 
900
    return email
878
901
 
879
902
 
880
903
class TreeConfig(IniBasedConfig):
881
904
    """Branch configuration data associated with its contents, not location"""
882
905
 
 
906
    # XXX: Really needs a better name, as this is not part of the tree! -- mbp 20080507
 
907
 
883
908
    def __init__(self, branch):
 
909
        # XXX: Really this should be asking the branch for its configuration
 
910
        # data, rather than relying on a Transport, so that it can work 
 
911
        # more cleanly with a RemoteBranch that has no transport.
 
912
        self._config = TransportConfig(branch._transport, 'branch.conf')
884
913
        self.branch = branch
885
914
 
886
915
    def _get_parser(self, file=None):
887
916
        if file is not None:
888
917
            return IniBasedConfig._get_parser(file)
889
 
        return self._get_config()
890
 
 
891
 
    def _get_config(self):
892
 
        try:
893
 
            obj = ConfigObj(self.branch.control_files.get('branch.conf'),
894
 
                            encoding='utf-8')
895
 
        except errors.NoSuchFile:
896
 
            obj = ConfigObj(encoding='utf=8')
897
 
        return obj
 
918
        return self._config._get_configobj()
898
919
 
899
920
    def get_option(self, name, section=None, default=None):
900
921
        self.branch.lock_read()
901
922
        try:
902
 
            obj = self._get_config()
903
 
            try:
904
 
                if section is not None:
905
 
                    obj = obj[section]
906
 
                result = obj[name]
907
 
            except KeyError:
908
 
                result = default
 
923
            return self._config.get_option(name, section, default)
909
924
        finally:
910
925
            self.branch.unlock()
911
926
        return result
914
929
        """Set a per-branch configuration option"""
915
930
        self.branch.lock_write()
916
931
        try:
917
 
            cfg_obj = self._get_config()
918
 
            if section is None:
919
 
                obj = cfg_obj
920
 
            else:
921
 
                try:
922
 
                    obj = cfg_obj[section]
923
 
                except KeyError:
924
 
                    cfg_obj[section] = {}
925
 
                    obj = cfg_obj[section]
926
 
            obj[name] = value
927
 
            out_file = StringIO()
928
 
            cfg_obj.write(out_file)
929
 
            out_file.seek(0)
930
 
            self.branch.control_files.put('branch.conf', out_file)
 
932
            self._config.set_option(value, name, section)
931
933
        finally:
932
934
            self.branch.unlock()
933
935
 
1005
1007
        """
1006
1008
        credentials = None
1007
1009
        for auth_def_name, auth_def in self._get_config().items():
 
1010
            if type(auth_def) is not configobj.Section:
 
1011
                raise ValueError("%s defined outside a section" % auth_def_name)
 
1012
 
1008
1013
            a_scheme, a_host, a_user, a_path = map(
1009
1014
                auth_def.get, ['scheme', 'host', 'user', 'path'])
1010
1015
 
1042
1047
                # Can't find a user
1043
1048
                continue
1044
1049
            credentials = dict(name=auth_def_name,
1045
 
                               user=a_user, password=auth_def['password'],
 
1050
                               user=a_user,
 
1051
                               password=auth_def.get('password', None),
1046
1052
                               verify_certificates=a_verify_certificates)
1047
1053
            self.decode_password(credentials,
1048
1054
                                 auth_def.get('password_encoding', None))
1097
1103
        credentials = self.get_credentials(scheme, host, port, user, path)
1098
1104
        if credentials is not None:
1099
1105
            password = credentials['password']
 
1106
            if password is not None and scheme is 'ssh':
 
1107
                trace.warning('password ignored in section [%s],'
 
1108
                              ' use an ssh agent instead'
 
1109
                              % credentials['name'])
 
1110
                password = None
1100
1111
        else:
1101
1112
            password = None
1102
1113
        # Prompt user only if we could't find a password
1103
1114
        if password is None:
1104
1115
            if prompt is None:
1105
 
                # Create a default prompt suitable for most of the cases
 
1116
                # Create a default prompt suitable for most cases
1106
1117
                prompt = '%s' % scheme.upper() + ' %(user)s@%(host)s password'
1107
1118
            # Special handling for optional fields in the prompt
1108
1119
            if port is not None:
1115
1126
 
1116
1127
    def decode_password(self, credentials, encoding):
1117
1128
        return credentials
 
1129
 
 
1130
 
 
1131
class BzrDirConfig(object):
 
1132
 
 
1133
    def __init__(self, transport):
 
1134
        self._config = TransportConfig(transport, 'control.conf')
 
1135
 
 
1136
    def set_default_stack_on(self, value):
 
1137
        """Set the default stacking location.
 
1138
 
 
1139
        It may be set to a location, or None.
 
1140
 
 
1141
        This policy affects all branches contained by this bzrdir, except for
 
1142
        those under repositories.
 
1143
        """
 
1144
        if value is None:
 
1145
            self._config.set_option('', 'default_stack_on')
 
1146
        else:
 
1147
            self._config.set_option(value, 'default_stack_on')
 
1148
 
 
1149
    def get_default_stack_on(self):
 
1150
        """Return the default stacking location.
 
1151
 
 
1152
        This will either be a location, or None.
 
1153
 
 
1154
        This policy affects all branches contained by this bzrdir, except for
 
1155
        those under repositories.
 
1156
        """
 
1157
        value = self._config.get_option('default_stack_on')
 
1158
        if value == '':
 
1159
            value = None
 
1160
        return value
 
1161
 
 
1162
 
 
1163
class TransportConfig(object):
 
1164
    """A Config that reads/writes a config file on a Transport.
 
1165
 
 
1166
    It is a low-level object that considers config data to be name/value pairs
 
1167
    that may be associated with a section.  Assigning meaning to the these
 
1168
    values is done at higher levels like TreeConfig.
 
1169
    """
 
1170
 
 
1171
    def __init__(self, transport, filename):
 
1172
        self._transport = transport
 
1173
        self._filename = filename
 
1174
 
 
1175
    def get_option(self, name, section=None, default=None):
 
1176
        """Return the value associated with a named option.
 
1177
 
 
1178
        :param name: The name of the value
 
1179
        :param section: The section the option is in (if any)
 
1180
        :param default: The value to return if the value is not set
 
1181
        :return: The value or default value
 
1182
        """
 
1183
        configobj = self._get_configobj()
 
1184
        if section is None:
 
1185
            section_obj = configobj
 
1186
        else:
 
1187
            try:
 
1188
                section_obj = configobj[section]
 
1189
            except KeyError:
 
1190
                return default
 
1191
        return section_obj.get(name, default)
 
1192
 
 
1193
    def set_option(self, value, name, section=None):
 
1194
        """Set the value associated with a named option.
 
1195
 
 
1196
        :param value: The value to set
 
1197
        :param name: The name of the value to set
 
1198
        :param section: The section the option is in (if any)
 
1199
        """
 
1200
        configobj = self._get_configobj()
 
1201
        if section is None:
 
1202
            configobj[name] = value
 
1203
        else:
 
1204
            configobj.setdefault(section, {})[name] = value
 
1205
        self._set_configobj(configobj)
 
1206
 
 
1207
    def _get_configobj(self):
 
1208
        try:
 
1209
            return ConfigObj(self._transport.get(self._filename),
 
1210
                             encoding='utf-8')
 
1211
        except errors.NoSuchFile:
 
1212
            return ConfigObj(encoding='utf-8')
 
1213
 
 
1214
    def _set_configobj(self, configobj):
 
1215
        out_file = StringIO()
 
1216
        configobj.write(out_file)
 
1217
        out_file.seek(0)
 
1218
        self._transport.put_file(self._filename, out_file)