~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
63
63
"""
64
64
 
65
65
import os
 
66
import string
66
67
import sys
67
68
 
68
69
from bzrlib import commands
69
70
from bzrlib.decorators import needs_write_lock
70
71
from bzrlib.lazy_import import lazy_import
71
72
lazy_import(globals(), """
72
 
import errno
73
73
import fnmatch
74
74
import re
75
75
from cStringIO import StringIO
279
279
        # We need to iterate until no more refs appear ({{foo}} will need two
280
280
        # iterations for example).
281
281
        while True:
282
 
            try:
283
 
                raw_chunks = self.option_ref_re.split(result)
284
 
            except TypeError:
285
 
                import pdb; pdb.set_trace()
 
282
            raw_chunks = self.option_ref_re.split(result)
286
283
            if len(raw_chunks) == 1:
287
284
                # Shorcut the trivial case: no refs
288
285
                return result
444
441
        the concrete policy type is checked, and finally
445
442
        $EMAIL is examined.
446
443
        If no username can be found, errors.NoWhoami exception is raised.
447
 
 
448
 
        TODO: Check it's reasonably well-formed.
449
444
        """
450
445
        v = os.environ.get('BZR_EMAIL')
451
446
        if v:
452
447
            return v.decode(osutils.get_user_encoding())
453
 
 
454
448
        v = self._get_user_id()
455
449
        if v:
456
450
            return v
457
 
 
458
451
        v = os.environ.get('EMAIL')
459
452
        if v:
460
453
            return v.decode(osutils.get_user_encoding())
461
 
 
 
454
        name, email = _auto_user_id()
 
455
        if name and email:
 
456
            return '%s <%s>' % (name, email)
 
457
        elif email:
 
458
            return email
462
459
        raise errors.NoWhoami()
463
460
 
464
461
    def ensure_username(self):
970
967
        super(LockableConfig, self).remove_user_option(option_name,
971
968
                                                       section_name)
972
969
 
 
970
def _iter_for_location_by_parts(sections, location):
 
971
    """Keep only the sessions matching the specified location.
 
972
 
 
973
    :param sections: An iterable of section names.
 
974
 
 
975
    :param location: An url or a local path to match against.
 
976
 
 
977
    :returns: An iterator of (section, extra_path, nb_parts) where nb is the
 
978
        number of path components in the section name, section is the section
 
979
        name and extra_path is the difference between location and the section
 
980
        name.
 
981
    """
 
982
    location_parts = location.rstrip('/').split('/')
 
983
 
 
984
    for section in sections:
 
985
        # location is a local path if possible, so we need
 
986
        # to convert 'file://' urls to local paths if necessary.
 
987
 
 
988
        # FIXME: I don't think the above comment is still up to date,
 
989
        # LocationConfig is always instantiated with an url -- vila 2011-04-07
 
990
 
 
991
        # This also avoids having file:///path be a more exact
 
992
        # match than '/path'.
 
993
 
 
994
        # FIXME: Not sure about the above either, but since the path components
 
995
        # are compared in sync, adding two empty components (//) is likely to
 
996
        # trick the comparison and also trick the check on the number of
 
997
        # components, so we *should* take only the relevant part of the url. On
 
998
        # the other hand, this means 'file://' urls *can't* be used in sections
 
999
        # so more work is probably needed -- vila 2011-04-07
 
1000
 
 
1001
        if section.startswith('file://'):
 
1002
            section_path = urlutils.local_path_from_url(section)
 
1003
        else:
 
1004
            section_path = section
 
1005
        section_parts = section_path.rstrip('/').split('/')
 
1006
 
 
1007
        matched = True
 
1008
        if len(section_parts) > len(location_parts):
 
1009
            # More path components in the section, they can't match
 
1010
            matched = False
 
1011
        else:
 
1012
            # Rely on zip truncating in length to the length of the shortest
 
1013
            # argument sequence.
 
1014
            names = zip(location_parts, section_parts)
 
1015
            for name in names:
 
1016
                if not fnmatch.fnmatch(name[0], name[1]):
 
1017
                    matched = False
 
1018
                    break
 
1019
        if not matched:
 
1020
            continue
 
1021
        # build the path difference between the section and the location
 
1022
        extra_path = '/'.join(location_parts[len(section_parts):])
 
1023
        yield section, extra_path, len(section_parts)
 
1024
 
973
1025
 
974
1026
class LocationConfig(LockableConfig):
975
1027
    """A configuration object that gives the policy for a location."""
1004
1056
 
1005
1057
    def _get_matching_sections(self):
1006
1058
        """Return an ordered list of section names matching this location."""
1007
 
        sections = self._get_parser()
1008
 
        location_names = self.location.split('/')
1009
 
        if self.location.endswith('/'):
1010
 
            del location_names[-1]
1011
 
        matches=[]
1012
 
        for section in sections:
1013
 
            # location is a local path if possible, so we need
1014
 
            # to convert 'file://' urls to local paths if necessary.
1015
 
            # This also avoids having file:///path be a more exact
1016
 
            # match than '/path'.
1017
 
            if section.startswith('file://'):
1018
 
                section_path = urlutils.local_path_from_url(section)
1019
 
            else:
1020
 
                section_path = section
1021
 
            section_names = section_path.split('/')
1022
 
            if section.endswith('/'):
1023
 
                del section_names[-1]
1024
 
            names = zip(location_names, section_names)
1025
 
            matched = True
1026
 
            for name in names:
1027
 
                if not fnmatch.fnmatch(name[0], name[1]):
1028
 
                    matched = False
1029
 
                    break
1030
 
            if not matched:
1031
 
                continue
1032
 
            # so, for the common prefix they matched.
1033
 
            # if section is longer, no match.
1034
 
            if len(section_names) > len(location_names):
1035
 
                continue
1036
 
            matches.append((len(section_names), section,
1037
 
                            '/'.join(location_names[len(section_names):])))
 
1059
        matches = list(_iter_for_location_by_parts(self._get_parser(),
 
1060
                                                   self.location))
1038
1061
        # put the longest (aka more specific) locations first
1039
 
        matches.sort(reverse=True)
1040
 
        sections = []
1041
 
        for (length, section, extra_path) in matches:
1042
 
            sections.append((section, extra_path))
 
1062
        matches.sort(
 
1063
            key=lambda (section, extra_path, length): (length, section),
 
1064
            reverse=True)
 
1065
        for (section, extra_path, length) in matches:
 
1066
            yield section, extra_path
1043
1067
            # should we stop looking for parent configs here?
1044
1068
            try:
1045
1069
                if self._get_parser()[section].as_bool('ignore_parents'):
1046
1070
                    break
1047
1071
            except KeyError:
1048
1072
                pass
1049
 
        return sections
1050
1073
 
1051
1074
    def _get_sections(self, name=None):
1052
1075
        """See IniBasedConfig._get_sections()."""
1410
1433
        return os.path.expanduser('~/.cache')
1411
1434
 
1412
1435
 
 
1436
def _get_default_mail_domain():
 
1437
    """If possible, return the assumed default email domain.
 
1438
 
 
1439
    :returns: string mail domain, or None.
 
1440
    """
 
1441
    if sys.platform == 'win32':
 
1442
        # No implementation yet; patches welcome
 
1443
        return None
 
1444
    try:
 
1445
        f = open('/etc/mailname')
 
1446
    except (IOError, OSError), e:
 
1447
        return None
 
1448
    try:
 
1449
        domain = f.read().strip()
 
1450
        return domain
 
1451
    finally:
 
1452
        f.close()
 
1453
 
 
1454
 
 
1455
def _auto_user_id():
 
1456
    """Calculate automatic user identification.
 
1457
 
 
1458
    :returns: (realname, email), either of which may be None if they can't be
 
1459
    determined.
 
1460
 
 
1461
    Only used when none is set in the environment or the id file.
 
1462
 
 
1463
    This only returns an email address if we can be fairly sure the 
 
1464
    address is reasonable, ie if /etc/mailname is set on unix.
 
1465
 
 
1466
    This doesn't use the FQDN as the default domain because that may be 
 
1467
    slow, and it doesn't use the hostname alone because that's not normally 
 
1468
    a reasonable address.
 
1469
    """
 
1470
    if sys.platform == 'win32':
 
1471
        # No implementation to reliably determine Windows default mail
 
1472
        # address; please add one.
 
1473
        return None, None
 
1474
 
 
1475
    default_mail_domain = _get_default_mail_domain()
 
1476
    if not default_mail_domain:
 
1477
        return None, None
 
1478
 
 
1479
    import pwd
 
1480
    uid = os.getuid()
 
1481
    try:
 
1482
        w = pwd.getpwuid(uid)
 
1483
    except KeyError:
 
1484
        mutter('no passwd entry for uid %d?' % uid)
 
1485
        return None, None
 
1486
 
 
1487
    # we try utf-8 first, because on many variants (like Linux),
 
1488
    # /etc/passwd "should" be in utf-8, and because it's unlikely to give
 
1489
    # false positives.  (many users will have their user encoding set to
 
1490
    # latin-1, which cannot raise UnicodeError.)
 
1491
    try:
 
1492
        gecos = w.pw_gecos.decode('utf-8')
 
1493
        encoding = 'utf-8'
 
1494
    except UnicodeError:
 
1495
        try:
 
1496
            encoding = osutils.get_user_encoding()
 
1497
            gecos = w.pw_gecos.decode(encoding)
 
1498
        except UnicodeError, e:
 
1499
            mutter("cannot decode passwd entry %s" % w)
 
1500
            return None, None
 
1501
    try:
 
1502
        username = w.pw_name.decode(encoding)
 
1503
    except UnicodeError, e:
 
1504
        mutter("cannot decode passwd entry %s" % w)
 
1505
        return None, None
 
1506
 
 
1507
    comma = gecos.find(',')
 
1508
    if comma == -1:
 
1509
        realname = gecos
 
1510
    else:
 
1511
        realname = gecos[:comma]
 
1512
 
 
1513
    return realname, (username + '@' + default_mail_domain)
 
1514
 
 
1515
 
1413
1516
def parse_username(username):
1414
1517
    """Parse e-mail username and return a (name, address) tuple."""
1415
1518
    match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)