1
# Copyright (C) 2005-2014, 2016 Canonical Ltd
2
# Authors: Robert Collins <robert.collins@canonical.com>
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
"""Configuration that affects the behaviour of Breezy.
21
Currently this configuration resides in ~/.config/breezy/breezy.conf
22
and ~/.config/breezy/locations.conf, which is written to by brz.
24
If the first location doesn't exist, then brz falls back to reading
25
Bazaar configuration files in ~/.bazaar or ~/.config/bazaar.
27
In breezy.conf the following options may be set:
29
editor=name-of-program
30
email=Your Name <your@email.address>
31
check_signatures=require|ignore|check-available(default)
32
create_signatures=always|never|when-required(default)
33
log_format=name-of-format
34
validate_signatures_in_log=true|false(default)
35
acceptable_keys=pattern1,pattern2
36
gpg_signing_key=amy@example.com
38
in locations.conf, you specify the url of a branch and options for it.
39
Wildcards may be used - * and ? as normal in shell completion. Options
40
set in both breezy.conf and locations.conf are overridden by the locations.conf
42
[/home/robertc/source]
43
recurse=False|True(default)
45
check_signatures= as above
46
create_signatures= as above.
47
validate_signatures_in_log=as above
48
acceptable_keys=as above
50
explanation of options
51
----------------------
52
editor - this option sets the pop up editor to use during commits.
53
email - this option sets the user id brz will use when committing.
54
check_signatures - this option will control whether brz will require good gpg
55
signatures, ignore them, or check them if they are
56
present. Currently it is unused except that
57
check_signatures turns on create_signatures.
58
create_signatures - this option controls whether brz will always create
59
gpg signatures or not on commits. There is an unused
60
option which in future is expected to work if
61
branch settings require signatures.
62
log_format - this option sets the default log format. Possible values are
63
long, short, line, or a plugin can register new formats.
64
validate_signatures_in_log - show GPG signature validity in log output
65
acceptable_keys - comma separated list of key patterns acceptable for
66
verify-signatures command
68
In breezy.conf you can also define aliases in the ALIASES sections, example
71
lastlog=log --line -r-10..-1
72
ll=log --line -r-10..-1
77
from __future__ import absolute_import
82
from io import BytesIO
85
from .lazy_import import lazy_import
86
lazy_import(globals(), """
109
from breezy.i18n import gettext
121
CHECK_IF_POSSIBLE = 0
126
SIGN_WHEN_REQUIRED = 0
133
POLICY_APPENDPATH = 2
137
POLICY_NORECURSE: 'norecurse',
138
POLICY_APPENDPATH: 'appendpath',
143
'norecurse': POLICY_NORECURSE,
144
'appendpath': POLICY_APPENDPATH,
148
STORE_LOCATION = POLICY_NONE
149
STORE_LOCATION_NORECURSE = POLICY_NORECURSE
150
STORE_LOCATION_APPENDPATH = POLICY_APPENDPATH
155
class OptionExpansionLoop(errors.BzrError):
157
_fmt = 'Loop involving %(refs)r while expanding "%(string)s".'
159
def __init__(self, string, refs):
161
self.refs = '->'.join(refs)
164
class ExpandingUnknownOption(errors.BzrError):
166
_fmt = 'Option "%(name)s" is not defined while expanding "%(string)s".'
168
def __init__(self, name, string):
173
class IllegalOptionName(errors.BzrError):
175
_fmt = 'Option "%(name)s" is not allowed.'
177
def __init__(self, name):
181
class ConfigContentError(errors.BzrError):
183
_fmt = "Config file %(filename)s is not UTF-8 encoded\n"
185
def __init__(self, filename):
186
self.filename = filename
189
class ParseConfigError(errors.BzrError):
191
_fmt = "Error(s) parsing config file %(filename)s:\n%(errors)s"
193
def __init__(self, errors, filename):
194
self.filename = filename
195
self.errors = '\n'.join(e.msg for e in errors)
198
class ConfigOptionValueError(errors.BzrError):
200
_fmt = ('Bad value "%(value)s" for option "%(name)s".\n'
201
'See ``brz help %(name)s``')
203
def __init__(self, name, value):
204
errors.BzrError.__init__(self, name=name, value=value)
207
class NoEmailInUsername(errors.BzrError):
209
_fmt = "%(username)r does not seem to contain a reasonable email address"
211
def __init__(self, username):
212
self.username = username
215
class NoSuchConfig(errors.BzrError):
217
_fmt = ('The "%(config_id)s" configuration does not exist.')
219
def __init__(self, config_id):
220
errors.BzrError.__init__(self, config_id=config_id)
223
class NoSuchConfigOption(errors.BzrError):
225
_fmt = ('The "%(option_name)s" configuration option does not exist.')
227
def __init__(self, option_name):
228
errors.BzrError.__init__(self, option_name=option_name)
231
def signature_policy_from_unicode(signature_string):
232
"""Convert a string to a signing policy."""
233
if signature_string.lower() == 'check-available':
234
return CHECK_IF_POSSIBLE
235
if signature_string.lower() == 'ignore':
237
if signature_string.lower() == 'require':
239
raise ValueError("Invalid signatures policy '%s'"
243
def signing_policy_from_unicode(signature_string):
244
"""Convert a string to a signing policy."""
245
if signature_string.lower() == 'when-required':
246
return SIGN_WHEN_REQUIRED
247
if signature_string.lower() == 'never':
249
if signature_string.lower() == 'always':
251
raise ValueError("Invalid signing policy '%s'"
255
def _has_triplequote_bug():
256
"""True if triple quote logic is reversed, see lp:710410."""
257
conf = configobj.ConfigObj()
258
quote = getattr(conf, "_get_triple_quote", None)
259
if quote and quote('"""') != "'''":
264
class ConfigObj(configobj.ConfigObj):
266
def __init__(self, infile=None, **kwargs):
267
# We define our own interpolation mechanism calling it option expansion
268
super(ConfigObj, self).__init__(infile=infile,
272
if _has_triplequote_bug():
273
def _get_triple_quote(self, value):
274
quot = super(ConfigObj, self)._get_triple_quote(value)
275
if quot == configobj.tdquot:
276
return configobj.tsquot
277
return configobj.tdquot
279
def get_bool(self, section, key):
280
return self[section].as_bool(key)
282
def get_value(self, section, name):
283
# Try [] for the old DEFAULT section.
284
if section == "DEFAULT":
289
return self[section][name]
292
class Config(object):
293
"""A configuration policy - what username, editor, gpg needs etc."""
296
super(Config, self).__init__()
299
"""Returns a unique ID for the config."""
300
raise NotImplementedError(self.config_id)
302
def get_change_editor(self, old_tree, new_tree):
303
from breezy import diff
304
cmd = self._get_change_editor()
307
cmd = cmd.replace('@old_path', '{old_path}')
308
cmd = cmd.replace('@new_path', '{new_path}')
309
cmd = cmdline.split(cmd)
310
if '{old_path}' not in cmd:
311
cmd.extend(['{old_path}', '{new_path}'])
312
return diff.DiffFromTool.from_string(cmd, old_tree, new_tree,
315
def _get_signature_checking(self):
316
"""Template method to override signature checking policy."""
318
def _get_signing_policy(self):
319
"""Template method to override signature creation policy."""
323
def expand_options(self, string, env=None):
324
"""Expand option references in the string in the configuration context.
326
:param string: The string containing option to expand.
328
:param env: An option dict defining additional configuration options or
329
overriding existing ones.
331
:returns: The expanded string.
333
return self._expand_options_in_string(string, env)
335
def _expand_options_in_list(self, slist, env=None, _ref_stack=None):
336
"""Expand options in a list of strings in the configuration context.
338
:param slist: A list of strings.
340
:param env: An option dict defining additional configuration options or
341
overriding existing ones.
343
:param _ref_stack: Private list containing the options being
344
expanded to detect loops.
346
:returns: The flatten list of expanded strings.
348
# expand options in each value separately flattening lists
351
value = self._expand_options_in_string(s, env, _ref_stack)
352
if isinstance(value, list):
358
def _expand_options_in_string(self, string, env=None, _ref_stack=None):
359
"""Expand options in the string in the configuration context.
361
:param string: The string to be expanded.
363
:param env: An option dict defining additional configuration options or
364
overriding existing ones.
366
:param _ref_stack: Private list containing the options being
367
expanded to detect loops.
369
:returns: The expanded string.
372
# Not much to expand there
374
if _ref_stack is None:
375
# What references are currently resolved (to detect loops)
377
if self.option_ref_re is None:
378
# We want to match the most embedded reference first (i.e. for
379
# '{{foo}}' we will get '{foo}',
380
# for '{bar{baz}}' we will get '{baz}'
381
self.option_ref_re = re.compile('({[^{}]+})')
383
# We need to iterate until no more refs appear ({{foo}} will need two
384
# iterations for example).
386
raw_chunks = self.option_ref_re.split(result)
387
if len(raw_chunks) == 1:
388
# Shorcut the trivial case: no refs
392
# Split will isolate refs so that every other chunk is a ref
394
for chunk in raw_chunks:
397
# Keep only non-empty strings (or we get bogus empty
398
# slots when a list value is involved).
403
if name in _ref_stack:
404
raise OptionExpansionLoop(string, _ref_stack)
405
_ref_stack.append(name)
406
value = self._expand_option(name, env, _ref_stack)
408
raise ExpandingUnknownOption(name, string)
409
if isinstance(value, list):
417
# Once a list appears as the result of an expansion, all
418
# callers will get a list result. This allows a consistent
419
# behavior even when some options in the expansion chain
420
# defined as strings (no comma in their value) but their
421
# expanded value is a list.
422
return self._expand_options_in_list(chunks, env, _ref_stack)
424
result = ''.join(chunks)
427
def _expand_option(self, name, env, _ref_stack):
428
if env is not None and name in env:
429
# Special case, values provided in env takes precedence over
433
# FIXME: This is a limited implementation, what we really need is a
434
# way to query the brz config for the value of an option,
435
# respecting the scope rules (That is, once we implement fallback
436
# configs, getting the option value should restart from the top
437
# config, not the current one) -- vila 20101222
438
value = self.get_user_option(name, expand=False)
439
if isinstance(value, list):
440
value = self._expand_options_in_list(value, env, _ref_stack)
442
value = self._expand_options_in_string(value, env, _ref_stack)
445
def _get_user_option(self, option_name):
446
"""Template method to provide a user option."""
449
def get_user_option(self, option_name, expand=True):
450
"""Get a generic option - no special process, no default.
452
:param option_name: The queried option.
454
:param expand: Whether options references should be expanded.
456
:returns: The value of the option.
458
value = self._get_user_option(option_name)
460
if isinstance(value, list):
461
value = self._expand_options_in_list(value)
462
elif isinstance(value, dict):
463
trace.warning('Cannot expand "%s":'
464
' Dicts do not support option expansion'
467
value = self._expand_options_in_string(value)
468
for hook in OldConfigHooks['get']:
469
hook(self, option_name, value)
472
def get_user_option_as_bool(self, option_name, expand=None, default=None):
473
"""Get a generic option as a boolean.
475
:param expand: Allow expanding references to other config values.
476
:param default: Default value if nothing is configured
477
:return None if the option doesn't exist or its value can't be
478
interpreted as a boolean. Returns True or False otherwise.
480
s = self.get_user_option(option_name, expand=expand)
482
# The option doesn't exist
484
val = ui.bool_from_string(s)
486
# The value can't be interpreted as a boolean
487
trace.warning('Value "%s" is not a boolean for "%s"',
491
def get_user_option_as_list(self, option_name, expand=None):
492
"""Get a generic option as a list - no special process, no default.
494
:return None if the option doesn't exist. Returns the value as a list
497
l = self.get_user_option(option_name, expand=expand)
498
if isinstance(l, str):
499
# A single value, most probably the user forgot (or didn't care to
504
def _log_format(self):
505
"""See log_format()."""
508
def validate_signatures_in_log(self):
509
"""Show GPG signature validity in log"""
510
result = self._validate_signatures_in_log()
517
def _validate_signatures_in_log(self):
518
"""See validate_signatures_in_log()."""
521
def _post_commit(self):
522
"""See Config.post_commit."""
525
def user_email(self):
526
"""Return just the email component of a username."""
527
return extract_email_address(self.username())
530
"""Return email-style username.
532
Something similar to 'Martin Pool <mbp@sourcefrog.net>'
534
$BRZ_EMAIL can be set to override this, then
535
the concrete policy type is checked, and finally
537
If no username can be found, NoWhoami exception is raised.
539
v = os.environ.get('BRZ_EMAIL')
542
v = self._get_user_id()
545
return bedding.default_email()
547
def get_alias(self, value):
548
return self._get_alias(value)
550
def _get_alias(self, value):
553
def get_nickname(self):
554
return self._get_nickname()
556
def _get_nickname(self):
559
def get_bzr_remote_path(self):
561
return os.environ['BZR_REMOTE_PATH']
563
path = self.get_user_option("bzr_remote_path")
568
def suppress_warning(self, warning):
569
"""Should the warning be suppressed or emitted.
571
:param warning: The name of the warning being tested.
573
:returns: True if the warning should be suppressed, False otherwise.
575
warnings = self.get_user_option_as_list('suppress_warnings')
576
if warnings is None or warning not in warnings:
581
def get_merge_tools(self):
583
for (oname, value, section, conf_id, parser) in self._get_options():
584
if oname.startswith('bzr.mergetool.'):
585
tool_name = oname[len('bzr.mergetool.'):]
586
tools[tool_name] = self.get_user_option(oname, False)
587
trace.mutter('loaded merge tools: %r' % tools)
590
def find_merge_tool(self, name):
591
# We fake a defaults mechanism here by checking if the given name can
592
# be found in the known_merge_tools if it's not found in the config.
593
# This should be done through the proposed config defaults mechanism
594
# when it becomes available in the future.
595
command_line = (self.get_user_option('bzr.mergetool.%s' % name,
597
mergetools.known_merge_tools.get(name, None))
601
class _ConfigHooks(hooks.Hooks):
602
"""A dict mapping hook names and a list of callables for configs.
606
"""Create the default hooks.
608
These are all empty initially, because by default nothing should get
611
super(_ConfigHooks, self).__init__('breezy.config', 'ConfigHooks')
612
self.add_hook('load',
613
'Invoked when a config store is loaded.'
614
' The signature is (store).',
616
self.add_hook('save',
617
'Invoked when a config store is saved.'
618
' The signature is (store).',
620
# The hooks for config options
622
'Invoked when a config option is read.'
623
' The signature is (stack, name, value).',
626
'Invoked when a config option is set.'
627
' The signature is (stack, name, value).',
629
self.add_hook('remove',
630
'Invoked when a config option is removed.'
631
' The signature is (stack, name).',
635
ConfigHooks = _ConfigHooks()
638
class _OldConfigHooks(hooks.Hooks):
639
"""A dict mapping hook names and a list of callables for configs.
643
"""Create the default hooks.
645
These are all empty initially, because by default nothing should get
648
super(_OldConfigHooks, self).__init__(
649
'breezy.config', 'OldConfigHooks')
650
self.add_hook('load',
651
'Invoked when a config store is loaded.'
652
' The signature is (config).',
654
self.add_hook('save',
655
'Invoked when a config store is saved.'
656
' The signature is (config).',
658
# The hooks for config options
660
'Invoked when a config option is read.'
661
' The signature is (config, name, value).',
664
'Invoked when a config option is set.'
665
' The signature is (config, name, value).',
667
self.add_hook('remove',
668
'Invoked when a config option is removed.'
669
' The signature is (config, name).',
673
OldConfigHooks = _OldConfigHooks()
676
class IniBasedConfig(Config):
677
"""A configuration policy that draws from ini files."""
679
def __init__(self, file_name=None):
680
"""Base class for configuration files using an ini-like syntax.
682
:param file_name: The configuration file path.
684
super(IniBasedConfig, self).__init__()
685
self.file_name = file_name
686
self.file_name = file_name
691
def from_string(cls, str_or_unicode, file_name=None, save=False):
692
"""Create a config object from a string.
694
:param str_or_unicode: A string representing the file content. This
695
will be utf-8 encoded.
697
:param file_name: The configuration file path.
699
:param _save: Whether the file should be saved upon creation.
701
conf = cls(file_name=file_name)
702
conf._create_from_string(str_or_unicode, save)
705
def _create_from_string(self, str_or_unicode, save):
706
if isinstance(str_or_unicode, str):
707
str_or_unicode = str_or_unicode.encode('utf-8')
708
self._content = BytesIO(str_or_unicode)
709
# Some tests use in-memory configs, some other always need the config
710
# file to exist on disk.
712
self._write_config_file()
714
def _get_parser(self):
715
if self._parser is not None:
717
if self._content is not None:
718
co_input = self._content
719
elif self.file_name is None:
720
raise AssertionError('We have no content to create the config')
722
co_input = self.file_name
724
self._parser = ConfigObj(co_input, encoding='utf-8')
725
except configobj.ConfigObjError as e:
726
raise ParseConfigError(e.errors, e.config.filename)
727
except UnicodeDecodeError:
728
raise ConfigContentError(self.file_name)
729
# Make sure self.reload() will use the right file name
730
self._parser.filename = self.file_name
731
for hook in OldConfigHooks['load']:
736
"""Reload the config file from disk."""
737
if self.file_name is None:
738
raise AssertionError('We need a file name to reload the config')
739
if self._parser is not None:
740
self._parser.reload()
741
for hook in ConfigHooks['load']:
744
def _get_matching_sections(self):
745
"""Return an ordered list of (section_name, extra_path) pairs.
747
If the section contains inherited configuration, extra_path is
748
a string containing the additional path components.
750
section = self._get_section()
751
if section is not None:
752
return [(section, '')]
756
def _get_section(self):
757
"""Override this to define the section used by the config."""
760
def _get_sections(self, name=None):
761
"""Returns an iterator of the sections specified by ``name``.
763
:param name: The section name. If None is supplied, the default
764
configurations are yielded.
766
:return: A tuple (name, section, config_id) for all sections that will
767
be walked by user_get_option() in the 'right' order. The first one
768
is where set_user_option() will update the value.
770
parser = self._get_parser()
772
yield (name, parser[name], self.config_id())
774
# No section name has been given so we fallback to the configobj
775
# itself which holds the variables defined outside of any section.
776
yield (None, parser, self.config_id())
778
def _get_options(self, sections=None):
779
"""Return an ordered list of (name, value, section, config_id) tuples.
781
All options are returned with their associated value and the section
782
they appeared in. ``config_id`` is a unique identifier for the
783
configuration file the option is defined in.
785
:param sections: Default to ``_get_matching_sections`` if not
786
specified. This gives a better control to daughter classes about
787
which sections should be searched. This is a list of (name,
791
parser = self._get_parser()
793
for (section_name, _) in self._get_matching_sections():
795
section = parser[section_name]
797
# This could happen for an empty file for which we define a
798
# DEFAULT section. FIXME: Force callers to provide sections
799
# instead ? -- vila 20100930
801
sections.append((section_name, section))
802
config_id = self.config_id()
803
for (section_name, section) in sections:
804
for (name, value) in section.iteritems():
805
yield (name, parser._quote(value), section_name,
808
def _get_option_policy(self, section, option_name):
809
"""Return the policy for the given (section, option_name) pair."""
812
def _get_change_editor(self):
813
return self.get_user_option('change_editor', expand=False)
815
def _get_signature_checking(self):
816
"""See Config._get_signature_checking."""
817
policy = self._get_user_option('check_signatures')
819
return signature_policy_from_unicode(policy)
821
def _get_signing_policy(self):
822
"""See Config._get_signing_policy"""
823
policy = self._get_user_option('create_signatures')
825
return signing_policy_from_unicode(policy)
827
def _get_user_id(self):
828
"""Get the user id from the 'email' key in the current section."""
829
return self._get_user_option('email')
831
def _get_user_option(self, option_name):
832
"""See Config._get_user_option."""
833
for (section, extra_path) in self._get_matching_sections():
835
value = self._get_parser().get_value(section, option_name)
838
policy = self._get_option_policy(section, option_name)
839
if policy == POLICY_NONE:
841
elif policy == POLICY_NORECURSE:
842
# norecurse items only apply to the exact path
847
elif policy == POLICY_APPENDPATH:
849
value = urlutils.join(value, extra_path)
852
raise AssertionError('Unexpected config policy %r' % policy)
856
def _log_format(self):
857
"""See Config.log_format."""
858
return self._get_user_option('log_format')
860
def _validate_signatures_in_log(self):
861
"""See Config.validate_signatures_in_log."""
862
return self._get_user_option('validate_signatures_in_log')
864
def _acceptable_keys(self):
865
"""See Config.acceptable_keys."""
866
return self._get_user_option('acceptable_keys')
868
def _post_commit(self):
869
"""See Config.post_commit."""
870
return self._get_user_option('post_commit')
872
def _get_alias(self, value):
874
return self._get_parser().get_value("ALIASES",
879
def _get_nickname(self):
880
return self.get_user_option('nickname')
882
def remove_user_option(self, option_name, section_name=None):
883
"""Remove a user option and save the configuration file.
885
:param option_name: The option to be removed.
887
:param section_name: The section the option is defined in, default to
891
parser = self._get_parser()
892
if section_name is None:
895
section = parser[section_name]
897
del section[option_name]
899
raise NoSuchConfigOption(option_name)
900
self._write_config_file()
901
for hook in OldConfigHooks['remove']:
902
hook(self, option_name)
904
def _write_config_file(self):
905
if self.file_name is None:
906
raise AssertionError('We cannot save, self.file_name is None')
907
conf_dir = os.path.dirname(self.file_name)
908
bedding.ensure_config_dir_exists(conf_dir)
909
with atomicfile.AtomicFile(self.file_name) as atomic_file:
910
self._get_parser().write(atomic_file)
911
osutils.copy_ownership_from_path(self.file_name)
912
for hook in OldConfigHooks['save']:
916
class LockableConfig(IniBasedConfig):
917
"""A configuration needing explicit locking for access.
919
If several processes try to write the config file, the accesses need to be
922
Daughter classes should use the self.lock_write() decorator method when
923
they upate a config (they call, directly or indirectly, the
924
``_write_config_file()`` method. These methods (typically ``set_option()``
925
and variants must reload the config file from disk before calling
926
``_write_config_file()``), this can be achieved by calling the
927
``self.reload()`` method. Note that the lock scope should cover both the
928
reading and the writing of the config file which is why the decorator can't
929
be applied to ``_write_config_file()`` only.
931
This should be enough to implement the following logic:
932
- lock for exclusive write access,
933
- reload the config file from disk,
937
This logic guarantees that a writer can update a value without erasing an
938
update made by another writer.
943
def __init__(self, file_name):
944
super(LockableConfig, self).__init__(file_name=file_name)
945
self.dir = osutils.dirname(osutils.safe_unicode(self.file_name))
946
# FIXME: It doesn't matter that we don't provide possible_transports
947
# below since this is currently used only for local config files ;
948
# local transports are not shared. But if/when we start using
949
# LockableConfig for other kind of transports, we will need to reuse
950
# whatever connection is already established -- vila 20100929
951
self.transport = transport.get_transport_from_path(self.dir)
952
self._lock = lockdir.LockDir(self.transport, self.lock_name)
954
def _create_from_string(self, unicode_bytes, save):
955
super(LockableConfig, self)._create_from_string(unicode_bytes, False)
957
# We need to handle the saving here (as opposed to IniBasedConfig)
960
self._write_config_file()
963
def lock_write(self, token=None):
964
"""Takes a write lock in the directory containing the config file.
966
If the directory doesn't exist it is created.
968
bedding.ensure_config_dir_exists(self.dir)
969
token = self._lock.lock_write(token)
970
return lock.LogicalLockResult(self.unlock, token)
975
def break_lock(self):
976
self._lock.break_lock()
978
def remove_user_option(self, option_name, section_name=None):
979
with self.lock_write():
980
super(LockableConfig, self).remove_user_option(
981
option_name, section_name)
983
def _write_config_file(self):
984
if self._lock is None or not self._lock.is_held:
985
# NB: if the following exception is raised it probably means a
986
# missing call to lock_write() by one of the callers.
987
raise errors.ObjectNotLocked(self)
988
super(LockableConfig, self)._write_config_file()
991
class GlobalConfig(LockableConfig):
992
"""The configuration that should be used for a specific location."""
995
super(GlobalConfig, self).__init__(file_name=bedding.config_path())
1001
def from_string(cls, str_or_unicode, save=False):
1002
"""Create a config object from a string.
1004
:param str_or_unicode: A string representing the file content. This
1005
will be utf-8 encoded.
1007
:param save: Whether the file should be saved upon creation.
1010
conf._create_from_string(str_or_unicode, save)
1013
def set_user_option(self, option, value):
1014
"""Save option and its value in the configuration."""
1015
with self.lock_write():
1016
self._set_option(option, value, 'DEFAULT')
1018
def get_aliases(self):
1019
"""Return the aliases section."""
1020
if 'ALIASES' in self._get_parser():
1021
return self._get_parser()['ALIASES']
1025
def set_alias(self, alias_name, alias_command):
1026
"""Save the alias in the configuration."""
1027
with self.lock_write():
1028
self._set_option(alias_name, alias_command, 'ALIASES')
1030
def unset_alias(self, alias_name):
1031
"""Unset an existing alias."""
1032
with self.lock_write():
1034
aliases = self._get_parser().get('ALIASES')
1035
if not aliases or alias_name not in aliases:
1036
raise errors.NoSuchAlias(alias_name)
1037
del aliases[alias_name]
1038
self._write_config_file()
1040
def _set_option(self, option, value, section):
1042
self._get_parser().setdefault(section, {})[option] = value
1043
self._write_config_file()
1044
for hook in OldConfigHooks['set']:
1045
hook(self, option, value)
1047
def _get_sections(self, name=None):
1048
"""See IniBasedConfig._get_sections()."""
1049
parser = self._get_parser()
1050
# We don't give access to options defined outside of any section, we
1051
# used the DEFAULT section by... default.
1052
if name in (None, 'DEFAULT'):
1053
# This could happen for an empty file where the DEFAULT section
1054
# doesn't exist yet. So we force DEFAULT when yielding
1056
if 'DEFAULT' not in parser:
1057
parser['DEFAULT'] = {}
1058
yield (name, parser[name], self.config_id())
1060
def remove_user_option(self, option_name, section_name=None):
1061
if section_name is None:
1062
# We need to force the default section.
1063
section_name = 'DEFAULT'
1064
with self.lock_write():
1065
# We need to avoid the LockableConfig implementation or we'll lock
1067
super(LockableConfig, self).remove_user_option(
1068
option_name, section_name)
1071
def _iter_for_location_by_parts(sections, location):
1072
"""Keep only the sessions matching the specified location.
1074
:param sections: An iterable of section names.
1076
:param location: An url or a local path to match against.
1078
:returns: An iterator of (section, extra_path, nb_parts) where nb is the
1079
number of path components in the section name, section is the section
1080
name and extra_path is the difference between location and the section
1083
``location`` will always be a local path and never a 'file://' url but the
1084
section names themselves can be in either form.
1086
location_parts = location.rstrip('/').split('/')
1088
for section in sections:
1089
# location is a local path if possible, so we need to convert 'file://'
1090
# urls in section names to local paths if necessary.
1092
# This also avoids having file:///path be a more exact
1093
# match than '/path'.
1095
# FIXME: This still raises an issue if a user defines both file:///path
1096
# *and* /path. Should we raise an error in this case -- vila 20110505
1098
if section.startswith('file://'):
1099
section_path = urlutils.local_path_from_url(section)
1101
section_path = section
1102
section_parts = section_path.rstrip('/').split('/')
1105
if len(section_parts) > len(location_parts):
1106
# More path components in the section, they can't match
1109
# Rely on zip truncating in length to the length of the shortest
1110
# argument sequence.
1111
for name in zip(location_parts, section_parts):
1112
if not fnmatch.fnmatch(name[0], name[1]):
1117
# build the path difference between the section and the location
1118
extra_path = '/'.join(location_parts[len(section_parts):])
1119
yield section, extra_path, len(section_parts)
1122
class LocationConfig(LockableConfig):
1123
"""A configuration object that gives the policy for a location."""
1125
def __init__(self, location):
1126
super(LocationConfig, self).__init__(
1127
file_name=bedding.locations_config_path())
1128
# local file locations are looked up by local path, rather than
1129
# by file url. This is because the config file is a user
1130
# file, and we would rather not expose the user to file urls.
1131
if location.startswith('file://'):
1132
location = urlutils.local_path_from_url(location)
1133
self.location = location
1135
def config_id(self):
1139
def from_string(cls, str_or_unicode, location, save=False):
1140
"""Create a config object from a string.
1142
:param str_or_unicode: A string representing the file content. This will
1145
:param location: The location url to filter the configuration.
1147
:param save: Whether the file should be saved upon creation.
1149
conf = cls(location)
1150
conf._create_from_string(str_or_unicode, save)
1153
def _get_matching_sections(self):
1154
"""Return an ordered list of section names matching this location."""
1155
# put the longest (aka more specific) locations first
1157
_iter_for_location_by_parts(self._get_parser(), self.location),
1158
key=lambda match: (match[2], match[0]),
1160
for (section, extra_path, length) in matches:
1161
yield section, extra_path
1162
# should we stop looking for parent configs here?
1164
if self._get_parser()[section].as_bool('ignore_parents'):
1169
def _get_sections(self, name=None):
1170
"""See IniBasedConfig._get_sections()."""
1171
# We ignore the name here as the only sections handled are named with
1172
# the location path and we don't expose embedded sections either.
1173
parser = self._get_parser()
1174
for name, extra_path in self._get_matching_sections():
1175
yield (name, parser[name], self.config_id())
1177
def _get_option_policy(self, section, option_name):
1178
"""Return the policy for the given (section, option_name) pair."""
1179
# check for the old 'recurse=False' flag
1181
recurse = self._get_parser()[section].as_bool('recurse')
1185
return POLICY_NORECURSE
1187
policy_key = option_name + ':policy'
1189
policy_name = self._get_parser()[section][policy_key]
1193
return _policy_value[policy_name]
1195
def _set_option_policy(self, section, option_name, option_policy):
1196
"""Set the policy for the given option name in the given section."""
1197
policy_key = option_name + ':policy'
1198
policy_name = _policy_name[option_policy]
1199
if policy_name is not None:
1200
self._get_parser()[section][policy_key] = policy_name
1202
if policy_key in self._get_parser()[section]:
1203
del self._get_parser()[section][policy_key]
1205
def set_user_option(self, option, value, store=STORE_LOCATION):
1206
"""Save option and its value in the configuration."""
1207
if store not in [STORE_LOCATION,
1208
STORE_LOCATION_NORECURSE,
1209
STORE_LOCATION_APPENDPATH]:
1210
raise ValueError('bad storage policy %r for %r' %
1212
with self.lock_write():
1214
location = self.location
1215
if location.endswith('/'):
1216
location = location[:-1]
1217
parser = self._get_parser()
1218
if location not in parser and not location + '/' in parser:
1219
parser[location] = {}
1220
elif location + '/' in parser:
1221
location = location + '/'
1222
parser[location][option] = value
1223
# the allowed values of store match the config policies
1224
self._set_option_policy(location, option, store)
1225
self._write_config_file()
1226
for hook in OldConfigHooks['set']:
1227
hook(self, option, value)
1230
class BranchConfig(Config):
1231
"""A configuration object giving the policy for a branch."""
1233
def __init__(self, branch):
1234
super(BranchConfig, self).__init__()
1235
self._location_config = None
1236
self._branch_data_config = None
1237
self._global_config = None
1238
self.branch = branch
1239
self.option_sources = (self._get_location_config,
1240
self._get_branch_data_config,
1241
self._get_global_config)
1243
def config_id(self):
1246
def _get_branch_data_config(self):
1247
if self._branch_data_config is None:
1248
self._branch_data_config = TreeConfig(self.branch)
1249
self._branch_data_config.config_id = self.config_id
1250
return self._branch_data_config
1252
def _get_location_config(self):
1253
if self._location_config is None:
1254
self._location_config = LocationConfig(self.branch.base)
1255
return self._location_config
1257
def _get_global_config(self):
1258
if self._global_config is None:
1259
self._global_config = GlobalConfig()
1260
return self._global_config
1262
def _get_best_value(self, option_name):
1263
"""This returns a user option from local, tree or global config.
1265
They are tried in that order. Use get_safe_value if trusted values
1268
for source in self.option_sources:
1269
value = getattr(source(), option_name)()
1270
if value is not None:
1274
def _get_safe_value(self, option_name):
1275
"""This variant of get_best_value never returns untrusted values.
1277
It does not return values from the branch data, because the branch may
1278
not be controlled by the user.
1280
We may wish to allow locations.conf to control whether branches are
1281
trusted in the future.
1283
for source in (self._get_location_config, self._get_global_config):
1284
value = getattr(source(), option_name)()
1285
if value is not None:
1289
def _get_user_id(self):
1290
"""Return the full user id for the branch.
1292
e.g. "John Hacker <jhacker@example.com>"
1293
This is looked up in the email controlfile for the branch.
1295
return self._get_best_value('_get_user_id')
1297
def _get_change_editor(self):
1298
return self._get_best_value('_get_change_editor')
1300
def _get_signature_checking(self):
1301
"""See Config._get_signature_checking."""
1302
return self._get_best_value('_get_signature_checking')
1304
def _get_signing_policy(self):
1305
"""See Config._get_signing_policy."""
1306
return self._get_best_value('_get_signing_policy')
1308
def _get_user_option(self, option_name):
1309
"""See Config._get_user_option."""
1310
for source in self.option_sources:
1311
value = source()._get_user_option(option_name)
1312
if value is not None:
1316
def _get_sections(self, name=None):
1317
"""See IniBasedConfig.get_sections()."""
1318
for source in self.option_sources:
1319
for section in source()._get_sections(name):
1322
def _get_options(self, sections=None):
1323
# First the locations options
1324
for option in self._get_location_config()._get_options():
1326
# Then the branch options
1327
branch_config = self._get_branch_data_config()
1328
if sections is None:
1329
sections = [('DEFAULT', branch_config._get_parser())]
1330
# FIXME: We shouldn't have to duplicate the code in IniBasedConfig but
1331
# Config itself has no notion of sections :( -- vila 20101001
1332
config_id = self.config_id()
1333
for (section_name, section) in sections:
1334
for (name, value) in section.iteritems():
1335
yield (name, value, section_name,
1336
config_id, branch_config._get_parser())
1337
# Then the global options
1338
for option in self._get_global_config()._get_options():
1341
def set_user_option(self, name, value, store=STORE_BRANCH,
1343
if store == STORE_BRANCH:
1344
self._get_branch_data_config().set_option(value, name)
1345
elif store == STORE_GLOBAL:
1346
self._get_global_config().set_user_option(name, value)
1348
self._get_location_config().set_user_option(name, value, store)
1351
if store in (STORE_GLOBAL, STORE_BRANCH):
1352
mask_value = self._get_location_config().get_user_option(name)
1353
if mask_value is not None:
1354
trace.warning('Value "%s" is masked by "%s" from'
1355
' locations.conf', value, mask_value)
1357
if store == STORE_GLOBAL:
1358
branch_config = self._get_branch_data_config()
1359
mask_value = branch_config.get_user_option(name)
1360
if mask_value is not None:
1361
trace.warning('Value "%s" is masked by "%s" from'
1362
' branch.conf', value, mask_value)
1364
def remove_user_option(self, option_name, section_name=None):
1365
self._get_branch_data_config().remove_option(option_name, section_name)
1367
def _post_commit(self):
1368
"""See Config.post_commit."""
1369
return self._get_safe_value('_post_commit')
1371
def _get_nickname(self):
1372
value = self._get_explicit_nickname()
1373
if value is not None:
1375
if self.branch.name:
1376
return self.branch.name
1377
return urlutils.unescape(self.branch.base.split('/')[-2])
1379
def has_explicit_nickname(self):
1380
"""Return true if a nickname has been explicitly assigned."""
1381
return self._get_explicit_nickname() is not None
1383
def _get_explicit_nickname(self):
1384
return self._get_best_value('_get_nickname')
1386
def _log_format(self):
1387
"""See Config.log_format."""
1388
return self._get_best_value('_log_format')
1390
def _validate_signatures_in_log(self):
1391
"""See Config.validate_signatures_in_log."""
1392
return self._get_best_value('_validate_signatures_in_log')
1394
def _acceptable_keys(self):
1395
"""See Config.acceptable_keys."""
1396
return self._get_best_value('_acceptable_keys')
1399
def parse_username(username):
1400
"""Parse e-mail username and return a (name, address) tuple."""
1401
match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
1403
return (username, '')
1404
return (match.group(1), match.group(2))
1407
def extract_email_address(e):
1408
"""Return just the address part of an email string.
1410
That is just the user@domain part, nothing else.
1411
This part is required to contain only ascii characters.
1412
If it can't be extracted, raises an error.
1414
>>> extract_email_address('Jane Tester <jane@test.com>')
1417
name, email = parse_username(e)
1419
raise NoEmailInUsername(e)
1423
class TreeConfig(IniBasedConfig):
1424
"""Branch configuration data associated with its contents, not location"""
1426
# XXX: Really needs a better name, as this is not part of the tree!
1429
def __init__(self, branch):
1430
self._config = branch._get_config()
1431
self.branch = branch
1433
def _get_parser(self, file=None):
1434
if file is not None:
1435
return IniBasedConfig._get_parser(file)
1436
return self._config._get_configobj()
1438
def get_option(self, name, section=None, default=None):
1439
with self.branch.lock_read():
1440
return self._config.get_option(name, section, default)
1442
def set_option(self, value, name, section=None):
1443
"""Set a per-branch configuration option"""
1444
# FIXME: We shouldn't need to lock explicitly here but rather rely on
1445
# higher levels providing the right lock -- vila 20101004
1446
with self.branch.lock_write():
1447
self._config.set_option(value, name, section)
1449
def remove_option(self, option_name, section_name=None):
1450
# FIXME: We shouldn't need to lock explicitly here but rather rely on
1451
# higher levels providing the right lock -- vila 20101004
1452
with self.branch.lock_write():
1453
self._config.remove_option(option_name, section_name)
1456
_authentication_config_permission_errors = set()
1459
class AuthenticationConfig(object):
1460
"""The authentication configuration file based on a ini file.
1462
Implements the authentication.conf file described in
1463
doc/developers/authentication-ring.txt.
1466
def __init__(self, _file=None):
1467
self._config = None # The ConfigObj
1469
self._input = self._filename = bedding.authentication_config_path()
1470
self._check_permissions()
1472
# Tests can provide a string as _file
1473
self._filename = None
1476
def _get_config(self):
1477
if self._config is not None:
1480
# FIXME: Should we validate something here ? Includes: empty
1481
# sections are useless, at least one of
1482
# user/password/password_encoding should be defined, etc.
1484
# Note: the encoding below declares that the file itself is utf-8
1485
# encoded, but the values in the ConfigObj are always Unicode.
1486
self._config = ConfigObj(self._input, encoding='utf-8')
1487
except configobj.ConfigObjError as e:
1488
raise ParseConfigError(e.errors, e.config.filename)
1489
except UnicodeError:
1490
raise ConfigContentError(self._filename)
1493
def _check_permissions(self):
1494
"""Check permission of auth file are user read/write able only."""
1496
st = os.stat(self._filename)
1497
except OSError as e:
1498
if e.errno != errno.ENOENT:
1499
trace.mutter('Unable to stat %r: %r', self._filename, e)
1501
mode = stat.S_IMODE(st.st_mode)
1502
if ((stat.S_IXOTH | stat.S_IWOTH | stat.S_IROTH | stat.S_IXGRP
1503
| stat.S_IWGRP | stat.S_IRGRP) & mode):
1505
if (self._filename not in _authentication_config_permission_errors and
1506
not GlobalConfig().suppress_warning(
1507
'insecure_permissions')):
1508
trace.warning("The file '%s' has insecure "
1509
"file permissions. Saved passwords may be accessible "
1510
"by other users.", self._filename)
1511
_authentication_config_permission_errors.add(self._filename)
1514
"""Save the config file, only tests should use it for now."""
1515
conf_dir = os.path.dirname(self._filename)
1516
bedding.ensure_config_dir_exists(conf_dir)
1517
fd = os.open(self._filename, os.O_RDWR | os.O_CREAT, 0o600)
1519
f = os.fdopen(fd, 'wb')
1520
self._get_config().write(f)
1524
def _set_option(self, section_name, option_name, value):
1525
"""Set an authentication configuration option"""
1526
conf = self._get_config()
1527
section = conf.get(section_name)
1530
section = conf[section]
1531
section[option_name] = value
1534
def get_credentials(self, scheme, host, port=None, user=None, path=None,
1536
"""Returns the matching credentials from authentication.conf file.
1538
:param scheme: protocol
1540
:param host: the server address
1542
:param port: the associated port (optional)
1544
:param user: login (optional)
1546
:param path: the absolute path on the server (optional)
1548
:param realm: the http authentication realm (optional)
1550
:return: A dict containing the matching credentials or None.
1552
- name: the section name of the credentials in the
1553
authentication.conf file,
1554
- user: can't be different from the provided user if any,
1555
- scheme: the server protocol,
1556
- host: the server address,
1557
- port: the server port (can be None),
1558
- path: the absolute server path (can be None),
1559
- realm: the http specific authentication realm (can be None),
1560
- password: the decoded password, could be None if the credential
1561
defines only the user
1562
- verify_certificates: https specific, True if the server
1563
certificate should be verified, False otherwise.
1566
for auth_def_name, auth_def in self._get_config().iteritems():
1567
if not isinstance(auth_def, configobj.Section):
1568
raise ValueError("%s defined outside a section" %
1571
a_scheme, a_host, a_user, a_path = map(
1572
auth_def.get, ['scheme', 'host', 'user', 'path'])
1575
a_port = auth_def.as_int('port')
1579
raise ValueError("'port' not numeric in %s" % auth_def_name)
1581
a_verify_certificates = auth_def.as_bool('verify_certificates')
1583
a_verify_certificates = True
1586
"'verify_certificates' not boolean in %s" % auth_def_name)
1589
if a_scheme is not None and scheme != a_scheme:
1591
if a_host is not None:
1592
if not (host == a_host or
1593
(a_host.startswith('.') and host.endswith(a_host))):
1595
if a_port is not None and port != a_port:
1597
if (a_path is not None and path is not None and
1598
not path.startswith(a_path)):
1600
if (a_user is not None and user is not None and
1602
# Never contradict the caller about the user to be used
1607
# Prepare a credentials dictionary with additional keys
1608
# for the credential providers
1609
credentials = dict(name=auth_def_name,
1616
password=auth_def.get('password', None),
1617
verify_certificates=a_verify_certificates)
1618
# Decode the password in the credentials (or get one)
1619
self.decode_password(credentials,
1620
auth_def.get('password_encoding', None))
1621
if 'auth' in debug.debug_flags:
1622
trace.mutter("Using authentication section: %r", auth_def_name)
1625
if credentials is None:
1626
# No credentials were found in authentication.conf, try the fallback
1627
# credentials stores.
1628
credentials = credential_store_registry.get_fallback_credentials(
1629
scheme, host, port, user, path, realm)
1633
def set_credentials(self, name, host, user, scheme=None, password=None,
1634
port=None, path=None, verify_certificates=None,
1636
"""Set authentication credentials for a host.
1638
Any existing credentials with matching scheme, host, port and path
1639
will be deleted, regardless of name.
1641
:param name: An arbitrary name to describe this set of credentials.
1642
:param host: Name of the host that accepts these credentials.
1643
:param user: The username portion of these credentials.
1644
:param scheme: The URL scheme (e.g. ssh, http) the credentials apply
1646
:param password: Password portion of these credentials.
1647
:param port: The IP port on the host that these credentials apply to.
1648
:param path: A filesystem path on the host that these credentials
1650
:param verify_certificates: On https, verify server certificates if
1652
:param realm: The http authentication realm (optional).
1654
values = {'host': host, 'user': user}
1655
if password is not None:
1656
values['password'] = password
1657
if scheme is not None:
1658
values['scheme'] = scheme
1659
if port is not None:
1660
values['port'] = '%d' % port
1661
if path is not None:
1662
values['path'] = path
1663
if verify_certificates is not None:
1664
values['verify_certificates'] = str(verify_certificates)
1665
if realm is not None:
1666
values['realm'] = realm
1667
config = self._get_config()
1668
for section, existing_values in config.iteritems():
1669
for key in ('scheme', 'host', 'port', 'path', 'realm'):
1670
if existing_values.get(key) != values.get(key):
1674
config.update({name: values})
1677
def get_user(self, scheme, host, port=None, realm=None, path=None,
1678
prompt=None, ask=False, default=None):
1679
"""Get a user from authentication file.
1681
:param scheme: protocol
1683
:param host: the server address
1685
:param port: the associated port (optional)
1687
:param realm: the realm sent by the server (optional)
1689
:param path: the absolute path on the server (optional)
1691
:param ask: Ask the user if there is no explicitly configured username
1694
:param default: The username returned if none is defined (optional).
1696
:return: The found user.
1698
credentials = self.get_credentials(scheme, host, port, user=None,
1699
path=path, realm=realm)
1700
if credentials is not None:
1701
user = credentials['user']
1707
# Create a default prompt suitable for most cases
1708
prompt = u'%s' % (scheme.upper(),) + u' %(host)s username'
1709
# Special handling for optional fields in the prompt
1710
if port is not None:
1711
prompt_host = '%s:%d' % (host, port)
1714
user = ui.ui_factory.get_username(prompt, host=prompt_host)
1719
def get_password(self, scheme, host, user, port=None,
1720
realm=None, path=None, prompt=None):
1721
"""Get a password from authentication file or prompt the user for one.
1723
:param scheme: protocol
1725
:param host: the server address
1727
:param port: the associated port (optional)
1731
:param realm: the realm sent by the server (optional)
1733
:param path: the absolute path on the server (optional)
1735
:return: The found password or the one entered by the user.
1737
credentials = self.get_credentials(scheme, host, port, user, path,
1739
if credentials is not None:
1740
password = credentials['password']
1741
if password is not None and scheme == 'ssh':
1742
trace.warning('password ignored in section [%s],'
1743
' use an ssh agent instead'
1744
% credentials['name'])
1748
# Prompt user only if we could't find a password
1749
if password is None:
1751
# Create a default prompt suitable for most cases
1753
scheme.upper() + u' %(user)s@%(host)s password')
1754
# Special handling for optional fields in the prompt
1755
if port is not None:
1756
prompt_host = '%s:%d' % (host, port)
1759
password = ui.ui_factory.get_password(prompt,
1760
host=prompt_host, user=user)
1763
def decode_password(self, credentials, encoding):
1765
cs = credential_store_registry.get_credential_store(encoding)
1767
raise ValueError('%r is not a known password_encoding' % encoding)
1768
credentials['password'] = cs.decode_password(credentials)
1772
class CredentialStoreRegistry(registry.Registry):
1773
"""A class that registers credential stores.
1775
A credential store provides access to credentials via the password_encoding
1776
field in authentication.conf sections.
1778
Except for stores provided by brz itself, most stores are expected to be
1779
provided by plugins that will therefore use
1780
register_lazy(password_encoding, module_name, member_name, help=help,
1781
fallback=fallback) to install themselves.
1783
A fallback credential store is one that is queried if no credentials can be
1784
found via authentication.conf.
1787
def get_credential_store(self, encoding=None):
1788
cs = self.get(encoding)
1793
def is_fallback(self, name):
1794
"""Check if the named credentials store should be used as fallback."""
1795
return self.get_info(name)
1797
def get_fallback_credentials(self, scheme, host, port=None, user=None,
1798
path=None, realm=None):
1799
"""Request credentials from all fallback credentials stores.
1801
The first credentials store that can provide credentials wins.
1804
for name in self.keys():
1805
if not self.is_fallback(name):
1807
cs = self.get_credential_store(name)
1808
credentials = cs.get_credentials(scheme, host, port, user,
1810
if credentials is not None:
1811
# We found some credentials
1815
def register(self, key, obj, help=None, override_existing=False,
1817
"""Register a new object to a name.
1819
:param key: This is the key to use to request the object later.
1820
:param obj: The object to register.
1821
:param help: Help text for this entry. This may be a string or
1822
a callable. If it is a callable, it should take two
1823
parameters (registry, key): this registry and the key that
1824
the help was registered under.
1825
:param override_existing: Raise KeyErorr if False and something has
1826
already been registered for that key. If True, ignore if there
1827
is an existing key (always register the new value).
1828
:param fallback: Whether this credential store should be
1831
return super(CredentialStoreRegistry,
1832
self).register(key, obj, help, info=fallback,
1833
override_existing=override_existing)
1835
def register_lazy(self, key, module_name, member_name,
1836
help=None, override_existing=False,
1838
"""Register a new credential store to be loaded on request.
1840
:param module_name: The python path to the module. Such as 'os.path'.
1841
:param member_name: The member of the module to return. If empty or
1842
None, get() will return the module itself.
1843
:param help: Help text for this entry. This may be a string or
1845
:param override_existing: If True, replace the existing object
1846
with the new one. If False, if there is already something
1847
registered with the same key, raise a KeyError
1848
:param fallback: Whether this credential store should be
1851
return super(CredentialStoreRegistry, self).register_lazy(
1852
key, module_name, member_name, help,
1853
info=fallback, override_existing=override_existing)
1856
credential_store_registry = CredentialStoreRegistry()
1859
class CredentialStore(object):
1860
"""An abstract class to implement storage for credentials"""
1862
def decode_password(self, credentials):
1863
"""Returns a clear text password for the provided credentials."""
1864
raise NotImplementedError(self.decode_password)
1866
def get_credentials(self, scheme, host, port=None, user=None, path=None,
1868
"""Return the matching credentials from this credential store.
1870
This method is only called on fallback credential stores.
1872
raise NotImplementedError(self.get_credentials)
1875
class PlainTextCredentialStore(CredentialStore):
1876
__doc__ = """Plain text credential store for the authentication.conf file"""
1878
def decode_password(self, credentials):
1879
"""See CredentialStore.decode_password."""
1880
return credentials['password']
1883
credential_store_registry.register('plain', PlainTextCredentialStore,
1884
help=PlainTextCredentialStore.__doc__)
1885
credential_store_registry.default_key = 'plain'
1888
class Base64CredentialStore(CredentialStore):
1889
__doc__ = """Base64 credential store for the authentication.conf file"""
1891
def decode_password(self, credentials):
1892
"""See CredentialStore.decode_password."""
1893
# GZ 2012-07-28: Will raise binascii.Error if password is not base64,
1894
# should probably propogate as something more useful.
1895
return base64.standard_b64decode(credentials['password'])
1898
credential_store_registry.register('base64', Base64CredentialStore,
1899
help=Base64CredentialStore.__doc__)
1902
class BzrDirConfig(object):
1904
def __init__(self, bzrdir):
1905
self._bzrdir = bzrdir
1906
self._config = bzrdir._get_config()
1908
def set_default_stack_on(self, value):
1909
"""Set the default stacking location.
1911
It may be set to a location, or None.
1913
This policy affects all branches contained by this control dir, except
1914
for those under repositories.
1916
if self._config is None:
1917
raise errors.BzrError("Cannot set configuration in %s"
1920
self._config.set_option('', 'default_stack_on')
1922
self._config.set_option(value, 'default_stack_on')
1924
def get_default_stack_on(self):
1925
"""Return the default stacking location.
1927
This will either be a location, or None.
1929
This policy affects all branches contained by this control dir, except
1930
for those under repositories.
1932
if self._config is None:
1934
value = self._config.get_option('default_stack_on')
1940
class TransportConfig(object):
1941
"""A Config that reads/writes a config file on a Transport.
1943
It is a low-level object that considers config data to be name/value pairs
1944
that may be associated with a section. Assigning meaning to these values
1945
is done at higher levels like TreeConfig.
1948
def __init__(self, transport, filename):
1949
self._transport = transport
1950
self._filename = filename
1952
def get_option(self, name, section=None, default=None):
1953
"""Return the value associated with a named option.
1955
:param name: The name of the value
1956
:param section: The section the option is in (if any)
1957
:param default: The value to return if the value is not set
1958
:return: The value or default value
1960
configobj = self._get_configobj()
1962
section_obj = configobj
1965
section_obj = configobj[section]
1968
value = section_obj.get(name, default)
1969
for hook in OldConfigHooks['get']:
1970
hook(self, name, value)
1973
def set_option(self, value, name, section=None):
1974
"""Set the value associated with a named option.
1976
:param value: The value to set
1977
:param name: The name of the value to set
1978
:param section: The section the option is in (if any)
1980
configobj = self._get_configobj()
1982
configobj[name] = value
1984
configobj.setdefault(section, {})[name] = value
1985
for hook in OldConfigHooks['set']:
1986
hook(self, name, value)
1987
self._set_configobj(configobj)
1989
def remove_option(self, option_name, section_name=None):
1990
configobj = self._get_configobj()
1991
if section_name is None:
1992
del configobj[option_name]
1994
del configobj[section_name][option_name]
1995
for hook in OldConfigHooks['remove']:
1996
hook(self, option_name)
1997
self._set_configobj(configobj)
1999
def _get_config_file(self):
2001
f = BytesIO(self._transport.get_bytes(self._filename))
2002
for hook in OldConfigHooks['load']:
2005
except errors.NoSuchFile:
2007
except errors.PermissionDenied:
2009
"Permission denied while trying to open "
2010
"configuration file %s.",
2011
urlutils.unescape_for_display(
2012
urlutils.join(self._transport.base, self._filename),
2016
def _external_url(self):
2017
return urlutils.join(self._transport.external_url(), self._filename)
2019
def _get_configobj(self):
2020
f = self._get_config_file()
2023
conf = ConfigObj(f, encoding='utf-8')
2024
except configobj.ConfigObjError as e:
2025
raise ParseConfigError(e.errors, self._external_url())
2026
except UnicodeDecodeError:
2027
raise ConfigContentError(self._external_url())
2032
def _set_configobj(self, configobj):
2033
out_file = BytesIO()
2034
configobj.write(out_file)
2036
self._transport.put_file(self._filename, out_file)
2037
for hook in OldConfigHooks['save']:
2041
class Option(object):
2042
"""An option definition.
2044
The option *values* are stored in config files and found in sections.
2046
Here we define various properties about the option itself, its default
2047
value, how to convert it from stores, what to do when invalid values are
2048
encoutered, in which config files it can be stored.
2051
def __init__(self, name, override_from_env=None,
2052
default=None, default_from_env=None,
2053
help=None, from_unicode=None, invalid=None, unquote=True):
2054
"""Build an option definition.
2056
:param name: the name used to refer to the option.
2058
:param override_from_env: A list of environment variables which can
2059
provide override any configuration setting.
2061
:param default: the default value to use when none exist in the config
2062
stores. This is either a string that ``from_unicode`` will convert
2063
into the proper type, a callable returning a unicode string so that
2064
``from_unicode`` can be used on the return value, or a python
2065
object that can be stringified (so only the empty list is supported
2068
:param default_from_env: A list of environment variables which can
2069
provide a default value. 'default' will be used only if none of the
2070
variables specified here are set in the environment.
2072
:param help: a doc string to explain the option to the user.
2074
:param from_unicode: a callable to convert the unicode string
2075
representing the option value in a store or its default value.
2077
:param invalid: the action to be taken when an invalid value is
2078
encountered in a store. This is called only when from_unicode is
2079
invoked to convert a string and returns None or raise ValueError or
2080
TypeError. Accepted values are: None (ignore invalid values),
2081
'warning' (emit a warning), 'error' (emit an error message and
2084
:param unquote: should the unicode value be unquoted before conversion.
2085
This should be used only when the store providing the values cannot
2086
safely unquote them (see http://pad.lv/906897). It is provided so
2087
daughter classes can handle the quoting themselves.
2089
if override_from_env is None:
2090
override_from_env = []
2091
if default_from_env is None:
2092
default_from_env = []
2094
self.override_from_env = override_from_env
2095
# Convert the default value to a unicode string so all values are
2096
# strings internally before conversion (via from_unicode) is attempted.
2099
elif isinstance(default, list):
2100
# Only the empty list is supported
2102
raise AssertionError(
2103
'Only empty lists are supported as default values')
2105
elif isinstance(default, (bytes, str, bool, int, float)):
2106
# Rely on python to convert strings, booleans and integers
2107
self.default = u'%s' % (default,)
2108
elif callable(default):
2109
self.default = default
2111
# other python objects are not expected
2112
raise AssertionError('%r is not supported as a default value'
2114
self.default_from_env = default_from_env
2116
self.from_unicode = from_unicode
2117
self.unquote = unquote
2118
if invalid and invalid not in ('warning', 'error'):
2119
raise AssertionError("%s not supported for 'invalid'" % (invalid,))
2120
self.invalid = invalid
2126
def convert_from_unicode(self, store, unicode_value):
2127
if self.unquote and store is not None and unicode_value is not None:
2128
unicode_value = store.unquote(unicode_value)
2129
if self.from_unicode is None or unicode_value is None:
2130
# Don't convert or nothing to convert
2131
return unicode_value
2133
converted = self.from_unicode(unicode_value)
2134
except (ValueError, TypeError):
2135
# Invalid values are ignored
2137
if converted is None and self.invalid is not None:
2138
# The conversion failed
2139
if self.invalid == 'warning':
2140
trace.warning('Value "%s" is not valid for "%s"',
2141
unicode_value, self.name)
2142
elif self.invalid == 'error':
2143
raise ConfigOptionValueError(self.name, unicode_value)
2146
def get_override(self):
2148
for var in self.override_from_env:
2150
# If the env variable is defined, its value takes precedence
2151
value = os.environ[var]
2157
def get_default(self):
2159
for var in self.default_from_env:
2161
# If the env variable is defined, its value is the default one
2162
value = os.environ[var]
2167
# Otherwise, fallback to the value defined at registration
2168
if callable(self.default):
2169
value = self.default()
2170
if not isinstance(value, str):
2171
raise AssertionError(
2172
"Callable default value for '%s' should be unicode"
2175
value = self.default
2178
def get_help_topic(self):
2181
def get_help_text(self, additional_see_also=None, plain=True):
2183
from breezy import help_topics
2184
result += help_topics._format_see_also(additional_see_also)
2186
result = help_topics.help_as_plain_text(result)
2190
# Predefined converters to get proper values from store
2192
def bool_from_store(unicode_str):
2193
return ui.bool_from_string(unicode_str)
2196
def int_from_store(unicode_str):
2197
return int(unicode_str)
2200
_unit_suffixes = dict(K=10**3, M=10**6, G=10**9)
2203
def int_SI_from_store(unicode_str):
2204
"""Convert a human readable size in SI units, e.g 10MB into an integer.
2206
Accepted suffixes are K,M,G. It is case-insensitive and may be followed
2207
by a trailing b (i.e. Kb, MB). This is intended to be practical and not
2210
:return Integer, expanded to its base-10 value if a proper SI unit is
2211
found, None otherwise.
2213
regexp = "^(\\d+)(([" + ''.join(_unit_suffixes) + "])b?)?$"
2214
p = re.compile(regexp, re.IGNORECASE)
2215
m = p.match(unicode_str)
2218
val, _, unit = m.groups()
2222
coeff = _unit_suffixes[unit.upper()]
2225
gettext('{0} is not an SI unit.').format(unit))
2230
def float_from_store(unicode_str):
2231
return float(unicode_str)
2234
# Use an empty dict to initialize an empty configobj avoiding all parsing and
2236
_list_converter_config = configobj.ConfigObj(
2237
{}, encoding='utf-8', list_values=True, interpolation=False)
2240
class ListOption(Option):
2242
def __init__(self, name, default=None, default_from_env=None,
2243
help=None, invalid=None):
2244
"""A list Option definition.
2246
This overrides the base class so the conversion from a unicode string
2247
can take quoting into account.
2249
super(ListOption, self).__init__(
2250
name, default=default, default_from_env=default_from_env,
2251
from_unicode=self.from_unicode, help=help,
2252
invalid=invalid, unquote=False)
2254
def from_unicode(self, unicode_str):
2255
if not isinstance(unicode_str, str):
2257
# Now inject our string directly as unicode. All callers got their
2258
# value from configobj, so values that need to be quoted are already
2260
_list_converter_config.reset()
2261
_list_converter_config._parse([u"list=%s" % (unicode_str,)])
2262
maybe_list = _list_converter_config['list']
2263
if isinstance(maybe_list, str):
2265
# A single value, most probably the user forgot (or didn't care
2266
# to add) the final ','
2269
# The empty string, convert to empty list
2272
# We rely on ConfigObj providing us with a list already
2277
class RegistryOption(Option):
2278
"""Option for a choice from a registry."""
2280
def __init__(self, name, registry, default_from_env=None,
2281
help=None, invalid=None):
2282
"""A registry based Option definition.
2284
This overrides the base class so the conversion from a unicode string
2285
can take quoting into account.
2287
super(RegistryOption, self).__init__(
2288
name, default=lambda: registry.default_key,
2289
default_from_env=default_from_env,
2290
from_unicode=self.from_unicode, help=help,
2291
invalid=invalid, unquote=False)
2292
self.registry = registry
2294
def from_unicode(self, unicode_str):
2295
if not isinstance(unicode_str, str):
2298
return self.registry.get(unicode_str)
2301
"Invalid value %s for %s."
2302
"See help for a list of possible values." % (unicode_str,
2307
ret = [self._help, "\n\nThe following values are supported:\n"]
2308
for key in self.registry.keys():
2309
ret.append(" %s - %s\n" % (key, self.registry.get_help(key)))
2313
_option_ref_re = lazy_regex.lazy_compile('({[^\\d\\W](?:\\.\\w|-\\w|\\w)*})')
2314
"""Describes an expandable option reference.
2316
We want to match the most embedded reference first.
2318
I.e. for '{{foo}}' we will get '{foo}',
2319
for '{bar{baz}}' we will get '{baz}'
2323
def iter_option_refs(string):
2324
# Split isolate refs so every other chunk is a ref
2326
for chunk in _option_ref_re.split(string):
2331
class OptionRegistry(registry.Registry):
2332
"""Register config options by their name.
2334
This overrides ``registry.Registry`` to simplify registration by acquiring
2335
some information from the option object itself.
2338
def _check_option_name(self, option_name):
2339
"""Ensures an option name is valid.
2341
:param option_name: The name to validate.
2343
if _option_ref_re.match('{%s}' % option_name) is None:
2344
raise IllegalOptionName(option_name)
2346
def register(self, option):
2347
"""Register a new option to its name.
2349
:param option: The option to register. Its name is used as the key.
2351
self._check_option_name(option.name)
2352
super(OptionRegistry, self).register(option.name, option,
2355
def register_lazy(self, key, module_name, member_name):
2356
"""Register a new option to be loaded on request.
2358
:param key: the key to request the option later. Since the registration
2359
is lazy, it should be provided and match the option name.
2361
:param module_name: the python path to the module. Such as 'os.path'.
2363
:param member_name: the member of the module to return. If empty or
2364
None, get() will return the module itself.
2366
self._check_option_name(key)
2367
super(OptionRegistry, self).register_lazy(key,
2368
module_name, member_name)
2370
def get_help(self, key=None):
2371
"""Get the help text associated with the given key"""
2372
option = self.get(key)
2373
the_help = option.help
2374
if callable(the_help):
2375
return the_help(self, key)
2379
option_registry = OptionRegistry()
2382
# Registered options in lexicographical order
2384
option_registry.register(
2385
Option('append_revisions_only',
2386
default=None, from_unicode=bool_from_store, invalid='warning',
2388
Whether to only append revisions to the mainline.
2390
If this is set to true, then it is not possible to change the
2391
existing mainline of the branch.
2393
option_registry.register(
2394
ListOption('acceptable_keys',
2397
List of GPG key patterns which are acceptable for verification.
2399
option_registry.register(
2400
Option('add.maximum_file_size',
2401
default=u'20MB', from_unicode=int_SI_from_store,
2403
Size above which files should be added manually.
2405
Files below this size are added automatically when using ``bzr add`` without
2408
A negative value means disable the size check.
2410
option_registry.register(
2412
default=None, from_unicode=bool_from_store,
2414
Is the branch bound to ``bound_location``.
2416
If set to "True", the branch should act as a checkout, and push each commit to
2417
the bound_location. This option is normally set by ``bind``/``unbind``.
2419
See also: bound_location.
2421
option_registry.register(
2422
Option('bound_location',
2425
The location that commits should go to when acting as a checkout.
2427
This option is normally set by ``bind``.
2431
option_registry.register(
2432
Option('branch.fetch_tags', default=False, from_unicode=bool_from_store,
2434
Whether revisions associated with tags should be fetched.
2436
option_registry.register_lazy(
2437
'transform.orphan_policy', 'breezy.transform', 'opt_transform_orphan')
2438
option_registry.register(
2439
Option('bzr.workingtree.worth_saving_limit', default=10,
2440
from_unicode=int_from_store, invalid='warning',
2442
How many changes before saving the dirstate.
2444
-1 means that we will never rewrite the dirstate file for only
2445
stat-cache changes. Regardless of this setting, we will always rewrite
2446
the dirstate file if a file is added/removed/renamed/etc. This flag only
2447
affects the behavior of updating the dirstate file after we notice that
2448
a file has been touched.
2450
option_registry.register(
2451
Option('bugtracker', default=None,
2453
Default bug tracker to use.
2455
This bug tracker will be used for example when marking bugs
2456
as fixed using ``bzr commit --fixes``, if no explicit
2457
bug tracker was specified.
2459
option_registry.register(
2460
Option('calculate_revnos', default=True,
2461
from_unicode=bool_from_store,
2463
Calculate revision numbers if they are not known.
2465
Always show revision numbers, even for branch formats that don't store them
2466
natively (such as Git). Calculating the revision number requires traversing
2467
the left hand ancestry of the branch and can be slow on very large branches.
2469
option_registry.register(
2470
Option('check_signatures', default=CHECK_IF_POSSIBLE,
2471
from_unicode=signature_policy_from_unicode,
2473
GPG checking policy.
2475
Possible values: require, ignore, check-available (default)
2477
this option will control whether bzr will require good gpg
2478
signatures, ignore them, or check them if they are
2481
option_registry.register(
2482
Option('child_submit_format',
2483
help='''The preferred format of submissions to this branch.'''))
2484
option_registry.register(
2485
Option('child_submit_to',
2486
help='''Where submissions to this branch are mailed to.'''))
2487
option_registry.register(
2488
Option('create_signatures', default=SIGN_WHEN_REQUIRED,
2489
from_unicode=signing_policy_from_unicode,
2493
Possible values: always, never, when-required (default)
2495
This option controls whether bzr will always create
2496
gpg signatures or not on commits.
2498
option_registry.register(
2499
Option('dirstate.fdatasync', default=True,
2500
from_unicode=bool_from_store,
2502
Flush dirstate changes onto physical disk?
2504
If true (default), working tree metadata changes are flushed through the
2505
OS buffers to physical disk. This is somewhat slower, but means data
2506
should not be lost if the machine crashes. See also repository.fdatasync.
2508
option_registry.register(
2509
ListOption('debug_flags', default=[],
2510
help='Debug flags to activate.'))
2511
option_registry.register(
2512
Option('default_format', default='2a',
2513
help='Format used when creating branches.'))
2514
option_registry.register(
2516
help='The command called to launch an editor to enter a message.'))
2517
option_registry.register(
2518
Option('email', override_from_env=['BRZ_EMAIL'],
2519
default=bedding.default_email, help='The users identity'))
2520
option_registry.register(
2521
Option('gpg_signing_key',
2524
GPG key to use for signing.
2526
This defaults to the first key associated with the users email.
2528
option_registry.register(
2530
help='Language to translate messages into.'))
2531
option_registry.register(
2532
Option('locks.steal_dead', default=True, from_unicode=bool_from_store,
2534
Steal locks that appears to be dead.
2536
If set to True, bzr will check if a lock is supposed to be held by an
2537
active process from the same user on the same machine. If the user and
2538
machine match, but no process with the given PID is active, then bzr
2539
will automatically break the stale lock, and create a new lock for
2541
Otherwise, bzr will prompt as normal to break the lock.
2543
option_registry.register(
2544
Option('log_format', default='long',
2546
Log format to use when displaying revisions.
2548
Standard log formats are ``long``, ``short`` and ``line``. Additional formats
2549
may be provided by plugins.
2551
option_registry.register_lazy('mail_client', 'breezy.mail_client',
2553
option_registry.register(
2554
Option('output_encoding',
2555
help='Unicode encoding for output'
2556
' (terminal encoding if not specified).'))
2557
option_registry.register(
2558
Option('parent_location',
2561
The location of the default branch for pull or merge.
2563
This option is normally set when creating a branch, the first ``pull`` or by
2564
``pull --remember``.
2566
option_registry.register(
2567
Option('post_commit', default=None,
2569
Post commit functions.
2571
An ordered list of python functions to call, separated by spaces.
2573
Each function takes branch, rev_id as parameters.
2575
option_registry.register_lazy('progress_bar', 'breezy.ui.text',
2577
option_registry.register(
2578
Option('public_branch',
2581
A publically-accessible version of this branch.
2583
This implies that the branch setting this option is not publically-accessible.
2584
Used and set by ``bzr send``.
2586
option_registry.register(
2587
Option('push_location',
2590
The location of the default branch for push.
2592
This option is normally set by the first ``push`` or ``push --remember``.
2594
option_registry.register(
2595
Option('push_strict', default=None,
2596
from_unicode=bool_from_store,
2598
The default value for ``push --strict``.
2600
If present, defines the ``--strict`` option default value for checking
2601
uncommitted changes before sending a merge directive.
2603
option_registry.register(
2604
Option('repository.fdatasync', default=True,
2605
from_unicode=bool_from_store,
2607
Flush repository changes onto physical disk?
2609
If true (default), repository changes are flushed through the OS buffers
2610
to physical disk. This is somewhat slower, but means data should not be
2611
lost if the machine crashes. See also dirstate.fdatasync.
2613
option_registry.register_lazy('smtp_server',
2614
'breezy.smtp_connection', 'smtp_server')
2615
option_registry.register_lazy('smtp_password',
2616
'breezy.smtp_connection', 'smtp_password')
2617
option_registry.register_lazy('smtp_username',
2618
'breezy.smtp_connection', 'smtp_username')
2619
option_registry.register(
2620
Option('selftest.timeout',
2622
from_unicode=int_from_store,
2623
help='Abort selftest if one test takes longer than this many seconds',
2626
option_registry.register(
2627
Option('send_strict', default=None,
2628
from_unicode=bool_from_store,
2630
The default value for ``send --strict``.
2632
If present, defines the ``--strict`` option default value for checking
2633
uncommitted changes before sending a bundle.
2636
option_registry.register(
2637
Option('serve.client_timeout',
2638
default=300.0, from_unicode=float_from_store,
2639
help="If we wait for a new request from a client for more than"
2640
" X seconds, consider the client idle, and hangup."))
2641
option_registry.register(
2643
default=None, override_from_env=['BRZ_SSH'],
2644
help='SSH vendor to use.'))
2645
option_registry.register(
2646
Option('stacked_on_location',
2648
help="""The location where this branch is stacked on."""))
2649
option_registry.register(
2650
Option('submit_branch',
2653
The branch you intend to submit your current work to.
2655
This is automatically set by ``bzr send`` and ``bzr merge``, and is also used
2656
by the ``submit:`` revision spec.
2658
option_registry.register(
2660
help='''Where submissions from this branch are mailed to.'''))
2661
option_registry.register(
2662
ListOption('suppress_warnings',
2664
help="List of warning classes to suppress."))
2665
option_registry.register(
2666
Option('validate_signatures_in_log', default=False,
2667
from_unicode=bool_from_store, invalid='warning',
2668
help='''Whether to validate signatures in brz log.'''))
2669
option_registry.register_lazy('ssl.ca_certs',
2670
'breezy.transport.http', 'opt_ssl_ca_certs')
2672
option_registry.register_lazy('ssl.cert_reqs',
2673
'breezy.transport.http', 'opt_ssl_cert_reqs')
2676
class Section(object):
2677
"""A section defines a dict of option name => value.
2679
This is merely a read-only dict which can add some knowledge about the
2680
options. It is *not* a python dict object though and doesn't try to mimic
2684
def __init__(self, section_id, options):
2685
self.id = section_id
2686
# We re-use the dict-like object received
2687
self.options = options
2689
def get(self, name, default=None, expand=True):
2690
return self.options.get(name, default)
2692
def iter_option_names(self):
2693
for k in self.options.keys():
2697
# Mostly for debugging use
2698
return "<config.%s id=%s>" % (self.__class__.__name__, self.id)
2701
_NewlyCreatedOption = object()
2702
"""Was the option created during the MutableSection lifetime"""
2703
_DeletedOption = object()
2704
"""Was the option deleted during the MutableSection lifetime"""
2707
class MutableSection(Section):
2708
"""A section allowing changes and keeping track of the original values."""
2710
def __init__(self, section_id, options):
2711
super(MutableSection, self).__init__(section_id, options)
2712
self.reset_changes()
2714
def set(self, name, value):
2715
if name not in self.options:
2716
# This is a new option
2717
self.orig[name] = _NewlyCreatedOption
2718
elif name not in self.orig:
2719
self.orig[name] = self.get(name, None)
2720
self.options[name] = value
2722
def remove(self, name):
2723
if name not in self.orig and name in self.options:
2724
self.orig[name] = self.get(name, None)
2725
del self.options[name]
2727
def reset_changes(self):
2730
def apply_changes(self, dirty, store):
2731
"""Apply option value changes.
2733
``self`` has been reloaded from the persistent storage. ``dirty``
2734
contains the changes made since the previous loading.
2736
:param dirty: the mutable section containing the changes.
2738
:param store: the store containing the section
2740
for k, expected in dirty.orig.items():
2741
actual = dirty.get(k, _DeletedOption)
2742
reloaded = self.get(k, _NewlyCreatedOption)
2743
if actual is _DeletedOption:
2744
if k in self.options:
2748
# Report concurrent updates in an ad-hoc way. This should only
2749
# occurs when different processes try to update the same option
2750
# which is not supported (as in: the config framework is not meant
2751
# to be used as a sharing mechanism).
2752
if expected != reloaded:
2753
if actual is _DeletedOption:
2754
actual = '<DELETED>'
2755
if reloaded is _NewlyCreatedOption:
2756
reloaded = '<CREATED>'
2757
if expected is _NewlyCreatedOption:
2758
expected = '<CREATED>'
2759
# Someone changed the value since we get it from the persistent
2761
trace.warning(gettext(
2762
"Option {0} in section {1} of {2} was changed"
2763
" from {3} to {4}. The {5} value will be saved.".format(
2764
k, self.id, store.external_url(), expected,
2766
# No need to keep track of these changes
2767
self.reset_changes()
2770
class Store(object):
2771
"""Abstract interface to persistent storage for configuration options."""
2773
readonly_section_class = Section
2774
mutable_section_class = MutableSection
2777
# Which sections need to be saved (by section id). We use a dict here
2778
# so the dirty sections can be shared by multiple callers.
2779
self.dirty_sections = {}
2781
def is_loaded(self):
2782
"""Returns True if the Store has been loaded.
2784
This is used to implement lazy loading and ensure the persistent
2785
storage is queried only when needed.
2787
raise NotImplementedError(self.is_loaded)
2790
"""Loads the Store from persistent storage."""
2791
raise NotImplementedError(self.load)
2793
def _load_from_string(self, bytes):
2794
"""Create a store from a string in configobj syntax.
2796
:param bytes: A string representing the file content.
2798
raise NotImplementedError(self._load_from_string)
2801
"""Unloads the Store.
2803
This should make is_loaded() return False. This is used when the caller
2804
knows that the persistent storage has changed or may have change since
2807
raise NotImplementedError(self.unload)
2809
def quote(self, value):
2810
"""Quote a configuration option value for storing purposes.
2812
This allows Stacks to present values as they will be stored.
2816
def unquote(self, value):
2817
"""Unquote a configuration option value into unicode.
2819
The received value is quoted as stored.
2824
"""Saves the Store to persistent storage."""
2825
raise NotImplementedError(self.save)
2827
def _need_saving(self):
2828
for s in self.dirty_sections.values():
2830
# At least one dirty section contains a modification
2834
def apply_changes(self, dirty_sections):
2835
"""Apply changes from dirty sections while checking for coherency.
2837
The Store content is discarded and reloaded from persistent storage to
2838
acquire up-to-date values.
2840
Dirty sections are MutableSection which kept track of the value they
2841
are expected to update.
2843
# We need an up-to-date version from the persistent storage, unload the
2844
# store. The reload will occur when needed (triggered by the first
2845
# get_mutable_section() call below.
2847
# Apply the changes from the preserved dirty sections
2848
for section_id, dirty in dirty_sections.items():
2849
clean = self.get_mutable_section(section_id)
2850
clean.apply_changes(dirty, self)
2851
# Everything is clean now
2852
self.dirty_sections = {}
2854
def save_changes(self):
2855
"""Saves the Store to persistent storage if changes occurred.
2857
Apply the changes recorded in the mutable sections to a store content
2858
refreshed from persistent storage.
2860
raise NotImplementedError(self.save_changes)
2862
def external_url(self):
2863
raise NotImplementedError(self.external_url)
2865
def get_sections(self):
2866
"""Returns an ordered iterable of existing sections.
2868
:returns: An iterable of (store, section).
2870
raise NotImplementedError(self.get_sections)
2872
def get_mutable_section(self, section_id=None):
2873
"""Returns the specified mutable section.
2875
:param section_id: The section identifier
2877
raise NotImplementedError(self.get_mutable_section)
2880
# Mostly for debugging use
2881
return "<config.%s(%s)>" % (self.__class__.__name__,
2882
self.external_url())
2885
class CommandLineStore(Store):
2886
"A store to carry command line overrides for the config options."""
2888
def __init__(self, opts=None):
2889
super(CommandLineStore, self).__init__()
2896
# The dict should be cleared but not replaced so it can be shared.
2897
self.options.clear()
2899
def _from_cmdline(self, overrides):
2900
# Reset before accepting new definitions
2902
for over in overrides:
2904
name, value = over.split('=', 1)
2906
raise errors.BzrCommandError(
2907
gettext("Invalid '%s', should be of the form 'name=value'")
2909
self.options[name] = value
2911
def external_url(self):
2912
# Not an url but it makes debugging easier and is never needed
2916
def get_sections(self):
2917
yield self, self.readonly_section_class(None, self.options)
2920
class IniFileStore(Store):
2921
"""A config Store using ConfigObj for storage.
2923
:ivar _config_obj: Private member to hold the ConfigObj instance used to
2924
serialize/deserialize the config file.
2928
"""A config Store using ConfigObj for storage.
2930
super(IniFileStore, self).__init__()
2931
self._config_obj = None
2933
def is_loaded(self):
2934
return self._config_obj is not None
2937
self._config_obj = None
2938
self.dirty_sections = {}
2940
def _load_content(self):
2941
"""Load the config file bytes.
2943
This should be provided by subclasses
2945
:return: Byte string
2947
raise NotImplementedError(self._load_content)
2949
def _save_content(self, content):
2950
"""Save the config file bytes.
2952
This should be provided by subclasses
2954
:param content: Config file bytes to write
2956
raise NotImplementedError(self._save_content)
2959
"""Load the store from the associated file."""
2960
if self.is_loaded():
2962
content = self._load_content()
2963
self._load_from_string(content)
2964
for hook in ConfigHooks['load']:
2967
def _load_from_string(self, bytes):
2968
"""Create a config store from a string.
2970
:param bytes: A string representing the file content.
2972
if self.is_loaded():
2973
raise AssertionError('Already loaded: %r' % (self._config_obj,))
2974
co_input = BytesIO(bytes)
2976
# The config files are always stored utf8-encoded
2977
self._config_obj = ConfigObj(co_input, encoding='utf-8',
2979
except configobj.ConfigObjError as e:
2980
self._config_obj = None
2981
raise ParseConfigError(e.errors, self.external_url())
2982
except UnicodeDecodeError:
2983
raise ConfigContentError(self.external_url())
2985
def save_changes(self):
2986
if not self.is_loaded():
2989
if not self._need_saving():
2991
# Preserve the current version
2992
dirty_sections = self.dirty_sections.copy()
2993
self.apply_changes(dirty_sections)
2994
# Save to the persistent storage
2998
if not self.is_loaded():
3002
self._config_obj.write(out)
3003
self._save_content(out.getvalue())
3004
for hook in ConfigHooks['save']:
3007
def get_sections(self):
3008
"""Get the configobj section in the file order.
3010
:returns: An iterable of (store, section).
3012
# We need a loaded store
3015
except (errors.NoSuchFile, errors.PermissionDenied):
3016
# If the file can't be read, there is no sections
3018
cobj = self._config_obj
3020
yield self, self.readonly_section_class(None, cobj)
3021
for section_name in cobj.sections:
3023
self.readonly_section_class(section_name,
3024
cobj[section_name]))
3026
def get_mutable_section(self, section_id=None):
3027
# We need a loaded store
3030
except errors.NoSuchFile:
3031
# The file doesn't exist, let's pretend it was empty
3032
self._load_from_string(b'')
3033
if section_id in self.dirty_sections:
3034
# We already created a mutable section for this id
3035
return self.dirty_sections[section_id]
3036
if section_id is None:
3037
section = self._config_obj
3039
section = self._config_obj.setdefault(section_id, {})
3040
mutable_section = self.mutable_section_class(section_id, section)
3041
# All mutable sections can become dirty
3042
self.dirty_sections[section_id] = mutable_section
3043
return mutable_section
3045
def quote(self, value):
3047
# configobj conflates automagical list values and quoting
3048
self._config_obj.list_values = True
3049
return self._config_obj._quote(value)
3051
self._config_obj.list_values = False
3053
def unquote(self, value):
3054
if value and isinstance(value, str):
3055
# _unquote doesn't handle None nor empty strings nor anything that
3056
# is not a string, really.
3057
value = self._config_obj._unquote(value)
3060
def external_url(self):
3061
# Since an IniFileStore can be used without a file (at least in tests),
3062
# it's better to provide something than raising a NotImplementedError.
3063
# All daughter classes are supposed to provide an implementation
3065
return 'In-Process Store, no URL'
3068
class TransportIniFileStore(IniFileStore):
3069
"""IniFileStore that loads files from a transport.
3071
:ivar transport: The transport object where the config file is located.
3073
:ivar file_name: The config file basename in the transport directory.
3076
def __init__(self, transport, file_name):
3077
"""A Store using a ini file on a Transport
3079
:param transport: The transport object where the config file is located.
3080
:param file_name: The config file basename in the transport directory.
3082
super(TransportIniFileStore, self).__init__()
3083
self.transport = transport
3084
self.file_name = file_name
3086
def _load_content(self):
3088
return self.transport.get_bytes(self.file_name)
3089
except errors.PermissionDenied:
3090
trace.warning("Permission denied while trying to load "
3091
"configuration store %s.", self.external_url())
3094
def _save_content(self, content):
3095
self.transport.put_bytes(self.file_name, content)
3097
def external_url(self):
3098
# FIXME: external_url should really accepts an optional relpath
3099
# parameter (bug #750169) :-/ -- vila 2011-04-04
3100
# The following will do in the interim but maybe we don't want to
3101
# expose a path here but rather a config ID and its associated
3102
# object </hand wawe>.
3103
return urlutils.join(
3104
self.transport.external_url(), urlutils.escape(self.file_name))
3107
# Note that LockableConfigObjStore inherits from ConfigObjStore because we need
3108
# unlockable stores for use with objects that can already ensure the locking
3109
# (think branches). If different stores (not based on ConfigObj) are created,
3110
# they may face the same issue.
3113
class LockableIniFileStore(TransportIniFileStore):
3114
"""A ConfigObjStore using locks on save to ensure store integrity."""
3116
def __init__(self, transport, file_name, lock_dir_name=None):
3117
"""A config Store using ConfigObj for storage.
3119
:param transport: The transport object where the config file is located.
3121
:param file_name: The config file basename in the transport directory.
3123
if lock_dir_name is None:
3124
lock_dir_name = 'lock'
3125
self.lock_dir_name = lock_dir_name
3126
super(LockableIniFileStore, self).__init__(transport, file_name)
3127
self._lock = lockdir.LockDir(self.transport, self.lock_dir_name)
3129
def lock_write(self, token=None):
3130
"""Takes a write lock in the directory containing the config file.
3132
If the directory doesn't exist it is created.
3134
# FIXME: This doesn't check the ownership of the created directories as
3135
# ensure_config_dir_exists does. It should if the transport is local
3136
# -- vila 2011-04-06
3137
self.transport.create_prefix()
3138
token = self._lock.lock_write(token)
3139
return lock.LogicalLockResult(self.unlock, token)
3144
def break_lock(self):
3145
self._lock.break_lock()
3148
with self.lock_write():
3149
# We need to be able to override the undecorated implementation
3150
self.save_without_locking()
3152
def save_without_locking(self):
3153
super(LockableIniFileStore, self).save()
3156
# FIXME: global, breezy, shouldn't that be 'user' instead or even
3157
# 'user_defaults' as opposed to 'user_overrides', 'system_defaults'
3158
# (/etc/bzr/bazaar.conf) and 'system_overrides' ? -- vila 2011-04-05
3160
# FIXME: Moreover, we shouldn't need classes for these stores either, factory
3161
# functions or a registry will make it easier and clearer for tests, focusing
3162
# on the relevant parts of the API that needs testing -- vila 20110503 (based
3163
# on a poolie's remark)
3164
class GlobalStore(LockableIniFileStore):
3165
"""A config store for global options.
3167
There is a single GlobalStore for a given process.
3170
def __init__(self, possible_transports=None):
3171
path, kind = bedding._config_dir()
3172
t = transport.get_transport_from_path(
3173
path, possible_transports=possible_transports)
3174
super(GlobalStore, self).__init__(t, kind + '.conf')
3178
class LocationStore(LockableIniFileStore):
3179
"""A config store for options specific to a location.
3181
There is a single LocationStore for a given process.
3184
def __init__(self, possible_transports=None):
3185
t = transport.get_transport_from_path(
3186
bedding.config_dir(), possible_transports=possible_transports)
3187
super(LocationStore, self).__init__(t, 'locations.conf')
3188
self.id = 'locations'
3191
class BranchStore(TransportIniFileStore):
3192
"""A config store for branch options.
3194
There is a single BranchStore for a given branch.
3197
def __init__(self, branch):
3198
super(BranchStore, self).__init__(branch.control_transport,
3200
self.branch = branch
3204
class ControlStore(LockableIniFileStore):
3206
def __init__(self, bzrdir):
3207
super(ControlStore, self).__init__(bzrdir.transport,
3209
lock_dir_name='branch_lock')
3213
class SectionMatcher(object):
3214
"""Select sections into a given Store.
3216
This is intended to be used to postpone getting an iterable of sections
3220
def __init__(self, store):
3223
def get_sections(self):
3224
# This is where we require loading the store so we can see all defined
3226
sections = self.store.get_sections()
3227
# Walk the revisions in the order provided
3228
for store, s in sections:
3232
def match(self, section):
3233
"""Does the proposed section match.
3235
:param section: A Section object.
3237
:returns: True if the section matches, False otherwise.
3239
raise NotImplementedError(self.match)
3242
class NameMatcher(SectionMatcher):
3244
def __init__(self, store, section_id):
3245
super(NameMatcher, self).__init__(store)
3246
self.section_id = section_id
3248
def match(self, section):
3249
return section.id == self.section_id
3252
class LocationSection(Section):
3254
def __init__(self, section, extra_path, branch_name=None):
3255
super(LocationSection, self).__init__(section.id, section.options)
3256
self.extra_path = extra_path
3257
if branch_name is None:
3259
self.locals = {'relpath': extra_path,
3260
'basename': urlutils.basename(extra_path),
3261
'branchname': branch_name}
3263
def get(self, name, default=None, expand=True):
3264
value = super(LocationSection, self).get(name, default)
3265
if value is not None and expand:
3266
policy_name = self.get(name + ':policy', None)
3267
policy = _policy_value.get(policy_name, POLICY_NONE)
3268
if policy == POLICY_APPENDPATH:
3269
value = urlutils.join(value, self.extra_path)
3270
# expand section local options right now (since POLICY_APPENDPATH
3271
# will never add options references, it's ok to expand after it).
3273
for is_ref, chunk in iter_option_refs(value):
3275
chunks.append(chunk)
3278
if ref in self.locals:
3279
chunks.append(self.locals[ref])
3281
chunks.append(chunk)
3282
value = ''.join(chunks)
3286
class StartingPathMatcher(SectionMatcher):
3287
"""Select sections for a given location respecting the Store order."""
3289
# FIXME: Both local paths and urls can be used for section names as well as
3290
# ``location`` to stay consistent with ``LocationMatcher`` which itself
3291
# inherited the fuzziness from the previous ``LocationConfig``
3292
# implementation. We probably need to revisit which encoding is allowed for
3293
# both ``location`` and section names and how we normalize
3294
# them. http://pad.lv/85479, http://pad.lv/437009 and http://359320 are
3295
# related too. -- vila 2012-01-04
3297
def __init__(self, store, location):
3298
super(StartingPathMatcher, self).__init__(store)
3299
if location.startswith('file://'):
3300
location = urlutils.local_path_from_url(location)
3301
self.location = location
3303
def get_sections(self):
3304
"""Get all sections matching ``location`` in the store.
3306
The most generic sections are described first in the store, then more
3307
specific ones can be provided for reduced scopes.
3309
The returned section are therefore returned in the reversed order so
3310
the most specific ones can be found first.
3312
location_parts = self.location.rstrip('/').split('/')
3314
# Later sections are more specific, they should be returned first
3315
for _, section in reversed(list(store.get_sections())):
3316
if section.id is None:
3317
# The no-name section is always included if present
3318
yield store, LocationSection(section, self.location)
3320
section_path = section.id
3321
if section_path.startswith('file://'):
3322
# the location is already a local path or URL, convert the
3323
# section id to the same format
3324
section_path = urlutils.local_path_from_url(section_path)
3325
if (self.location.startswith(section_path) or
3326
fnmatch.fnmatch(self.location, section_path)):
3327
section_parts = section_path.rstrip('/').split('/')
3328
extra_path = '/'.join(location_parts[len(section_parts):])
3329
yield store, LocationSection(section, extra_path)
3332
class LocationMatcher(SectionMatcher):
3334
def __init__(self, store, location):
3335
super(LocationMatcher, self).__init__(store)
3336
url, params = urlutils.split_segment_parameters(location)
3337
if location.startswith('file://'):
3338
location = urlutils.local_path_from_url(location)
3339
self.location = location
3340
branch_name = params.get('branch')
3341
if branch_name is None:
3342
self.branch_name = urlutils.basename(self.location)
3344
self.branch_name = urlutils.unescape(branch_name)
3346
def _get_matching_sections(self):
3347
"""Get all sections matching ``location``."""
3348
# We slightly diverge from LocalConfig here by allowing the no-name
3349
# section as the most generic one and the lower priority.
3350
no_name_section = None
3352
# Filter out the no_name_section so _iter_for_location_by_parts can be
3353
# used (it assumes all sections have a name).
3354
for _, section in self.store.get_sections():
3355
if section.id is None:
3356
no_name_section = section
3358
all_sections.append(section)
3359
# Unfortunately _iter_for_location_by_parts deals with section names so
3360
# we have to resync.
3361
filtered_sections = _iter_for_location_by_parts(
3362
[s.id for s in all_sections], self.location)
3363
iter_all_sections = iter(all_sections)
3364
matching_sections = []
3365
if no_name_section is not None:
3366
matching_sections.append(
3367
(0, LocationSection(no_name_section, self.location)))
3368
for section_id, extra_path, length in filtered_sections:
3369
# a section id is unique for a given store so it's safe to take the
3370
# first matching section while iterating. Also, all filtered
3371
# sections are part of 'all_sections' and will always be found
3374
section = next(iter_all_sections)
3375
if section_id == section.id:
3376
section = LocationSection(section, extra_path,
3378
matching_sections.append((length, section))
3380
return matching_sections
3382
def get_sections(self):
3383
# Override the default implementation as we want to change the order
3384
# We want the longest (aka more specific) locations first
3385
sections = sorted(self._get_matching_sections(),
3386
key=lambda match: (match[0], match[1].id),
3388
# Sections mentioning 'ignore_parents' restrict the selection
3389
for _, section in sections:
3390
# FIXME: We really want to use as_bool below -- vila 2011-04-07
3391
ignore = section.get('ignore_parents', None)
3392
if ignore is not None:
3393
ignore = ui.bool_from_string(ignore)
3396
# Finally, we have a valid section
3397
yield self.store, section
3400
# FIXME: _shared_stores should be an attribute of a library state once a
3401
# library_state object is always available.
3403
_shared_stores_at_exit_installed = False
3406
class Stack(object):
3407
"""A stack of configurations where an option can be defined"""
3409
def __init__(self, sections_def, store=None, mutable_section_id=None):
3410
"""Creates a stack of sections with an optional store for changes.
3412
:param sections_def: A list of Section or callables that returns an
3413
iterable of Section. This defines the Sections for the Stack and
3414
can be called repeatedly if needed.
3416
:param store: The optional Store where modifications will be
3417
recorded. If none is specified, no modifications can be done.
3419
:param mutable_section_id: The id of the MutableSection where changes
3420
are recorded. This requires the ``store`` parameter to be
3423
self.sections_def = sections_def
3425
self.mutable_section_id = mutable_section_id
3427
def iter_sections(self):
3428
"""Iterate all the defined sections."""
3429
# Ensuring lazy loading is achieved by delaying section matching (which
3430
# implies querying the persistent storage) until it can't be avoided
3431
# anymore by using callables to describe (possibly empty) section
3433
for sections in self.sections_def:
3434
for store, section in sections():
3435
yield store, section
3437
def get(self, name, expand=True, convert=True):
3438
"""Return the *first* option value found in the sections.
3440
This is where we guarantee that sections coming from Store are loaded
3441
lazily: the loading is delayed until we need to either check that an
3442
option exists or get its value, which in turn may require to discover
3443
in which sections it can be defined. Both of these (section and option
3444
existence) require loading the store (even partially).
3446
:param name: The queried option.
3448
:param expand: Whether options references should be expanded.
3450
:param convert: Whether the option value should be converted from
3451
unicode (do nothing for non-registered options).
3453
:returns: The value of the option.
3455
# FIXME: No caching of options nor sections yet -- vila 20110503
3457
found_store = None # Where the option value has been found
3458
# If the option is registered, it may provide additional info about
3461
opt = option_registry.get(name)
3466
def expand_and_convert(val):
3467
# This may need to be called in different contexts if the value is
3468
# None or ends up being None during expansion or conversion.
3471
if isinstance(val, str):
3472
val = self._expand_options_in_string(val)
3474
trace.warning('Cannot expand "%s":'
3475
' %s does not support option expansion'
3476
% (name, type(val)))
3478
val = found_store.unquote(val)
3480
val = opt.convert_from_unicode(found_store, val)
3483
# First of all, check if the environment can override the configuration
3485
if opt is not None and opt.override_from_env:
3486
value = opt.get_override()
3487
value = expand_and_convert(value)
3489
for store, section in self.iter_sections():
3490
value = section.get(name)
3491
if value is not None:
3494
value = expand_and_convert(value)
3495
if opt is not None and value is None:
3496
# If the option is registered, it may provide a default value
3497
value = opt.get_default()
3498
value = expand_and_convert(value)
3499
for hook in ConfigHooks['get']:
3500
hook(self, name, value)
3503
def expand_options(self, string, env=None):
3504
"""Expand option references in the string in the configuration context.
3506
:param string: The string containing option(s) to expand.
3508
:param env: An option dict defining additional configuration options or
3509
overriding existing ones.
3511
:returns: The expanded string.
3513
return self._expand_options_in_string(string, env)
3515
def _expand_options_in_string(self, string, env=None, _refs=None):
3516
"""Expand options in the string in the configuration context.
3518
:param string: The string to be expanded.
3520
:param env: An option dict defining additional configuration options or
3521
overriding existing ones.
3523
:param _refs: Private list (FIFO) containing the options being expanded
3526
:returns: The expanded string.
3529
# Not much to expand there
3532
# What references are currently resolved (to detect loops)
3535
# We need to iterate until no more refs appear ({{foo}} will need two
3536
# iterations for example).
3541
for is_ref, chunk in iter_option_refs(result):
3543
chunks.append(chunk)
3548
raise OptionExpansionLoop(string, _refs)
3550
value = self._expand_option(name, env, _refs)
3552
raise ExpandingUnknownOption(name, string)
3553
chunks.append(value)
3555
result = ''.join(chunks)
3558
def _expand_option(self, name, env, _refs):
3559
if env is not None and name in env:
3560
# Special case, values provided in env takes precedence over
3564
value = self.get(name, expand=False, convert=False)
3565
value = self._expand_options_in_string(value, env, _refs)
3568
def _get_mutable_section(self):
3569
"""Get the MutableSection for the Stack.
3571
This is where we guarantee that the mutable section is lazily loaded:
3572
this means we won't load the corresponding store before setting a value
3573
or deleting an option. In practice the store will often be loaded but
3574
this helps catching some programming errors.
3577
section = store.get_mutable_section(self.mutable_section_id)
3578
return store, section
3580
def set(self, name, value):
3581
"""Set a new value for the option."""
3582
store, section = self._get_mutable_section()
3583
section.set(name, store.quote(value))
3584
for hook in ConfigHooks['set']:
3585
hook(self, name, value)
3587
def remove(self, name):
3588
"""Remove an existing option."""
3589
_, section = self._get_mutable_section()
3590
section.remove(name)
3591
for hook in ConfigHooks['remove']:
3595
# Mostly for debugging use
3596
return "<config.%s(%s)>" % (self.__class__.__name__, id(self))
3598
def _get_overrides(self):
3599
if breezy._global_state is not None:
3600
# TODO(jelmer): Urgh, this is circular so we can't call breezy.get_global_state()
3601
return breezy._global_state.cmdline_overrides.get_sections()
3604
def get_shared_store(self, store, state=None):
3605
"""Get a known shared store.
3607
Store urls uniquely identify them and are used to ensure a single copy
3608
is shared across all users.
3610
:param store: The store known to the caller.
3612
:param state: The library state where the known stores are kept.
3614
:returns: The store received if it's not a known one, an already known
3618
# TODO(jelmer): Urgh, this is circular so we can't call breezy.get_global_state()
3619
state = breezy._global_state
3621
global _shared_stores_at_exit_installed
3622
stores = _shared_stores
3624
def save_config_changes():
3625
for k, store in stores.items():
3626
store.save_changes()
3627
if not _shared_stores_at_exit_installed:
3628
# FIXME: Ugly hack waiting for library_state to always be
3629
# available. -- vila 20120731
3631
atexit.register(save_config_changes)
3632
_shared_stores_at_exit_installed = True
3634
stores = state.config_stores
3635
url = store.external_url()
3643
class MemoryStack(Stack):
3644
"""A configuration stack defined from a string.
3646
This is mainly intended for tests and requires no disk resources.
3649
def __init__(self, content=None):
3650
"""Create an in-memory stack from a given content.
3652
It uses a single store based on configobj and support reading and
3655
:param content: The initial content of the store. If None, the store is
3656
not loaded and ``_load_from_string`` can and should be used if
3659
store = IniFileStore()
3660
if content is not None:
3661
store._load_from_string(content)
3662
super(MemoryStack, self).__init__(
3663
[store.get_sections], store)
3666
class _CompatibleStack(Stack):
3667
"""Place holder for compatibility with previous design.
3669
This is intended to ease the transition from the Config-based design to the
3670
Stack-based design and should not be used nor relied upon by plugins.
3672
One assumption made here is that the daughter classes will all use Stores
3673
derived from LockableIniFileStore).
3675
It implements set() and remove () by re-loading the store before applying
3676
the modification and saving it.
3678
The long term plan being to implement a single write by store to save
3679
all modifications, this class should not be used in the interim.
3682
def set(self, name, value):
3685
super(_CompatibleStack, self).set(name, value)
3686
# Force a write to persistent storage
3689
def remove(self, name):
3692
super(_CompatibleStack, self).remove(name)
3693
# Force a write to persistent storage
3697
class GlobalStack(Stack):
3698
"""Global options only stack.
3700
The following sections are queried:
3702
* command-line overrides,
3704
* the 'DEFAULT' section in bazaar.conf
3706
This stack will use the ``DEFAULT`` section in bazaar.conf as its
3711
gstore = self.get_shared_store(GlobalStore())
3712
super(GlobalStack, self).__init__(
3713
[self._get_overrides,
3714
NameMatcher(gstore, 'DEFAULT').get_sections],
3715
gstore, mutable_section_id='DEFAULT')
3718
class LocationStack(Stack):
3719
"""Per-location options falling back to global options stack.
3722
The following sections are queried:
3724
* command-line overrides,
3726
* the sections matching ``location`` in ``locations.conf``, the order being
3727
defined by the number of path components in the section glob, higher
3728
numbers first (from most specific section to most generic).
3730
* the 'DEFAULT' section in bazaar.conf
3732
This stack will use the ``location`` section in locations.conf as its
3736
def __init__(self, location):
3737
"""Make a new stack for a location and global configuration.
3739
:param location: A URL prefix to """
3740
lstore = self.get_shared_store(LocationStore())
3741
if location.startswith('file://'):
3742
location = urlutils.local_path_from_url(location)
3743
gstore = self.get_shared_store(GlobalStore())
3744
super(LocationStack, self).__init__(
3745
[self._get_overrides,
3746
LocationMatcher(lstore, location).get_sections,
3747
NameMatcher(gstore, 'DEFAULT').get_sections],
3748
lstore, mutable_section_id=location)
3751
class BranchStack(Stack):
3752
"""Per-location options falling back to branch then global options stack.
3754
The following sections are queried:
3756
* command-line overrides,
3758
* the sections matching ``location`` in ``locations.conf``, the order being
3759
defined by the number of path components in the section glob, higher
3760
numbers first (from most specific section to most generic),
3762
* the no-name section in branch.conf,
3764
* the ``DEFAULT`` section in ``bazaar.conf``.
3766
This stack will use the no-name section in ``branch.conf`` as its
3770
def __init__(self, branch):
3771
lstore = self.get_shared_store(LocationStore())
3772
bstore = branch._get_config_store()
3773
gstore = self.get_shared_store(GlobalStore())
3774
super(BranchStack, self).__init__(
3775
[self._get_overrides,
3776
LocationMatcher(lstore, branch.base).get_sections,
3777
NameMatcher(bstore, None).get_sections,
3778
NameMatcher(gstore, 'DEFAULT').get_sections],
3780
self.branch = branch
3782
def lock_write(self, token=None):
3783
return self.branch.lock_write(token)
3786
return self.branch.unlock()
3788
def set(self, name, value):
3789
with self.lock_write():
3790
super(BranchStack, self).set(name, value)
3791
# Unlocking the branch will trigger a store.save_changes() so the
3792
# last unlock saves all the changes.
3794
def remove(self, name):
3795
with self.lock_write():
3796
super(BranchStack, self).remove(name)
3797
# Unlocking the branch will trigger a store.save_changes() so the
3798
# last unlock saves all the changes.
3801
class RemoteControlStack(Stack):
3802
"""Remote control-only options stack."""
3804
# FIXME 2011-11-22 JRV This should probably be renamed to avoid confusion
3805
# with the stack used for remote bzr dirs. RemoteControlStack only uses
3806
# control.conf and is used only for stack options.
3808
def __init__(self, bzrdir):
3809
cstore = bzrdir._get_config_store()
3810
super(RemoteControlStack, self).__init__(
3811
[NameMatcher(cstore, None).get_sections],
3813
self.controldir = bzrdir
3816
class BranchOnlyStack(Stack):
3817
"""Branch-only options stack."""
3819
# FIXME: _BranchOnlyStack only uses branch.conf and is used only for the
3820
# stacked_on_location options waiting for http://pad.lv/832042 to be fixed.
3821
# -- vila 2011-12-16
3823
def __init__(self, branch):
3824
bstore = branch._get_config_store()
3825
super(BranchOnlyStack, self).__init__(
3826
[NameMatcher(bstore, None).get_sections],
3828
self.branch = branch
3830
def lock_write(self, token=None):
3831
return self.branch.lock_write(token)
3834
return self.branch.unlock()
3836
def set(self, name, value):
3837
with self.lock_write():
3838
super(BranchOnlyStack, self).set(name, value)
3839
# Force a write to persistent storage
3840
self.store.save_changes()
3842
def remove(self, name):
3843
with self.lock_write():
3844
super(BranchOnlyStack, self).remove(name)
3845
# Force a write to persistent storage
3846
self.store.save_changes()
3849
class cmd_config(commands.Command):
3850
__doc__ = """Display, set or remove a configuration option.
3852
Display the active value for option NAME.
3854
If --all is specified, NAME is interpreted as a regular expression and all
3855
matching options are displayed mentioning their scope and without resolving
3856
option references in the value). The active value that bzr will take into
3857
account is the first one displayed for each option.
3859
If NAME is not given, --all .* is implied (all options are displayed for the
3862
Setting a value is achieved by using NAME=value without spaces. The value
3863
is set in the most relevant scope and can be checked by displaying the
3866
Removing a value is achieved by using --remove NAME.
3869
takes_args = ['name?']
3873
# FIXME: This should be a registry option so that plugins can register
3874
# their own config files (or not) and will also address
3875
# http://pad.lv/788991 -- vila 20101115
3876
commands.Option('scope', help='Reduce the scope to the specified'
3877
' configuration file.',
3879
commands.Option('all',
3880
help='Display all the defined values for the matching options.',
3882
commands.Option('remove', help='Remove the option from'
3883
' the configuration file.'),
3886
_see_also = ['configuration']
3888
@commands.display_command
3889
def run(self, name=None, all=False, directory=None, scope=None,
3891
if directory is None:
3893
directory = directory_service.directories.dereference(directory)
3894
directory = urlutils.normalize_url(directory)
3896
raise errors.BzrError(
3897
'--all and --remove are mutually exclusive.')
3899
# Delete the option in the given scope
3900
self._remove_config_option(name, directory, scope)
3902
# Defaults to all options
3903
self._show_matching_options('.*', directory, scope)
3906
name, value = name.split('=', 1)
3908
# Display the option(s) value(s)
3910
self._show_matching_options(name, directory, scope)
3912
self._show_value(name, directory, scope)
3915
raise errors.BzrError(
3916
'Only one option can be set.')
3917
# Set the option value
3918
self._set_config_option(name, value, directory, scope)
3920
def _get_stack(self, directory, scope=None, write_access=False):
3921
"""Get the configuration stack specified by ``directory`` and ``scope``.
3923
:param directory: Where the configurations are derived from.
3925
:param scope: A specific config to start from.
3927
:param write_access: Whether a write access to the stack will be
3930
# FIXME: scope should allow access to plugin-specific stacks (even
3931
# reduced to the plugin-specific store), related to
3932
# http://pad.lv/788991 -- vila 2011-11-15
3933
if scope is not None:
3934
if scope == 'breezy':
3935
return GlobalStack()
3936
elif scope == 'locations':
3937
return LocationStack(directory)
3938
elif scope == 'branch':
3940
controldir.ControlDir.open_containing_tree_or_branch(
3943
self.add_cleanup(br.lock_write().unlock)
3944
return br.get_config_stack()
3945
raise NoSuchConfig(scope)
3949
controldir.ControlDir.open_containing_tree_or_branch(
3952
self.add_cleanup(br.lock_write().unlock)
3953
return br.get_config_stack()
3954
except errors.NotBranchError:
3955
return LocationStack(directory)
3957
def _quote_multiline(self, value):
3959
value = '"""' + value + '"""'
3962
def _show_value(self, name, directory, scope):
3963
conf = self._get_stack(directory, scope)
3964
value = conf.get(name, expand=True, convert=False)
3965
if value is not None:
3966
# Quote the value appropriately
3967
value = self._quote_multiline(value)
3968
self.outf.write('%s\n' % (value,))
3970
raise NoSuchConfigOption(name)
3972
def _show_matching_options(self, name, directory, scope):
3973
name = lazy_regex.lazy_compile(name)
3974
# We want any error in the regexp to be raised *now* so we need to
3975
# avoid the delay introduced by the lazy regexp. But, we still do
3976
# want the nicer errors raised by lazy_regex.
3977
name._compile_and_collapse()
3980
conf = self._get_stack(directory, scope)
3981
for store, section in conf.iter_sections():
3982
for oname in section.iter_option_names():
3983
if name.search(oname):
3984
if cur_store_id != store.id:
3985
# Explain where the options are defined
3986
self.outf.write('%s:\n' % (store.id,))
3987
cur_store_id = store.id
3989
if (section.id is not None and cur_section != section.id):
3990
# Display the section id as it appears in the store
3991
# (None doesn't appear by definition)
3992
self.outf.write(' [%s]\n' % (section.id,))
3993
cur_section = section.id
3994
value = section.get(oname, expand=False)
3995
# Quote the value appropriately
3996
value = self._quote_multiline(value)
3997
self.outf.write(' %s = %s\n' % (oname, value))
3999
def _set_config_option(self, name, value, directory, scope):
4000
conf = self._get_stack(directory, scope, write_access=True)
4001
conf.set(name, value)
4002
# Explicitly save the changes
4003
conf.store.save_changes()
4005
def _remove_config_option(self, name, directory, scope):
4007
raise errors.BzrCommandError(
4008
'--remove expects an option to remove.')
4009
conf = self._get_stack(directory, scope, write_access=True)
4012
# Explicitly save the changes
4013
conf.store.save_changes()
4015
raise NoSuchConfigOption(name)
4020
# We need adapters that can build a Store or a Stack in a test context. Test
4021
# classes, based on TestCaseWithTransport, can use the registry to parametrize
4022
# themselves. The builder will receive a test instance and should return a
4023
# ready-to-use store or stack. Plugins that define new store/stacks can also
4024
# register themselves here to be tested against the tests defined in
4025
# breezy.tests.test_config. Note that the builder can be called multiple times
4026
# for the same test.
4028
# The registered object should be a callable receiving a test instance
4029
# parameter (inheriting from tests.TestCaseWithTransport) and returning a Store
4031
test_store_builder_registry = registry.Registry()
4033
# The registered object should be a callable receiving a test instance
4034
# parameter (inheriting from tests.TestCaseWithTransport) and returning a Stack
4036
test_stack_builder_registry = registry.Registry()