Imported Upstream version 4.3.1

This commit is contained in:
Mario Fetka
2021-08-10 02:37:58 +02:00
parent a791de49a2
commit 2f177da8f2
2056 changed files with 421730 additions and 1668138 deletions

View File

@@ -7,17 +7,13 @@ Command line support.
"""
import collections
import enum
import logging
import optparse # pylint: disable=deprecated-module
import optparse
import signal
import six
from ipapython import admintool
from ipapython.ipa_log_manager import standard_logging_setup
from ipapython.ipautil import (CheckedIPAddress, CheckedIPAddressLoopback,
private_ccache)
from ipapython import admintool, ipa_log_manager
from ipapython.ipautil import CheckedIPAddress, private_ccache
from . import core, common
@@ -26,65 +22,22 @@ __all__ = ['install_tool', 'uninstall_tool']
if six.PY3:
long = int
NoneType = type(None)
logger = logging.getLogger(__name__)
def _get_usage(configurable_class):
usage = '%prog [options]'
for owner_cls, name in configurable_class.knobs():
knob_cls = getattr(owner_cls, name)
if knob_cls.is_cli_positional():
if knob_cls.cli_metavar is not None:
metavar = knob_cls.cli_metavar
elif knob_cls.cli_names:
metavar = knob_cls.cli_names[0].upper()
else:
metavar = name.replace('_', '-').upper()
try:
knob_cls.default
except AttributeError:
fmt = ' {}'
else:
fmt = ' [{}]'
usage += fmt.format(metavar)
return usage
def install_tool(configurable_class, command_name, log_file_name,
debug_option=False, verbose=False, console_format=None,
use_private_ccache=True, uninstall_log_file_name=None,
ignore_return_codes=()):
"""
Some commands represent multiple related tools, e.g.
``ipa-server-install`` and ``ipa-server-install --uninstall`` would be
represented by separate classes. Only their options are the same.
:param configurable_class: the command class for options
:param command_name: the command name shown in logs/output
:param log_file_name: if None, logging is to stderr only
:param debug_option: log level is DEBUG
:param verbose: log level is INFO
:param console_format: logging format for stderr
:param use_private_ccache: a temporary ccache is created and used
:param uninstall_log_file_name: if not None the log for uninstall
:param ignore_return_codes: tuple of error codes to not log errors
for. Let the caller do it if it wants.
"""
if uninstall_log_file_name is not None:
positional_arguments=None, usage=None, debug_option=False,
use_private_ccache=True,
uninstall_log_file_name=None,
uninstall_positional_arguments=None, uninstall_usage=None):
if (uninstall_log_file_name is not None or
uninstall_positional_arguments is not None or
uninstall_usage is not None):
uninstall_kwargs = dict(
configurable_class=configurable_class,
command_name=command_name,
log_file_name=uninstall_log_file_name,
positional_arguments=uninstall_positional_arguments,
usage=uninstall_usage,
debug_option=debug_option,
verbose=verbose,
console_format=console_format,
ignore_return_codes=ignore_return_codes,
)
else:
uninstall_kwargs = None
@@ -96,20 +49,17 @@ def install_tool(configurable_class, command_name, log_file_name,
configurable_class=configurable_class,
command_name=command_name,
log_file_name=log_file_name,
usage=_get_usage(configurable_class),
positional_arguments=positional_arguments,
usage=usage,
debug_option=debug_option,
verbose=verbose,
console_format=console_format,
uninstall_kwargs=uninstall_kwargs,
use_private_ccache=use_private_ccache,
ignore_return_codes=ignore_return_codes,
)
)
def uninstall_tool(configurable_class, command_name, log_file_name,
debug_option=False, verbose=False, console_format=None,
ignore_return_codes=()):
positional_arguments=None, usage=None, debug_option=False):
return type(
'uninstall_tool({0})'.format(configurable_class.__name__),
(UninstallTool,),
@@ -117,11 +67,9 @@ def uninstall_tool(configurable_class, command_name, log_file_name,
configurable_class=configurable_class,
command_name=command_name,
log_file_name=log_file_name,
usage=_get_usage(configurable_class),
positional_arguments=positional_arguments,
usage=usage,
debug_option=debug_option,
verbose=verbose,
console_format=console_format,
ignore_return_codes=ignore_return_codes,
)
)
@@ -129,8 +77,7 @@ def uninstall_tool(configurable_class, command_name, log_file_name,
class ConfigureTool(admintool.AdminTool):
configurable_class = None
debug_option = False
verbose = False
console_format = None
positional_arguments = None
use_private_ccache = True
@staticmethod
@@ -138,7 +85,7 @@ class ConfigureTool(admintool.AdminTool):
raise NotImplementedError
@classmethod
def add_options(cls, parser, positional=False):
def add_options(cls, parser):
transformed_cls = cls._transform(cls.configurable_class)
if issubclass(transformed_cls, common.Interactive):
@@ -150,141 +97,172 @@ class ConfigureTool(admintool.AdminTool):
help="unattended (un)installation never prompts the user",
)
basic_group = optparse.OptionGroup(parser, "basic options")
groups = collections.OrderedDict()
# if no group is defined, add the option to the parser top level
groups[None] = parser
groups[None] = basic_group
for owner_cls, name in transformed_cls.knobs():
knob_cls = getattr(owner_cls, name)
if knob_cls.is_cli_positional() is not positional:
if cls.positional_arguments and name in cls.positional_arguments:
continue
group_cls = knob_cls.group()
group_cls = owner_cls.group()
try:
opt_group = groups[group_cls]
except KeyError:
opt_group = groups[group_cls] = optparse.OptionGroup(
parser, "{0} options".format(group_cls.description))
parser.add_option_group(opt_group)
knob_type = knob_cls.type
if issubclass(knob_type, list):
try:
# typing.List[X].__parameters__ == (X,)
knob_scalar_type = knob_type.__parameters__[0]
except AttributeError:
knob_scalar_type = str
else:
knob_scalar_type = knob_type
parser, "{0} options".format(group_cls.description))
kwargs = dict()
if knob_scalar_type is NoneType:
if knob_cls.type is bool:
kwargs['type'] = None
kwargs['const'] = True
kwargs['default'] = False
elif knob_scalar_type is str:
else:
kwargs['type'] = 'string'
elif knob_scalar_type is int:
kwargs['type'] = 'int'
elif knob_scalar_type is long:
kwargs['type'] = 'long'
elif knob_scalar_type is CheckedIPAddressLoopback:
kwargs['type'] = 'ip_with_loopback'
elif knob_scalar_type is CheckedIPAddress:
kwargs['type'] = 'ip'
elif issubclass(knob_scalar_type, enum.Enum):
kwargs['type'] = 'choice'
kwargs['choices'] = [i.value for i in knob_scalar_type]
kwargs['metavar'] = "{{{0}}}".format(
",".join(kwargs['choices']))
else:
kwargs['type'] = 'constructor'
kwargs['constructor'] = knob_scalar_type
kwargs['dest'] = name
if issubclass(knob_type, list):
if kwargs['type'] is None:
kwargs['action'] = 'append_const'
else:
kwargs['action'] = 'append'
else:
if kwargs['type'] is None:
kwargs['action'] = 'store_const'
else:
kwargs['action'] = 'store'
kwargs['action'] = 'callback'
kwargs['callback'] = cls._option_callback
kwargs['callback_args'] = (knob_cls,)
if knob_cls.sensitive:
kwargs['sensitive'] = True
if knob_cls.cli_metavar:
kwargs['metavar'] = knob_cls.cli_metavar
if not positional:
cli_info = (
(knob_cls.deprecated, knob_cls.cli_names),
(True, knob_cls.cli_deprecated_names),
)
if knob_cls.cli_short_name:
short_opt_str = '-{0}'.format(knob_cls.cli_short_name)
else:
cli_info = (
(knob_cls.deprecated, (None,)),
)
for hidden, cli_names in cli_info:
opt_strs = []
for cli_name in cli_names:
if cli_name is None:
cli_name = '--{}'.format(name.replace('_', '-'))
opt_strs.append(cli_name)
if not opt_strs:
continue
if not hidden:
help = knob_cls.description
else:
help = optparse.SUPPRESS_HELP
short_opt_str = ''
cli_name = knob_cls.cli_name or name.replace('_', '-')
opt_str = '--{0}'.format(cli_name)
if not knob_cls.deprecated:
help = knob_cls.description
else:
help = optparse.SUPPRESS_HELP
opt_group.add_option(
short_opt_str, opt_str,
help=help,
**kwargs
)
if knob_cls.cli_aliases:
opt_strs = ['--{0}'.format(a) for a in knob_cls.cli_aliases]
opt_group.add_option(
*opt_strs,
help=help,
help=optparse.SUPPRESS_HELP,
**kwargs
)
for group, opt_group in groups.items():
parser.add_option_group(opt_group)
super(ConfigureTool, cls).add_options(parser,
debug_option=cls.debug_option)
def __init__(self, options, args):
super(ConfigureTool, self).__init__(options, args)
@classmethod
def _option_callback(cls, option, opt_str, value, parser, knob_cls):
old_value = getattr(parser.values, option.dest, None)
try:
value = cls._parse_knob(knob_cls, old_value, value)
except ValueError as e:
raise optparse.OptionValueError(
"option {0}: {1}".format(opt_str, e))
self.transformed_cls = self._transform(self.configurable_class)
self.positional_arguments = []
setattr(parser.values, option.dest, value)
for owner_cls, name in self.transformed_cls.knobs():
knob_cls = getattr(owner_cls, name)
if knob_cls.is_cli_positional():
self.positional_arguments.append(name)
@classmethod
def _parse_knob(cls, knob_cls, old_value, value):
if knob_cls.type is bool:
parse = bool
is_list = False
value = True
else:
if isinstance(knob_cls.type, tuple):
assert knob_cls.type[0] is list
value_type = knob_cls.type[1]
is_list = True
else:
value_type = knob_cls.type
is_list = False
# fake option parser to parse positional arguments
# (because optparse does not support positional argument parsing)
fake_option_parser = optparse.OptionParser()
self.add_options(fake_option_parser, True)
if value_type is int:
def parse(value):
try:
return int(value, 0)
except ValueError:
raise ValueError(
"invalid integer value: {0}".format(repr(value)))
elif value_type is long:
def parse(value):
try:
return long(value, 0)
except ValueError:
raise ValueError(
"invalid long integer value: {0}".format(
repr(value)))
elif value_type == 'ip':
def parse(value):
try:
return CheckedIPAddress(value)
except Exception as e:
raise ValueError("invalid IP address {0}: {1}".format(
value, e))
elif value_type == 'ip-local':
def parse(value):
try:
return CheckedIPAddress(value, match_local=True)
except Exception as e:
raise ValueError("invalid IP address {0}: {1}".format(
value, e))
elif isinstance(value_type, set):
def parse(value):
if value not in value_type:
raise ValueError(
"invalid choice {0} (choose from {1})".format(
repr(value), ', '.join(
sorted(repr(v) for v in value_type))))
return value
else:
parse = value_type
fake_option_map = {option.dest: option
for group in fake_option_parser.option_groups
for option in group.option_list}
value = parse(value)
for index, name in enumerate(self.positional_arguments):
try:
value = self.args.pop(0)
except IndexError:
break
if is_list:
old_value = old_value or []
old_value.append(value)
value = old_value
fake_option = fake_option_map[name]
fake_option.process('argument {}'.format(index + 1),
value,
self.options,
self.option_parser)
return value
def validate_options(self, needs_root=True):
super(ConfigureTool, self).validate_options(needs_root=needs_root)
if self.args:
self.option_parser.error("Too many arguments provided")
if self.positional_arguments:
if len(self.args) > len(self.positional_arguments):
self.option_parser.error("Too many arguments provided")
index = 0
transformed_cls = self._transform(self.configurable_class)
for owner_cls, name in transformed_cls.knobs():
knob_cls = getattr(owner_cls, name)
if name not in self.positional_arguments:
continue
try:
value = self.args[index]
except IndexError:
break
old_value = getattr(self.options, name, None)
try:
value = self._parse_knob(knob_cls, old_value, value)
except ValueError as e:
self.option_parser.error(
"argument {0}: {1}".format(index + 1, e))
setattr(self.options, name, value)
index += 1
def _setup_logging(self, log_file_mode='w', no_file=False):
if no_file:
@@ -293,21 +271,15 @@ class ConfigureTool(admintool.AdminTool):
log_file_name = self.options.log_file
else:
log_file_name = self.log_file_name
standard_logging_setup(
log_file_name,
verbose=self.verbose,
debug=self.options.verbose,
console_format=self.console_format)
ipa_log_manager.standard_logging_setup(log_file_name,
debug=self.options.verbose)
self.log = ipa_log_manager.log_mgr.get_logger(self)
if log_file_name:
logger.debug('Logging to %s', log_file_name)
self.log.debug('Logging to %s' % log_file_name)
elif not no_file:
logger.debug('Not logging to a file')
self.log.debug('Not logging to a file')
def init_configurator(self):
"""Executes transformation, getting a flattened Installer object
:returns: common.installer.Installer object
"""
def run(self):
kwargs = {}
transformed_cls = self._transform(self.configurable_class)
@@ -322,32 +294,31 @@ class ConfigureTool(admintool.AdminTool):
kwargs['interactive'] = True
try:
return transformed_cls(**kwargs)
cfgr = transformed_cls(**kwargs)
except core.KnobValueError as e:
knob_cls = knob_classes[e.name]
try:
if self.positional_arguments is None:
raise IndexError
index = self.positional_arguments.index(e.name)
except ValueError:
cli_name = knob_cls.cli_names[0] or e.name.replace('_', '-')
desc = "option {0}".format(cli_name)
except IndexError:
cli_name = knob_cls.cli_name or e.name.replace('_', '-')
desc = "option --{0}".format(cli_name)
else:
desc = "argument {0}".format(index + 1)
self.option_parser.error("{0}: {1}".format(desc, e))
except RuntimeError as e:
self.option_parser.error(str(e))
def run(self):
cfgr = self.init_configurator()
signal.signal(signal.SIGTERM, self.__signal_handler)
if self.use_private_ccache:
with private_ccache():
super(ConfigureTool, self).run()
return cfgr.run()
cfgr.run()
else:
super(ConfigureTool, self).run()
return cfgr.run()
cfgr.run()
@staticmethod
def __signal_handler(signum, frame):
@@ -360,11 +331,12 @@ class InstallTool(ConfigureTool):
_transform = staticmethod(common.installer)
@classmethod
def add_options(cls, parser, positional=False):
super(InstallTool, cls).add_options(parser, positional)
def add_options(cls, parser):
super(InstallTool, cls).add_options(parser)
if cls.uninstall_kwargs is not None:
parser.add_option(
uninstall_group = optparse.OptionGroup(parser, "uninstall options")
uninstall_group.add_option(
'--uninstall',
dest='uninstall',
default=False,
@@ -372,6 +344,7 @@ class InstallTool(ConfigureTool):
help=("uninstall an existing installation. The uninstall can "
"be run with --unattended option"),
)
parser.add_option_group(uninstall_group)
@classmethod
def get_command_class(cls, options, args):

View File

@@ -6,16 +6,14 @@
Common stuff.
"""
import logging
import traceback
from . import core
from .util import from_
__all__ = ['step', 'Installable', 'Interactive', 'installer',
__all__ = ['step', 'Installable', 'Interactive', 'Continuous', 'installer',
'uninstaller']
logger = logging.getLogger(__name__)
def step():
def decorator(func):
@@ -62,7 +60,7 @@ class Step(Installable):
raise AttributeError('parent')
def _install(self):
for unused in self._installer(self.parent):
for nothing in self._installer(self.parent):
yield from_(super(Step, self)._install())
@staticmethod
@@ -70,7 +68,7 @@ class Step(Installable):
yield
def _uninstall(self):
for unused in self._uninstaller(self.parent):
for nothing in self._uninstaller(self.parent):
yield from_(super(Step, self)._uninstall())
@staticmethod
@@ -87,6 +85,16 @@ class Interactive(core.Configurable):
interactive = core.Property(False)
class Continuous(core.Configurable):
def _handle_exception(self, exc_info):
try:
super(Continuous, self)._handle_exception(exc_info)
except BaseException as e:
self.log.debug(traceback.format_exc())
if isinstance(e, Exception):
self.log.error("%s", e)
def installer(cls):
class Installer(cls, Installable):
def __init__(self, **kwargs):
@@ -98,7 +106,7 @@ def installer(cls):
def uninstaller(cls):
class Uninstaller(cls, Installable):
class Uninstaller(Continuous, cls, Installable):
def __init__(self, **kwargs):
super(Uninstaller, self).__init__(uninstalling=True,
**kwargs)

View File

@@ -14,14 +14,13 @@ import sys
import six
from ipapython.ipa_log_manager import root_logger
from . import util
from .util import from_
__all__ = ['InvalidStateError', 'KnobValueError', 'Property', 'knob',
'Configurable', 'group', 'Component', 'Composite']
NoneType = type(None)
builtin_type = type
__all__ = ['InvalidStateError', 'KnobValueError', 'Property', 'Knob',
'Configurable', 'Group', 'Component', 'Composite']
# Configurable states
_VALIDATE_PENDING = 'VALIDATE_PENDING'
@@ -111,8 +110,9 @@ class KnobBase(PropertyBase):
sensitive = False
deprecated = False
description = None
cli_names = (None,)
cli_deprecated_names = ()
cli_name = None
cli_short_name = None
cli_aliases = None
cli_metavar = None
def __init__(self, outer):
@@ -121,15 +121,6 @@ class KnobBase(PropertyBase):
def validate(self, value):
pass
@classmethod
def group(cls):
return cls.__outer_class__.group()
@classmethod
def is_cli_positional(cls):
return all(n is not None and not n.startswith('-')
for n in cls.cli_names)
@classmethod
def default_getter(cls, func):
@property
@@ -149,82 +140,35 @@ class KnobBase(PropertyBase):
return cls
def _knob(type=_missing, default=_missing, bases=_missing, _order=_missing,
sensitive=_missing, deprecated=_missing, description=_missing,
group=_missing, cli_names=_missing, cli_deprecated_names=_missing,
cli_metavar=_missing):
if type is None:
type = NoneType
if bases is _missing:
bases = (KnobBase,)
elif isinstance(bases, builtin_type):
bases = (bases,)
if cli_names is None or isinstance(cli_names, str):
cli_names = (cli_names,)
elif cli_names is not _missing:
cli_names = tuple(cli_names)
if isinstance(cli_deprecated_names, str):
cli_deprecated_names = (cli_deprecated_names,)
elif cli_deprecated_names is not _missing:
cli_deprecated_names = tuple(cli_deprecated_names)
def Knob(type_or_base, default=_missing, sensitive=_missing,
deprecated=_missing, description=_missing, cli_name=_missing,
cli_short_name=_missing, cli_aliases=_missing, cli_metavar=_missing):
class_dict = {}
if type is not _missing:
class_dict['type'] = type
class_dict['_order'] = next(_counter)
if (not isinstance(type_or_base, type) or
not issubclass(type_or_base, KnobBase)):
class_dict['type'] = type_or_base
type_or_base = KnobBase
if default is not _missing:
class_dict['default'] = default
if _order is not _missing:
class_dict['_order'] = _order
if sensitive is not _missing:
class_dict['sensitive'] = sensitive
if deprecated is not _missing:
class_dict['deprecated'] = deprecated
if description is not _missing:
class_dict['description'] = description
if group is not _missing:
class_dict['group'] = group
if cli_names is not _missing:
class_dict['cli_names'] = cli_names
if cli_deprecated_names is not _missing:
class_dict['cli_deprecated_names'] = cli_deprecated_names
if cli_name is not _missing:
class_dict['cli_name'] = cli_name
if cli_short_name is not _missing:
class_dict['cli_short_name'] = cli_short_name
if cli_aliases is not _missing:
class_dict['cli_aliases'] = cli_aliases
if cli_metavar is not _missing:
class_dict['cli_metavar'] = cli_metavar
return util.InnerClassMeta('Knob', bases, class_dict)
def knob(type, default=_missing, **kwargs):
"""
Define a new knob.
"""
return _knob(
type, default,
_order=next(_counter),
**kwargs
)
def extend_knob(base, default=_missing, bases=_missing, group=_missing,
**kwargs):
"""
Extend an existing knob.
"""
if bases is _missing:
bases = (base,)
if group is _missing:
group = staticmethod(base.group)
return _knob(
_missing, default,
bases=bases,
_order=_missing,
group=group,
**kwargs
)
return util.InnerClassMeta('Knob', (type_or_base,), class_dict)
class Configurable(six.with_metaclass(abc.ABCMeta, object)):
@@ -261,7 +205,7 @@ class Configurable(six.with_metaclass(abc.ABCMeta, object)):
result = sorted(result, key=lambda r: r[0])
for _order, owner_cls, name in result:
for order, owner_cls, name in result:
yield owner_cls, name
@classmethod
@@ -275,11 +219,15 @@ class Configurable(six.with_metaclass(abc.ABCMeta, object)):
def group(cls):
assert not hasattr(super(Configurable, cls), 'group')
return None
def __init__(self, **kwargs):
"""
Initialize the configurable.
"""
self.log = root_logger
cls = self.__class__
for owner_cls, name in cls.properties():
if name.startswith('_'):
@@ -310,6 +258,8 @@ class Configurable(six.with_metaclass(abc.ABCMeta, object)):
prop = prop_cls(self)
try:
prop.validate(value)
except KnobValueError:
raise
except ValueError as e:
raise KnobValueError(name, str(e))
@@ -335,7 +285,7 @@ class Configurable(six.with_metaclass(abc.ABCMeta, object)):
raise TypeError("{0} is not composite".format(self))
def _get_fallback(self):
pass
return None
@abc.abstractmethod
def _configure(self):
@@ -357,15 +307,14 @@ class Configurable(six.with_metaclass(abc.ABCMeta, object)):
self.validate()
if self.__state == _EXECUTE_PENDING:
return self.execute()
return None
self.execute()
def validate(self):
"""
Run the validation part of the configurable.
"""
for _nothing in self._validator():
for nothing in self._validator():
pass
def _validator(self):
@@ -373,30 +322,22 @@ class Configurable(six.with_metaclass(abc.ABCMeta, object)):
Coroutine which runs the validation part of the configurable.
"""
return self.__runner(_VALIDATE_PENDING,
_VALIDATE_RUNNING,
self._handle_validate_exception)
return self.__runner(_VALIDATE_PENDING, _VALIDATE_RUNNING)
def execute(self):
"""
Run the execution part of the configurable.
"""
return_value = 0
for rval in self._executor():
if rval is not None and rval > return_value:
return_value = rval
return return_value
for nothing in self._executor():
pass
def _executor(self):
"""
Coroutine which runs the execution part of the configurable.
"""
return self.__runner(_EXECUTE_PENDING,
_EXECUTE_RUNNING,
self._handle_execute_exception)
return self.__runner(_EXECUTE_PENDING, _EXECUTE_RUNNING)
def done(self):
"""
@@ -412,7 +353,7 @@ class Configurable(six.with_metaclass(abc.ABCMeta, object)):
except StopIteration:
break
def __runner(self, pending_state, running_state, exc_handler):
def __runner(self, pending_state, running_state):
self.__transition(pending_state, running_state)
step = lambda: next(self.__gen)
@@ -428,10 +369,13 @@ class Configurable(six.with_metaclass(abc.ABCMeta, object)):
except BaseException:
exc_info = sys.exc_info()
try:
exc_handler(exc_info)
self._handle_exception(exc_info)
except BaseException:
self.__transition(running_state, _FAILED)
raise
else:
break
finally:
self.__transition(running_state, _FAILED)
if self.__state != running_state:
break
@@ -449,16 +393,6 @@ class Configurable(six.with_metaclass(abc.ABCMeta, object)):
six.reraise(*exc_info)
def _handle_validate_exception(self, exc_info):
assert not hasattr(super(Configurable, self),
'_handle_validate_exception')
self._handle_exception(exc_info)
def _handle_execute_exception(self, exc_info):
assert not hasattr(super(Configurable, self),
'_handle_execute_exception')
self._handle_exception(exc_info)
def __transition(self, from_state, to_state):
if self.__state != from_state:
raise InvalidStateError(self.__state)
@@ -466,14 +400,11 @@ class Configurable(six.with_metaclass(abc.ABCMeta, object)):
self.__state = to_state
def group(cls):
def group():
class Group(Configurable):
@classmethod
def group(cls):
return cls
cls.group = staticmethod(group)
return cls
class ComponentMeta(util.InnerClassMeta, abc.ABCMeta):
pass
@@ -597,7 +528,7 @@ class Composite(Configurable):
result = sorted(result, key=lambda r: r[0])
for _order, owner_cls, name in result:
for order, owner_cls, name in result:
yield owner_cls, name
def __getattr__(self, name):
@@ -621,7 +552,7 @@ class Composite(Configurable):
super(Composite, self)._reset()
def _get_components(self):
for _owner_cls, name in self.components():
for owner_cls, name in self.components():
yield getattr(self, name)
def _configure(self):

View File

@@ -1,34 +0,0 @@
#
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
#
import weakref
import six
_cache = weakref.WeakValueDictionary()
class ListMeta(type):
def __getitem__(cls, key):
if not isinstance(key, type):
raise TypeError("Parameters to generic types must be types. "
"Got {!r}.".format(key))
t = ListMeta(
cls.__name__,
cls.__bases__,
{
'__parameters__': (key,),
'__init__': cls.__init__,
}
)
return _cache.get(key, t)
class List(six.with_metaclass(ListMeta, list)):
__parameters__ = ()
def __init__(self, *_args, **_kwargs):
raise TypeError("Type List cannot be instantiated; use list() instead")

View File

@@ -82,23 +82,22 @@ def run_generator_with_yield_from(gen):
class InnerClassMeta(type):
# pylint: disable=no-value-for-parameter
def __new__(cls, name, bases, class_dict):
class_dict.pop('__outer_class__', None)
class_dict.pop('__outer_name__', None)
return super(InnerClassMeta, cls).__new__(cls, name, bases, class_dict)
def __get__(cls, obj, obj_type):
outer_class, outer_name = cls.__bind(obj_type)
def __get__(self, obj, obj_type):
outer_class, outer_name = self.__bind(obj_type)
if obj is None:
return cls
return self
assert isinstance(obj, outer_class)
try:
return obj.__dict__[outer_name]
except KeyError:
inner = cls(obj)
inner = self(obj)
try:
getter = inner.__get__
except AttributeError:
@@ -106,11 +105,11 @@ class InnerClassMeta(type):
else:
return getter(obj, obj_type)
def __set__(cls, obj, value):
outer_class, outer_name = cls.__bind(obj.__class__)
def __set__(self, obj, value):
outer_class, outer_name = self.__bind(obj.__class__)
assert isinstance(obj, outer_class)
inner = cls(obj)
inner = self(obj)
try:
setter = inner.__set__
except AttributeError:
@@ -123,11 +122,11 @@ class InnerClassMeta(type):
else:
setter(obj, value)
def __delete__(cls, obj):
outer_class, outer_name = cls.__bind(obj.__class__)
def __delete__(self, obj):
outer_class, outer_name = self.__bind(obj.__class__)
assert isinstance(obj, outer_class)
inner = cls(obj)
inner = self(obj)
try:
deleter = inner.__delete__
except AttributeError:
@@ -143,23 +142,23 @@ class InnerClassMeta(type):
else:
deleter(obj)
def __bind(cls, obj_type):
def __bind(self, obj_type):
try:
outer_class = cls.__dict__['__outer_class__']
name = cls.__dict__['__outer_name__']
cls = self.__dict__['__outer_class__']
name = self.__dict__['__outer_name__']
except KeyError:
outer_class, name, value = None, None, None
for outer_class in obj_type.__mro__:
for name, value in six.iteritems(outer_class.__dict__):
if value is cls:
cls, name, value = None, None, None
for cls in obj_type.__mro__:
for name, value in six.iteritems(cls.__dict__):
if value is self:
break
if value is cls:
if value is self:
break
assert value is cls
assert value is self
cls.__outer_class__ = outer_class
cls.__outer_name__ = name
cls.__name__ = '.'.join((outer_class.__name__, name))
cls.__qualname__ = cls.__name__
self.__outer_class__ = cls
self.__outer_name__ = name
self.__name__ = '.'.join((cls.__name__, name))
self.__qualname__ = self.__name__
return outer_class, name
return cls, name