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.
197
196
return self[section][name]
199
# FIXME: Until we can guarantee that each config file is loaded once and
200
# only once for a given bzrlib session, we don't want to re-read the file every
201
# time we query for an option so we cache the value (bad ! watch out for tests
202
# needing to restore the proper value). -- vila 20110219
203
_expand_default_value = None
204
def _get_expand_default_value():
205
global _expand_default_value
206
if _expand_default_value is not None:
207
return _expand_default_value
208
conf = GlobalConfig()
209
# Note that we must not use None for the expand value below or we'll run
210
# into infinite recursion. Using False really would be quite silly ;)
211
expand = conf.get_user_option_as_bool('bzr.config.expand', expand=True)
213
# This is an opt-in feature, you *really* need to clearly say you want
216
_expand_default_value = expand
200
220
class Config(object):
201
221
"""A configuration policy - what username, editor, gpg needs etc."""
207
227
"""Returns a unique ID for the config."""
208
228
raise NotImplementedError(self.config_id)
230
@deprecated_method(deprecated_in((2, 4, 0)))
231
def get_editor(self):
232
"""Get the users pop up editor."""
233
raise NotImplementedError
210
235
def get_change_editor(self, old_tree, new_tree):
211
236
from bzrlib import diff
212
237
cmd = self._get_change_editor()
215
240
return diff.DiffFromTool.from_string(cmd, old_tree, new_tree,
243
def get_mail_client(self):
244
"""Get a mail client to use"""
245
selected_client = self.get_user_option('mail_client')
246
_registry = mail_client.mail_client_registry
248
mail_client_class = _registry.get(selected_client)
250
raise errors.UnknownMailClient(selected_client)
251
return mail_client_class(self)
218
253
def _get_signature_checking(self):
219
254
"""Template method to override signature checking policy."""
611
648
for (oname, value, section, conf_id, parser) in self._get_options():
612
649
if oname.startswith('bzr.mergetool.'):
613
650
tool_name = oname[len('bzr.mergetool.'):]
614
tools[tool_name] = self.get_user_option(oname, False)
651
tools[tool_name] = self.get_user_option(oname)
615
652
trace.mutter('loaded merge tools: %r' % tools)
1487
1526
def config_dir():
1488
"""Return per-user configuration directory as unicode string
1527
"""Return per-user configuration directory.
1490
1529
By default this is %APPDATA%/bazaar/2.0 on Windows, ~/.bazaar on Mac OS X
1491
and Linux. On Mac OS X and Linux, if there is a $XDG_CONFIG_HOME/bazaar directory,
1530
and Linux. On Linux, if there is a $XDG_CONFIG_HOME/bazaar directory,
1492
1531
that will be used instead.
1494
1533
TODO: Global option --config-dir to override this.
1496
base = osutils.path_from_environ('BZR_HOME')
1535
base = os.environ.get('BZR_HOME', None)
1497
1536
if sys.platform == 'win32':
1499
base = win32utils.get_appdata_location()
1501
base = win32utils.get_home_location()
1502
# GZ 2012-02-01: Really the two level subdirs only make sense inside
1503
# APPDATA, but hard to move. See bug 348640 for more.
1537
# environ variables on Windows are in user encoding/mbcs. So decode
1539
if base is not None:
1540
base = base.decode('mbcs')
1542
base = win32utils.get_appdata_location_unicode()
1544
base = os.environ.get('HOME', None)
1545
if base is not None:
1546
base = base.decode('mbcs')
1548
raise errors.BzrError('You must have one of BZR_HOME, APPDATA,'
1504
1550
return osutils.pathjoin(base, 'bazaar', '2.0')
1506
xdg_dir = osutils.path_from_environ('XDG_CONFIG_HOME')
1508
xdg_dir = osutils.pathjoin(osutils._get_home_dir(), ".config")
1509
xdg_dir = osutils.pathjoin(xdg_dir, 'bazaar')
1510
if osutils.isdir(xdg_dir):
1512
"Using configuration in XDG directory %s." % xdg_dir)
1514
base = osutils._get_home_dir()
1515
return osutils.pathjoin(base, ".bazaar")
1552
if base is not None:
1553
base = base.decode(osutils._fs_enc)
1554
if sys.platform == 'darwin':
1556
# this takes into account $HOME
1557
base = os.path.expanduser("~")
1558
return osutils.pathjoin(base, '.bazaar')
1561
xdg_dir = os.environ.get('XDG_CONFIG_HOME', None)
1563
xdg_dir = osutils.pathjoin(os.path.expanduser("~"), ".config")
1564
xdg_dir = osutils.pathjoin(xdg_dir, 'bazaar')
1565
if osutils.isdir(xdg_dir):
1567
"Using configuration in XDG directory %s." % xdg_dir)
1569
base = os.path.expanduser("~")
1570
return osutils.pathjoin(base, ".bazaar")
1518
1573
def config_filename():
1555
1610
def xdg_cache_dir():
1556
1611
# See http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
1557
1612
# Possibly this should be different on Windows?
1558
e = os.environ.get('XDG_CACHE_HOME', None)
1613
e = os.environ.get('XDG_CACHE_DIR', None)
1562
1617
return os.path.expanduser('~/.cache')
1565
def _get_default_mail_domain(mailname_file='/etc/mailname'):
1620
def _get_default_mail_domain():
1566
1621
"""If possible, return the assumed default email domain.
1568
1623
:returns: string mail domain, or None.
2131
2186
credential_store_registry.default_key = 'plain'
2134
class Base64CredentialStore(CredentialStore):
2135
__doc__ = """Base64 credential store for the authentication.conf file"""
2137
def decode_password(self, credentials):
2138
"""See CredentialStore.decode_password."""
2139
# GZ 2012-07-28: Will raise binascii.Error if password is not base64,
2140
# should probably propogate as something more useful.
2141
return base64.decodestring(credentials['password'])
2143
credential_store_registry.register('base64', Base64CredentialStore,
2144
help=Base64CredentialStore.__doc__)
2147
2189
class BzrDirConfig(object):
2149
2191
def __init__(self, bzrdir):
2156
2198
It may be set to a location, or None.
2158
This policy affects all branches contained by this control dir, except
2159
for those under repositories.
2200
This policy affects all branches contained by this bzrdir, except for
2201
those under repositories.
2161
2203
if self._config is None:
2162
raise errors.BzrError("Cannot set configuration in %s"
2204
raise errors.BzrError("Cannot set configuration in %s" % self._bzrdir)
2164
2205
if value is None:
2165
2206
self._config.set_option('', 'default_stack_on')
2354
2396
raise AssertionError('%r is not supported as a default value'
2356
2398
self.default_from_env = default_from_env
2358
2400
self.from_unicode = from_unicode
2359
2401
self.unquote = unquote
2360
2402
if invalid and invalid not in ('warning', 'error'):
2361
2403
raise AssertionError("%s not supported for 'invalid'" % (invalid,))
2362
2404
self.invalid = invalid
2368
2406
def convert_from_unicode(self, store, unicode_value):
2369
2407
if self.unquote and store is not None and unicode_value is not None:
2370
2408
unicode_value = store.unquote(unicode_value)
2517
class RegistryOption(Option):
2518
"""Option for a choice from a registry."""
2520
def __init__(self, name, registry, default_from_env=None,
2521
help=None, invalid=None):
2522
"""A registry based Option definition.
2524
This overrides the base class so the conversion from a unicode string
2525
can take quoting into account.
2527
super(RegistryOption, self).__init__(
2528
name, default=lambda: unicode(registry.default_key),
2529
default_from_env=default_from_env,
2530
from_unicode=self.from_unicode, help=help,
2531
invalid=invalid, unquote=False)
2532
self.registry = registry
2534
def from_unicode(self, unicode_str):
2535
if not isinstance(unicode_str, basestring):
2538
return self.registry.get(unicode_str)
2541
"Invalid value %s for %s."
2542
"See help for a list of possible values." % (unicode_str,
2547
ret = [self._help, "\n\nThe following values are supported:\n"]
2548
for key in self.registry.keys():
2549
ret.append(" %s - %s\n" % (key, self.registry.get_help(key)))
2553
_option_ref_re = lazy_regex.lazy_compile('({[^\d\W](?:\.\w|-\w|\w)*})')
2554
"""Describes an expandable option reference.
2556
We want to match the most embedded reference first.
2558
I.e. for '{{foo}}' we will get '{foo}',
2559
for '{bar{baz}}' we will get '{baz}'
2562
def iter_option_refs(string):
2563
# Split isolate refs so every other chunk is a ref
2565
for chunk in _option_ref_re.split(string):
2570
2551
class OptionRegistry(registry.Registry):
2571
2552
"""Register config options by their name.
2574
2555
some information from the option object itself.
2577
def _check_option_name(self, option_name):
2578
"""Ensures an option name is valid.
2580
:param option_name: The name to validate.
2582
if _option_ref_re.match('{%s}' % option_name) is None:
2583
raise errors.IllegalOptionName(option_name)
2585
2558
def register(self, option):
2586
2559
"""Register a new option to its name.
2588
2561
:param option: The option to register. Its name is used as the key.
2590
self._check_option_name(option.name)
2591
2563
super(OptionRegistry, self).register(option.name, option,
2592
2564
help=option.help)
2673
2644
Whether revisions associated with tags should be fetched.
2675
option_registry.register_lazy(
2676
'bzr.transform.orphan_policy', 'bzrlib.transform', 'opt_transform_orphan')
2677
2646
option_registry.register(
2678
2647
Option('bzr.workingtree.worth_saving_limit', default=10,
2679
2648
from_unicode=int_from_store, invalid='warning',
2687
2656
a file has been touched.
2689
2658
option_registry.register(
2690
Option('bugtracker', default=None,
2692
Default bug tracker to use.
2694
This bug tracker will be used for example when marking bugs
2695
as fixed using ``bzr commit --fixes``, if no explicit
2696
bug tracker was specified.
2698
option_registry.register(
2699
2659
Option('check_signatures', default=CHECK_IF_POSSIBLE,
2700
2660
from_unicode=signature_policy_from_unicode,
2909
2865
option_registry.register(
2910
2866
Option('submit_to',
2911
2867
help='''Where submissions from this branch are mailed to.'''))
2912
option_registry.register(
2913
ListOption('suppress_warnings',
2915
help="List of warning classes to suppress."))
2916
option_registry.register(
2917
Option('validate_signatures_in_log', default=False,
2918
from_unicode=bool_from_store, invalid='warning',
2919
help='''Whether to validate signatures in bzr log.'''))
2920
2869
option_registry.register_lazy('ssl.ca_certs',
2921
2870
'bzrlib.transport.http._urllib2_wrappers', 'opt_ssl_ca_certs')
3096
3045
# get_mutable_section() call below.
3098
3047
# Apply the changes from the preserved dirty sections
3099
for section_id, dirty in dirty_sections.iteritems():
3100
clean = self.get_mutable_section(section_id)
3048
for dirty in dirty_sections:
3049
clean = self.get_mutable_section(dirty.id)
3101
3050
clean.apply_changes(dirty, self)
3102
3051
# Everything is clean now
3103
self.dirty_sections = {}
3052
self.dirty_sections = []
3105
3054
def save_changes(self):
3106
3055
"""Saves the Store to persistent storage if changes occurred.
3281
3231
except errors.NoSuchFile:
3282
3232
# The file doesn't exist, let's pretend it was empty
3283
3233
self._load_from_string('')
3284
if section_id in self.dirty_sections:
3285
# We already created a mutable section for this id
3286
return self.dirty_sections[section_id]
3287
3234
if section_id is None:
3288
3235
section = self._config_obj
3290
3237
section = self._config_obj.setdefault(section_id, {})
3291
3238
mutable_section = self.mutable_section_class(section_id, section)
3292
3239
# All mutable sections can become dirty
3293
self.dirty_sections[section_id] = mutable_section
3240
self.dirty_sections.append(mutable_section)
3294
3241
return mutable_section
3296
3243
def quote(self, value):
3448
3382
self.branch = branch
3449
3383
self.id = 'branch'
3385
def lock_write(self, token=None):
3386
return self.branch.lock_write(token)
3389
return self.branch.unlock()
3393
# We need to be able to override the undecorated implementation
3394
self.save_without_locking()
3396
def save_without_locking(self):
3397
super(BranchStore, self).save()
3452
3400
class ControlStore(LockableIniFileStore):
3500
3448
class LocationSection(Section):
3502
def __init__(self, section, extra_path, branch_name=None):
3450
def __init__(self, section, extra_path):
3503
3451
super(LocationSection, self).__init__(section.id, section.options)
3504
3452
self.extra_path = extra_path
3505
if branch_name is None:
3507
3453
self.locals = {'relpath': extra_path,
3508
'basename': urlutils.basename(extra_path),
3509
'branchname': branch_name}
3454
'basename': urlutils.basename(extra_path)}
3511
3456
def get(self, name, default=None, expand=True):
3512
3457
value = super(LocationSection, self).get(name, default)
3582
3528
def __init__(self, store, location):
3583
3529
super(LocationMatcher, self).__init__(store)
3584
url, params = urlutils.split_segment_parameters(location)
3585
3530
if location.startswith('file://'):
3586
3531
location = urlutils.local_path_from_url(location)
3587
3532
self.location = location
3588
branch_name = params.get('branch')
3589
if branch_name is None:
3590
self.branch_name = urlutils.basename(self.location)
3592
self.branch_name = urlutils.unescape(branch_name)
3594
3534
def _get_matching_sections(self):
3595
3535
"""Get all sections matching ``location``."""
3622
3562
section = iter_all_sections.next()
3623
3563
if section_id == section.id:
3624
section = LocationSection(section, extra_path,
3626
matching_sections.append((length, section))
3564
matching_sections.append(
3565
(length, LocationSection(section, extra_path)))
3628
3567
return matching_sections
3646
3585
yield self.store, section
3649
# FIXME: _shared_stores should be an attribute of a library state once a
3650
# library_state object is always available.
3652
_shared_stores_at_exit_installed = False
3588
_option_ref_re = lazy_regex.lazy_compile('({[^{}\n]+})')
3589
"""Describes an expandable option reference.
3591
We want to match the most embedded reference first.
3593
I.e. for '{{foo}}' we will get '{foo}',
3594
for '{bar{baz}}' we will get '{baz}'
3597
def iter_option_refs(string):
3598
# Split isolate refs so every other chunk is a ref
3600
for chunk in _option_ref_re.split(string):
3654
3605
class Stack(object):
3655
3606
"""A stack of configurations where an option can be defined"""
3672
3623
self.store = store
3673
3624
self.mutable_section_id = mutable_section_id
3675
def iter_sections(self):
3676
"""Iterate all the defined sections."""
3677
# Ensuring lazy loading is achieved by delaying section matching (which
3678
# implies querying the persistent storage) until it can't be avoided
3679
# anymore by using callables to describe (possibly empty) section
3681
for sections in self.sections_def:
3682
for store, section in sections():
3683
yield store, section
3685
def get(self, name, expand=True, convert=True):
3626
def get(self, name, expand=None):
3686
3627
"""Return the *first* option value found in the sections.
3688
3629
This is where we guarantee that sections coming from Store are loaded
3696
3637
:param expand: Whether options references should be expanded.
3698
:param convert: Whether the option value should be converted from
3699
unicode (do nothing for non-registered options).
3701
3639
:returns: The value of the option.
3703
3641
# FIXME: No caching of options nor sections yet -- vila 20110503
3643
expand = _get_expand_default_value()
3705
3645
found_store = None # Where the option value has been found
3706
3646
# If the option is registered, it may provide additional info about
3734
3674
value = opt.get_override()
3735
3675
value = expand_and_convert(value)
3736
3676
if value is None:
3737
for store, section in self.iter_sections():
3738
value = section.get(name)
3677
# Ensuring lazy loading is achieved by delaying section matching
3678
# (which implies querying the persistent storage) until it can't be
3679
# avoided anymore by using callables to describe (possibly empty)
3681
for sections in self.sections_def:
3682
for store, section in sections():
3683
value = section.get(name)
3684
if value is not None:
3739
3687
if value is not None:
3742
3689
value = expand_and_convert(value)
3743
3690
if opt is not None and value is None:
3844
3791
return "<config.%s(%s)>" % (self.__class__.__name__, id(self))
3846
3793
def _get_overrides(self):
3847
# FIXME: Hack around library_state.initialize never called
3794
# Hack around library_state.initialize never called
3848
3795
if bzrlib.global_state is not None:
3849
3796
return bzrlib.global_state.cmdline_overrides.get_sections()
3852
def get_shared_store(self, store, state=None):
3853
"""Get a known shared store.
3855
Store urls uniquely identify them and are used to ensure a single copy
3856
is shared across all users.
3858
:param store: The store known to the caller.
3860
:param state: The library state where the known stores are kept.
3862
:returns: The store received if it's not a known one, an already known
3866
state = bzrlib.global_state
3868
global _shared_stores_at_exit_installed
3869
stores = _shared_stores
3870
def save_config_changes():
3871
for k, store in stores.iteritems():
3872
store.save_changes()
3873
if not _shared_stores_at_exit_installed:
3874
# FIXME: Ugly hack waiting for library_state to always be
3875
# available. -- vila 20120731
3877
atexit.register(save_config_changes)
3878
_shared_stores_at_exit_installed = True
3880
stores = state.config_stores
3881
url = store.external_url()
3889
3800
class MemoryStack(Stack):
3890
3801
"""A configuration stack defined from a string.
3956
3867
def __init__(self):
3957
gstore = self.get_shared_store(GlobalStore())
3868
gstore = GlobalStore()
3958
3869
super(GlobalStack, self).__init__(
3959
3870
[self._get_overrides,
3960
3871
NameMatcher(gstore, 'DEFAULT').get_sections],
3961
3872
gstore, mutable_section_id='DEFAULT')
3964
class LocationStack(Stack):
3875
class LocationStack(_CompatibleStack):
3965
3876
"""Per-location options falling back to global options stack.
3983
3894
"""Make a new stack for a location and global configuration.
3985
3896
:param location: A URL prefix to """
3986
lstore = self.get_shared_store(LocationStore())
3897
lstore = LocationStore()
3987
3898
if location.startswith('file://'):
3988
3899
location = urlutils.local_path_from_url(location)
3989
gstore = self.get_shared_store(GlobalStore())
3900
gstore = GlobalStore()
3990
3901
super(LocationStack, self).__init__(
3991
3902
[self._get_overrides,
3992
3903
LocationMatcher(lstore, location).get_sections,
4026
3937
self.branch = branch
4028
def lock_write(self, token=None):
4029
return self.branch.lock_write(token)
4032
return self.branch.unlock()
4035
def set(self, name, value):
4036
super(BranchStack, self).set(name, value)
4037
# Unlocking the branch will trigger a store.save_changes() so the last
4038
# unlock saves all the changes.
4041
def remove(self, name):
4042
super(BranchStack, self).remove(name)
4043
# Unlocking the branch will trigger a store.save_changes() so the last
4044
# unlock saves all the changes.
4047
class RemoteControlStack(Stack):
3940
class RemoteControlStack(_CompatibleStack):
4048
3941
"""Remote control-only options stack."""
4050
3943
# FIXME 2011-11-22 JRV This should probably be renamed to avoid confusion
4074
3967
self.branch = branch
4076
def lock_write(self, token=None):
4077
return self.branch.lock_write(token)
4080
return self.branch.unlock()
4083
def set(self, name, value):
4084
super(BranchOnlyStack, self).set(name, value)
4085
# Force a write to persistent storage
4086
self.store.save_changes()
4089
def remove(self, name):
4090
super(BranchOnlyStack, self).remove(name)
4091
# Force a write to persistent storage
4092
self.store.save_changes()
3970
# Use a an empty dict to initialize an empty configobj avoiding all
3971
# parsing and encoding checks
3972
_quoting_config = configobj.ConfigObj(
3973
{}, encoding='utf-8', interpolation=False, list_values=True)
4095
3975
class cmd_config(commands.Command):
4096
3976
__doc__ = """Display, set or remove a configuration option.
4098
Display the active value for option NAME.
3978
Display the active value for a given option.
4100
3980
If --all is specified, NAME is interpreted as a regular expression and all
4101
matching options are displayed mentioning their scope and without resolving
4102
option references in the value). The active value that bzr will take into
4103
account is the first one displayed for each option.
4105
If NAME is not given, --all .* is implied (all options are displayed for the
4108
Setting a value is achieved by using NAME=value without spaces. The value
3981
matching options are displayed mentioning their scope. The active value
3982
that bzr will take into account is the first one displayed for each option.
3984
If no NAME is given, --all .* is implied.
3986
Setting a value is achieved by using name=value without spaces. The value
4109
3987
is set in the most relevant scope and can be checked by displaying the
4112
Removing a value is achieved by using --remove NAME.
4115
3991
takes_args = ['name?']
4163
4038
# Set the option value
4164
4039
self._set_config_option(name, value, directory, scope)
4166
def _get_stack(self, directory, scope=None, write_access=False):
4041
def _get_stack(self, directory, scope=None):
4167
4042
"""Get the configuration stack specified by ``directory`` and ``scope``.
4169
4044
:param directory: Where the configurations are derived from.
4171
4046
:param scope: A specific config to start from.
4173
:param write_access: Whether a write access to the stack will be
4176
4048
# FIXME: scope should allow access to plugin-specific stacks (even
4177
4049
# reduced to the plugin-specific store), related to
4195
4065
controldir.ControlDir.open_containing_tree_or_branch(
4198
self.add_cleanup(br.lock_write().unlock)
4199
4067
return br.get_config_stack()
4200
4068
except errors.NotBranchError:
4201
4069
return LocationStack(directory)
4203
def _quote_multiline(self, value):
4205
value = '"""' + value + '"""'
4208
4071
def _show_value(self, name, directory, scope):
4209
4072
conf = self._get_stack(directory, scope)
4210
value = conf.get(name, expand=True, convert=False)
4073
value = conf.get(name, expand=True)
4211
4074
if value is not None:
4212
4075
# Quote the value appropriately
4213
value = self._quote_multiline(value)
4076
value = _quoting_config._quote(value)
4214
4077
self.outf.write('%s\n' % (value,))
4216
4079
raise errors.NoSuchConfigOption(name)
4224
4087
cur_store_id = None
4225
4088
cur_section = None
4226
4089
conf = self._get_stack(directory, scope)
4227
for store, section in conf.iter_sections():
4228
for oname in section.iter_option_names():
4229
if name.search(oname):
4230
if cur_store_id != store.id:
4231
# Explain where the options are defined
4232
self.outf.write('%s:\n' % (store.id,))
4233
cur_store_id = store.id
4235
if (section.id is not None and cur_section != section.id):
4236
# Display the section id as it appears in the store
4237
# (None doesn't appear by definition)
4238
self.outf.write(' [%s]\n' % (section.id,))
4239
cur_section = section.id
4240
value = section.get(oname, expand=False)
4241
# Quote the value appropriately
4242
value = self._quote_multiline(value)
4243
self.outf.write(' %s = %s\n' % (oname, value))
4090
for sections in conf.sections_def:
4091
for store, section in sections():
4092
for oname in section.iter_option_names():
4093
if name.search(oname):
4094
if cur_store_id != store.id:
4095
# Explain where the options are defined
4096
self.outf.write('%s:\n' % (store.id,))
4097
cur_store_id = store.id
4099
if (section.id is not None
4100
and cur_section != section.id):
4101
# Display the section id as it appears in the store
4102
# (None doesn't appear by definition)
4103
self.outf.write(' [%s]\n' % (section.id,))
4104
cur_section = section.id
4105
value = section.get(oname, expand=False)
4106
# Since we don't use the stack, we need to restore a
4109
opt = option_registry.get(oname)
4110
value = opt.convert_from_unicode(store, value)
4112
value = store.unquote(value)
4113
value = _quoting_config._quote(value)
4114
self.outf.write(' %s = %s\n' % (oname, value))
4245
4116
def _set_config_option(self, name, value, directory, scope):
4246
conf = self._get_stack(directory, scope, write_access=True)
4117
conf = self._get_stack(directory, scope)
4247
4118
conf.set(name, value)
4248
# Explicitly save the changes
4249
conf.store.save_changes()
4251
4120
def _remove_config_option(self, name, directory, scope):
4252
4121
if name is None:
4253
4122
raise errors.BzrCommandError(
4254
4123
'--remove expects an option to remove.')
4255
conf = self._get_stack(directory, scope, write_access=True)
4124
conf = self._get_stack(directory, scope)
4257
4126
conf.remove(name)
4258
# Explicitly save the changes
4259
conf.store.save_changes()
4260
4127
except KeyError:
4261
4128
raise errors.NoSuchConfigOption(name)