1# Copyright (C) 2005-2014, 2016 Canonical Ltd 2# Authors: Robert Collins <robert.collins@canonical.com> 3# and others 4# 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. 9# 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. 14# 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 18 19"""Configuration that affects the behaviour of Breezy. 20 21Currently this configuration resides in ~/.config/breezy/breezy.conf 22and ~/.config/breezy/locations.conf, which is written to by brz. 23 24If the first location doesn't exist, then brz falls back to reading 25Bazaar configuration files in ~/.bazaar or ~/.config/bazaar. 26 27In breezy.conf the following options may be set: 28[DEFAULT] 29editor=name-of-program 30email=Your Name <your@email.address> 31check_signatures=require|ignore|check-available(default) 32create_signatures=always|never|when-required(default) 33log_format=name-of-format 34validate_signatures_in_log=true|false(default) 35acceptable_keys=pattern1,pattern2 36gpg_signing_key=amy@example.com 37 38in locations.conf, you specify the url of a branch and options for it. 39Wildcards may be used - * and ? as normal in shell completion. Options 40set in both breezy.conf and locations.conf are overridden by the locations.conf 41setting. 42[/home/robertc/source] 43recurse=False|True(default) 44email= as above 45check_signatures= as above 46create_signatures= as above. 47validate_signatures_in_log=as above 48acceptable_keys=as above 49 50explanation of options 51---------------------- 52editor - this option sets the pop up editor to use during commits. 53email - this option sets the user id brz will use when committing. 54check_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. 58create_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. 62log_format - this option sets the default log format. Possible values are 63 long, short, line, or a plugin can register new formats. 64validate_signatures_in_log - show GPG signature validity in log output 65acceptable_keys - comma separated list of key patterns acceptable for 66 verify-signatures command 67 68In breezy.conf you can also define aliases in the ALIASES sections, example 69 70[ALIASES] 71lastlog=log --line -r-10..-1 72ll=log --line -r-10..-1 73h=help 74up=pull 75""" 76 77import os 78import sys 79 80import configobj 81from io import BytesIO 82 83import breezy 84from .lazy_import import lazy_import 85lazy_import(globals(), """ 86import base64 87import errno 88import fnmatch 89import re 90import stat 91 92from breezy import ( 93 cmdline, 94 controldir, 95 debug, 96 directory_service, 97 lock, 98 lockdir, 99 mergetools, 100 osutils, 101 trace, 102 transport, 103 ui, 104 urlutils, 105 win32utils, 106 ) 107from breezy.i18n import gettext 108""") 109from . import ( 110 commands, 111 bedding, 112 errors, 113 hooks, 114 lazy_regex, 115 registry, 116 ) 117 118 119CHECK_IF_POSSIBLE = 0 120CHECK_ALWAYS = 1 121CHECK_NEVER = 2 122 123 124SIGN_WHEN_REQUIRED = 0 125SIGN_ALWAYS = 1 126SIGN_NEVER = 2 127 128 129POLICY_NONE = 0 130POLICY_NORECURSE = 1 131POLICY_APPENDPATH = 2 132 133_policy_name = { 134 POLICY_NONE: None, 135 POLICY_NORECURSE: 'norecurse', 136 POLICY_APPENDPATH: 'appendpath', 137 } 138_policy_value = { 139 None: POLICY_NONE, 140 'none': POLICY_NONE, 141 'norecurse': POLICY_NORECURSE, 142 'appendpath': POLICY_APPENDPATH, 143 } 144 145 146STORE_LOCATION = POLICY_NONE 147STORE_LOCATION_NORECURSE = POLICY_NORECURSE 148STORE_LOCATION_APPENDPATH = POLICY_APPENDPATH 149STORE_BRANCH = 3 150STORE_GLOBAL = 4 151 152 153class OptionExpansionLoop(errors.BzrError): 154 155 _fmt = 'Loop involving %(refs)r while expanding "%(string)s".' 156 157 def __init__(self, string, refs): 158 self.string = string 159 self.refs = '->'.join(refs) 160 161 162class ExpandingUnknownOption(errors.BzrError): 163 164 _fmt = 'Option "%(name)s" is not defined while expanding "%(string)s".' 165 166 def __init__(self, name, string): 167 self.name = name 168 self.string = string 169 170 171class IllegalOptionName(errors.BzrError): 172 173 _fmt = 'Option "%(name)s" is not allowed.' 174 175 def __init__(self, name): 176 self.name = name 177 178 179class ConfigContentError(errors.BzrError): 180 181 _fmt = "Config file %(filename)s is not UTF-8 encoded\n" 182 183 def __init__(self, filename): 184 self.filename = filename 185 186 187class ParseConfigError(errors.BzrError): 188 189 _fmt = "Error(s) parsing config file %(filename)s:\n%(errors)s" 190 191 def __init__(self, errors, filename): 192 self.filename = filename 193 self.errors = '\n'.join(e.msg for e in errors) 194 195 196class ConfigOptionValueError(errors.BzrError): 197 198 _fmt = ('Bad value "%(value)s" for option "%(name)s".\n' 199 'See ``brz help %(name)s``') 200 201 def __init__(self, name, value): 202 errors.BzrError.__init__(self, name=name, value=value) 203 204 205class NoEmailInUsername(errors.BzrError): 206 207 _fmt = "%(username)r does not seem to contain a reasonable email address" 208 209 def __init__(self, username): 210 self.username = username 211 212 213class NoSuchConfig(errors.BzrError): 214 215 _fmt = ('The "%(config_id)s" configuration does not exist.') 216 217 def __init__(self, config_id): 218 errors.BzrError.__init__(self, config_id=config_id) 219 220 221class NoSuchConfigOption(errors.BzrError): 222 223 _fmt = ('The "%(option_name)s" configuration option does not exist.') 224 225 def __init__(self, option_name): 226 errors.BzrError.__init__(self, option_name=option_name) 227 228 229def signature_policy_from_unicode(signature_string): 230 """Convert a string to a signing policy.""" 231 if signature_string.lower() == 'check-available': 232 return CHECK_IF_POSSIBLE 233 if signature_string.lower() == 'ignore': 234 return CHECK_NEVER 235 if signature_string.lower() == 'require': 236 return CHECK_ALWAYS 237 raise ValueError("Invalid signatures policy '%s'" 238 % signature_string) 239 240 241def signing_policy_from_unicode(signature_string): 242 """Convert a string to a signing policy.""" 243 if signature_string.lower() == 'when-required': 244 return SIGN_WHEN_REQUIRED 245 if signature_string.lower() == 'never': 246 return SIGN_NEVER 247 if signature_string.lower() == 'always': 248 return SIGN_ALWAYS 249 raise ValueError("Invalid signing policy '%s'" 250 % signature_string) 251 252 253def _has_triplequote_bug(): 254 """True if triple quote logic is reversed, see lp:710410.""" 255 conf = configobj.ConfigObj() 256 quote = getattr(conf, "_get_triple_quote", None) 257 if quote and quote('"""') != "'''": 258 return True 259 return False 260 261 262class ConfigObj(configobj.ConfigObj): 263 264 def __init__(self, infile=None, **kwargs): 265 # We define our own interpolation mechanism calling it option expansion 266 super(ConfigObj, self).__init__(infile=infile, 267 interpolation=False, 268 **kwargs) 269 270 if _has_triplequote_bug(): 271 def _get_triple_quote(self, value): 272 quot = super(ConfigObj, self)._get_triple_quote(value) 273 if quot == configobj.tdquot: 274 return configobj.tsquot 275 return configobj.tdquot 276 277 def get_bool(self, section, key): 278 return self[section].as_bool(key) 279 280 def get_value(self, section, name): 281 # Try [] for the old DEFAULT section. 282 if section == "DEFAULT": 283 try: 284 return self[name] 285 except KeyError: 286 pass 287 return self[section][name] 288 289 290class Config(object): 291 """A configuration policy - what username, editor, gpg needs etc.""" 292 293 def __init__(self): 294 super(Config, self).__init__() 295 296 def config_id(self): 297 """Returns a unique ID for the config.""" 298 raise NotImplementedError(self.config_id) 299 300 def get_change_editor(self, old_tree, new_tree): 301 from breezy import diff 302 cmd = self._get_change_editor() 303 if cmd is None: 304 return None 305 cmd = cmd.replace('@old_path', '{old_path}') 306 cmd = cmd.replace('@new_path', '{new_path}') 307 cmd = cmdline.split(cmd) 308 if '{old_path}' not in cmd: 309 cmd.extend(['{old_path}', '{new_path}']) 310 return diff.DiffFromTool.from_string(cmd, old_tree, new_tree, 311 sys.stdout) 312 313 def _get_signature_checking(self): 314 """Template method to override signature checking policy.""" 315 316 def _get_signing_policy(self): 317 """Template method to override signature creation policy.""" 318 319 option_ref_re = None 320 321 def expand_options(self, string, env=None): 322 """Expand option references in the string in the configuration context. 323 324 :param string: The string containing option to expand. 325 326 :param env: An option dict defining additional configuration options or 327 overriding existing ones. 328 329 :returns: The expanded string. 330 """ 331 return self._expand_options_in_string(string, env) 332 333 def _expand_options_in_list(self, slist, env=None, _ref_stack=None): 334 """Expand options in a list of strings in the configuration context. 335 336 :param slist: A list of strings. 337 338 :param env: An option dict defining additional configuration options or 339 overriding existing ones. 340 341 :param _ref_stack: Private list containing the options being 342 expanded to detect loops. 343 344 :returns: The flatten list of expanded strings. 345 """ 346 # expand options in each value separately flattening lists 347 result = [] 348 for s in slist: 349 value = self._expand_options_in_string(s, env, _ref_stack) 350 if isinstance(value, list): 351 result.extend(value) 352 else: 353 result.append(value) 354 return result 355 356 def _expand_options_in_string(self, string, env=None, _ref_stack=None): 357 """Expand options in the string in the configuration context. 358 359 :param string: The string to be expanded. 360 361 :param env: An option dict defining additional configuration options or 362 overriding existing ones. 363 364 :param _ref_stack: Private list containing the options being 365 expanded to detect loops. 366 367 :returns: The expanded string. 368 """ 369 if string is None: 370 # Not much to expand there 371 return None 372 if _ref_stack is None: 373 # What references are currently resolved (to detect loops) 374 _ref_stack = [] 375 if self.option_ref_re is None: 376 # We want to match the most embedded reference first (i.e. for 377 # '{{foo}}' we will get '{foo}', 378 # for '{bar{baz}}' we will get '{baz}' 379 self.option_ref_re = re.compile('({[^{}]+})') 380 result = string 381 # We need to iterate until no more refs appear ({{foo}} will need two 382 # iterations for example). 383 while True: 384 raw_chunks = self.option_ref_re.split(result) 385 if len(raw_chunks) == 1: 386 # Shorcut the trivial case: no refs 387 return result 388 chunks = [] 389 list_value = False 390 # Split will isolate refs so that every other chunk is a ref 391 chunk_is_ref = False 392 for chunk in raw_chunks: 393 if not chunk_is_ref: 394 if chunk: 395 # Keep only non-empty strings (or we get bogus empty 396 # slots when a list value is involved). 397 chunks.append(chunk) 398 chunk_is_ref = True 399 else: 400 name = chunk[1:-1] 401 if name in _ref_stack: 402 raise OptionExpansionLoop(string, _ref_stack) 403 _ref_stack.append(name) 404 value = self._expand_option(name, env, _ref_stack) 405 if value is None: 406 raise ExpandingUnknownOption(name, string) 407 if isinstance(value, list): 408 list_value = True 409 chunks.extend(value) 410 else: 411 chunks.append(value) 412 _ref_stack.pop() 413 chunk_is_ref = False 414 if list_value: 415 # Once a list appears as the result of an expansion, all 416 # callers will get a list result. This allows a consistent 417 # behavior even when some options in the expansion chain 418 # defined as strings (no comma in their value) but their 419 # expanded value is a list. 420 return self._expand_options_in_list(chunks, env, _ref_stack) 421 else: 422 result = ''.join(chunks) 423 return result 424 425 def _expand_option(self, name, env, _ref_stack): 426 if env is not None and name in env: 427 # Special case, values provided in env takes precedence over 428 # anything else 429 value = env[name] 430 else: 431 # FIXME: This is a limited implementation, what we really need is a 432 # way to query the brz config for the value of an option, 433 # respecting the scope rules (That is, once we implement fallback 434 # configs, getting the option value should restart from the top 435 # config, not the current one) -- vila 20101222 436 value = self.get_user_option(name, expand=False) 437 if isinstance(value, list): 438 value = self._expand_options_in_list(value, env, _ref_stack) 439 else: 440 value = self._expand_options_in_string(value, env, _ref_stack) 441 return value 442 443 def _get_user_option(self, option_name): 444 """Template method to provide a user option.""" 445 return None 446 447 def get_user_option(self, option_name, expand=True): 448 """Get a generic option - no special process, no default. 449 450 :param option_name: The queried option. 451 452 :param expand: Whether options references should be expanded. 453 454 :returns: The value of the option. 455 """ 456 value = self._get_user_option(option_name) 457 if expand: 458 if isinstance(value, list): 459 value = self._expand_options_in_list(value) 460 elif isinstance(value, dict): 461 trace.warning('Cannot expand "%s":' 462 ' Dicts do not support option expansion' 463 % (option_name,)) 464 else: 465 value = self._expand_options_in_string(value) 466 for hook in OldConfigHooks['get']: 467 hook(self, option_name, value) 468 return value 469 470 def get_user_option_as_bool(self, option_name, expand=None, default=None): 471 """Get a generic option as a boolean. 472 473 :param expand: Allow expanding references to other config values. 474 :param default: Default value if nothing is configured 475 :return None if the option doesn't exist or its value can't be 476 interpreted as a boolean. Returns True or False otherwise. 477 """ 478 s = self.get_user_option(option_name, expand=expand) 479 if s is None: 480 # The option doesn't exist 481 return default 482 val = ui.bool_from_string(s) 483 if val is None: 484 # The value can't be interpreted as a boolean 485 trace.warning('Value "%s" is not a boolean for "%s"', 486 s, option_name) 487 return val 488 489 def get_user_option_as_list(self, option_name, expand=None): 490 """Get a generic option as a list - no special process, no default. 491 492 :return None if the option doesn't exist. Returns the value as a list 493 otherwise. 494 """ 495 l = self.get_user_option(option_name, expand=expand) 496 if isinstance(l, str): 497 # A single value, most probably the user forgot (or didn't care to 498 # add) the final ',' 499 l = [l] 500 return l 501 502 def _log_format(self): 503 """See log_format().""" 504 return None 505 506 def validate_signatures_in_log(self): 507 """Show GPG signature validity in log""" 508 result = self._validate_signatures_in_log() 509 if result == "true": 510 result = True 511 else: 512 result = False 513 return result 514 515 def _validate_signatures_in_log(self): 516 """See validate_signatures_in_log().""" 517 return None 518 519 def _post_commit(self): 520 """See Config.post_commit.""" 521 return None 522 523 def user_email(self): 524 """Return just the email component of a username.""" 525 return extract_email_address(self.username()) 526 527 def username(self): 528 """Return email-style username. 529 530 Something similar to 'Martin Pool <mbp@sourcefrog.net>' 531 532 $BRZ_EMAIL or $BZR_EMAIL can be set to override this, then 533 the concrete policy type is checked, and finally 534 $EMAIL is examined. 535 If no username can be found, NoWhoami exception is raised. 536 """ 537 v = os.environ.get('BRZ_EMAIL') or os.environ.get('BZR_EMAIL') 538 if v: 539 return v 540 v = self._get_user_id() 541 if v: 542 return v 543 return bedding.default_email() 544 545 def get_alias(self, value): 546 return self._get_alias(value) 547 548 def _get_alias(self, value): 549 pass 550 551 def get_nickname(self): 552 return self._get_nickname() 553 554 def _get_nickname(self): 555 return None 556 557 def get_bzr_remote_path(self): 558 try: 559 return os.environ['BZR_REMOTE_PATH'] 560 except KeyError: 561 path = self.get_user_option("bzr_remote_path") 562 if path is None: 563 path = 'bzr' 564 return path 565 566 def suppress_warning(self, warning): 567 """Should the warning be suppressed or emitted. 568 569 :param warning: The name of the warning being tested. 570 571 :returns: True if the warning should be suppressed, False otherwise. 572 """ 573 warnings = self.get_user_option_as_list('suppress_warnings') 574 if warnings is None or warning not in warnings: 575 return False 576 else: 577 return True 578 579 def get_merge_tools(self): 580 tools = {} 581 for (oname, value, section, conf_id, parser) in self._get_options(): 582 if oname.startswith('bzr.mergetool.'): 583 tool_name = oname[len('bzr.mergetool.'):] 584 tools[tool_name] = self.get_user_option(oname, False) 585 trace.mutter('loaded merge tools: %r' % tools) 586 return tools 587 588 def find_merge_tool(self, name): 589 # We fake a defaults mechanism here by checking if the given name can 590 # be found in the known_merge_tools if it's not found in the config. 591 # This should be done through the proposed config defaults mechanism 592 # when it becomes available in the future. 593 command_line = (self.get_user_option('bzr.mergetool.%s' % name, 594 expand=False) or 595 mergetools.known_merge_tools.get(name, None)) 596 return command_line 597 598 599class _ConfigHooks(hooks.Hooks): 600 """A dict mapping hook names and a list of callables for configs. 601 """ 602 603 def __init__(self): 604 """Create the default hooks. 605 606 These are all empty initially, because by default nothing should get 607 notified. 608 """ 609 super(_ConfigHooks, self).__init__('breezy.config', 'ConfigHooks') 610 self.add_hook('load', 611 'Invoked when a config store is loaded.' 612 ' The signature is (store).', 613 (2, 4)) 614 self.add_hook('save', 615 'Invoked when a config store is saved.' 616 ' The signature is (store).', 617 (2, 4)) 618 # The hooks for config options 619 self.add_hook('get', 620 'Invoked when a config option is read.' 621 ' The signature is (stack, name, value).', 622 (2, 4)) 623 self.add_hook('set', 624 'Invoked when a config option is set.' 625 ' The signature is (stack, name, value).', 626 (2, 4)) 627 self.add_hook('remove', 628 'Invoked when a config option is removed.' 629 ' The signature is (stack, name).', 630 (2, 4)) 631 632 633ConfigHooks = _ConfigHooks() 634 635 636class _OldConfigHooks(hooks.Hooks): 637 """A dict mapping hook names and a list of callables for configs. 638 """ 639 640 def __init__(self): 641 """Create the default hooks. 642 643 These are all empty initially, because by default nothing should get 644 notified. 645 """ 646 super(_OldConfigHooks, self).__init__( 647 'breezy.config', 'OldConfigHooks') 648 self.add_hook('load', 649 'Invoked when a config store is loaded.' 650 ' The signature is (config).', 651 (2, 4)) 652 self.add_hook('save', 653 'Invoked when a config store is saved.' 654 ' The signature is (config).', 655 (2, 4)) 656 # The hooks for config options 657 self.add_hook('get', 658 'Invoked when a config option is read.' 659 ' The signature is (config, name, value).', 660 (2, 4)) 661 self.add_hook('set', 662 'Invoked when a config option is set.' 663 ' The signature is (config, name, value).', 664 (2, 4)) 665 self.add_hook('remove', 666 'Invoked when a config option is removed.' 667 ' The signature is (config, name).', 668 (2, 4)) 669 670 671OldConfigHooks = _OldConfigHooks() 672 673 674class IniBasedConfig(Config): 675 """A configuration policy that draws from ini files.""" 676 677 def __init__(self, file_name=None): 678 """Base class for configuration files using an ini-like syntax. 679 680 :param file_name: The configuration file path. 681 """ 682 super(IniBasedConfig, self).__init__() 683 self.file_name = file_name 684 self.file_name = file_name 685 self._content = None 686 self._parser = None 687 688 @classmethod 689 def from_string(cls, str_or_unicode, file_name=None, save=False): 690 """Create a config object from a string. 691 692 :param str_or_unicode: A string representing the file content. This 693 will be utf-8 encoded. 694 695 :param file_name: The configuration file path. 696 697 :param _save: Whether the file should be saved upon creation. 698 """ 699 conf = cls(file_name=file_name) 700 conf._create_from_string(str_or_unicode, save) 701 return conf 702 703 def _create_from_string(self, str_or_unicode, save): 704 if isinstance(str_or_unicode, str): 705 str_or_unicode = str_or_unicode.encode('utf-8') 706 self._content = BytesIO(str_or_unicode) 707 # Some tests use in-memory configs, some other always need the config 708 # file to exist on disk. 709 if save: 710 self._write_config_file() 711 712 def _get_parser(self): 713 if self._parser is not None: 714 return self._parser 715 if self._content is not None: 716 co_input = self._content 717 elif self.file_name is None: 718 raise AssertionError('We have no content to create the config') 719 else: 720 co_input = self.file_name 721 try: 722 self._parser = ConfigObj(co_input, encoding='utf-8') 723 except configobj.ConfigObjError as e: 724 raise ParseConfigError(e.errors, e.config.filename) 725 except UnicodeDecodeError: 726 raise ConfigContentError(self.file_name) 727 # Make sure self.reload() will use the right file name 728 self._parser.filename = self.file_name 729 for hook in OldConfigHooks['load']: 730 hook(self) 731 return self._parser 732 733 def reload(self): 734 """Reload the config file from disk.""" 735 if self.file_name is None: 736 raise AssertionError('We need a file name to reload the config') 737 if self._parser is not None: 738 self._parser.reload() 739 for hook in ConfigHooks['load']: 740 hook(self) 741 742 def _get_matching_sections(self): 743 """Return an ordered list of (section_name, extra_path) pairs. 744 745 If the section contains inherited configuration, extra_path is 746 a string containing the additional path components. 747 """ 748 section = self._get_section() 749 if section is not None: 750 return [(section, '')] 751 else: 752 return [] 753 754 def _get_section(self): 755 """Override this to define the section used by the config.""" 756 return "DEFAULT" 757 758 def _get_sections(self, name=None): 759 """Returns an iterator of the sections specified by ``name``. 760 761 :param name: The section name. If None is supplied, the default 762 configurations are yielded. 763 764 :return: A tuple (name, section, config_id) for all sections that will 765 be walked by user_get_option() in the 'right' order. The first one 766 is where set_user_option() will update the value. 767 """ 768 parser = self._get_parser() 769 if name is not None: 770 yield (name, parser[name], self.config_id()) 771 else: 772 # No section name has been given so we fallback to the configobj 773 # itself which holds the variables defined outside of any section. 774 yield (None, parser, self.config_id()) 775 776 def _get_options(self, sections=None): 777 """Return an ordered list of (name, value, section, config_id) tuples. 778 779 All options are returned with their associated value and the section 780 they appeared in. ``config_id`` is a unique identifier for the 781 configuration file the option is defined in. 782 783 :param sections: Default to ``_get_matching_sections`` if not 784 specified. This gives a better control to daughter classes about 785 which sections should be searched. This is a list of (name, 786 configobj) tuples. 787 """ 788 if sections is None: 789 parser = self._get_parser() 790 sections = [] 791 for (section_name, _) in self._get_matching_sections(): 792 try: 793 section = parser[section_name] 794 except KeyError: 795 # This could happen for an empty file for which we define a 796 # DEFAULT section. FIXME: Force callers to provide sections 797 # instead ? -- vila 20100930 798 continue 799 sections.append((section_name, section)) 800 config_id = self.config_id() 801 for (section_name, section) in sections: 802 for (name, value) in section.iteritems(): 803 yield (name, parser._quote(value), section_name, 804 config_id, parser) 805 806 def _get_option_policy(self, section, option_name): 807 """Return the policy for the given (section, option_name) pair.""" 808 return POLICY_NONE 809 810 def _get_change_editor(self): 811 return self.get_user_option('change_editor', expand=False) 812 813 def _get_signature_checking(self): 814 """See Config._get_signature_checking.""" 815 policy = self._get_user_option('check_signatures') 816 if policy: 817 return signature_policy_from_unicode(policy) 818 819 def _get_signing_policy(self): 820 """See Config._get_signing_policy""" 821 policy = self._get_user_option('create_signatures') 822 if policy: 823 return signing_policy_from_unicode(policy) 824 825 def _get_user_id(self): 826 """Get the user id from the 'email' key in the current section.""" 827 return self._get_user_option('email') 828 829 def _get_user_option(self, option_name): 830 """See Config._get_user_option.""" 831 for (section, extra_path) in self._get_matching_sections(): 832 try: 833 value = self._get_parser().get_value(section, option_name) 834 except KeyError: 835 continue 836 policy = self._get_option_policy(section, option_name) 837 if policy == POLICY_NONE: 838 return value 839 elif policy == POLICY_NORECURSE: 840 # norecurse items only apply to the exact path 841 if extra_path: 842 continue 843 else: 844 return value 845 elif policy == POLICY_APPENDPATH: 846 if extra_path: 847 value = urlutils.join(value, extra_path) 848 return value 849 else: 850 raise AssertionError('Unexpected config policy %r' % policy) 851 else: 852 return None 853 854 def _log_format(self): 855 """See Config.log_format.""" 856 return self._get_user_option('log_format') 857 858 def _validate_signatures_in_log(self): 859 """See Config.validate_signatures_in_log.""" 860 return self._get_user_option('validate_signatures_in_log') 861 862 def _acceptable_keys(self): 863 """See Config.acceptable_keys.""" 864 return self._get_user_option('acceptable_keys') 865 866 def _post_commit(self): 867 """See Config.post_commit.""" 868 return self._get_user_option('post_commit') 869 870 def _get_alias(self, value): 871 try: 872 return self._get_parser().get_value("ALIASES", 873 value) 874 except KeyError: 875 pass 876 877 def _get_nickname(self): 878 return self.get_user_option('nickname') 879 880 def remove_user_option(self, option_name, section_name=None): 881 """Remove a user option and save the configuration file. 882 883 :param option_name: The option to be removed. 884 885 :param section_name: The section the option is defined in, default to 886 the default section. 887 """ 888 self.reload() 889 parser = self._get_parser() 890 if section_name is None: 891 section = parser 892 else: 893 section = parser[section_name] 894 try: 895 del section[option_name] 896 except KeyError: 897 raise NoSuchConfigOption(option_name) 898 self._write_config_file() 899 for hook in OldConfigHooks['remove']: 900 hook(self, option_name) 901 902 def _write_config_file(self): 903 if self.file_name is None: 904 raise AssertionError('We cannot save, self.file_name is None') 905 from . import atomicfile 906 conf_dir = os.path.dirname(self.file_name) 907 bedding.ensure_config_dir_exists(conf_dir) 908 with atomicfile.AtomicFile(self.file_name) as atomic_file: 909 self._get_parser().write(atomic_file) 910 osutils.copy_ownership_from_path(self.file_name) 911 for hook in OldConfigHooks['save']: 912 hook(self) 913 914 915class LockableConfig(IniBasedConfig): 916 """A configuration needing explicit locking for access. 917 918 If several processes try to write the config file, the accesses need to be 919 serialized. 920 921 Daughter classes should use the self.lock_write() decorator method when 922 they upate a config (they call, directly or indirectly, the 923 ``_write_config_file()`` method. These methods (typically ``set_option()`` 924 and variants must reload the config file from disk before calling 925 ``_write_config_file()``), this can be achieved by calling the 926 ``self.reload()`` method. Note that the lock scope should cover both the 927 reading and the writing of the config file which is why the decorator can't 928 be applied to ``_write_config_file()`` only. 929 930 This should be enough to implement the following logic: 931 - lock for exclusive write access, 932 - reload the config file from disk, 933 - set the new value 934 - unlock 935 936 This logic guarantees that a writer can update a value without erasing an 937 update made by another writer. 938 """ 939 940 lock_name = 'lock' 941 942 def __init__(self, file_name): 943 super(LockableConfig, self).__init__(file_name=file_name) 944 self.dir = osutils.dirname(osutils.safe_unicode(self.file_name)) 945 # FIXME: It doesn't matter that we don't provide possible_transports 946 # below since this is currently used only for local config files ; 947 # local transports are not shared. But if/when we start using 948 # LockableConfig for other kind of transports, we will need to reuse 949 # whatever connection is already established -- vila 20100929 950 self.transport = transport.get_transport_from_path(self.dir) 951 self._lock = lockdir.LockDir(self.transport, self.lock_name) 952 953 def _create_from_string(self, unicode_bytes, save): 954 super(LockableConfig, self)._create_from_string(unicode_bytes, False) 955 if save: 956 # We need to handle the saving here (as opposed to IniBasedConfig) 957 # to be able to lock 958 self.lock_write() 959 self._write_config_file() 960 self.unlock() 961 962 def lock_write(self, token=None): 963 """Takes a write lock in the directory containing the config file. 964 965 If the directory doesn't exist it is created. 966 """ 967 bedding.ensure_config_dir_exists(self.dir) 968 token = self._lock.lock_write(token) 969 return lock.LogicalLockResult(self.unlock, token) 970 971 def unlock(self): 972 self._lock.unlock() 973 974 def break_lock(self): 975 self._lock.break_lock() 976 977 def remove_user_option(self, option_name, section_name=None): 978 with self.lock_write(): 979 super(LockableConfig, self).remove_user_option( 980 option_name, section_name) 981 982 def _write_config_file(self): 983 if self._lock is None or not self._lock.is_held: 984 # NB: if the following exception is raised it probably means a 985 # missing call to lock_write() by one of the callers. 986 raise errors.ObjectNotLocked(self) 987 super(LockableConfig, self)._write_config_file() 988 989 990class GlobalConfig(LockableConfig): 991 """The configuration that should be used for a specific location.""" 992 993 def __init__(self): 994 super(GlobalConfig, self).__init__(file_name=bedding.config_path()) 995 996 def config_id(self): 997 return 'breezy' 998 999 @classmethod 1000 def from_string(cls, str_or_unicode, save=False): 1001 """Create a config object from a string. 1002 1003 :param str_or_unicode: A string representing the file content. This 1004 will be utf-8 encoded. 1005 1006 :param save: Whether the file should be saved upon creation. 1007 """ 1008 conf = cls() 1009 conf._create_from_string(str_or_unicode, save) 1010 return conf 1011 1012 def set_user_option(self, option, value): 1013 """Save option and its value in the configuration.""" 1014 with self.lock_write(): 1015 self._set_option(option, value, 'DEFAULT') 1016 1017 def get_aliases(self): 1018 """Return the aliases section.""" 1019 if 'ALIASES' in self._get_parser(): 1020 return self._get_parser()['ALIASES'] 1021 else: 1022 return {} 1023 1024 def set_alias(self, alias_name, alias_command): 1025 """Save the alias in the configuration.""" 1026 with self.lock_write(): 1027 self._set_option(alias_name, alias_command, 'ALIASES') 1028 1029 def unset_alias(self, alias_name): 1030 """Unset an existing alias.""" 1031 with self.lock_write(): 1032 self.reload() 1033 aliases = self._get_parser().get('ALIASES') 1034 if not aliases or alias_name not in aliases: 1035 raise errors.NoSuchAlias(alias_name) 1036 del aliases[alias_name] 1037 self._write_config_file() 1038 1039 def _set_option(self, option, value, section): 1040 self.reload() 1041 self._get_parser().setdefault(section, {})[option] = value 1042 self._write_config_file() 1043 for hook in OldConfigHooks['set']: 1044 hook(self, option, value) 1045 1046 def _get_sections(self, name=None): 1047 """See IniBasedConfig._get_sections().""" 1048 parser = self._get_parser() 1049 # We don't give access to options defined outside of any section, we 1050 # used the DEFAULT section by... default. 1051 if name in (None, 'DEFAULT'): 1052 # This could happen for an empty file where the DEFAULT section 1053 # doesn't exist yet. So we force DEFAULT when yielding 1054 name = 'DEFAULT' 1055 if 'DEFAULT' not in parser: 1056 parser['DEFAULT'] = {} 1057 yield (name, parser[name], self.config_id()) 1058 1059 def remove_user_option(self, option_name, section_name=None): 1060 if section_name is None: 1061 # We need to force the default section. 1062 section_name = 'DEFAULT' 1063 with self.lock_write(): 1064 # We need to avoid the LockableConfig implementation or we'll lock 1065 # twice 1066 super(LockableConfig, self).remove_user_option( 1067 option_name, section_name) 1068 1069 1070def _iter_for_location_by_parts(sections, location): 1071 """Keep only the sessions matching the specified location. 1072 1073 :param sections: An iterable of section names. 1074 1075 :param location: An url or a local path to match against. 1076 1077 :returns: An iterator of (section, extra_path, nb_parts) where nb is the 1078 number of path components in the section name, section is the section 1079 name and extra_path is the difference between location and the section 1080 name. 1081 1082 ``location`` will always be a local path and never a 'file://' url but the 1083 section names themselves can be in either form. 1084 """ 1085 location_parts = location.rstrip('/').split('/') 1086 1087 for section in sections: 1088 # location is a local path if possible, so we need to convert 'file://' 1089 # urls in section names to local paths if necessary. 1090 1091 # This also avoids having file:///path be a more exact 1092 # match than '/path'. 1093 1094 # FIXME: This still raises an issue if a user defines both file:///path 1095 # *and* /path. Should we raise an error in this case -- vila 20110505 1096 1097 if section.startswith('file://'): 1098 section_path = urlutils.local_path_from_url(section) 1099 else: 1100 section_path = section 1101 section_parts = section_path.rstrip('/').split('/') 1102 1103 matched = True 1104 if len(section_parts) > len(location_parts): 1105 # More path components in the section, they can't match 1106 matched = False 1107 else: 1108 # Rely on zip truncating in length to the length of the shortest 1109 # argument sequence. 1110 for name in zip(location_parts, section_parts): 1111 if not fnmatch.fnmatch(name[0], name[1]): 1112 matched = False 1113 break 1114 if not matched: 1115 continue 1116 # build the path difference between the section and the location 1117 extra_path = '/'.join(location_parts[len(section_parts):]) 1118 yield section, extra_path, len(section_parts) 1119 1120 1121class LocationConfig(LockableConfig): 1122 """A configuration object that gives the policy for a location.""" 1123 1124 def __init__(self, location): 1125 super(LocationConfig, self).__init__( 1126 file_name=bedding.locations_config_path()) 1127 # local file locations are looked up by local path, rather than 1128 # by file url. This is because the config file is a user 1129 # file, and we would rather not expose the user to file urls. 1130 if location.startswith('file://'): 1131 location = urlutils.local_path_from_url(location) 1132 self.location = location 1133 1134 def config_id(self): 1135 return 'locations' 1136 1137 @classmethod 1138 def from_string(cls, str_or_unicode, location, save=False): 1139 """Create a config object from a string. 1140 1141 :param str_or_unicode: A string representing the file content. This will 1142 be utf-8 encoded. 1143 1144 :param location: The location url to filter the configuration. 1145 1146 :param save: Whether the file should be saved upon creation. 1147 """ 1148 conf = cls(location) 1149 conf._create_from_string(str_or_unicode, save) 1150 return conf 1151 1152 def _get_matching_sections(self): 1153 """Return an ordered list of section names matching this location.""" 1154 # put the longest (aka more specific) locations first 1155 matches = sorted( 1156 _iter_for_location_by_parts(self._get_parser(), self.location), 1157 key=lambda match: (match[2], match[0]), 1158 reverse=True) 1159 for (section, extra_path, length) in matches: 1160 yield section, extra_path 1161 # should we stop looking for parent configs here? 1162 try: 1163 if self._get_parser()[section].as_bool('ignore_parents'): 1164 break 1165 except KeyError: 1166 pass 1167 1168 def _get_sections(self, name=None): 1169 """See IniBasedConfig._get_sections().""" 1170 # We ignore the name here as the only sections handled are named with 1171 # the location path and we don't expose embedded sections either. 1172 parser = self._get_parser() 1173 for name, extra_path in self._get_matching_sections(): 1174 yield (name, parser[name], self.config_id()) 1175 1176 def _get_option_policy(self, section, option_name): 1177 """Return the policy for the given (section, option_name) pair.""" 1178 # check for the old 'recurse=False' flag 1179 try: 1180 recurse = self._get_parser()[section].as_bool('recurse') 1181 except KeyError: 1182 recurse = True 1183 if not recurse: 1184 return POLICY_NORECURSE 1185 1186 policy_key = option_name + ':policy' 1187 try: 1188 policy_name = self._get_parser()[section][policy_key] 1189 except KeyError: 1190 policy_name = None 1191 1192 return _policy_value[policy_name] 1193 1194 def _set_option_policy(self, section, option_name, option_policy): 1195 """Set the policy for the given option name in the given section.""" 1196 policy_key = option_name + ':policy' 1197 policy_name = _policy_name[option_policy] 1198 if policy_name is not None: 1199 self._get_parser()[section][policy_key] = policy_name 1200 else: 1201 if policy_key in self._get_parser()[section]: 1202 del self._get_parser()[section][policy_key] 1203 1204 def set_user_option(self, option, value, store=STORE_LOCATION): 1205 """Save option and its value in the configuration.""" 1206 if store not in [STORE_LOCATION, 1207 STORE_LOCATION_NORECURSE, 1208 STORE_LOCATION_APPENDPATH]: 1209 raise ValueError('bad storage policy %r for %r' % 1210 (store, option)) 1211 with self.lock_write(): 1212 self.reload() 1213 location = self.location 1214 if location.endswith('/'): 1215 location = location[:-1] 1216 parser = self._get_parser() 1217 if location not in parser and not location + '/' in parser: 1218 parser[location] = {} 1219 elif location + '/' in parser: 1220 location = location + '/' 1221 parser[location][option] = value 1222 # the allowed values of store match the config policies 1223 self._set_option_policy(location, option, store) 1224 self._write_config_file() 1225 for hook in OldConfigHooks['set']: 1226 hook(self, option, value) 1227 1228 1229class BranchConfig(Config): 1230 """A configuration object giving the policy for a branch.""" 1231 1232 def __init__(self, branch): 1233 super(BranchConfig, self).__init__() 1234 self._location_config = None 1235 self._branch_data_config = None 1236 self._global_config = None 1237 self.branch = branch 1238 self.option_sources = (self._get_location_config, 1239 self._get_branch_data_config, 1240 self._get_global_config) 1241 1242 def config_id(self): 1243 return 'branch' 1244 1245 def _get_branch_data_config(self): 1246 if self._branch_data_config is None: 1247 self._branch_data_config = TreeConfig(self.branch) 1248 self._branch_data_config.config_id = self.config_id 1249 return self._branch_data_config 1250 1251 def _get_location_config(self): 1252 if self._location_config is None: 1253 if self.branch.base is None: 1254 self.branch.base = 'memory://' 1255 self._location_config = LocationConfig(self.branch.base) 1256 return self._location_config 1257 1258 def _get_global_config(self): 1259 if self._global_config is None: 1260 self._global_config = GlobalConfig() 1261 return self._global_config 1262 1263 def _get_best_value(self, option_name): 1264 """This returns a user option from local, tree or global config. 1265 1266 They are tried in that order. Use get_safe_value if trusted values 1267 are necessary. 1268 """ 1269 for source in self.option_sources: 1270 value = getattr(source(), option_name)() 1271 if value is not None: 1272 return value 1273 return None 1274 1275 def _get_safe_value(self, option_name): 1276 """This variant of get_best_value never returns untrusted values. 1277 1278 It does not return values from the branch data, because the branch may 1279 not be controlled by the user. 1280 1281 We may wish to allow locations.conf to control whether branches are 1282 trusted in the future. 1283 """ 1284 for source in (self._get_location_config, self._get_global_config): 1285 value = getattr(source(), option_name)() 1286 if value is not None: 1287 return value 1288 return None 1289 1290 def _get_user_id(self): 1291 """Return the full user id for the branch. 1292 1293 e.g. "John Hacker <jhacker@example.com>" 1294 This is looked up in the email controlfile for the branch. 1295 """ 1296 return self._get_best_value('_get_user_id') 1297 1298 def _get_change_editor(self): 1299 return self._get_best_value('_get_change_editor') 1300 1301 def _get_signature_checking(self): 1302 """See Config._get_signature_checking.""" 1303 return self._get_best_value('_get_signature_checking') 1304 1305 def _get_signing_policy(self): 1306 """See Config._get_signing_policy.""" 1307 return self._get_best_value('_get_signing_policy') 1308 1309 def _get_user_option(self, option_name): 1310 """See Config._get_user_option.""" 1311 for source in self.option_sources: 1312 value = source()._get_user_option(option_name) 1313 if value is not None: 1314 return value 1315 return None 1316 1317 def _get_sections(self, name=None): 1318 """See IniBasedConfig.get_sections().""" 1319 for source in self.option_sources: 1320 for section in source()._get_sections(name): 1321 yield section 1322 1323 def _get_options(self, sections=None): 1324 # First the locations options 1325 for option in self._get_location_config()._get_options(): 1326 yield option 1327 # Then the branch options 1328 branch_config = self._get_branch_data_config() 1329 if sections is None: 1330 sections = [('DEFAULT', branch_config._get_parser())] 1331 # FIXME: We shouldn't have to duplicate the code in IniBasedConfig but 1332 # Config itself has no notion of sections :( -- vila 20101001 1333 config_id = self.config_id() 1334 for (section_name, section) in sections: 1335 for (name, value) in section.iteritems(): 1336 yield (name, value, section_name, 1337 config_id, branch_config._get_parser()) 1338 # Then the global options 1339 for option in self._get_global_config()._get_options(): 1340 yield option 1341 1342 def set_user_option(self, name, value, store=STORE_BRANCH, 1343 warn_masked=False): 1344 if store == STORE_BRANCH: 1345 self._get_branch_data_config().set_option(value, name) 1346 elif store == STORE_GLOBAL: 1347 self._get_global_config().set_user_option(name, value) 1348 else: 1349 self._get_location_config().set_user_option(name, value, store) 1350 if not warn_masked: 1351 return 1352 if store in (STORE_GLOBAL, STORE_BRANCH): 1353 mask_value = self._get_location_config().get_user_option(name) 1354 if mask_value is not None: 1355 trace.warning('Value "%s" is masked by "%s" from' 1356 ' locations.conf', value, mask_value) 1357 else: 1358 if store == STORE_GLOBAL: 1359 branch_config = self._get_branch_data_config() 1360 mask_value = branch_config.get_user_option(name) 1361 if mask_value is not None: 1362 trace.warning('Value "%s" is masked by "%s" from' 1363 ' branch.conf', value, mask_value) 1364 1365 def remove_user_option(self, option_name, section_name=None): 1366 self._get_branch_data_config().remove_option(option_name, section_name) 1367 1368 def _post_commit(self): 1369 """See Config.post_commit.""" 1370 return self._get_safe_value('_post_commit') 1371 1372 def _get_nickname(self): 1373 value = self._get_explicit_nickname() 1374 if value is not None: 1375 return value 1376 if self.branch.name: 1377 return self.branch.name 1378 return urlutils.unescape(self.branch.base.split('/')[-2]) 1379 1380 def has_explicit_nickname(self): 1381 """Return true if a nickname has been explicitly assigned.""" 1382 return self._get_explicit_nickname() is not None 1383 1384 def _get_explicit_nickname(self): 1385 return self._get_best_value('_get_nickname') 1386 1387 def _log_format(self): 1388 """See Config.log_format.""" 1389 return self._get_best_value('_log_format') 1390 1391 def _validate_signatures_in_log(self): 1392 """See Config.validate_signatures_in_log.""" 1393 return self._get_best_value('_validate_signatures_in_log') 1394 1395 def _acceptable_keys(self): 1396 """See Config.acceptable_keys.""" 1397 return self._get_best_value('_acceptable_keys') 1398 1399 1400def parse_username(username): 1401 """Parse e-mail username and return a (name, address) tuple.""" 1402 match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username) 1403 if match is None: 1404 return (username, '') 1405 return (match.group(1), match.group(2)) 1406 1407 1408def extract_email_address(e): 1409 """Return just the address part of an email string. 1410 1411 That is just the user@domain part, nothing else. 1412 This part is required to contain only ascii characters. 1413 If it can't be extracted, raises an error. 1414 1415 >>> extract_email_address('Jane Tester <jane@test.com>') 1416 "jane@test.com" 1417 """ 1418 name, email = parse_username(e) 1419 if not email: 1420 raise NoEmailInUsername(e) 1421 return email 1422 1423 1424class TreeConfig(IniBasedConfig): 1425 """Branch configuration data associated with its contents, not location""" 1426 1427 # XXX: Really needs a better name, as this is not part of the tree! 1428 # -- mbp 20080507 1429 1430 def __init__(self, branch): 1431 self._config = branch._get_config() 1432 self.branch = branch 1433 1434 def _get_parser(self, file=None): 1435 if file is not None: 1436 return IniBasedConfig._get_parser(file) 1437 return self._config._get_configobj() 1438 1439 def get_option(self, name, section=None, default=None): 1440 with self.branch.lock_read(): 1441 return self._config.get_option(name, section, default) 1442 1443 def set_option(self, value, name, section=None): 1444 """Set a per-branch configuration option""" 1445 # FIXME: We shouldn't need to lock explicitly here but rather rely on 1446 # higher levels providing the right lock -- vila 20101004 1447 with self.branch.lock_write(): 1448 self._config.set_option(value, name, section) 1449 1450 def remove_option(self, option_name, section_name=None): 1451 # FIXME: We shouldn't need to lock explicitly here but rather rely on 1452 # higher levels providing the right lock -- vila 20101004 1453 with self.branch.lock_write(): 1454 self._config.remove_option(option_name, section_name) 1455 1456 1457_authentication_config_permission_errors = set() 1458 1459 1460class AuthenticationConfig(object): 1461 """The authentication configuration file based on a ini file. 1462 1463 Implements the authentication.conf file described in 1464 doc/developers/authentication-ring.txt. 1465 """ 1466 1467 def __init__(self, _file=None): 1468 self._config = None # The ConfigObj 1469 if _file is None: 1470 self._input = self._filename = bedding.authentication_config_path() 1471 self._check_permissions() 1472 else: 1473 # Tests can provide a string as _file 1474 self._filename = None 1475 self._input = _file 1476 1477 def _get_config(self): 1478 if self._config is not None: 1479 return self._config 1480 try: 1481 # FIXME: Should we validate something here ? Includes: empty 1482 # sections are useless, at least one of 1483 # user/password/password_encoding should be defined, etc. 1484 1485 # Note: the encoding below declares that the file itself is utf-8 1486 # encoded, but the values in the ConfigObj are always Unicode. 1487 self._config = ConfigObj(self._input, encoding='utf-8') 1488 except configobj.ConfigObjError as e: 1489 raise ParseConfigError(e.errors, e.config.filename) 1490 except UnicodeError: 1491 raise ConfigContentError(self._filename) 1492 return self._config 1493 1494 def _check_permissions(self): 1495 """Check permission of auth file are user read/write able only.""" 1496 try: 1497 st = os.stat(self._filename) 1498 except OSError as e: 1499 if e.errno != errno.ENOENT: 1500 trace.mutter('Unable to stat %r: %r', self._filename, e) 1501 return 1502 mode = stat.S_IMODE(st.st_mode) 1503 if ((stat.S_IXOTH | stat.S_IWOTH | stat.S_IROTH | stat.S_IXGRP 1504 | stat.S_IWGRP | stat.S_IRGRP) & mode): 1505 # Only warn once 1506 if (self._filename not in _authentication_config_permission_errors and 1507 not GlobalConfig().suppress_warning( 1508 'insecure_permissions')): 1509 trace.warning("The file '%s' has insecure " 1510 "file permissions. Saved passwords may be accessible " 1511 "by other users.", self._filename) 1512 _authentication_config_permission_errors.add(self._filename) 1513 1514 def _save(self): 1515 """Save the config file, only tests should use it for now.""" 1516 conf_dir = os.path.dirname(self._filename) 1517 bedding.ensure_config_dir_exists(conf_dir) 1518 fd = os.open(self._filename, os.O_RDWR | os.O_CREAT, 0o600) 1519 try: 1520 f = os.fdopen(fd, 'wb') 1521 self._get_config().write(f) 1522 finally: 1523 f.close() 1524 1525 def _set_option(self, section_name, option_name, value): 1526 """Set an authentication configuration option""" 1527 conf = self._get_config() 1528 section = conf.get(section_name) 1529 if section is None: 1530 conf[section] = {} 1531 section = conf[section] 1532 section[option_name] = value 1533 self._save() 1534 1535 def get_credentials(self, scheme, host, port=None, user=None, path=None, 1536 realm=None): 1537 """Returns the matching credentials from authentication.conf file. 1538 1539 :param scheme: protocol 1540 1541 :param host: the server address 1542 1543 :param port: the associated port (optional) 1544 1545 :param user: login (optional) 1546 1547 :param path: the absolute path on the server (optional) 1548 1549 :param realm: the http authentication realm (optional) 1550 1551 :return: A dict containing the matching credentials or None. 1552 This includes: 1553 - name: the section name of the credentials in the 1554 authentication.conf file, 1555 - user: can't be different from the provided user if any, 1556 - scheme: the server protocol, 1557 - host: the server address, 1558 - port: the server port (can be None), 1559 - path: the absolute server path (can be None), 1560 - realm: the http specific authentication realm (can be None), 1561 - password: the decoded password, could be None if the credential 1562 defines only the user 1563 - verify_certificates: https specific, True if the server 1564 certificate should be verified, False otherwise. 1565 """ 1566 credentials = None 1567 for auth_def_name, auth_def in self._get_config().iteritems(): 1568 if not isinstance(auth_def, configobj.Section): 1569 raise ValueError("%s defined outside a section" % 1570 auth_def_name) 1571 1572 a_scheme, a_host, a_user, a_path = map( 1573 auth_def.get, ['scheme', 'host', 'user', 'path']) 1574 1575 try: 1576 a_port = auth_def.as_int('port') 1577 except KeyError: 1578 a_port = None 1579 except ValueError: 1580 raise ValueError("'port' not numeric in %s" % auth_def_name) 1581 try: 1582 a_verify_certificates = auth_def.as_bool('verify_certificates') 1583 except KeyError: 1584 a_verify_certificates = True 1585 except ValueError: 1586 raise ValueError( 1587 "'verify_certificates' not boolean in %s" % auth_def_name) 1588 1589 # Attempt matching 1590 if a_scheme is not None and scheme != a_scheme: 1591 continue 1592 if a_host is not None: 1593 if not (host == a_host or 1594 (a_host.startswith('.') and host.endswith(a_host))): 1595 continue 1596 if a_port is not None and port != a_port: 1597 continue 1598 if (a_path is not None and path is not None and 1599 not path.startswith(a_path)): 1600 continue 1601 if (a_user is not None and user is not None and 1602 a_user != user): 1603 # Never contradict the caller about the user to be used 1604 continue 1605 if a_user is None: 1606 # Can't find a user 1607 continue 1608 # Prepare a credentials dictionary with additional keys 1609 # for the credential providers 1610 credentials = dict(name=auth_def_name, 1611 user=a_user, 1612 scheme=a_scheme, 1613 host=host, 1614 port=port, 1615 path=path, 1616 realm=realm, 1617 password=auth_def.get('password', None), 1618 verify_certificates=a_verify_certificates) 1619 # Decode the password in the credentials (or get one) 1620 self.decode_password(credentials, 1621 auth_def.get('password_encoding', None)) 1622 if 'auth' in debug.debug_flags: 1623 trace.mutter("Using authentication section: %r", auth_def_name) 1624 break 1625 1626 if credentials is None: 1627 # No credentials were found in authentication.conf, try the fallback 1628 # credentials stores. 1629 credentials = credential_store_registry.get_fallback_credentials( 1630 scheme, host, port, user, path, realm) 1631 1632 return credentials 1633 1634 def set_credentials(self, name, host, user, scheme=None, password=None, 1635 port=None, path=None, verify_certificates=None, 1636 realm=None): 1637 """Set authentication credentials for a host. 1638 1639 Any existing credentials with matching scheme, host, port and path 1640 will be deleted, regardless of name. 1641 1642 :param name: An arbitrary name to describe this set of credentials. 1643 :param host: Name of the host that accepts these credentials. 1644 :param user: The username portion of these credentials. 1645 :param scheme: The URL scheme (e.g. ssh, http) the credentials apply 1646 to. 1647 :param password: Password portion of these credentials. 1648 :param port: The IP port on the host that these credentials apply to. 1649 :param path: A filesystem path on the host that these credentials 1650 apply to. 1651 :param verify_certificates: On https, verify server certificates if 1652 True. 1653 :param realm: The http authentication realm (optional). 1654 """ 1655 values = {'host': host, 'user': user} 1656 if password is not None: 1657 values['password'] = password 1658 if scheme is not None: 1659 values['scheme'] = scheme 1660 if port is not None: 1661 values['port'] = '%d' % port 1662 if path is not None: 1663 values['path'] = path 1664 if verify_certificates is not None: 1665 values['verify_certificates'] = str(verify_certificates) 1666 if realm is not None: 1667 values['realm'] = realm 1668 config = self._get_config() 1669 for section, existing_values in config.iteritems(): 1670 for key in ('scheme', 'host', 'port', 'path', 'realm'): 1671 if existing_values.get(key) != values.get(key): 1672 break 1673 else: 1674 del config[section] 1675 config.update({name: values}) 1676 self._save() 1677 1678 def get_user(self, scheme, host, port=None, realm=None, path=None, 1679 prompt=None, ask=False, default=None): 1680 """Get a user from authentication file. 1681 1682 :param scheme: protocol 1683 1684 :param host: the server address 1685 1686 :param port: the associated port (optional) 1687 1688 :param realm: the realm sent by the server (optional) 1689 1690 :param path: the absolute path on the server (optional) 1691 1692 :param ask: Ask the user if there is no explicitly configured username 1693 (optional) 1694 1695 :param default: The username returned if none is defined (optional). 1696 1697 :return: The found user. 1698 """ 1699 credentials = self.get_credentials(scheme, host, port, user=None, 1700 path=path, realm=realm) 1701 if credentials is not None: 1702 user = credentials['user'] 1703 else: 1704 user = None 1705 if user is None: 1706 if ask: 1707 if prompt is None: 1708 # Create a default prompt suitable for most cases 1709 prompt = u'%s' % (scheme.upper(),) + u' %(host)s username' 1710 # Special handling for optional fields in the prompt 1711 if port is not None: 1712 prompt_host = '%s:%d' % (host, port) 1713 else: 1714 prompt_host = host 1715 user = ui.ui_factory.get_username(prompt, host=prompt_host) 1716 else: 1717 user = default 1718 return user 1719 1720 def get_password(self, scheme, host, user, port=None, 1721 realm=None, path=None, prompt=None): 1722 """Get a password from authentication file or prompt the user for one. 1723 1724 :param scheme: protocol 1725 1726 :param host: the server address 1727 1728 :param port: the associated port (optional) 1729 1730 :param user: login 1731 1732 :param realm: the realm sent by the server (optional) 1733 1734 :param path: the absolute path on the server (optional) 1735 1736 :return: The found password or the one entered by the user. 1737 """ 1738 credentials = self.get_credentials(scheme, host, port, user, path, 1739 realm) 1740 if credentials is not None: 1741 password = credentials['password'] 1742 if password is not None and scheme == 'ssh': 1743 trace.warning('password ignored in section [%s],' 1744 ' use an ssh agent instead' 1745 % credentials['name']) 1746 password = None 1747 else: 1748 password = None 1749 # Prompt user only if we could't find a password 1750 if password is None: 1751 if prompt is None: 1752 # Create a default prompt suitable for most cases 1753 prompt = (u'%s' % 1754 scheme.upper() + u' %(user)s@%(host)s password') 1755 # Special handling for optional fields in the prompt 1756 if port is not None: 1757 prompt_host = '%s:%d' % (host, port) 1758 else: 1759 prompt_host = host 1760 password = ui.ui_factory.get_password(prompt, 1761 host=prompt_host, user=user) 1762 return password 1763 1764 def decode_password(self, credentials, encoding): 1765 try: 1766 cs = credential_store_registry.get_credential_store(encoding) 1767 except KeyError: 1768 raise ValueError('%r is not a known password_encoding' % encoding) 1769 credentials['password'] = cs.decode_password(credentials) 1770 return credentials 1771 1772 1773class CredentialStoreRegistry(registry.Registry): 1774 """A class that registers credential stores. 1775 1776 A credential store provides access to credentials via the password_encoding 1777 field in authentication.conf sections. 1778 1779 Except for stores provided by brz itself, most stores are expected to be 1780 provided by plugins that will therefore use 1781 register_lazy(password_encoding, module_name, member_name, help=help, 1782 fallback=fallback) to install themselves. 1783 1784 A fallback credential store is one that is queried if no credentials can be 1785 found via authentication.conf. 1786 """ 1787 1788 def get_credential_store(self, encoding=None): 1789 cs = self.get(encoding) 1790 if callable(cs): 1791 cs = cs() 1792 return cs 1793 1794 def is_fallback(self, name): 1795 """Check if the named credentials store should be used as fallback.""" 1796 return self.get_info(name) 1797 1798 def get_fallback_credentials(self, scheme, host, port=None, user=None, 1799 path=None, realm=None): 1800 """Request credentials from all fallback credentials stores. 1801 1802 The first credentials store that can provide credentials wins. 1803 """ 1804 credentials = None 1805 for name in self.keys(): 1806 if not self.is_fallback(name): 1807 continue 1808 cs = self.get_credential_store(name) 1809 credentials = cs.get_credentials(scheme, host, port, user, 1810 path, realm) 1811 if credentials is not None: 1812 # We found some credentials 1813 break 1814 return credentials 1815 1816 def register(self, key, obj, help=None, override_existing=False, 1817 fallback=False): 1818 """Register a new object to a name. 1819 1820 :param key: This is the key to use to request the object later. 1821 :param obj: The object to register. 1822 :param help: Help text for this entry. This may be a string or 1823 a callable. If it is a callable, it should take two 1824 parameters (registry, key): this registry and the key that 1825 the help was registered under. 1826 :param override_existing: Raise KeyErorr if False and something has 1827 already been registered for that key. If True, ignore if there 1828 is an existing key (always register the new value). 1829 :param fallback: Whether this credential store should be 1830 used as fallback. 1831 """ 1832 return super(CredentialStoreRegistry, 1833 self).register(key, obj, help, info=fallback, 1834 override_existing=override_existing) 1835 1836 def register_lazy(self, key, module_name, member_name, 1837 help=None, override_existing=False, 1838 fallback=False): 1839 """Register a new credential store to be loaded on request. 1840 1841 :param module_name: The python path to the module. Such as 'os.path'. 1842 :param member_name: The member of the module to return. If empty or 1843 None, get() will return the module itself. 1844 :param help: Help text for this entry. This may be a string or 1845 a callable. 1846 :param override_existing: If True, replace the existing object 1847 with the new one. If False, if there is already something 1848 registered with the same key, raise a KeyError 1849 :param fallback: Whether this credential store should be 1850 used as fallback. 1851 """ 1852 return super(CredentialStoreRegistry, self).register_lazy( 1853 key, module_name, member_name, help, 1854 info=fallback, override_existing=override_existing) 1855 1856 1857credential_store_registry = CredentialStoreRegistry() 1858 1859 1860class CredentialStore(object): 1861 """An abstract class to implement storage for credentials""" 1862 1863 def decode_password(self, credentials): 1864 """Returns a clear text password for the provided credentials.""" 1865 raise NotImplementedError(self.decode_password) 1866 1867 def get_credentials(self, scheme, host, port=None, user=None, path=None, 1868 realm=None): 1869 """Return the matching credentials from this credential store. 1870 1871 This method is only called on fallback credential stores. 1872 """ 1873 raise NotImplementedError(self.get_credentials) 1874 1875 1876class PlainTextCredentialStore(CredentialStore): 1877 __doc__ = """Plain text credential store for the authentication.conf file""" 1878 1879 def decode_password(self, credentials): 1880 """See CredentialStore.decode_password.""" 1881 return credentials['password'] 1882 1883 1884credential_store_registry.register('plain', PlainTextCredentialStore, 1885 help=PlainTextCredentialStore.__doc__) 1886credential_store_registry.default_key = 'plain' 1887 1888 1889class Base64CredentialStore(CredentialStore): 1890 __doc__ = """Base64 credential store for the authentication.conf file""" 1891 1892 def decode_password(self, credentials): 1893 """See CredentialStore.decode_password.""" 1894 # GZ 2012-07-28: Will raise binascii.Error if password is not base64, 1895 # should probably propogate as something more useful. 1896 return base64.standard_b64decode(credentials['password']) 1897 1898 1899credential_store_registry.register('base64', Base64CredentialStore, 1900 help=Base64CredentialStore.__doc__) 1901 1902 1903class BzrDirConfig(object): 1904 1905 def __init__(self, bzrdir): 1906 self._bzrdir = bzrdir 1907 self._config = bzrdir._get_config() 1908 1909 def set_default_stack_on(self, value): 1910 """Set the default stacking location. 1911 1912 It may be set to a location, or None. 1913 1914 This policy affects all branches contained by this control dir, except 1915 for those under repositories. 1916 """ 1917 if self._config is None: 1918 raise errors.BzrError("Cannot set configuration in %s" 1919 % self._bzrdir) 1920 if value is None: 1921 self._config.set_option('', 'default_stack_on') 1922 else: 1923 self._config.set_option(value, 'default_stack_on') 1924 1925 def get_default_stack_on(self): 1926 """Return the default stacking location. 1927 1928 This will either be a location, or None. 1929 1930 This policy affects all branches contained by this control dir, except 1931 for those under repositories. 1932 """ 1933 if self._config is None: 1934 return None 1935 value = self._config.get_option('default_stack_on') 1936 if value == '': 1937 value = None 1938 return value 1939 1940 1941class TransportConfig(object): 1942 """A Config that reads/writes a config file on a Transport. 1943 1944 It is a low-level object that considers config data to be name/value pairs 1945 that may be associated with a section. Assigning meaning to these values 1946 is done at higher levels like TreeConfig. 1947 """ 1948 1949 def __init__(self, transport, filename): 1950 self._transport = transport 1951 self._filename = filename 1952 1953 def get_option(self, name, section=None, default=None): 1954 """Return the value associated with a named option. 1955 1956 :param name: The name of the value 1957 :param section: The section the option is in (if any) 1958 :param default: The value to return if the value is not set 1959 :return: The value or default value 1960 """ 1961 configobj = self._get_configobj() 1962 if section is None: 1963 section_obj = configobj 1964 else: 1965 try: 1966 section_obj = configobj[section] 1967 except KeyError: 1968 return default 1969 value = section_obj.get(name, default) 1970 for hook in OldConfigHooks['get']: 1971 hook(self, name, value) 1972 return value 1973 1974 def set_option(self, value, name, section=None): 1975 """Set the value associated with a named option. 1976 1977 :param value: The value to set 1978 :param name: The name of the value to set 1979 :param section: The section the option is in (if any) 1980 """ 1981 configobj = self._get_configobj() 1982 if section is None: 1983 configobj[name] = value 1984 else: 1985 configobj.setdefault(section, {})[name] = value 1986 for hook in OldConfigHooks['set']: 1987 hook(self, name, value) 1988 self._set_configobj(configobj) 1989 1990 def remove_option(self, option_name, section_name=None): 1991 configobj = self._get_configobj() 1992 if section_name is None: 1993 del configobj[option_name] 1994 else: 1995 del configobj[section_name][option_name] 1996 for hook in OldConfigHooks['remove']: 1997 hook(self, option_name) 1998 self._set_configobj(configobj) 1999 2000 def _get_config_file(self): 2001 try: 2002 f = BytesIO(self._transport.get_bytes(self._filename)) 2003 for hook in OldConfigHooks['load']: 2004 hook(self) 2005 return f 2006 except errors.NoSuchFile: 2007 return BytesIO() 2008 except errors.PermissionDenied: 2009 trace.warning( 2010 "Permission denied while trying to open " 2011 "configuration file %s.", 2012 urlutils.unescape_for_display( 2013 urlutils.join(self._transport.base, self._filename), 2014 "utf-8")) 2015 return BytesIO() 2016 2017 def _external_url(self): 2018 return urlutils.join(self._transport.external_url(), self._filename) 2019 2020 def _get_configobj(self): 2021 f = self._get_config_file() 2022 try: 2023 try: 2024 conf = ConfigObj(f, encoding='utf-8') 2025 except configobj.ConfigObjError as e: 2026 raise ParseConfigError(e.errors, self._external_url()) 2027 except UnicodeDecodeError: 2028 raise ConfigContentError(self._external_url()) 2029 finally: 2030 f.close() 2031 return conf 2032 2033 def _set_configobj(self, configobj): 2034 out_file = BytesIO() 2035 configobj.write(out_file) 2036 out_file.seek(0) 2037 self._transport.put_file(self._filename, out_file) 2038 for hook in OldConfigHooks['save']: 2039 hook(self) 2040 2041 2042class Option(object): 2043 """An option definition. 2044 2045 The option *values* are stored in config files and found in sections. 2046 2047 Here we define various properties about the option itself, its default 2048 value, how to convert it from stores, what to do when invalid values are 2049 encoutered, in which config files it can be stored. 2050 """ 2051 2052 def __init__(self, name, override_from_env=None, 2053 default=None, default_from_env=None, 2054 help=None, from_unicode=None, invalid=None, unquote=True): 2055 """Build an option definition. 2056 2057 :param name: the name used to refer to the option. 2058 2059 :param override_from_env: A list of environment variables which can 2060 provide override any configuration setting. 2061 2062 :param default: the default value to use when none exist in the config 2063 stores. This is either a string that ``from_unicode`` will convert 2064 into the proper type, a callable returning a unicode string so that 2065 ``from_unicode`` can be used on the return value, or a python 2066 object that can be stringified (so only the empty list is supported 2067 for example). 2068 2069 :param default_from_env: A list of environment variables which can 2070 provide a default value. 'default' will be used only if none of the 2071 variables specified here are set in the environment. 2072 2073 :param help: a doc string to explain the option to the user. 2074 2075 :param from_unicode: a callable to convert the unicode string 2076 representing the option value in a store or its default value. 2077 2078 :param invalid: the action to be taken when an invalid value is 2079 encountered in a store. This is called only when from_unicode is 2080 invoked to convert a string and returns None or raise ValueError or 2081 TypeError. Accepted values are: None (ignore invalid values), 2082 'warning' (emit a warning), 'error' (emit an error message and 2083 terminates). 2084 2085 :param unquote: should the unicode value be unquoted before conversion. 2086 This should be used only when the store providing the values cannot 2087 safely unquote them (see http://pad.lv/906897). It is provided so 2088 daughter classes can handle the quoting themselves. 2089 """ 2090 if override_from_env is None: 2091 override_from_env = [] 2092 if default_from_env is None: 2093 default_from_env = [] 2094 self.name = name 2095 self.override_from_env = override_from_env 2096 # Convert the default value to a unicode string so all values are 2097 # strings internally before conversion (via from_unicode) is attempted. 2098 if default is None: 2099 self.default = None 2100 elif isinstance(default, list): 2101 # Only the empty list is supported 2102 if default: 2103 raise AssertionError( 2104 'Only empty lists are supported as default values') 2105 self.default = u',' 2106 elif isinstance(default, (bytes, str, bool, int, float)): 2107 # Rely on python to convert strings, booleans and integers 2108 self.default = u'%s' % (default,) 2109 elif callable(default): 2110 self.default = default 2111 else: 2112 # other python objects are not expected 2113 raise AssertionError('%r is not supported as a default value' 2114 % (default,)) 2115 self.default_from_env = default_from_env 2116 self._help = help 2117 self.from_unicode = from_unicode 2118 self.unquote = unquote 2119 if invalid and invalid not in ('warning', 'error'): 2120 raise AssertionError("%s not supported for 'invalid'" % (invalid,)) 2121 self.invalid = invalid 2122 2123 @property 2124 def help(self): 2125 return self._help 2126 2127 def convert_from_unicode(self, store, unicode_value): 2128 if self.unquote and store is not None and unicode_value is not None: 2129 unicode_value = store.unquote(unicode_value) 2130 if self.from_unicode is None or unicode_value is None: 2131 # Don't convert or nothing to convert 2132 return unicode_value 2133 try: 2134 converted = self.from_unicode(unicode_value) 2135 except (ValueError, TypeError): 2136 # Invalid values are ignored 2137 converted = None 2138 if converted is None and self.invalid is not None: 2139 # The conversion failed 2140 if self.invalid == 'warning': 2141 trace.warning('Value "%s" is not valid for "%s"', 2142 unicode_value, self.name) 2143 elif self.invalid == 'error': 2144 raise ConfigOptionValueError(self.name, unicode_value) 2145 return converted 2146 2147 def get_override(self): 2148 value = None 2149 for var in self.override_from_env: 2150 try: 2151 # If the env variable is defined, its value takes precedence 2152 value = os.environ[var] 2153 break 2154 except KeyError: 2155 continue 2156 return value 2157 2158 def get_default(self): 2159 value = None 2160 for var in self.default_from_env: 2161 try: 2162 # If the env variable is defined, its value is the default one 2163 value = os.environ[var] 2164 break 2165 except KeyError: 2166 continue 2167 if value is None: 2168 # Otherwise, fallback to the value defined at registration 2169 if callable(self.default): 2170 value = self.default() 2171 if not isinstance(value, str): 2172 raise AssertionError( 2173 "Callable default value for '%s' should be unicode" 2174 % (self.name)) 2175 else: 2176 value = self.default 2177 return value 2178 2179 def get_help_topic(self): 2180 return self.name 2181 2182 def get_help_text(self, additional_see_also=None, plain=True): 2183 result = self.help 2184 from breezy import help_topics 2185 result += help_topics._format_see_also(additional_see_also) 2186 if plain: 2187 result = help_topics.help_as_plain_text(result) 2188 return result 2189 2190 2191# Predefined converters to get proper values from store 2192 2193def bool_from_store(unicode_str): 2194 return ui.bool_from_string(unicode_str) 2195 2196 2197def int_from_store(unicode_str): 2198 return int(unicode_str) 2199 2200 2201_unit_suffixes = dict(K=10**3, M=10**6, G=10**9) 2202 2203 2204def int_SI_from_store(unicode_str): 2205 """Convert a human readable size in SI units, e.g 10MB into an integer. 2206 2207 Accepted suffixes are K,M,G. It is case-insensitive and may be followed 2208 by a trailing b (i.e. Kb, MB). This is intended to be practical and not 2209 pedantic. 2210 2211 :return Integer, expanded to its base-10 value if a proper SI unit is 2212 found, None otherwise. 2213 """ 2214 regexp = "^(\\d+)(([" + ''.join(_unit_suffixes) + "])b?)?$" 2215 p = re.compile(regexp, re.IGNORECASE) 2216 m = p.match(unicode_str) 2217 val = None 2218 if m is not None: 2219 val, _, unit = m.groups() 2220 val = int(val) 2221 if unit: 2222 try: 2223 coeff = _unit_suffixes[unit.upper()] 2224 except KeyError: 2225 raise ValueError( 2226 gettext('{0} is not an SI unit.').format(unit)) 2227 val *= coeff 2228 return val 2229 2230 2231def float_from_store(unicode_str): 2232 return float(unicode_str) 2233 2234 2235# Use an empty dict to initialize an empty configobj avoiding all parsing and 2236# encoding checks 2237_list_converter_config = configobj.ConfigObj( 2238 {}, encoding='utf-8', list_values=True, interpolation=False) 2239 2240 2241class ListOption(Option): 2242 2243 def __init__(self, name, default=None, default_from_env=None, 2244 help=None, invalid=None): 2245 """A list Option definition. 2246 2247 This overrides the base class so the conversion from a unicode string 2248 can take quoting into account. 2249 """ 2250 super(ListOption, self).__init__( 2251 name, default=default, default_from_env=default_from_env, 2252 from_unicode=self.from_unicode, help=help, 2253 invalid=invalid, unquote=False) 2254 2255 def from_unicode(self, unicode_str): 2256 if not isinstance(unicode_str, str): 2257 raise TypeError 2258 # Now inject our string directly as unicode. All callers got their 2259 # value from configobj, so values that need to be quoted are already 2260 # properly quoted. 2261 _list_converter_config.reset() 2262 _list_converter_config._parse([u"list=%s" % (unicode_str,)]) 2263 maybe_list = _list_converter_config['list'] 2264 if isinstance(maybe_list, str): 2265 if maybe_list: 2266 # A single value, most probably the user forgot (or didn't care 2267 # to add) the final ',' 2268 l = [maybe_list] 2269 else: 2270 # The empty string, convert to empty list 2271 l = [] 2272 else: 2273 # We rely on ConfigObj providing us with a list already 2274 l = maybe_list 2275 return l 2276 2277 2278class RegistryOption(Option): 2279 """Option for a choice from a registry.""" 2280 2281 def __init__(self, name, registry, default_from_env=None, 2282 help=None, invalid=None): 2283 """A registry based Option definition. 2284 2285 This overrides the base class so the conversion from a unicode string 2286 can take quoting into account. 2287 """ 2288 super(RegistryOption, self).__init__( 2289 name, default=lambda: registry.default_key, 2290 default_from_env=default_from_env, 2291 from_unicode=self.from_unicode, help=help, 2292 invalid=invalid, unquote=False) 2293 self.registry = registry 2294 2295 def from_unicode(self, unicode_str): 2296 if not isinstance(unicode_str, str): 2297 raise TypeError 2298 try: 2299 return self.registry.get(unicode_str) 2300 except KeyError: 2301 raise ValueError( 2302 "Invalid value %s for %s." 2303 "See help for a list of possible values." % (unicode_str, 2304 self.name)) 2305 2306 @property 2307 def help(self): 2308 ret = [self._help, "\n\nThe following values are supported:\n"] 2309 for key in self.registry.keys(): 2310 ret.append(" %s - %s\n" % (key, self.registry.get_help(key))) 2311 return "".join(ret) 2312 2313 2314_option_ref_re = lazy_regex.lazy_compile('({[^\\d\\W](?:\\.\\w|-\\w|\\w)*})') 2315"""Describes an expandable option reference. 2316 2317We want to match the most embedded reference first. 2318 2319I.e. for '{{foo}}' we will get '{foo}', 2320for '{bar{baz}}' we will get '{baz}' 2321""" 2322 2323 2324def iter_option_refs(string): 2325 # Split isolate refs so every other chunk is a ref 2326 is_ref = False 2327 for chunk in _option_ref_re.split(string): 2328 yield is_ref, chunk 2329 is_ref = not is_ref 2330 2331 2332class OptionRegistry(registry.Registry): 2333 """Register config options by their name. 2334 2335 This overrides ``registry.Registry`` to simplify registration by acquiring 2336 some information from the option object itself. 2337 """ 2338 2339 def _check_option_name(self, option_name): 2340 """Ensures an option name is valid. 2341 2342 :param option_name: The name to validate. 2343 """ 2344 if _option_ref_re.match('{%s}' % option_name) is None: 2345 raise IllegalOptionName(option_name) 2346 2347 def register(self, option): 2348 """Register a new option to its name. 2349 2350 :param option: The option to register. Its name is used as the key. 2351 """ 2352 self._check_option_name(option.name) 2353 super(OptionRegistry, self).register(option.name, option, 2354 help=option.help) 2355 2356 def register_lazy(self, key, module_name, member_name): 2357 """Register a new option to be loaded on request. 2358 2359 :param key: the key to request the option later. Since the registration 2360 is lazy, it should be provided and match the option name. 2361 2362 :param module_name: the python path to the module. Such as 'os.path'. 2363 2364 :param member_name: the member of the module to return. If empty or 2365 None, get() will return the module itself. 2366 """ 2367 self._check_option_name(key) 2368 super(OptionRegistry, self).register_lazy(key, 2369 module_name, member_name) 2370 2371 def get_help(self, key=None): 2372 """Get the help text associated with the given key""" 2373 option = self.get(key) 2374 the_help = option.help 2375 if callable(the_help): 2376 return the_help(self, key) 2377 return the_help 2378 2379 2380option_registry = OptionRegistry() 2381 2382 2383# Registered options in lexicographical order 2384 2385option_registry.register( 2386 Option('append_revisions_only', 2387 default=None, from_unicode=bool_from_store, invalid='warning', 2388 help='''\ 2389Whether to only append revisions to the mainline. 2390 2391If this is set to true, then it is not possible to change the 2392existing mainline of the branch. 2393''')) 2394option_registry.register( 2395 ListOption('acceptable_keys', 2396 default=None, 2397 help="""\ 2398List of GPG key patterns which are acceptable for verification. 2399""")) 2400option_registry.register( 2401 Option('add.maximum_file_size', 2402 default=u'20MB', from_unicode=int_SI_from_store, 2403 help="""\ 2404Size above which files should be added manually. 2405 2406Files below this size are added automatically when using ``bzr add`` without 2407arguments. 2408 2409A negative value means disable the size check. 2410""")) 2411option_registry.register( 2412 Option('bound', 2413 default=None, from_unicode=bool_from_store, 2414 help="""\ 2415Is the branch bound to ``bound_location``. 2416 2417If set to "True", the branch should act as a checkout, and push each commit to 2418the bound_location. This option is normally set by ``bind``/``unbind``. 2419 2420See also: bound_location. 2421""")) 2422option_registry.register( 2423 Option('bound_location', 2424 default=None, 2425 help="""\ 2426The location that commits should go to when acting as a checkout. 2427 2428This option is normally set by ``bind``. 2429 2430See also: bound. 2431""")) 2432option_registry.register( 2433 Option('branch.fetch_tags', default=False, from_unicode=bool_from_store, 2434 help="""\ 2435Whether revisions associated with tags should be fetched. 2436""")) 2437option_registry.register_lazy( 2438 'transform.orphan_policy', 'breezy.transform', 'opt_transform_orphan') 2439option_registry.register( 2440 Option('bzr.workingtree.worth_saving_limit', default=10, 2441 from_unicode=int_from_store, invalid='warning', 2442 help='''\ 2443How many changes before saving the dirstate. 2444 2445-1 means that we will never rewrite the dirstate file for only 2446stat-cache changes. Regardless of this setting, we will always rewrite 2447the dirstate file if a file is added/removed/renamed/etc. This flag only 2448affects the behavior of updating the dirstate file after we notice that 2449a file has been touched. 2450''')) 2451option_registry.register( 2452 Option('bugtracker', default=None, 2453 help='''\ 2454Default bug tracker to use. 2455 2456This bug tracker will be used for example when marking bugs 2457as fixed using ``bzr commit --fixes``, if no explicit 2458bug tracker was specified. 2459''')) 2460option_registry.register( 2461 Option('calculate_revnos', default=True, 2462 from_unicode=bool_from_store, 2463 help='''\ 2464Calculate revision numbers if they are not known. 2465 2466Always show revision numbers, even for branch formats that don't store them 2467natively (such as Git). Calculating the revision number requires traversing 2468the left hand ancestry of the branch and can be slow on very large branches. 2469''')) 2470option_registry.register( 2471 Option('check_signatures', default=CHECK_IF_POSSIBLE, 2472 from_unicode=signature_policy_from_unicode, 2473 help='''\ 2474GPG checking policy. 2475 2476Possible values: require, ignore, check-available (default) 2477 2478this option will control whether bzr will require good gpg 2479signatures, ignore them, or check them if they are 2480present. 2481''')) 2482option_registry.register( 2483 Option('child_submit_format', 2484 help='''The preferred format of submissions to this branch.''')) 2485option_registry.register( 2486 Option('child_submit_to', 2487 help='''Where submissions to this branch are mailed to.''')) 2488option_registry.register( 2489 Option('create_signatures', default=SIGN_WHEN_REQUIRED, 2490 from_unicode=signing_policy_from_unicode, 2491 help='''\ 2492GPG Signing policy. 2493 2494Possible values: always, never, when-required (default) 2495 2496This option controls whether bzr will always create 2497gpg signatures or not on commits. 2498''')) 2499option_registry.register( 2500 Option('dirstate.fdatasync', default=True, 2501 from_unicode=bool_from_store, 2502 help='''\ 2503Flush dirstate changes onto physical disk? 2504 2505If true (default), working tree metadata changes are flushed through the 2506OS buffers to physical disk. This is somewhat slower, but means data 2507should not be lost if the machine crashes. See also repository.fdatasync. 2508''')) 2509option_registry.register( 2510 ListOption('debug_flags', default=[], 2511 help='Debug flags to activate.')) 2512option_registry.register( 2513 Option('default_format', default='2a', 2514 help='Format used when creating branches.')) 2515option_registry.register( 2516 Option('editor', 2517 help='The command called to launch an editor to enter a message.')) 2518option_registry.register( 2519 Option('email', override_from_env=['BRZ_EMAIL', 'BZR_EMAIL'], 2520 default=bedding.default_email, help='The users identity')) 2521option_registry.register( 2522 Option('gpg_signing_key', 2523 default=None, 2524 help="""\ 2525GPG key to use for signing. 2526 2527This defaults to the first key associated with the users email. 2528""")) 2529option_registry.register( 2530 Option('language', 2531 help='Language to translate messages into.')) 2532option_registry.register( 2533 Option('locks.steal_dead', default=True, from_unicode=bool_from_store, 2534 help='''\ 2535Steal locks that appears to be dead. 2536 2537If set to True, bzr will check if a lock is supposed to be held by an 2538active process from the same user on the same machine. If the user and 2539machine match, but no process with the given PID is active, then bzr 2540will automatically break the stale lock, and create a new lock for 2541this process. 2542Otherwise, bzr will prompt as normal to break the lock. 2543''')) 2544option_registry.register( 2545 Option('log_format', default='long', 2546 help='''\ 2547Log format to use when displaying revisions. 2548 2549Standard log formats are ``long``, ``short`` and ``line``. Additional formats 2550may be provided by plugins. 2551''')) 2552option_registry.register_lazy('mail_client', 'breezy.mail_client', 2553 'opt_mail_client') 2554option_registry.register( 2555 Option('output_encoding', 2556 help='Unicode encoding for output' 2557 ' (terminal encoding if not specified).')) 2558option_registry.register( 2559 Option('parent_location', 2560 default=None, 2561 help="""\ 2562The location of the default branch for pull or merge. 2563 2564This option is normally set when creating a branch, the first ``pull`` or by 2565``pull --remember``. 2566""")) 2567option_registry.register( 2568 Option('post_commit', default=None, 2569 help='''\ 2570Post commit functions. 2571 2572An ordered list of python functions to call, separated by spaces. 2573 2574Each function takes branch, rev_id as parameters. 2575''')) 2576option_registry.register_lazy('progress_bar', 'breezy.ui.text', 2577 'opt_progress_bar') 2578option_registry.register( 2579 Option('public_branch', 2580 default=None, 2581 help="""\ 2582A publically-accessible version of this branch. 2583 2584This implies that the branch setting this option is not publically-accessible. 2585Used and set by ``bzr send``. 2586""")) 2587option_registry.register( 2588 Option('push_location', 2589 default=None, 2590 help="""\ 2591The location of the default branch for push. 2592 2593This option is normally set by the first ``push`` or ``push --remember``. 2594""")) 2595option_registry.register( 2596 Option('push_strict', default=None, 2597 from_unicode=bool_from_store, 2598 help='''\ 2599The default value for ``push --strict``. 2600 2601If present, defines the ``--strict`` option default value for checking 2602uncommitted changes before sending a merge directive. 2603''')) 2604option_registry.register( 2605 Option('repository.fdatasync', default=True, 2606 from_unicode=bool_from_store, 2607 help='''\ 2608Flush repository changes onto physical disk? 2609 2610If true (default), repository changes are flushed through the OS buffers 2611to physical disk. This is somewhat slower, but means data should not be 2612lost if the machine crashes. See also dirstate.fdatasync. 2613''')) 2614option_registry.register_lazy('smtp_server', 2615 'breezy.smtp_connection', 'smtp_server') 2616option_registry.register_lazy('smtp_password', 2617 'breezy.smtp_connection', 'smtp_password') 2618option_registry.register_lazy('smtp_username', 2619 'breezy.smtp_connection', 'smtp_username') 2620option_registry.register( 2621 Option('selftest.timeout', 2622 default='600', 2623 from_unicode=int_from_store, 2624 help='Abort selftest if one test takes longer than this many seconds', 2625 )) 2626 2627option_registry.register( 2628 Option('send_strict', default=None, 2629 from_unicode=bool_from_store, 2630 help='''\ 2631The default value for ``send --strict``. 2632 2633If present, defines the ``--strict`` option default value for checking 2634uncommitted changes before sending a bundle. 2635''')) 2636 2637option_registry.register( 2638 Option('serve.client_timeout', 2639 default=300.0, from_unicode=float_from_store, 2640 help="If we wait for a new request from a client for more than" 2641 " X seconds, consider the client idle, and hangup.")) 2642option_registry.register( 2643 Option('ssh', 2644 default=None, override_from_env=['BRZ_SSH'], 2645 help='SSH vendor to use.')) 2646option_registry.register( 2647 Option('stacked_on_location', 2648 default=None, 2649 help="""The location where this branch is stacked on.""")) 2650option_registry.register( 2651 Option('submit_branch', 2652 default=None, 2653 help="""\ 2654The branch you intend to submit your current work to. 2655 2656This is automatically set by ``bzr send`` and ``bzr merge``, and is also used 2657by the ``submit:`` revision spec. 2658""")) 2659option_registry.register( 2660 Option('submit_to', 2661 help='''Where submissions from this branch are mailed to.''')) 2662option_registry.register( 2663 ListOption('suppress_warnings', 2664 default=[], 2665 help="List of warning classes to suppress.")) 2666option_registry.register( 2667 Option('validate_signatures_in_log', default=False, 2668 from_unicode=bool_from_store, invalid='warning', 2669 help='''Whether to validate signatures in brz log.''')) 2670option_registry.register_lazy('ssl.ca_certs', 2671 'breezy.transport.http', 'opt_ssl_ca_certs') 2672 2673option_registry.register_lazy('ssl.cert_reqs', 2674 'breezy.transport.http', 'opt_ssl_cert_reqs') 2675 2676 2677class Section(object): 2678 """A section defines a dict of option name => value. 2679 2680 This is merely a read-only dict which can add some knowledge about the 2681 options. It is *not* a python dict object though and doesn't try to mimic 2682 its API. 2683 """ 2684 2685 def __init__(self, section_id, options): 2686 self.id = section_id 2687 # We re-use the dict-like object received 2688 self.options = options 2689 2690 def get(self, name, default=None, expand=True): 2691 return self.options.get(name, default) 2692 2693 def iter_option_names(self): 2694 for k in self.options.keys(): 2695 yield k 2696 2697 def __repr__(self): 2698 # Mostly for debugging use 2699 return "<config.%s id=%s>" % (self.__class__.__name__, self.id) 2700 2701 2702_NewlyCreatedOption = object() 2703"""Was the option created during the MutableSection lifetime""" 2704_DeletedOption = object() 2705"""Was the option deleted during the MutableSection lifetime""" 2706 2707 2708class MutableSection(Section): 2709 """A section allowing changes and keeping track of the original values.""" 2710 2711 def __init__(self, section_id, options): 2712 super(MutableSection, self).__init__(section_id, options) 2713 self.reset_changes() 2714 2715 def set(self, name, value): 2716 if name not in self.options: 2717 # This is a new option 2718 self.orig[name] = _NewlyCreatedOption 2719 elif name not in self.orig: 2720 self.orig[name] = self.get(name, None) 2721 self.options[name] = value 2722 2723 def remove(self, name): 2724 if name not in self.orig and name in self.options: 2725 self.orig[name] = self.get(name, None) 2726 del self.options[name] 2727 2728 def reset_changes(self): 2729 self.orig = {} 2730 2731 def apply_changes(self, dirty, store): 2732 """Apply option value changes. 2733 2734 ``self`` has been reloaded from the persistent storage. ``dirty`` 2735 contains the changes made since the previous loading. 2736 2737 :param dirty: the mutable section containing the changes. 2738 2739 :param store: the store containing the section 2740 """ 2741 for k, expected in dirty.orig.items(): 2742 actual = dirty.get(k, _DeletedOption) 2743 reloaded = self.get(k, _NewlyCreatedOption) 2744 if actual is _DeletedOption: 2745 if k in self.options: 2746 self.remove(k) 2747 else: 2748 self.set(k, actual) 2749 # Report concurrent updates in an ad-hoc way. This should only 2750 # occurs when different processes try to update the same option 2751 # which is not supported (as in: the config framework is not meant 2752 # to be used as a sharing mechanism). 2753 if expected != reloaded: 2754 if actual is _DeletedOption: 2755 actual = '<DELETED>' 2756 if reloaded is _NewlyCreatedOption: 2757 reloaded = '<CREATED>' 2758 if expected is _NewlyCreatedOption: 2759 expected = '<CREATED>' 2760 # Someone changed the value since we get it from the persistent 2761 # storage. 2762 trace.warning(gettext( 2763 "Option {0} in section {1} of {2} was changed" 2764 " from {3} to {4}. The {5} value will be saved.".format( 2765 k, self.id, store.external_url(), expected, 2766 reloaded, actual))) 2767 # No need to keep track of these changes 2768 self.reset_changes() 2769 2770 2771class Store(object): 2772 """Abstract interface to persistent storage for configuration options.""" 2773 2774 readonly_section_class = Section 2775 mutable_section_class = MutableSection 2776 2777 def __init__(self): 2778 # Which sections need to be saved (by section id). We use a dict here 2779 # so the dirty sections can be shared by multiple callers. 2780 self.dirty_sections = {} 2781 2782 def is_loaded(self): 2783 """Returns True if the Store has been loaded. 2784 2785 This is used to implement lazy loading and ensure the persistent 2786 storage is queried only when needed. 2787 """ 2788 raise NotImplementedError(self.is_loaded) 2789 2790 def load(self): 2791 """Loads the Store from persistent storage.""" 2792 raise NotImplementedError(self.load) 2793 2794 def _load_from_string(self, bytes): 2795 """Create a store from a string in configobj syntax. 2796 2797 :param bytes: A string representing the file content. 2798 """ 2799 raise NotImplementedError(self._load_from_string) 2800 2801 def unload(self): 2802 """Unloads the Store. 2803 2804 This should make is_loaded() return False. This is used when the caller 2805 knows that the persistent storage has changed or may have change since 2806 the last load. 2807 """ 2808 raise NotImplementedError(self.unload) 2809 2810 def quote(self, value): 2811 """Quote a configuration option value for storing purposes. 2812 2813 This allows Stacks to present values as they will be stored. 2814 """ 2815 return value 2816 2817 def unquote(self, value): 2818 """Unquote a configuration option value into unicode. 2819 2820 The received value is quoted as stored. 2821 """ 2822 return value 2823 2824 def save(self): 2825 """Saves the Store to persistent storage.""" 2826 raise NotImplementedError(self.save) 2827 2828 def _need_saving(self): 2829 for s in self.dirty_sections.values(): 2830 if s.orig: 2831 # At least one dirty section contains a modification 2832 return True 2833 return False 2834 2835 def apply_changes(self, dirty_sections): 2836 """Apply changes from dirty sections while checking for coherency. 2837 2838 The Store content is discarded and reloaded from persistent storage to 2839 acquire up-to-date values. 2840 2841 Dirty sections are MutableSection which kept track of the value they 2842 are expected to update. 2843 """ 2844 # We need an up-to-date version from the persistent storage, unload the 2845 # store. The reload will occur when needed (triggered by the first 2846 # get_mutable_section() call below. 2847 self.unload() 2848 # Apply the changes from the preserved dirty sections 2849 for section_id, dirty in dirty_sections.items(): 2850 clean = self.get_mutable_section(section_id) 2851 clean.apply_changes(dirty, self) 2852 # Everything is clean now 2853 self.dirty_sections = {} 2854 2855 def save_changes(self): 2856 """Saves the Store to persistent storage if changes occurred. 2857 2858 Apply the changes recorded in the mutable sections to a store content 2859 refreshed from persistent storage. 2860 """ 2861 raise NotImplementedError(self.save_changes) 2862 2863 def external_url(self): 2864 raise NotImplementedError(self.external_url) 2865 2866 def get_sections(self): 2867 """Returns an ordered iterable of existing sections. 2868 2869 :returns: An iterable of (store, section). 2870 """ 2871 raise NotImplementedError(self.get_sections) 2872 2873 def get_mutable_section(self, section_id=None): 2874 """Returns the specified mutable section. 2875 2876 :param section_id: The section identifier 2877 """ 2878 raise NotImplementedError(self.get_mutable_section) 2879 2880 def __repr__(self): 2881 # Mostly for debugging use 2882 return "<config.%s(%s)>" % (self.__class__.__name__, 2883 self.external_url()) 2884 2885 2886class CommandLineStore(Store): 2887 "A store to carry command line overrides for the config options.""" 2888 2889 def __init__(self, opts=None): 2890 super(CommandLineStore, self).__init__() 2891 if opts is None: 2892 opts = {} 2893 self.options = {} 2894 self.id = 'cmdline' 2895 2896 def _reset(self): 2897 # The dict should be cleared but not replaced so it can be shared. 2898 self.options.clear() 2899 2900 def _from_cmdline(self, overrides): 2901 # Reset before accepting new definitions 2902 self._reset() 2903 for over in overrides: 2904 try: 2905 name, value = over.split('=', 1) 2906 except ValueError: 2907 raise errors.CommandError( 2908 gettext("Invalid '%s', should be of the form 'name=value'") 2909 % (over,)) 2910 self.options[name] = value 2911 2912 def external_url(self): 2913 # Not an url but it makes debugging easier and is never needed 2914 # otherwise 2915 return 'cmdline' 2916 2917 def get_sections(self): 2918 yield self, self.readonly_section_class(None, self.options) 2919 2920 2921class IniFileStore(Store): 2922 """A config Store using ConfigObj for storage. 2923 2924 :ivar _config_obj: Private member to hold the ConfigObj instance used to 2925 serialize/deserialize the config file. 2926 """ 2927 2928 def __init__(self): 2929 """A config Store using ConfigObj for storage. 2930 """ 2931 super(IniFileStore, self).__init__() 2932 self._config_obj = None 2933 2934 def is_loaded(self): 2935 return self._config_obj is not None 2936 2937 def unload(self): 2938 self._config_obj = None 2939 self.dirty_sections = {} 2940 2941 def _load_content(self): 2942 """Load the config file bytes. 2943 2944 This should be provided by subclasses 2945 2946 :return: Byte string 2947 """ 2948 raise NotImplementedError(self._load_content) 2949 2950 def _save_content(self, content): 2951 """Save the config file bytes. 2952 2953 This should be provided by subclasses 2954 2955 :param content: Config file bytes to write 2956 """ 2957 raise NotImplementedError(self._save_content) 2958 2959 def load(self): 2960 """Load the store from the associated file.""" 2961 if self.is_loaded(): 2962 return 2963 content = self._load_content() 2964 self._load_from_string(content) 2965 for hook in ConfigHooks['load']: 2966 hook(self) 2967 2968 def _load_from_string(self, bytes): 2969 """Create a config store from a string. 2970 2971 :param bytes: A string representing the file content. 2972 """ 2973 if self.is_loaded(): 2974 raise AssertionError('Already loaded: %r' % (self._config_obj,)) 2975 co_input = BytesIO(bytes) 2976 try: 2977 # The config files are always stored utf8-encoded 2978 self._config_obj = ConfigObj(co_input, encoding='utf-8', 2979 list_values=False) 2980 except configobj.ConfigObjError as e: 2981 self._config_obj = None 2982 raise ParseConfigError(e.errors, self.external_url()) 2983 except UnicodeDecodeError: 2984 raise ConfigContentError(self.external_url()) 2985 2986 def save_changes(self): 2987 if not self.is_loaded(): 2988 # Nothing to save 2989 return 2990 if not self._need_saving(): 2991 return 2992 # Preserve the current version 2993 dirty_sections = self.dirty_sections.copy() 2994 self.apply_changes(dirty_sections) 2995 # Save to the persistent storage 2996 self.save() 2997 2998 def save(self): 2999 if not self.is_loaded(): 3000 # Nothing to save 3001 return 3002 out = BytesIO() 3003 self._config_obj.write(out) 3004 self._save_content(out.getvalue()) 3005 for hook in ConfigHooks['save']: 3006 hook(self) 3007 3008 def get_sections(self): 3009 """Get the configobj section in the file order. 3010 3011 :returns: An iterable of (store, section). 3012 """ 3013 # We need a loaded store 3014 try: 3015 self.load() 3016 except (errors.NoSuchFile, errors.PermissionDenied): 3017 # If the file can't be read, there is no sections 3018 return 3019 cobj = self._config_obj 3020 if cobj.scalars: 3021 yield self, self.readonly_section_class(None, cobj) 3022 for section_name in cobj.sections: 3023 yield (self, 3024 self.readonly_section_class(section_name, 3025 cobj[section_name])) 3026 3027 def get_mutable_section(self, section_id=None): 3028 # We need a loaded store 3029 try: 3030 self.load() 3031 except errors.NoSuchFile: 3032 # The file doesn't exist, let's pretend it was empty 3033 self._load_from_string(b'') 3034 if section_id in self.dirty_sections: 3035 # We already created a mutable section for this id 3036 return self.dirty_sections[section_id] 3037 if section_id is None: 3038 section = self._config_obj 3039 else: 3040 section = self._config_obj.setdefault(section_id, {}) 3041 mutable_section = self.mutable_section_class(section_id, section) 3042 # All mutable sections can become dirty 3043 self.dirty_sections[section_id] = mutable_section 3044 return mutable_section 3045 3046 def quote(self, value): 3047 try: 3048 # configobj conflates automagical list values and quoting 3049 self._config_obj.list_values = True 3050 return self._config_obj._quote(value) 3051 finally: 3052 self._config_obj.list_values = False 3053 3054 def unquote(self, value): 3055 if value and isinstance(value, str): 3056 # _unquote doesn't handle None nor empty strings nor anything that 3057 # is not a string, really. 3058 value = self._config_obj._unquote(value) 3059 return value 3060 3061 def external_url(self): 3062 # Since an IniFileStore can be used without a file (at least in tests), 3063 # it's better to provide something than raising a NotImplementedError. 3064 # All daughter classes are supposed to provide an implementation 3065 # anyway. 3066 return 'In-Process Store, no URL' 3067 3068 3069class TransportIniFileStore(IniFileStore): 3070 """IniFileStore that loads files from a transport. 3071 3072 :ivar transport: The transport object where the config file is located. 3073 3074 :ivar file_name: The config file basename in the transport directory. 3075 """ 3076 3077 def __init__(self, transport, file_name): 3078 """A Store using a ini file on a Transport 3079 3080 :param transport: The transport object where the config file is located. 3081 :param file_name: The config file basename in the transport directory. 3082 """ 3083 super(TransportIniFileStore, self).__init__() 3084 self.transport = transport 3085 self.file_name = file_name 3086 3087 def _load_content(self): 3088 try: 3089 return self.transport.get_bytes(self.file_name) 3090 except errors.PermissionDenied: 3091 trace.warning("Permission denied while trying to load " 3092 "configuration store %s.", self.external_url()) 3093 raise 3094 3095 def _save_content(self, content): 3096 self.transport.put_bytes(self.file_name, content) 3097 3098 def external_url(self): 3099 # FIXME: external_url should really accepts an optional relpath 3100 # parameter (bug #750169) :-/ -- vila 2011-04-04 3101 # The following will do in the interim but maybe we don't want to 3102 # expose a path here but rather a config ID and its associated 3103 # object </hand wawe>. 3104 return urlutils.join( 3105 self.transport.external_url(), urlutils.escape(self.file_name)) 3106 3107 3108# Note that LockableConfigObjStore inherits from ConfigObjStore because we need 3109# unlockable stores for use with objects that can already ensure the locking 3110# (think branches). If different stores (not based on ConfigObj) are created, 3111# they may face the same issue. 3112 3113 3114class LockableIniFileStore(TransportIniFileStore): 3115 """A ConfigObjStore using locks on save to ensure store integrity.""" 3116 3117 def __init__(self, transport, file_name, lock_dir_name=None): 3118 """A config Store using ConfigObj for storage. 3119 3120 :param transport: The transport object where the config file is located. 3121 3122 :param file_name: The config file basename in the transport directory. 3123 """ 3124 if lock_dir_name is None: 3125 lock_dir_name = 'lock' 3126 self.lock_dir_name = lock_dir_name 3127 super(LockableIniFileStore, self).__init__(transport, file_name) 3128 self._lock = lockdir.LockDir(self.transport, self.lock_dir_name) 3129 3130 def lock_write(self, token=None): 3131 """Takes a write lock in the directory containing the config file. 3132 3133 If the directory doesn't exist it is created. 3134 """ 3135 # FIXME: This doesn't check the ownership of the created directories as 3136 # ensure_config_dir_exists does. It should if the transport is local 3137 # -- vila 2011-04-06 3138 self.transport.create_prefix() 3139 token = self._lock.lock_write(token) 3140 return lock.LogicalLockResult(self.unlock, token) 3141 3142 def unlock(self): 3143 self._lock.unlock() 3144 3145 def break_lock(self): 3146 self._lock.break_lock() 3147 3148 def save(self): 3149 with self.lock_write(): 3150 # We need to be able to override the undecorated implementation 3151 self.save_without_locking() 3152 3153 def save_without_locking(self): 3154 super(LockableIniFileStore, self).save() 3155 3156 3157# FIXME: global, breezy, shouldn't that be 'user' instead or even 3158# 'user_defaults' as opposed to 'user_overrides', 'system_defaults' 3159# (/etc/bzr/bazaar.conf) and 'system_overrides' ? -- vila 2011-04-05 3160 3161# FIXME: Moreover, we shouldn't need classes for these stores either, factory 3162# functions or a registry will make it easier and clearer for tests, focusing 3163# on the relevant parts of the API that needs testing -- vila 20110503 (based 3164# on a poolie's remark) 3165class GlobalStore(LockableIniFileStore): 3166 """A config store for global options. 3167 3168 There is a single GlobalStore for a given process. 3169 """ 3170 3171 def __init__(self, possible_transports=None): 3172 path, kind = bedding._config_dir() 3173 t = transport.get_transport_from_path( 3174 path, possible_transports=possible_transports) 3175 super(GlobalStore, self).__init__(t, kind + '.conf') 3176 self.id = 'breezy' 3177 3178 3179class LocationStore(LockableIniFileStore): 3180 """A config store for options specific to a location. 3181 3182 There is a single LocationStore for a given process. 3183 """ 3184 3185 def __init__(self, possible_transports=None): 3186 t = transport.get_transport_from_path( 3187 bedding.config_dir(), possible_transports=possible_transports) 3188 super(LocationStore, self).__init__(t, 'locations.conf') 3189 self.id = 'locations' 3190 3191 3192class BranchStore(TransportIniFileStore): 3193 """A config store for branch options. 3194 3195 There is a single BranchStore for a given branch. 3196 """ 3197 3198 def __init__(self, branch): 3199 super(BranchStore, self).__init__(branch.control_transport, 3200 'branch.conf') 3201 self.branch = branch 3202 self.id = 'branch' 3203 3204 3205class ControlStore(LockableIniFileStore): 3206 3207 def __init__(self, bzrdir): 3208 super(ControlStore, self).__init__(bzrdir.transport, 3209 'control.conf', 3210 lock_dir_name='branch_lock') 3211 self.id = 'control' 3212 3213 3214class SectionMatcher(object): 3215 """Select sections into a given Store. 3216 3217 This is intended to be used to postpone getting an iterable of sections 3218 from a store. 3219 """ 3220 3221 def __init__(self, store): 3222 self.store = store 3223 3224 def get_sections(self): 3225 # This is where we require loading the store so we can see all defined 3226 # sections. 3227 sections = self.store.get_sections() 3228 # Walk the revisions in the order provided 3229 for store, s in sections: 3230 if self.match(s): 3231 yield store, s 3232 3233 def match(self, section): 3234 """Does the proposed section match. 3235 3236 :param section: A Section object. 3237 3238 :returns: True if the section matches, False otherwise. 3239 """ 3240 raise NotImplementedError(self.match) 3241 3242 3243class NameMatcher(SectionMatcher): 3244 3245 def __init__(self, store, section_id): 3246 super(NameMatcher, self).__init__(store) 3247 self.section_id = section_id 3248 3249 def match(self, section): 3250 return section.id == self.section_id 3251 3252 3253class LocationSection(Section): 3254 3255 def __init__(self, section, extra_path, branch_name=None): 3256 super(LocationSection, self).__init__(section.id, section.options) 3257 self.extra_path = extra_path 3258 if branch_name is None: 3259 branch_name = '' 3260 self.locals = {'relpath': extra_path, 3261 'basename': urlutils.basename(extra_path), 3262 'branchname': branch_name} 3263 3264 def get(self, name, default=None, expand=True): 3265 value = super(LocationSection, self).get(name, default) 3266 if value is not None and expand: 3267 policy_name = self.get(name + ':policy', None) 3268 policy = _policy_value.get(policy_name, POLICY_NONE) 3269 if policy == POLICY_APPENDPATH: 3270 value = urlutils.join(value, self.extra_path) 3271 # expand section local options right now (since POLICY_APPENDPATH 3272 # will never add options references, it's ok to expand after it). 3273 chunks = [] 3274 for is_ref, chunk in iter_option_refs(value): 3275 if not is_ref: 3276 chunks.append(chunk) 3277 else: 3278 ref = chunk[1:-1] 3279 if ref in self.locals: 3280 chunks.append(self.locals[ref]) 3281 else: 3282 chunks.append(chunk) 3283 value = ''.join(chunks) 3284 return value 3285 3286 3287class StartingPathMatcher(SectionMatcher): 3288 """Select sections for a given location respecting the Store order.""" 3289 3290 # FIXME: Both local paths and urls can be used for section names as well as 3291 # ``location`` to stay consistent with ``LocationMatcher`` which itself 3292 # inherited the fuzziness from the previous ``LocationConfig`` 3293 # implementation. We probably need to revisit which encoding is allowed for 3294 # both ``location`` and section names and how we normalize 3295 # them. http://pad.lv/85479, http://pad.lv/437009 and http://359320 are 3296 # related too. -- vila 2012-01-04 3297 3298 def __init__(self, store, location): 3299 super(StartingPathMatcher, self).__init__(store) 3300 if location.startswith('file://'): 3301 location = urlutils.local_path_from_url(location) 3302 self.location = location 3303 3304 def get_sections(self): 3305 """Get all sections matching ``location`` in the store. 3306 3307 The most generic sections are described first in the store, then more 3308 specific ones can be provided for reduced scopes. 3309 3310 The returned section are therefore returned in the reversed order so 3311 the most specific ones can be found first. 3312 """ 3313 location_parts = self.location.rstrip('/').split('/') 3314 store = self.store 3315 # Later sections are more specific, they should be returned first 3316 for _, section in reversed(list(store.get_sections())): 3317 if section.id is None: 3318 # The no-name section is always included if present 3319 yield store, LocationSection(section, self.location) 3320 continue 3321 section_path = section.id 3322 if section_path.startswith('file://'): 3323 # the location is already a local path or URL, convert the 3324 # section id to the same format 3325 section_path = urlutils.local_path_from_url(section_path) 3326 if (self.location.startswith(section_path) or 3327 fnmatch.fnmatch(self.location, section_path)): 3328 section_parts = section_path.rstrip('/').split('/') 3329 extra_path = '/'.join(location_parts[len(section_parts):]) 3330 yield store, LocationSection(section, extra_path) 3331 3332 3333class LocationMatcher(SectionMatcher): 3334 3335 def __init__(self, store, location): 3336 super(LocationMatcher, self).__init__(store) 3337 url, params = urlutils.split_segment_parameters(location) 3338 if location.startswith('file://'): 3339 location = urlutils.local_path_from_url(location) 3340 self.location = location 3341 branch_name = params.get('branch') 3342 if branch_name is None: 3343 self.branch_name = urlutils.basename(self.location) 3344 else: 3345 self.branch_name = urlutils.unescape(branch_name) 3346 3347 def _get_matching_sections(self): 3348 """Get all sections matching ``location``.""" 3349 # We slightly diverge from LocalConfig here by allowing the no-name 3350 # section as the most generic one and the lower priority. 3351 no_name_section = None 3352 all_sections = [] 3353 # Filter out the no_name_section so _iter_for_location_by_parts can be 3354 # used (it assumes all sections have a name). 3355 for _, section in self.store.get_sections(): 3356 if section.id is None: 3357 no_name_section = section 3358 else: 3359 all_sections.append(section) 3360 # Unfortunately _iter_for_location_by_parts deals with section names so 3361 # we have to resync. 3362 filtered_sections = _iter_for_location_by_parts( 3363 [s.id for s in all_sections], self.location) 3364 iter_all_sections = iter(all_sections) 3365 matching_sections = [] 3366 if no_name_section is not None: 3367 matching_sections.append( 3368 (0, LocationSection(no_name_section, self.location))) 3369 for section_id, extra_path, length in filtered_sections: 3370 # a section id is unique for a given store so it's safe to take the 3371 # first matching section while iterating. Also, all filtered 3372 # sections are part of 'all_sections' and will always be found 3373 # there. 3374 while True: 3375 section = next(iter_all_sections) 3376 if section_id == section.id: 3377 section = LocationSection(section, extra_path, 3378 self.branch_name) 3379 matching_sections.append((length, section)) 3380 break 3381 return matching_sections 3382 3383 def get_sections(self): 3384 # Override the default implementation as we want to change the order 3385 # We want the longest (aka more specific) locations first 3386 sections = sorted(self._get_matching_sections(), 3387 key=lambda match: (match[0], match[1].id), 3388 reverse=True) 3389 # Sections mentioning 'ignore_parents' restrict the selection 3390 for _, section in sections: 3391 # FIXME: We really want to use as_bool below -- vila 2011-04-07 3392 ignore = section.get('ignore_parents', None) 3393 if ignore is not None: 3394 ignore = ui.bool_from_string(ignore) 3395 if ignore: 3396 break 3397 # Finally, we have a valid section 3398 yield self.store, section 3399 3400 3401# FIXME: _shared_stores should be an attribute of a library state once a 3402# library_state object is always available. 3403_shared_stores = {} 3404_shared_stores_at_exit_installed = False 3405 3406 3407class Stack(object): 3408 """A stack of configurations where an option can be defined""" 3409 3410 def __init__(self, sections_def, store=None, mutable_section_id=None): 3411 """Creates a stack of sections with an optional store for changes. 3412 3413 :param sections_def: A list of Section or callables that returns an 3414 iterable of Section. This defines the Sections for the Stack and 3415 can be called repeatedly if needed. 3416 3417 :param store: The optional Store where modifications will be 3418 recorded. If none is specified, no modifications can be done. 3419 3420 :param mutable_section_id: The id of the MutableSection where changes 3421 are recorded. This requires the ``store`` parameter to be 3422 specified. 3423 """ 3424 self.sections_def = sections_def 3425 self.store = store 3426 self.mutable_section_id = mutable_section_id 3427 3428 def iter_sections(self): 3429 """Iterate all the defined sections.""" 3430 # Ensuring lazy loading is achieved by delaying section matching (which 3431 # implies querying the persistent storage) until it can't be avoided 3432 # anymore by using callables to describe (possibly empty) section 3433 # lists. 3434 for sections in self.sections_def: 3435 for store, section in sections(): 3436 yield store, section 3437 3438 def get(self, name, expand=True, convert=True): 3439 """Return the *first* option value found in the sections. 3440 3441 This is where we guarantee that sections coming from Store are loaded 3442 lazily: the loading is delayed until we need to either check that an 3443 option exists or get its value, which in turn may require to discover 3444 in which sections it can be defined. Both of these (section and option 3445 existence) require loading the store (even partially). 3446 3447 :param name: The queried option. 3448 3449 :param expand: Whether options references should be expanded. 3450 3451 :param convert: Whether the option value should be converted from 3452 unicode (do nothing for non-registered options). 3453 3454 :returns: The value of the option. 3455 """ 3456 # FIXME: No caching of options nor sections yet -- vila 20110503 3457 value = None 3458 found_store = None # Where the option value has been found 3459 # If the option is registered, it may provide additional info about 3460 # value handling 3461 try: 3462 opt = option_registry.get(name) 3463 except KeyError: 3464 # Not registered 3465 opt = None 3466 3467 def expand_and_convert(val): 3468 # This may need to be called in different contexts if the value is 3469 # None or ends up being None during expansion or conversion. 3470 if val is not None: 3471 if expand: 3472 if isinstance(val, str): 3473 val = self._expand_options_in_string(val) 3474 else: 3475 trace.warning('Cannot expand "%s":' 3476 ' %s does not support option expansion' 3477 % (name, type(val))) 3478 if opt is None: 3479 val = found_store.unquote(val) 3480 elif convert: 3481 val = opt.convert_from_unicode(found_store, val) 3482 return val 3483 3484 # First of all, check if the environment can override the configuration 3485 # value 3486 if opt is not None and opt.override_from_env: 3487 value = opt.get_override() 3488 value = expand_and_convert(value) 3489 if value is None: 3490 for store, section in self.iter_sections(): 3491 value = section.get(name) 3492 if value is not None: 3493 found_store = store 3494 break 3495 value = expand_and_convert(value) 3496 if opt is not None and value is None: 3497 # If the option is registered, it may provide a default value 3498 value = opt.get_default() 3499 value = expand_and_convert(value) 3500 for hook in ConfigHooks['get']: 3501 hook(self, name, value) 3502 return value 3503 3504 def expand_options(self, string, env=None): 3505 """Expand option references in the string in the configuration context. 3506 3507 :param string: The string containing option(s) to expand. 3508 3509 :param env: An option dict defining additional configuration options or 3510 overriding existing ones. 3511 3512 :returns: The expanded string. 3513 """ 3514 return self._expand_options_in_string(string, env) 3515 3516 def _expand_options_in_string(self, string, env=None, _refs=None): 3517 """Expand options in the string in the configuration context. 3518 3519 :param string: The string to be expanded. 3520 3521 :param env: An option dict defining additional configuration options or 3522 overriding existing ones. 3523 3524 :param _refs: Private list (FIFO) containing the options being expanded 3525 to detect loops. 3526 3527 :returns: The expanded string. 3528 """ 3529 if string is None: 3530 # Not much to expand there 3531 return None 3532 if _refs is None: 3533 # What references are currently resolved (to detect loops) 3534 _refs = [] 3535 result = string 3536 # We need to iterate until no more refs appear ({{foo}} will need two 3537 # iterations for example). 3538 expanded = True 3539 while expanded: 3540 expanded = False 3541 chunks = [] 3542 for is_ref, chunk in iter_option_refs(result): 3543 if not is_ref: 3544 chunks.append(chunk) 3545 else: 3546 expanded = True 3547 name = chunk[1:-1] 3548 if name in _refs: 3549 raise OptionExpansionLoop(string, _refs) 3550 _refs.append(name) 3551 value = self._expand_option(name, env, _refs) 3552 if value is None: 3553 raise ExpandingUnknownOption(name, string) 3554 chunks.append(value) 3555 _refs.pop() 3556 result = ''.join(chunks) 3557 return result 3558 3559 def _expand_option(self, name, env, _refs): 3560 if env is not None and name in env: 3561 # Special case, values provided in env takes precedence over 3562 # anything else 3563 value = env[name] 3564 else: 3565 value = self.get(name, expand=False, convert=False) 3566 value = self._expand_options_in_string(value, env, _refs) 3567 return value 3568 3569 def _get_mutable_section(self): 3570 """Get the MutableSection for the Stack. 3571 3572 This is where we guarantee that the mutable section is lazily loaded: 3573 this means we won't load the corresponding store before setting a value 3574 or deleting an option. In practice the store will often be loaded but 3575 this helps catching some programming errors. 3576 """ 3577 store = self.store 3578 section = store.get_mutable_section(self.mutable_section_id) 3579 return store, section 3580 3581 def set(self, name, value): 3582 """Set a new value for the option.""" 3583 store, section = self._get_mutable_section() 3584 section.set(name, store.quote(value)) 3585 for hook in ConfigHooks['set']: 3586 hook(self, name, value) 3587 3588 def remove(self, name): 3589 """Remove an existing option.""" 3590 _, section = self._get_mutable_section() 3591 section.remove(name) 3592 for hook in ConfigHooks['remove']: 3593 hook(self, name) 3594 3595 def __repr__(self): 3596 # Mostly for debugging use 3597 return "<config.%s(%s)>" % (self.__class__.__name__, id(self)) 3598 3599 def _get_overrides(self): 3600 if breezy._global_state is not None: 3601 # TODO(jelmer): Urgh, this is circular so we can't call breezy.get_global_state() 3602 return breezy._global_state.cmdline_overrides.get_sections() 3603 return [] 3604 3605 def get_shared_store(self, store, state=None): 3606 """Get a known shared store. 3607 3608 Store urls uniquely identify them and are used to ensure a single copy 3609 is shared across all users. 3610 3611 :param store: The store known to the caller. 3612 3613 :param state: The library state where the known stores are kept. 3614 3615 :returns: The store received if it's not a known one, an already known 3616 otherwise. 3617 """ 3618 if state is None: 3619 # TODO(jelmer): Urgh, this is circular so we can't call breezy.get_global_state() 3620 state = breezy._global_state 3621 if state is None: 3622 global _shared_stores_at_exit_installed 3623 stores = _shared_stores 3624 3625 def save_config_changes(): 3626 for k, store in stores.items(): 3627 store.save_changes() 3628 if not _shared_stores_at_exit_installed: 3629 # FIXME: Ugly hack waiting for library_state to always be 3630 # available. -- vila 20120731 3631 import atexit 3632 atexit.register(save_config_changes) 3633 _shared_stores_at_exit_installed = True 3634 else: 3635 stores = state.config_stores 3636 url = store.external_url() 3637 try: 3638 return stores[url] 3639 except KeyError: 3640 stores[url] = store 3641 return store 3642 3643 3644class MemoryStack(Stack): 3645 """A configuration stack defined from a string. 3646 3647 This is mainly intended for tests and requires no disk resources. 3648 """ 3649 3650 def __init__(self, content=None): 3651 """Create an in-memory stack from a given content. 3652 3653 It uses a single store based on configobj and support reading and 3654 writing options. 3655 3656 :param content: The initial content of the store. If None, the store is 3657 not loaded and ``_load_from_string`` can and should be used if 3658 needed. 3659 """ 3660 store = IniFileStore() 3661 if content is not None: 3662 store._load_from_string(content) 3663 super(MemoryStack, self).__init__( 3664 [store.get_sections], store) 3665 3666 3667class _CompatibleStack(Stack): 3668 """Place holder for compatibility with previous design. 3669 3670 This is intended to ease the transition from the Config-based design to the 3671 Stack-based design and should not be used nor relied upon by plugins. 3672 3673 One assumption made here is that the daughter classes will all use Stores 3674 derived from LockableIniFileStore). 3675 3676 It implements set() and remove () by re-loading the store before applying 3677 the modification and saving it. 3678 3679 The long term plan being to implement a single write by store to save 3680 all modifications, this class should not be used in the interim. 3681 """ 3682 3683 def set(self, name, value): 3684 # Force a reload 3685 self.store.unload() 3686 super(_CompatibleStack, self).set(name, value) 3687 # Force a write to persistent storage 3688 self.store.save() 3689 3690 def remove(self, name): 3691 # Force a reload 3692 self.store.unload() 3693 super(_CompatibleStack, self).remove(name) 3694 # Force a write to persistent storage 3695 self.store.save() 3696 3697 3698class GlobalStack(Stack): 3699 """Global options only stack. 3700 3701 The following sections are queried: 3702 3703 * command-line overrides, 3704 3705 * the 'DEFAULT' section in bazaar.conf 3706 3707 This stack will use the ``DEFAULT`` section in bazaar.conf as its 3708 MutableSection. 3709 """ 3710 3711 def __init__(self): 3712 gstore = self.get_shared_store(GlobalStore()) 3713 super(GlobalStack, self).__init__( 3714 [self._get_overrides, 3715 NameMatcher(gstore, 'DEFAULT').get_sections], 3716 gstore, mutable_section_id='DEFAULT') 3717 3718 3719class LocationStack(Stack): 3720 """Per-location options falling back to global options stack. 3721 3722 3723 The following sections are queried: 3724 3725 * command-line overrides, 3726 3727 * the sections matching ``location`` in ``locations.conf``, the order being 3728 defined by the number of path components in the section glob, higher 3729 numbers first (from most specific section to most generic). 3730 3731 * the 'DEFAULT' section in bazaar.conf 3732 3733 This stack will use the ``location`` section in locations.conf as its 3734 MutableSection. 3735 """ 3736 3737 def __init__(self, location): 3738 """Make a new stack for a location and global configuration. 3739 3740 :param location: A URL prefix to """ 3741 lstore = self.get_shared_store(LocationStore()) 3742 if location.startswith('file://'): 3743 location = urlutils.local_path_from_url(location) 3744 gstore = self.get_shared_store(GlobalStore()) 3745 super(LocationStack, self).__init__( 3746 [self._get_overrides, 3747 LocationMatcher(lstore, location).get_sections, 3748 NameMatcher(gstore, 'DEFAULT').get_sections], 3749 lstore, mutable_section_id=location) 3750 3751 3752class BranchStack(Stack): 3753 """Per-location options falling back to branch then global options stack. 3754 3755 The following sections are queried: 3756 3757 * command-line overrides, 3758 3759 * the sections matching ``location`` in ``locations.conf``, the order being 3760 defined by the number of path components in the section glob, higher 3761 numbers first (from most specific section to most generic), 3762 3763 * the no-name section in branch.conf, 3764 3765 * the ``DEFAULT`` section in ``bazaar.conf``. 3766 3767 This stack will use the no-name section in ``branch.conf`` as its 3768 MutableSection. 3769 """ 3770 3771 def __init__(self, branch): 3772 lstore = self.get_shared_store(LocationStore()) 3773 bstore = branch._get_config_store() 3774 gstore = self.get_shared_store(GlobalStore()) 3775 super(BranchStack, self).__init__( 3776 [self._get_overrides, 3777 LocationMatcher(lstore, branch.base).get_sections, 3778 NameMatcher(bstore, None).get_sections, 3779 NameMatcher(gstore, 'DEFAULT').get_sections], 3780 bstore) 3781 self.branch = branch 3782 3783 def lock_write(self, token=None): 3784 return self.branch.lock_write(token) 3785 3786 def unlock(self): 3787 return self.branch.unlock() 3788 3789 def set(self, name, value): 3790 with self.lock_write(): 3791 super(BranchStack, self).set(name, value) 3792 # Unlocking the branch will trigger a store.save_changes() so the 3793 # last unlock saves all the changes. 3794 3795 def remove(self, name): 3796 with self.lock_write(): 3797 super(BranchStack, self).remove(name) 3798 # Unlocking the branch will trigger a store.save_changes() so the 3799 # last unlock saves all the changes. 3800 3801 3802class RemoteControlStack(Stack): 3803 """Remote control-only options stack.""" 3804 3805 # FIXME 2011-11-22 JRV This should probably be renamed to avoid confusion 3806 # with the stack used for remote bzr dirs. RemoteControlStack only uses 3807 # control.conf and is used only for stack options. 3808 3809 def __init__(self, bzrdir): 3810 cstore = bzrdir._get_config_store() 3811 super(RemoteControlStack, self).__init__( 3812 [NameMatcher(cstore, None).get_sections], 3813 cstore) 3814 self.controldir = bzrdir 3815 3816 3817class BranchOnlyStack(Stack): 3818 """Branch-only options stack.""" 3819 3820 # FIXME: _BranchOnlyStack only uses branch.conf and is used only for the 3821 # stacked_on_location options waiting for http://pad.lv/832042 to be fixed. 3822 # -- vila 2011-12-16 3823 3824 def __init__(self, branch): 3825 bstore = branch._get_config_store() 3826 super(BranchOnlyStack, self).__init__( 3827 [NameMatcher(bstore, None).get_sections], 3828 bstore) 3829 self.branch = branch 3830 3831 def lock_write(self, token=None): 3832 return self.branch.lock_write(token) 3833 3834 def unlock(self): 3835 return self.branch.unlock() 3836 3837 def set(self, name, value): 3838 with self.lock_write(): 3839 super(BranchOnlyStack, self).set(name, value) 3840 # Force a write to persistent storage 3841 self.store.save_changes() 3842 3843 def remove(self, name): 3844 with self.lock_write(): 3845 super(BranchOnlyStack, self).remove(name) 3846 # Force a write to persistent storage 3847 self.store.save_changes() 3848 3849 3850class cmd_config(commands.Command): 3851 __doc__ = """Display, set or remove a configuration option. 3852 3853 Display the active value for option NAME. 3854 3855 If --all is specified, NAME is interpreted as a regular expression and all 3856 matching options are displayed mentioning their scope and without resolving 3857 option references in the value). The active value that bzr will take into 3858 account is the first one displayed for each option. 3859 3860 If NAME is not given, --all .* is implied (all options are displayed for the 3861 current scope). 3862 3863 Setting a value is achieved by using NAME=value without spaces. The value 3864 is set in the most relevant scope and can be checked by displaying the 3865 option again. 3866 3867 Removing a value is achieved by using --remove NAME. 3868 """ 3869 3870 takes_args = ['name?'] 3871 3872 takes_options = [ 3873 'directory', 3874 # FIXME: This should be a registry option so that plugins can register 3875 # their own config files (or not) and will also address 3876 # http://pad.lv/788991 -- vila 20101115 3877 commands.Option('scope', help='Reduce the scope to the specified' 3878 ' configuration file.', 3879 type=str), 3880 commands.Option('all', 3881 help='Display all the defined values for the matching options.', 3882 ), 3883 commands.Option('remove', help='Remove the option from' 3884 ' the configuration file.'), 3885 ] 3886 3887 _see_also = ['configuration'] 3888 3889 @commands.display_command 3890 def run(self, name=None, all=False, directory=None, scope=None, 3891 remove=False): 3892 if directory is None: 3893 directory = '.' 3894 directory = directory_service.directories.dereference(directory) 3895 directory = urlutils.normalize_url(directory) 3896 if remove and all: 3897 raise errors.BzrError( 3898 '--all and --remove are mutually exclusive.') 3899 elif remove: 3900 # Delete the option in the given scope 3901 self._remove_config_option(name, directory, scope) 3902 elif name is None: 3903 # Defaults to all options 3904 self._show_matching_options('.*', directory, scope) 3905 else: 3906 try: 3907 name, value = name.split('=', 1) 3908 except ValueError: 3909 # Display the option(s) value(s) 3910 if all: 3911 self._show_matching_options(name, directory, scope) 3912 else: 3913 self._show_value(name, directory, scope) 3914 else: 3915 if all: 3916 raise errors.BzrError( 3917 'Only one option can be set.') 3918 # Set the option value 3919 self._set_config_option(name, value, directory, scope) 3920 3921 def _get_stack(self, directory, scope=None, write_access=False): 3922 """Get the configuration stack specified by ``directory`` and ``scope``. 3923 3924 :param directory: Where the configurations are derived from. 3925 3926 :param scope: A specific config to start from. 3927 3928 :param write_access: Whether a write access to the stack will be 3929 attempted. 3930 """ 3931 # FIXME: scope should allow access to plugin-specific stacks (even 3932 # reduced to the plugin-specific store), related to 3933 # http://pad.lv/788991 -- vila 2011-11-15 3934 if scope is not None: 3935 if scope == 'breezy': 3936 return GlobalStack() 3937 elif scope == 'locations': 3938 return LocationStack(directory) 3939 elif scope == 'branch': 3940 (_, br, _) = ( 3941 controldir.ControlDir.open_containing_tree_or_branch( 3942 directory)) 3943 if write_access: 3944 self.add_cleanup(br.lock_write().unlock) 3945 return br.get_config_stack() 3946 raise NoSuchConfig(scope) 3947 else: 3948 try: 3949 (_, br, _) = ( 3950 controldir.ControlDir.open_containing_tree_or_branch( 3951 directory)) 3952 if write_access: 3953 self.add_cleanup(br.lock_write().unlock) 3954 return br.get_config_stack() 3955 except errors.NotBranchError: 3956 return LocationStack(directory) 3957 3958 def _quote_multiline(self, value): 3959 if '\n' in value: 3960 value = '"""' + value + '"""' 3961 return value 3962 3963 def _show_value(self, name, directory, scope): 3964 conf = self._get_stack(directory, scope) 3965 value = conf.get(name, expand=True, convert=False) 3966 if value is not None: 3967 # Quote the value appropriately 3968 value = self._quote_multiline(value) 3969 self.outf.write('%s\n' % (value,)) 3970 else: 3971 raise NoSuchConfigOption(name) 3972 3973 def _show_matching_options(self, name, directory, scope): 3974 name = lazy_regex.lazy_compile(name) 3975 # We want any error in the regexp to be raised *now* so we need to 3976 # avoid the delay introduced by the lazy regexp. But, we still do 3977 # want the nicer errors raised by lazy_regex. 3978 name._compile_and_collapse() 3979 cur_store_id = None 3980 cur_section = None 3981 conf = self._get_stack(directory, scope) 3982 for store, section in conf.iter_sections(): 3983 for oname in section.iter_option_names(): 3984 if name.search(oname): 3985 if cur_store_id != store.id: 3986 # Explain where the options are defined 3987 self.outf.write('%s:\n' % (store.id,)) 3988 cur_store_id = store.id 3989 cur_section = None 3990 if (section.id is not None and cur_section != section.id): 3991 # Display the section id as it appears in the store 3992 # (None doesn't appear by definition) 3993 self.outf.write(' [%s]\n' % (section.id,)) 3994 cur_section = section.id 3995 value = section.get(oname, expand=False) 3996 # Quote the value appropriately 3997 value = self._quote_multiline(value) 3998 self.outf.write(' %s = %s\n' % (oname, value)) 3999 4000 def _set_config_option(self, name, value, directory, scope): 4001 conf = self._get_stack(directory, scope, write_access=True) 4002 conf.set(name, value) 4003 # Explicitly save the changes 4004 conf.store.save_changes() 4005 4006 def _remove_config_option(self, name, directory, scope): 4007 if name is None: 4008 raise errors.CommandError( 4009 '--remove expects an option to remove.') 4010 conf = self._get_stack(directory, scope, write_access=True) 4011 try: 4012 conf.remove(name) 4013 # Explicitly save the changes 4014 conf.store.save_changes() 4015 except KeyError: 4016 raise NoSuchConfigOption(name) 4017 4018 4019# Test registries 4020# 4021# We need adapters that can build a Store or a Stack in a test context. Test 4022# classes, based on TestCaseWithTransport, can use the registry to parametrize 4023# themselves. The builder will receive a test instance and should return a 4024# ready-to-use store or stack. Plugins that define new store/stacks can also 4025# register themselves here to be tested against the tests defined in 4026# breezy.tests.test_config. Note that the builder can be called multiple times 4027# for the same test. 4028 4029# The registered object should be a callable receiving a test instance 4030# parameter (inheriting from tests.TestCaseWithTransport) and returning a Store 4031# object. 4032test_store_builder_registry = registry.Registry() 4033 4034# The registered object should be a callable receiving a test instance 4035# parameter (inheriting from tests.TestCaseWithTransport) and returning a Stack 4036# object. 4037test_stack_builder_registry = registry.Registry() 4038