~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Vincent Ladeuil
  • Date: 2012-02-14 17:22:37 UTC
  • mfrom: (6466 +trunk)
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120214172237-7dv7er3n4uy8d5m4
Merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
55
55
                   turns on create_signatures.
56
56
create_signatures - this option controls whether bzr will always create
57
57
                    gpg signatures or not on commits.  There is an unused
58
 
                    option which in future is expected to work if               
 
58
                    option which in future is expected to work if
59
59
                    branch settings require signatures.
60
60
log_format - this option sets the default log format.  Possible values are
61
61
             long, short, line, or a plugin can register new formats.
73
73
"""
74
74
 
75
75
from __future__ import absolute_import
76
 
 
 
76
from cStringIO import StringIO
77
77
import os
78
78
import sys
79
79
 
83
83
lazy_import(globals(), """
84
84
import fnmatch
85
85
import re
86
 
from cStringIO import StringIO
87
86
 
88
87
from bzrlib import (
89
88
    atomicfile,
93
92
    lazy_regex,
94
93
    library_state,
95
94
    lockdir,
96
 
    mail_client,
97
95
    mergetools,
98
96
    osutils,
99
97
    symbol_versioning,
241
239
        return diff.DiffFromTool.from_string(cmd, old_tree, new_tree,
242
240
                                             sys.stdout)
243
241
 
244
 
    def get_mail_client(self):
245
 
        """Get a mail client to use"""
246
 
        selected_client = self.get_user_option('mail_client')
247
 
        _registry = mail_client.mail_client_registry
248
 
        try:
249
 
            mail_client_class = _registry.get(selected_client)
250
 
        except KeyError:
251
 
            raise errors.UnknownMailClient(selected_client)
252
 
        return mail_client_class(self)
253
 
 
254
242
    def _get_signature_checking(self):
255
243
        """Template method to override signature checking policy."""
256
244
 
1525
1513
 
1526
1514
 
1527
1515
def config_dir():
1528
 
    """Return per-user configuration directory.
 
1516
    """Return per-user configuration directory as unicode string
1529
1517
 
1530
1518
    By default this is %APPDATA%/bazaar/2.0 on Windows, ~/.bazaar on Mac OS X
1531
1519
    and Linux.  On Linux, if there is a $XDG_CONFIG_HOME/bazaar directory,
1533
1521
 
1534
1522
    TODO: Global option --config-dir to override this.
1535
1523
    """
1536
 
    base = os.environ.get('BZR_HOME', None)
 
1524
    base = osutils.path_from_environ('BZR_HOME')
1537
1525
    if sys.platform == 'win32':
1538
 
        # environ variables on Windows are in user encoding/mbcs. So decode
1539
 
        # before using one
1540
 
        if base is not None:
1541
 
            base = base.decode('mbcs')
1542
 
        if base is None:
1543
 
            base = win32utils.get_appdata_location_unicode()
1544
 
        if base is None:
1545
 
            base = os.environ.get('HOME', None)
1546
 
            if base is not None:
1547
 
                base = base.decode('mbcs')
1548
 
        if base is None:
1549
 
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA,'
1550
 
                                  ' or HOME set')
 
1526
        if base is None:
 
1527
            base = win32utils.get_appdata_location()
 
1528
        if base is None:
 
1529
            base = win32utils.get_home_location()
 
1530
        # GZ 2012-02-01: Really the two level subdirs only make sense inside
 
1531
        #                APPDATA, but hard to move. See bug 348640 for more.
1551
1532
        return osutils.pathjoin(base, 'bazaar', '2.0')
1552
 
    else:
1553
 
        if base is not None:
1554
 
            base = base.decode(osutils._fs_enc)
1555
 
    if sys.platform == 'darwin':
1556
 
        if base is None:
1557
 
            # this takes into account $HOME
1558
 
            base = os.path.expanduser("~")
1559
 
        return osutils.pathjoin(base, '.bazaar')
1560
 
    else:
1561
 
        if base is None:
1562
 
            xdg_dir = os.environ.get('XDG_CONFIG_HOME', None)
 
1533
    if base is None:
 
1534
        # GZ 2012-02-01: What should OSX use instead of XDG if anything?
 
1535
        if sys.platform != 'darwin':
 
1536
            xdg_dir = osutils.path_from_environ('XDG_CONFIG_HOME')
1563
1537
            if xdg_dir is None:
1564
 
                xdg_dir = osutils.pathjoin(os.path.expanduser("~"), ".config")
 
1538
                xdg_dir = osutils.pathjoin(osutils._get_home_dir(), ".config")
1565
1539
            xdg_dir = osutils.pathjoin(xdg_dir, 'bazaar')
1566
1540
            if osutils.isdir(xdg_dir):
1567
1541
                trace.mutter(
1568
1542
                    "Using configuration in XDG directory %s." % xdg_dir)
1569
1543
                return xdg_dir
1570
 
            base = os.path.expanduser("~")
1571
 
        return osutils.pathjoin(base, ".bazaar")
 
1544
        base = osutils._get_home_dir()
 
1545
    return osutils.pathjoin(base, ".bazaar")
1572
1546
 
1573
1547
 
1574
1548
def config_filename():
2397
2371
            raise AssertionError('%r is not supported as a default value'
2398
2372
                                 % (default,))
2399
2373
        self.default_from_env = default_from_env
2400
 
        self.help = help
 
2374
        self._help = help
2401
2375
        self.from_unicode = from_unicode
2402
2376
        self.unquote = unquote
2403
2377
        if invalid and invalid not in ('warning', 'error'):
2404
2378
            raise AssertionError("%s not supported for 'invalid'" % (invalid,))
2405
2379
        self.invalid = invalid
2406
2380
 
 
2381
    @property
 
2382
    def help(self):
 
2383
        return self._help
 
2384
 
2407
2385
    def convert_from_unicode(self, store, unicode_value):
2408
2386
        if self.unquote and store is not None and unicode_value is not None:
2409
2387
            unicode_value = store.unquote(unicode_value)
2549
2527
        return l
2550
2528
 
2551
2529
 
 
2530
class RegistryOption(Option):
 
2531
    """Option for a choice from a registry."""
 
2532
 
 
2533
    def __init__(self, name, registry, default_from_env=None,
 
2534
                 help=None, invalid=None):
 
2535
        """A registry based Option definition.
 
2536
 
 
2537
        This overrides the base class so the conversion from a unicode string
 
2538
        can take quoting into account.
 
2539
        """
 
2540
        super(RegistryOption, self).__init__(
 
2541
            name, default=lambda: unicode(registry.default_key),
 
2542
            default_from_env=default_from_env,
 
2543
            from_unicode=self.from_unicode, help=help,
 
2544
            invalid=invalid, unquote=False)
 
2545
        self.registry = registry
 
2546
 
 
2547
    def from_unicode(self, unicode_str):
 
2548
        if not isinstance(unicode_str, basestring):
 
2549
            raise TypeError
 
2550
        try:
 
2551
            return self.registry.get(unicode_str)
 
2552
        except KeyError:
 
2553
            raise ValueError(
 
2554
                "Invalid value %s for %s."
 
2555
                "See help for a list of possible values." % (unicode_str,
 
2556
                    self.name))
 
2557
 
 
2558
    @property
 
2559
    def help(self):
 
2560
        ret = [self._help, "\n\nThe following values are supported:\n"]
 
2561
        for key in self.registry.keys():
 
2562
            ret.append(" %s - %s\n" % (key, self.registry.get_help(key)))
 
2563
        return "".join(ret)
 
2564
 
 
2565
 
2552
2566
class OptionRegistry(registry.Registry):
2553
2567
    """Register config options by their name.
2554
2568
 
2644
2658
           help="""\
2645
2659
Whether revisions associated with tags should be fetched.
2646
2660
"""))
 
2661
option_registry.register_lazy(
 
2662
    'bzr.transform.orphan_policy', 'bzrlib.transform', 'opt_transform_orphan')
2647
2663
option_registry.register(
2648
2664
    Option('bzr.workingtree.worth_saving_limit', default=10,
2649
2665
           from_unicode=int_from_store,  invalid='warning',
2657
2673
a file has been touched.
2658
2674
'''))
2659
2675
option_registry.register(
 
2676
    Option('bugtracker', default=None,
 
2677
           help='''\
 
2678
Default bug tracker to use.
 
2679
 
 
2680
This bug tracker will be used for example when marking bugs
 
2681
as fixed using ``bzr commit --fixes``, if no explicit
 
2682
bug tracker was specified.
 
2683
'''))
 
2684
option_registry.register(
2660
2685
    Option('check_signatures', default=CHECK_IF_POSSIBLE,
2661
2686
           from_unicode=signature_policy_from_unicode,
2662
2687
           help='''\
2764
2789
Standard log formats are ``long``, ``short`` and ``line``. Additional formats
2765
2790
may be provided by plugins.
2766
2791
'''))
 
2792
option_registry.register_lazy('mail_client', 'bzrlib.mail_client',
 
2793
    'opt_mail_client')
2767
2794
option_registry.register(
2768
2795
    Option('output_encoding',
2769
2796
           help= 'Unicode encoding for output'
2866
2893
option_registry.register(
2867
2894
    Option('submit_to',
2868
2895
           help='''Where submissions from this branch are mailed to.'''))
 
2896
option_registry.register(
 
2897
    ListOption('suppress_warnings',
 
2898
           default=[],
 
2899
           help="List of warning classes to suppress."))
 
2900
option_registry.register(
 
2901
    Option('validate_signatures_in_log', default=False,
 
2902
           from_unicode=bool_from_store, invalid='warning',
 
2903
           help='''Whether to validate signatures in bzr log.'''))
 
2904
option_registry.register_lazy('ssl.ca_certs',
 
2905
    'bzrlib.transport.http._urllib2_wrappers', 'opt_ssl_ca_certs')
 
2906
 
 
2907
option_registry.register_lazy('ssl.cert_reqs',
 
2908
    'bzrlib.transport.http._urllib2_wrappers', 'opt_ssl_cert_reqs')
2869
2909
 
2870
2910
 
2871
2911
class Section(object):
3114
3154
class IniFileStore(Store):
3115
3155
    """A config Store using ConfigObj for storage.
3116
3156
 
3117
 
    :ivar transport: The transport object where the config file is located.
3118
 
 
3119
 
    :ivar file_name: The config file basename in the transport directory.
3120
 
 
3121
3157
    :ivar _config_obj: Private member to hold the ConfigObj instance used to
3122
3158
        serialize/deserialize the config file.
3123
3159
    """
3253
3289
            value = self._config_obj._unquote(value)
3254
3290
        return value
3255
3291
 
 
3292
    def external_url(self):
 
3293
        # Since an IniFileStore can be used without a file (at least in tests),
 
3294
        # it's better to provide something than raising a NotImplementedError.
 
3295
        # All daughter classes are supposed to provide an implementation
 
3296
        # anyway.
 
3297
        return 'In-Process Store, no URL'
3256
3298
 
3257
3299
class TransportIniFileStore(IniFileStore):
3258
3300
    """IniFileStore that loads files from a transport.
 
3301
 
 
3302
    :ivar transport: The transport object where the config file is located.
 
3303
 
 
3304
    :ivar file_name: The config file basename in the transport directory.
3259
3305
    """
3260
3306
 
3261
3307
    def __init__(self, transport, file_name):
3421
3467
 
3422
3468
class LocationSection(Section):
3423
3469
 
3424
 
    def __init__(self, section, length, extra_path):
 
3470
    def __init__(self, section, extra_path):
3425
3471
        super(LocationSection, self).__init__(section.id, section.options)
3426
 
        self.length = length
3427
3472
        self.extra_path = extra_path
3428
3473
        self.locals = {'relpath': extra_path,
3429
3474
                       'basename': urlutils.basename(extra_path)}
3451
3496
        return value
3452
3497
 
3453
3498
 
 
3499
class StartingPathMatcher(SectionMatcher):
 
3500
    """Select sections for a given location respecting the Store order."""
 
3501
 
 
3502
    # FIXME: Both local paths and urls can be used for section names as well as
 
3503
    # ``location`` to stay consistent with ``LocationMatcher`` which itself
 
3504
    # inherited the fuzziness from the previous ``LocationConfig``
 
3505
    # implementation. We probably need to revisit which encoding is allowed for
 
3506
    # both ``location`` and section names and how we normalize
 
3507
    # them. http://pad.lv/85479, http://pad.lv/437009 and http://359320 are
 
3508
    # related too. -- vila 2012-01-04
 
3509
 
 
3510
    def __init__(self, store, location):
 
3511
        super(StartingPathMatcher, self).__init__(store)
 
3512
        if location.startswith('file://'):
 
3513
            location = urlutils.local_path_from_url(location)
 
3514
        self.location = location
 
3515
 
 
3516
    def get_sections(self):
 
3517
        """Get all sections matching ``location`` in the store.
 
3518
 
 
3519
        The most generic sections are described first in the store, then more
 
3520
        specific ones can be provided for reduced scopes.
 
3521
 
 
3522
        The returned section are therefore returned in the reversed order so
 
3523
        the most specific ones can be found first.
 
3524
        """
 
3525
        location_parts = self.location.rstrip('/').split('/')
 
3526
        store = self.store
 
3527
        sections = []
 
3528
        # Later sections are more specific, they should be returned first
 
3529
        for _, section in reversed(list(store.get_sections())):
 
3530
            if section.id is None:
 
3531
                # The no-name section is always included if present
 
3532
                yield store, LocationSection(section, self.location)
 
3533
                continue
 
3534
            section_path = section.id
 
3535
            if section_path.startswith('file://'):
 
3536
                # the location is already a local path or URL, convert the
 
3537
                # section id to the same format
 
3538
                section_path = urlutils.local_path_from_url(section_path)
 
3539
            if (self.location.startswith(section_path)
 
3540
                or fnmatch.fnmatch(self.location, section_path)):
 
3541
                section_parts = section_path.rstrip('/').split('/')
 
3542
                extra_path = '/'.join(location_parts[len(section_parts):])
 
3543
                yield store, LocationSection(section, extra_path)
 
3544
 
 
3545
 
3454
3546
class LocationMatcher(SectionMatcher):
3455
3547
 
3456
3548
    def __init__(self, store, location):
3480
3572
        matching_sections = []
3481
3573
        if no_name_section is not None:
3482
3574
            matching_sections.append(
3483
 
                LocationSection(no_name_section, 0, self.location))
 
3575
                (0, LocationSection(no_name_section, self.location)))
3484
3576
        for section_id, extra_path, length in filtered_sections:
3485
3577
            # a section id is unique for a given store so it's safe to take the
3486
3578
            # first matching section while iterating. Also, all filtered
3490
3582
                section = iter_all_sections.next()
3491
3583
                if section_id == section.id:
3492
3584
                    matching_sections.append(
3493
 
                        LocationSection(section, length, extra_path))
 
3585
                        (length, LocationSection(section, extra_path)))
3494
3586
                    break
3495
3587
        return matching_sections
3496
3588
 
3499
3591
        matching_sections = self._get_matching_sections()
3500
3592
        # We want the longest (aka more specific) locations first
3501
3593
        sections = sorted(matching_sections,
3502
 
                          key=lambda section: (section.length, section.id),
 
3594
                          key=lambda (length, section): (length, section.id),
3503
3595
                          reverse=True)
3504
3596
        # Sections mentioning 'ignore_parents' restrict the selection
3505
 
        for section in sections:
 
3597
        for _, section in sections:
3506
3598
            # FIXME: We really want to use as_bool below -- vila 2011-04-07
3507
3599
            ignore = section.get('ignore_parents', None)
3508
3600
            if ignore is not None: