~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Patch Queue Manager
  • Date: 2011-12-21 21:27:34 UTC
  • mfrom: (6385.1.7 906897-quoting-stores)
  • Revision ID: pqm@pqm.ubuntu.com-20111221212734-aea6s92gkpux3fky
(vila) Stores allow Stacks to control when values are quoted/unquoted
 (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
2344
2344
    """
2345
2345
 
2346
2346
    def __init__(self, name, default=None, default_from_env=None,
2347
 
                 help=None, from_unicode=None, invalid=None):
 
2347
                 help=None, from_unicode=None, invalid=None,
 
2348
                 unquote=True):
2348
2349
        """Build an option definition.
2349
2350
 
2350
2351
        :param name: the name used to refer to the option.
2372
2373
            TypeError. Accepted values are: None (ignore invalid values),
2373
2374
            'warning' (emit a warning), 'error' (emit an error message and
2374
2375
            terminates).
 
2376
 
 
2377
        :param unquote: should the unicode value be unquoted before conversion.
 
2378
           This should be used only when the store providing the values cannot
 
2379
           safely unquote them (see http://pad.lv/906897). It is provided so
 
2380
           daughter classes can handle the quoting themselves.
2375
2381
        """
2376
2382
        if default_from_env is None:
2377
2383
            default_from_env = []
2398
2404
        self.default_from_env = default_from_env
2399
2405
        self.help = help
2400
2406
        self.from_unicode = from_unicode
 
2407
        self.unquote = unquote
2401
2408
        if invalid and invalid not in ('warning', 'error'):
2402
2409
            raise AssertionError("%s not supported for 'invalid'" % (invalid,))
2403
2410
        self.invalid = invalid
2404
2411
 
2405
 
    def convert_from_unicode(self, unicode_value):
 
2412
    def convert_from_unicode(self, store, unicode_value):
 
2413
        if self.unquote and store is not None and unicode_value is not None:
 
2414
            unicode_value = store.unquote(unicode_value)
2406
2415
        if self.from_unicode is None or unicode_value is None:
2407
2416
            # Don't convert or nothing to convert
2408
2417
            return unicode_value
2459
2468
    return int(unicode_str)
2460
2469
 
2461
2470
 
2462
 
_unit_sfxs = dict(K=10**3, M=10**6, G=10**9)
 
2471
_unit_suffixes = dict(K=10**3, M=10**6, G=10**9)
2463
2472
 
2464
2473
def int_SI_from_store(unicode_str):
2465
2474
    """Convert a human readable size in SI units, e.g 10MB into an integer.
2471
2480
    :return Integer, expanded to its base-10 value if a proper SI unit is 
2472
2481
        found, None otherwise.
2473
2482
    """
2474
 
    regexp = "^(\d+)(([" + ''.join(_unit_sfxs) + "])b?)?$"
 
2483
    regexp = "^(\d+)(([" + ''.join(_unit_suffixes) + "])b?)?$"
2475
2484
    p = re.compile(regexp, re.IGNORECASE)
2476
2485
    m = p.match(unicode_str)
2477
2486
    val = None
2480
2489
        val = int(val)
2481
2490
        if unit:
2482
2491
            try:
2483
 
                coeff = _unit_sfxs[unit.upper()]
 
2492
                coeff = _unit_suffixes[unit.upper()]
2484
2493
            except KeyError:
2485
2494
                raise ValueError(gettext('{0} is not an SI unit.').format(unit))
2486
2495
            val *= coeff
2497
2506
    {}, encoding='utf-8', list_values=True, interpolation=False)
2498
2507
 
2499
2508
 
2500
 
def list_from_store(unicode_str):
2501
 
    if not isinstance(unicode_str, basestring):
2502
 
        raise TypeError
2503
 
    # Now inject our string directly as unicode. All callers got their value
2504
 
    # from configobj, so values that need to be quoted are already properly
2505
 
    # quoted.
2506
 
    _list_converter_config.reset()
2507
 
    _list_converter_config._parse([u"list=%s" % (unicode_str,)])
2508
 
    maybe_list = _list_converter_config['list']
2509
 
    if isinstance(maybe_list, basestring):
2510
 
        if maybe_list:
2511
 
            # A single value, most probably the user forgot (or didn't care to
2512
 
            # add) the final ','
2513
 
            l = [maybe_list]
 
2509
class ListOption(Option):
 
2510
 
 
2511
    def __init__(self, name, default=None, default_from_env=None,
 
2512
                 help=None, invalid=None):
 
2513
        """A list Option definition.
 
2514
 
 
2515
        This overrides the base class so the conversion from a unicode string
 
2516
        can take quoting into account.
 
2517
        """
 
2518
        super(ListOption, self).__init__(
 
2519
            name, default=default, default_from_env=default_from_env,
 
2520
            from_unicode=self.from_unicode, help=help,
 
2521
            invalid=invalid, unquote=False)
 
2522
 
 
2523
    def from_unicode(self, unicode_str):
 
2524
        if not isinstance(unicode_str, basestring):
 
2525
            raise TypeError
 
2526
        # Now inject our string directly as unicode. All callers got their
 
2527
        # value from configobj, so values that need to be quoted are already
 
2528
        # properly quoted.
 
2529
        _list_converter_config.reset()
 
2530
        _list_converter_config._parse([u"list=%s" % (unicode_str,)])
 
2531
        maybe_list = _list_converter_config['list']
 
2532
        if isinstance(maybe_list, basestring):
 
2533
            if maybe_list:
 
2534
                # A single value, most probably the user forgot (or didn't care
 
2535
                # to add) the final ','
 
2536
                l = [maybe_list]
 
2537
            else:
 
2538
                # The empty string, convert to empty list
 
2539
                l = []
2514
2540
        else:
2515
 
            # The empty string, convert to empty list
2516
 
            l = []
2517
 
    else:
2518
 
        # We rely on ConfigObj providing us with a list already
2519
 
        l = maybe_list
2520
 
    return l
 
2541
            # We rely on ConfigObj providing us with a list already
 
2542
            l = maybe_list
 
2543
        return l
2521
2544
 
2522
2545
 
2523
2546
class OptionRegistry(registry.Registry):
2573
2596
existing mainline of the branch.
2574
2597
'''))
2575
2598
option_registry.register(
2576
 
    Option('acceptable_keys',
2577
 
           default=None, from_unicode=list_from_store,
 
2599
    ListOption('acceptable_keys',
 
2600
           default=None,
2578
2601
           help="""\
2579
2602
List of GPG key patterns which are acceptable for verification.
2580
2603
"""))
2656
2679
should not be lost if the machine crashes.  See also repository.fdatasync.
2657
2680
'''))
2658
2681
option_registry.register(
2659
 
    Option('debug_flags', default=[], from_unicode=list_from_store,
 
2682
    ListOption('debug_flags', default=[],
2660
2683
           help='Debug flags to activate.'))
2661
2684
option_registry.register(
2662
2685
    Option('default_format', default='2a',
2910
2933
        """
2911
2934
        raise NotImplementedError(self.unload)
2912
2935
 
 
2936
    def quote(self, value):
 
2937
        """Quote a configuration option value for storing purposes.
 
2938
 
 
2939
        This allows Stacks to present values as they will be stored.
 
2940
        """
 
2941
        return value
 
2942
 
 
2943
    def unquote(self, value):
 
2944
        """Unquote a configuration option value into unicode.
 
2945
 
 
2946
        The received value is quoted as stored.
 
2947
        """
 
2948
        return value
 
2949
 
2913
2950
    def save(self):
2914
2951
        """Saves the Store to persistent storage."""
2915
2952
        raise NotImplementedError(self.save)
3082
3119
            section = self._config_obj.setdefault(section_id, {})
3083
3120
        return self.mutable_section_class(section_id, section)
3084
3121
 
 
3122
    def quote(self, value):
 
3123
        try:
 
3124
            # configobj conflates automagical list values and quoting
 
3125
            self._config_obj.list_values = True
 
3126
            return self._config_obj._quote(value)
 
3127
        finally:
 
3128
            self._config_obj.list_values = False
 
3129
 
 
3130
    def unquote(self, value):
 
3131
        if value:
 
3132
            # _unquote doesn't handle None nor empty strings
 
3133
            value = self._config_obj._unquote(value)
 
3134
        return value
 
3135
 
3085
3136
 
3086
3137
class TransportIniFileStore(IniFileStore):
3087
3138
    """IniFileStore that loads files from a transport.
3416
3467
        # implies querying the persistent storage) until it can't be avoided
3417
3468
        # anymore by using callables to describe (possibly empty) section
3418
3469
        # lists.
 
3470
        found_store = None # Where the option value has been found
3419
3471
        for sections in self.sections_def:
3420
3472
            for store, section in sections():
3421
3473
                value = section.get(name)
3422
3474
                if value is not None:
 
3475
                    found_store = store
3423
3476
                    break
3424
3477
            if value is not None:
3425
3478
                break
3441
3494
                        trace.warning('Cannot expand "%s":'
3442
3495
                                      ' %s does not support option expansion'
3443
3496
                                      % (name, type(val)))
3444
 
                if opt is not None:
3445
 
                    val = opt.convert_from_unicode(val)
 
3497
                if opt is None:
 
3498
                    val = found_store.unquote(val)
 
3499
                else:
 
3500
                    val = opt.convert_from_unicode(found_store, val)
3446
3501
            return val
3447
3502
        value = expand_and_convert(value)
3448
3503
        if opt is not None and value is None:
3526
3581
        or deleting an option. In practice the store will often be loaded but
3527
3582
        this helps catching some programming errors.
3528
3583
        """
3529
 
        section = self.store.get_mutable_section(self.mutable_section_id)
3530
 
        return section
 
3584
        store = self.store
 
3585
        section = store.get_mutable_section(self.mutable_section_id)
 
3586
        return store, section
3531
3587
 
3532
3588
    def set(self, name, value):
3533
3589
        """Set a new value for the option."""
3534
 
        section = self._get_mutable_section()
3535
 
        section.set(name, value)
 
3590
        store, section = self._get_mutable_section()
 
3591
        section.set(name, store.quote(value))
3536
3592
        for hook in ConfigHooks['set']:
3537
3593
            hook(self, name, value)
3538
3594
 
3539
3595
    def remove(self, name):
3540
3596
        """Remove an existing option."""
3541
 
        section = self._get_mutable_section()
 
3597
        _, section = self._get_mutable_section()
3542
3598
        section.remove(name)
3543
3599
        for hook in ConfigHooks['remove']:
3544
3600
            hook(self, name)
3727
3783
# Use a an empty dict to initialize an empty configobj avoiding all
3728
3784
# parsing and encoding checks
3729
3785
_quoting_config = configobj.ConfigObj(
3730
 
    {}, encoding='utf-8', interpolation=False)
 
3786
    {}, encoding='utf-8', interpolation=False, list_values=True)
3731
3787
 
3732
3788
class cmd_config(commands.Command):
3733
3789
    __doc__ = """Display, set or remove a configuration option.
3860
3916
                            self.outf.write('  [%s]\n' % (section.id,))
3861
3917
                            cur_section = section.id
3862
3918
                        value = section.get(oname, expand=False)
 
3919
                        # Since we don't use the stack, we need to restore a
 
3920
                        # proper quoting.
 
3921
                        try:
 
3922
                            opt = option_registry.get(oname)
 
3923
                            value = opt.convert_from_unicode(store, value)
 
3924
                        except KeyError:
 
3925
                            value = store.unquote(value)
3863
3926
                        value = _quoting_config._quote(value)
3864
3927
                        self.outf.write('  %s = %s\n' % (oname, value))
3865
3928