~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-12-04 03:52:13 UTC
  • mfrom: (1551.19.15 Aaron's mergeable stuff)
  • Revision ID: pqm@pqm.ubuntu.com-20071204035213-2kot5u403spjchen
Merge warns when criss-cross encountered + docs

Show diffs side-by-side

added added

removed removed

Lines of Context:
151
151
            mail_client_class = {
152
152
                None: mail_client.DefaultMail,
153
153
                # Specific clients
154
 
                'emacsclient': mail_client.EmacsMail,
155
154
                'evolution': mail_client.Evolution,
156
155
                'kmail': mail_client.KMail,
157
156
                'mutt': mail_client.Mutt,
570
569
 
571
570
    def set_user_option(self, option, value, store=STORE_LOCATION):
572
571
        """Save option and its value in the configuration."""
573
 
        if store not in [STORE_LOCATION,
 
572
        assert store in [STORE_LOCATION,
574
573
                         STORE_LOCATION_NORECURSE,
575
 
                         STORE_LOCATION_APPENDPATH]:
576
 
            raise ValueError('bad storage policy %r for %r' %
577
 
                (store, option))
 
574
                         STORE_LOCATION_APPENDPATH], 'bad storage policy'
578
575
        # FIXME: RBC 20051029 This should refresh the parser and also take a
579
576
        # file lock on locations.conf.
580
577
        conf_dir = os.path.dirname(self._get_filename())
641
638
    def _get_user_id(self):
642
639
        """Return the full user id for the branch.
643
640
    
644
 
        e.g. "John Hacker <jhacker@example.com>"
 
641
        e.g. "John Hacker <jhacker@foo.org>"
645
642
        This is looked up in the email controlfile for the branch.
646
643
        """
647
644
        try:
648
 
            return (self.branch._transport.get_bytes("email")
 
645
            return (self.branch.control_files.get_utf8("email") 
 
646
                    .read()
649
647
                    .decode(bzrlib.user_encoding)
650
648
                    .rstrip("\r\n"))
651
649
        except errors.NoSuchFile, e:
864
862
    return realname, (username + '@' + socket.gethostname())
865
863
 
866
864
 
867
 
def parse_username(username):
868
 
    """Parse e-mail username and return a (name, address) tuple."""
869
 
    match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
870
 
    if match is None:
871
 
        return (username, '')
872
 
    else:
873
 
        return (match.group(1), match.group(2))
874
 
 
875
 
 
876
865
def extract_email_address(e):
877
866
    """Return just the address part of an email string.
878
 
 
 
867
    
879
868
    That is just the user@domain part, nothing else. 
880
869
    This part is required to contain only ascii characters.
881
870
    If it can't be extracted, raises an error.
882
 
 
 
871
    
883
872
    >>> extract_email_address('Jane Tester <jane@test.com>')
884
873
    "jane@test.com"
885
874
    """
886
 
    name, email = parse_username(e)
887
 
    if not email:
 
875
    m = re.search(r'[\w+.-]+@[\w+.-]+', e)
 
876
    if not m:
888
877
        raise errors.NoEmailInUsername(e)
889
 
    return email
 
878
    return m.group(0)
890
879
 
891
880
 
892
881
class TreeConfig(IniBasedConfig):
893
882
    """Branch configuration data associated with its contents, not location"""
894
883
 
895
 
    # XXX: Really needs a better name, as this is not part of the tree! -- mbp 20080507
896
 
 
897
884
    def __init__(self, branch):
898
 
        # XXX: Really this should be asking the branch for its configuration
899
 
        # data, rather than relying on a Transport, so that it can work 
900
 
        # more cleanly with a RemoteBranch that has no transport.
901
 
        self._config = TransportConfig(branch._transport, 'branch.conf')
902
885
        self.branch = branch
903
886
 
904
887
    def _get_parser(self, file=None):
905
888
        if file is not None:
906
889
            return IniBasedConfig._get_parser(file)
907
 
        return self._config._get_configobj()
 
890
        return self._get_config()
 
891
 
 
892
    def _get_config(self):
 
893
        try:
 
894
            obj = ConfigObj(self.branch.control_files.get('branch.conf'),
 
895
                            encoding='utf-8')
 
896
        except errors.NoSuchFile:
 
897
            obj = ConfigObj(encoding='utf=8')
 
898
        return obj
908
899
 
909
900
    def get_option(self, name, section=None, default=None):
910
901
        self.branch.lock_read()
911
902
        try:
912
 
            return self._config.get_option(name, section, default)
 
903
            obj = self._get_config()
 
904
            try:
 
905
                if section is not None:
 
906
                    obj = obj[section]
 
907
                result = obj[name]
 
908
            except KeyError:
 
909
                result = default
913
910
        finally:
914
911
            self.branch.unlock()
915
912
        return result
918
915
        """Set a per-branch configuration option"""
919
916
        self.branch.lock_write()
920
917
        try:
921
 
            self._config.set_option(value, name, section)
 
918
            cfg_obj = self._get_config()
 
919
            if section is None:
 
920
                obj = cfg_obj
 
921
            else:
 
922
                try:
 
923
                    obj = cfg_obj[section]
 
924
                except KeyError:
 
925
                    cfg_obj[section] = {}
 
926
                    obj = cfg_obj[section]
 
927
            obj[name] = value
 
928
            out_file = StringIO()
 
929
            cfg_obj.write(out_file)
 
930
            out_file.seek(0)
 
931
            self.branch.control_files.put('branch.conf', out_file)
922
932
        finally:
923
933
            self.branch.unlock()
924
934
 
996
1006
        """
997
1007
        credentials = None
998
1008
        for auth_def_name, auth_def in self._get_config().items():
999
 
            if type(auth_def) is not configobj.Section:
1000
 
                raise ValueError("%s defined outside a section" % auth_def_name)
1001
 
 
1002
1009
            a_scheme, a_host, a_user, a_path = map(
1003
1010
                auth_def.get, ['scheme', 'host', 'user', 'path'])
1004
1011
 
1036
1043
                # Can't find a user
1037
1044
                continue
1038
1045
            credentials = dict(name=auth_def_name,
1039
 
                               user=a_user,
1040
 
                               password=auth_def.get('password', None),
 
1046
                               user=a_user, password=auth_def['password'],
1041
1047
                               verify_certificates=a_verify_certificates)
1042
1048
            self.decode_password(credentials,
1043
1049
                                 auth_def.get('password_encoding', None))
1092
1098
        credentials = self.get_credentials(scheme, host, port, user, path)
1093
1099
        if credentials is not None:
1094
1100
            password = credentials['password']
1095
 
            if password is not None and scheme is 'ssh':
1096
 
                trace.warning('password ignored in section [%s],'
1097
 
                              ' use an ssh agent instead'
1098
 
                              % credentials['name'])
1099
 
                password = None
1100
1101
        else:
1101
1102
            password = None
1102
1103
        # Prompt user only if we could't find a password
1103
1104
        if password is None:
1104
1105
            if prompt is None:
1105
 
                # Create a default prompt suitable for most cases
 
1106
                # Create a default prompt suitable for most of the cases
1106
1107
                prompt = '%s' % scheme.upper() + ' %(user)s@%(host)s password'
1107
1108
            # Special handling for optional fields in the prompt
1108
1109
            if port is not None:
1115
1116
 
1116
1117
    def decode_password(self, credentials, encoding):
1117
1118
        return credentials
1118
 
 
1119
 
 
1120
 
class TransportConfig(object):
1121
 
    """A Config that reads/writes a config file on a Transport.
1122
 
 
1123
 
    It is a low-level object that considers config data to be name/value pairs
1124
 
    that may be associated with a section.  Assigning meaning to the these
1125
 
    values is done at higher levels like TreeConfig.
1126
 
    """
1127
 
 
1128
 
    def __init__(self, transport, filename):
1129
 
        self._transport = transport
1130
 
        self._filename = filename
1131
 
 
1132
 
    def get_option(self, name, section=None, default=None):
1133
 
        """Return the value associated with a named option.
1134
 
 
1135
 
        :param name: The name of the value
1136
 
        :param section: The section the option is in (if any)
1137
 
        :param default: The value to return if the value is not set
1138
 
        :return: The value or default value
1139
 
        """
1140
 
        configobj = self._get_configobj()
1141
 
        if section is None:
1142
 
            section_obj = configobj
1143
 
        else:
1144
 
            try:
1145
 
                section_obj = configobj[section]
1146
 
            except KeyError:
1147
 
                return default
1148
 
        return section_obj.get(name, default)
1149
 
 
1150
 
    def set_option(self, value, name, section=None):
1151
 
        """Set the value associated with a named option.
1152
 
 
1153
 
        :param value: The value to set
1154
 
        :param name: The name of the value to set
1155
 
        :param section: The section the option is in (if any)
1156
 
        """
1157
 
        configobj = self._get_configobj()
1158
 
        if section is None:
1159
 
            configobj[name] = value
1160
 
        else:
1161
 
            configobj.setdefault(section, {})[name] = value
1162
 
        self._set_configobj(configobj)
1163
 
 
1164
 
    def _get_configobj(self):
1165
 
        try:
1166
 
            return ConfigObj(self._transport.get(self._filename),
1167
 
                             encoding='utf-8')
1168
 
        except errors.NoSuchFile:
1169
 
            return ConfigObj(encoding='utf-8')
1170
 
 
1171
 
    def _set_configobj(self, configobj):
1172
 
        out_file = StringIO()
1173
 
        configobj.write(out_file)
1174
 
        out_file.seek(0)
1175
 
        self._transport.put_file(self._filename, out_file)