| Server IP : 217.160.0.244 / Your IP : 216.73.217.138 Web Server : Apache System : Linux infong-eu155 4.4.400-icpu-108 #2 SMP Wed Feb 11 11:51:01 UTC 2026 x86_64 User : u100174116 ( 6746176) PHP Version : 8.5.10 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /lib/python3/dist-packages/breezy/ |
Upload File : |
# Copyright (C) 2005-2010 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
# TODO: For things like --diff-prefix, we want a way to customize the display
# of the option argument.
__docformat__ = "google"
import optparse
import re
from typing import Callable
from . import errors, revisionspec
from . import registry as _mod_registry
class BadOptionValue(errors.BzrError):
_fmt = """Bad value "%(value)s" for option "%(name)s"."""
def __init__(self, name, value):
errors.BzrError.__init__(self, name=name, value=value)
def _parse_revision_str(revstr):
r"""This handles a revision string -> revno.
This always returns a list. The list will have one element for
each revision specifier supplied.
>>> _parse_revision_str('234')
[<RevisionSpec_dwim 234>]
>>> _parse_revision_str('234..567')
[<RevisionSpec_dwim 234>, <RevisionSpec_dwim 567>]
>>> _parse_revision_str('..')
[<RevisionSpec None>, <RevisionSpec None>]
>>> _parse_revision_str('..234')
[<RevisionSpec None>, <RevisionSpec_dwim 234>]
>>> _parse_revision_str('234..')
[<RevisionSpec_dwim 234>, <RevisionSpec None>]
>>> _parse_revision_str('234..456..789') # Maybe this should be an error
[<RevisionSpec_dwim 234>, <RevisionSpec_dwim 456>, <RevisionSpec_dwim 789>]
>>> _parse_revision_str('234....789') #Error ?
[<RevisionSpec_dwim 234>, <RevisionSpec None>, <RevisionSpec_dwim 789>]
>>> _parse_revision_str('revid:test@other.com-234234')
[<RevisionSpec_revid revid:test@other.com-234234>]
>>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
[<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_revid revid:test@other.com-234235>]
>>> _parse_revision_str('revid:test@other.com-234234..23')
[<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_dwim 23>]
>>> _parse_revision_str('date:2005-04-12')
[<RevisionSpec_date date:2005-04-12>]
>>> _parse_revision_str('date:2005-04-12 12:24:33')
[<RevisionSpec_date date:2005-04-12 12:24:33>]
>>> _parse_revision_str('date:2005-04-12T12:24:33')
[<RevisionSpec_date date:2005-04-12T12:24:33>]
>>> _parse_revision_str('date:2005-04-12,12:24:33')
[<RevisionSpec_date date:2005-04-12,12:24:33>]
>>> _parse_revision_str('-5..23')
[<RevisionSpec_dwim -5>, <RevisionSpec_dwim 23>]
>>> _parse_revision_str('-5')
[<RevisionSpec_dwim -5>]
>>> _parse_revision_str('123a')
[<RevisionSpec_dwim 123a>]
>>> _parse_revision_str('abc')
[<RevisionSpec_dwim abc>]
>>> _parse_revision_str('branch:../branch2')
[<RevisionSpec_branch branch:../branch2>]
>>> _parse_revision_str('branch:../../branch2')
[<RevisionSpec_branch branch:../../branch2>]
>>> _parse_revision_str('branch:../../branch2..23')
[<RevisionSpec_branch branch:../../branch2>, <RevisionSpec_dwim 23>]
>>> _parse_revision_str('branch:..\\\\branch2')
[<RevisionSpec_branch branch:..\\branch2>]
>>> _parse_revision_str('branch:..\\\\..\\\\branch2..23')
[<RevisionSpec_branch branch:..\\..\\branch2>, <RevisionSpec_dwim 23>]
"""
# TODO: Maybe move this into revisionspec.py
revs = []
# split on .. that is not followed by a / or \
sep = re.compile(r"\.\.(?![\\/])")
for x in sep.split(revstr):
revs.append(revisionspec.RevisionSpec.from_string(x or None))
return revs
def _parse_change_str(revstr):
"""Parse the revision string and return a tuple with left-most
parent of the revision.
>>> _parse_change_str('123')
(<RevisionSpec_before before:123>, <RevisionSpec_dwim 123>)
>>> _parse_change_str('123..124')
Traceback (most recent call last):
...
breezy.errors.RangeInChangeOption: Option --change does not accept revision ranges
"""
revs = _parse_revision_str(revstr)
if len(revs) > 1:
raise errors.RangeInChangeOption()
return (revisionspec.RevisionSpec.from_string("before:" + revstr), revs[0])
def _parse_merge_type(typestring):
return get_merge_type(typestring)
def get_merge_type(typestring):
"""Attempt to find the merge class/factory associated with a string."""
from merge import merge_types
try:
return merge_types[typestring][0]
except KeyError:
templ = "%s%%7s: %%s" % (" " * 12)
lines = [templ % (f[0], f[1][1]) for f in merge_types.items()]
type_list = "\n".join(lines)
msg = "No known merge type {}. Supported types are:\n{}".format(
typestring,
type_list,
)
raise errors.CommandError(msg)
class Option:
"""Description of a command line option.
Attributes:
_short_name: If this option has a single-letter name, this is it.
Otherwise None.
"""
# The dictionary of standard options. These are always legal.
STD_OPTIONS: dict[str, "Option"] = {}
# The dictionary of commonly used options. these are only legal
# if a command explicitly references them by name in the list
# of supported options.
OPTIONS: dict[str, "Option"] = {}
def __init__(
self,
name,
help="",
type=None,
argname=None,
short_name=None,
param_name=None,
custom_callback=None,
hidden=False,
):
"""Make a new command option.
Args:
name: regular name of the command, used in the double-dash
form and also as the parameter to the command's run()
method (unless param_name is specified).
help: help message displayed in command help
type: function called to parse the option argument, or
None (default) if this option doesn't take an argument.
argname: name of option argument, if any
short_name: short option code for use with a single -, e.g.
short_name="v" to enable parsing of -v.
param_name: name of the parameter which will be passed to
the command's run() method.
custom_callback: a callback routine to be called after normal
processing. The signature of the callback routine is
(option, name, new_value, parser).
hidden: If True, the option should be hidden in help and
documentation.
"""
self.name = name
self.help = help
self.type = type
self._short_name = short_name
if type is None:
if argname:
raise ValueError("argname not valid for booleans")
elif argname is None:
argname = "ARG"
self.argname = argname
if param_name is None:
self._param_name = self.name.replace("-", "_")
else:
self._param_name = param_name
self.custom_callback = custom_callback
self.hidden = hidden
def short_name(self):
if self._short_name:
return self._short_name
def set_short_name(self, short_name):
self._short_name = short_name
def get_negation_name(self):
if self.name.startswith("no-"):
return self.name[3:]
else:
return "no-" + self.name
def add_option(self, parser, short_name):
"""Add this option to an Optparse parser."""
option_strings = ["--{}".format(self.name)]
if short_name is not None:
option_strings.append("-{}".format(short_name))
if self.hidden:
help = optparse.SUPPRESS_HELP
else:
help = self.help
optargfn = self.type
if optargfn is None:
parser.add_option(
action="callback",
callback=self._optparse_bool_callback,
callback_args=(True,),
help=help,
*option_strings,
)
negation_strings = ["--{}".format(self.get_negation_name())]
parser.add_option(
action="callback",
callback=self._optparse_bool_callback,
callback_args=(False,),
help=optparse.SUPPRESS_HELP,
*negation_strings,
)
else:
parser.add_option(
action="callback",
callback=self._optparse_callback,
type="string",
metavar=self.argname.upper(),
help=help,
default=OptionParser.DEFAULT_VALUE,
*option_strings,
)
def _optparse_bool_callback(self, option, opt_str, value, parser, bool_v):
setattr(parser.values, self._param_name, bool_v)
if self.custom_callback is not None:
self.custom_callback(option, self._param_name, bool_v, parser)
def _optparse_callback(self, option, opt, value, parser):
try:
v = self.type(value)
except ValueError:
raise optparse.OptionValueError(
"invalid value for option {}: {}".format(option, value)
)
setattr(parser.values, self._param_name, v)
if self.custom_callback is not None:
self.custom_callback(option, self.name, v, parser)
def iter_switches(self):
"""Iterate through the list of switches provided by the option.
:return: an iterator of (name, short_name, argname, help)
"""
argname = self.argname
if argname is not None:
argname = argname.upper()
yield self.name, self.short_name(), argname, self.help
def is_hidden(self, name):
return self.hidden
class ListOption(Option):
"""Option used to provide a list of values.
On the command line, arguments are specified by a repeated use of the
option. '-' is a special argument that resets the list. For example,
--foo=a --foo=b
sets the value of the 'foo' option to ['a', 'b'], and
--foo=a --foo=b --foo=- --foo=c
sets the value of the 'foo' option to ['c'].
"""
def add_option(self, parser, short_name):
"""Add this option to an Optparse parser."""
option_strings = ["--{}".format(self.name)]
if short_name is not None:
option_strings.append("-{}".format(short_name))
parser.add_option(
action="callback",
callback=self._optparse_callback,
type="string",
metavar=self.argname.upper(),
help=self.help,
dest=self._param_name,
default=[],
*option_strings,
)
def _optparse_callback(self, option, opt, value, parser):
values = getattr(parser.values, self._param_name)
if value == "-":
del values[:]
else:
values.append(self.type(value))
if self.custom_callback is not None:
self.custom_callback(option, self._param_name, values, parser)
class RegistryOption(Option):
"""Option based on a registry.
The values for the options correspond to entries in the registry. Input
must be a registry key. After validation, it is converted into an object
using Registry.get or a caller-provided converter.
"""
def validate_value(self, value):
"""Validate a value name."""
if value not in self.registry:
raise BadOptionValue(self.name, value)
def convert(self, value):
"""Convert a value name into an output type."""
self.validate_value(value)
if self.converter is None:
return self.registry.get(value)
else:
return self.converter(value)
def __init__(
self,
name,
help,
registry=None,
converter=None,
value_switches=False,
title=None,
enum_switch=True,
lazy_registry=None,
short_name=None,
short_value_switches=None,
):
"""Constructor.
Args:
name: The option name.
help: Help for the option.
registry: A Registry containing the values
converter: Callable to invoke with the value name to produce
the value. If not supplied, self.registry.get is used.
value_switches: If true, each possible value is assigned its
own switch. For example, instead of '--format knit',
'--knit' can be used interchangeably.
enum_switch: If true, a switch is provided with the option name,
which takes a value.
lazy_registry: A tuple of (module name, attribute name) for a
registry to be lazily loaded.
short_name: The short name for the enum switch, if any
short_value_switches: A dict mapping values to short names
"""
Option.__init__(self, name, help, type=self.convert, short_name=short_name)
self._registry = registry
if registry is None:
if lazy_registry is None:
raise AssertionError("One of registry or lazy_registry must be given.")
self._lazy_registry = _mod_registry._LazyObjectGetter(*lazy_registry)
if registry is not None and lazy_registry is not None:
raise AssertionError("registry and lazy_registry are mutually exclusive")
self.name = name
self.converter = converter
self.value_switches = value_switches
self.enum_switch = enum_switch
self.short_value_switches = short_value_switches
self.title = title
if self.title is None:
self.title = name
@property
def registry(self):
if self._registry is None:
self._registry = self._lazy_registry.get_obj()
return self._registry
@staticmethod
def from_kwargs(
name_, help=None, title=None, value_switches=False, enum_switch=True, **kwargs
):
"""Convenience method to generate string-map registry options.
name, help, value_switches and enum_switch are passed to the
RegistryOption constructor. Any other keyword arguments are treated
as values for the option, and their value is treated as the help.
"""
reg = _mod_registry.Registry()
for name, switch_help in sorted(kwargs.items()):
name = name.replace("_", "-")
reg.register(name, name, help=switch_help)
if not value_switches:
help = help + ' "' + name + '": ' + switch_help
if not help.endswith("."):
help = help + "."
return RegistryOption(
name_,
help,
reg,
title=title,
value_switches=value_switches,
enum_switch=enum_switch,
)
def add_option(self, parser, short_name):
"""Add this option to an Optparse parser."""
if self.value_switches:
parser = parser.add_option_group(self.title)
if self.enum_switch:
Option.add_option(self, parser, short_name)
if self.value_switches:
alias_map = self.registry.alias_map()
for key in self.registry.keys():
if key in self.registry.aliases():
continue
option_strings = [
("--{}".format(name))
for name in [key]
+ [
alias
for alias in alias_map.get(key, [])
if not self.is_hidden(alias)
]
]
if self.is_hidden(key):
help = optparse.SUPPRESS_HELP
else:
help = self.registry.get_help(key)
if self.short_value_switches and key in self.short_value_switches:
option_strings.append("-{}".format(self.short_value_switches[key]))
parser.add_option(
action="callback",
callback=self._optparse_value_callback(key),
help=help,
*option_strings,
)
def _optparse_value_callback(self, cb_value):
def cb(option, opt, value, parser):
v = self.type(cb_value)
setattr(parser.values, self._param_name, v)
if self.custom_callback is not None:
self.custom_callback(option, self._param_name, v, parser)
return cb
def iter_switches(self):
"""Iterate through the list of switches provided by the option.
:return: an iterator of (name, short_name, argname, help)
"""
yield from Option.iter_switches(self)
if self.value_switches:
for key in sorted(self.registry.keys()):
yield key, None, None, self.registry.get_help(key)
def is_alias(self, name):
"""Check whether a particular format is an alias."""
if name == self.name:
return False
return name in self.registry.aliases()
def is_hidden(self, name):
if name == self.name:
return Option.is_hidden(self, name)
return getattr(self.registry.get_info(name), "hidden", False)
class OptionParser(optparse.OptionParser):
"""OptionParser that raises exceptions instead of exiting."""
DEFAULT_VALUE = object()
def __init__(self):
optparse.OptionParser.__init__(self)
self.formatter = GettextIndentedHelpFormatter()
def error(self, message):
raise errors.CommandError(message)
class GettextIndentedHelpFormatter(optparse.IndentedHelpFormatter):
"""Adds gettext() call to format_option()."""
def __init__(self):
optparse.IndentedHelpFormatter.__init__(self)
def format_option(self, option):
"""Code taken from Python's optparse.py."""
if option.help:
from .i18n import gettext
option.help = gettext(option.help)
return optparse.IndentedHelpFormatter.format_option(self, option)
def get_optparser(options):
"""Generate an optparse parser for breezy-style options."""
parser = OptionParser()
parser.remove_option("--help")
for option in options:
option.add_option(parser, option.short_name())
return parser
def custom_help(name, help):
"""Clone a common option overriding the help."""
import copy
o = copy.copy(Option.OPTIONS[name])
o.help = help
return o
def _standard_option(name, **kwargs):
"""Register a standard option."""
# All standard options are implicitly 'global' ones
Option.STD_OPTIONS[name] = Option(name, **kwargs)
Option.OPTIONS[name] = Option.STD_OPTIONS[name]
def _standard_list_option(name, **kwargs):
"""Register a standard option."""
# All standard options are implicitly 'global' ones
Option.STD_OPTIONS[name] = ListOption(name, **kwargs)
Option.OPTIONS[name] = Option.STD_OPTIONS[name]
def _global_option(name, **kwargs):
"""Register a global option."""
Option.OPTIONS[name] = Option(name, **kwargs)
def _global_registry_option(name, help, registry=None, **kwargs):
Option.OPTIONS[name] = RegistryOption(name, help, registry, **kwargs)
# This is the verbosity level detected during command line parsing.
# Note that the final value is dependent on the order in which the
# various flags (verbose, quiet, no-verbose, no-quiet) are given.
# The final value will be one of the following:
#
# * -ve for quiet
# * 0 for normal
# * +ve for verbose
_verbosity_level = 0
def _verbosity_level_callback(option, opt_str, value, parser):
global _verbosity_level
if not value:
# Either --no-verbose or --no-quiet was specified
_verbosity_level = 0
elif opt_str == "verbose":
if _verbosity_level > 0:
_verbosity_level += 1
else:
_verbosity_level = 1
else:
if _verbosity_level < 0:
_verbosity_level -= 1
else:
_verbosity_level = -1
# Declare the standard options
_standard_option("help", short_name="h", help="Show help message.")
_standard_option(
"quiet",
short_name="q",
help="Only display errors and warnings.",
custom_callback=_verbosity_level_callback,
)
_standard_option("usage", help="Show usage message and options.")
_standard_option(
"verbose",
short_name="v",
help="Display more information.",
custom_callback=_verbosity_level_callback,
)
# Declare commonly used options
_global_option(
"change",
type=_parse_change_str,
short_name="c",
param_name="revision",
help='Select changes introduced by the specified revision. See also "help revisionspec".',
)
_global_option(
"directory",
short_name="d",
type=str,
help="Branch to operate on, instead of working directory.",
)
_global_option("file", type=str, short_name="F")
_global_registry_option(
"log-format",
"Use specified log format.",
lazy_registry=("breezy.log", "log_formatter_registry"),
value_switches=True,
title="Log format",
short_value_switches={"short": "S"},
)
_global_registry_option(
"merge-type",
"Select a particular merge algorithm.",
lazy_registry=("breezy.merge", "merge_type_registry"),
value_switches=True,
title="Merge algorithm",
)
_global_option("message", type=str, short_name="m", help="Message string.")
_global_option(
"null",
short_name="0",
help="Use an ASCII NUL (\\0) separator rather than a newline.",
)
_global_option(
"overwrite",
help="Ignore differences between branches and overwrite unconditionally.",
)
_global_option("remember", help="Remember the specified location as a default.")
_global_option("reprocess", help="Reprocess to reduce spurious conflicts.")
_global_option(
"revision",
type=_parse_revision_str,
short_name="r",
help='See "help revisionspec" for details.',
)
_global_option("show-ids", help="Show internal object ids.")
_global_option(
"timezone", type=str, help="Display timezone as local, original, or utc."
)
diff_writer_registry = _mod_registry.Registry[str, Callable]()
diff_writer_registry.register("plain", lambda x: x, "Plaintext diff output.")
diff_writer_registry.default_key = "plain"