freeipa/ipapython/install/util.py

166 lines
4.2 KiB
Python
Raw Normal View History

#
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
#
"""
Utilities.
"""
import sys
import six
class from_(object):
"""
Wrapper for delegating to a subgenerator.
See `run_generator_with_yield_from`.
"""
__slots__ = ('obj',)
def __init__(self, obj):
self.obj = obj
def run_generator_with_yield_from(gen):
"""
Iterate over a generator object with subgenerator delegation.
This implements Python 3's ``yield from`` expressions, using Python 2
syntax:
>>> def subgen():
... yield 'B'
... yield 'C'
...
>>> def gen():
... yield 'A'
... yield from_(subgen())
... yield 'D'
...
>>> list(run_generator_with_yield_from(gen()))
['A', 'B', 'C', 'D']
Returning value from a subgenerator is not supported.
"""
exc_info = None
value = None
stack = [gen]
while stack:
prev_exc_info, exc_info = exc_info, None
prev_value, value = value, None
gen = stack[-1]
try:
if prev_exc_info is None:
value = gen.send(prev_value)
else:
value = gen.throw(*prev_exc_info)
except StopIteration:
stack.pop()
continue
except BaseException:
exc_info = sys.exc_info()
stack.pop()
continue
else:
if isinstance(value, from_):
stack.append(value.obj)
value = None
continue
try:
value = (yield value)
except BaseException:
exc_info = sys.exc_info()
if exc_info is not None:
six.reraise(*exc_info)
class InnerClassMeta(type):
# pylint: disable=no-value-for-parameter
Disable Pylint 2.0 violations Globally disabling the following violations: - `assignment-from-no-return` (E1111): Assigning to function call which doesn't return. Used when an assignment is done on a function call but the inferred function doesn't return anything. - `keyword-arg-before-vararg` (W1113): Keyword argument before variable positional arguments list in the definition of %s function When defining a keyword argument before variable positional arguments, one can end up in having multiple values passed for the aforementioned parameter in case the method is called with keyword arguments. Locally disabling the following: - `subprocess-popen-preexec-fn` (W1509): Using preexec_fn keyword which may be unsafe in the presence of threads The preexec_fn parameter is not safe to use in the presence of threads in your application. The child process could deadlock before exec is called. If you must use it, keep it trivial! Minimize the number of libraries you call into. https://docs.python.org/3/library/subprocess.html#popen-constructor Fixed violations: - `bad-mcs-classmethod-argument` (C0204): Metaclass class method %s should have %s as first argument Used when a metaclass class method has a first argument named differently than the value specified in valid-metaclass-classmethod-first-arg option (default to "mcs"), recommended to easily differentiate them from regular instance methods. - Note: Actually `cls` is the default first arg for `__new__`. - `consider-using-get` (R1715): Consider using dict.get for getting values from a dict if a key is present or a default if not Using the builtin dict.get for getting a value from a dictionary if a key is present or a default if not, is simpler and considered more idiomatic, although sometimes a bit slower Issue: https://pagure.io/freeipa/issue/7614 Signed-off-by: Armando Neto <abiagion@redhat.com> Reviewed-By: Christian Heimes <cheimes@redhat.com>
2018-07-12 15:20:59 -05:00
def __new__(cls, name, bases, class_dict):
class_dict.pop('__outer_class__', None)
class_dict.pop('__outer_name__', None)
Disable Pylint 2.0 violations Globally disabling the following violations: - `assignment-from-no-return` (E1111): Assigning to function call which doesn't return. Used when an assignment is done on a function call but the inferred function doesn't return anything. - `keyword-arg-before-vararg` (W1113): Keyword argument before variable positional arguments list in the definition of %s function When defining a keyword argument before variable positional arguments, one can end up in having multiple values passed for the aforementioned parameter in case the method is called with keyword arguments. Locally disabling the following: - `subprocess-popen-preexec-fn` (W1509): Using preexec_fn keyword which may be unsafe in the presence of threads The preexec_fn parameter is not safe to use in the presence of threads in your application. The child process could deadlock before exec is called. If you must use it, keep it trivial! Minimize the number of libraries you call into. https://docs.python.org/3/library/subprocess.html#popen-constructor Fixed violations: - `bad-mcs-classmethod-argument` (C0204): Metaclass class method %s should have %s as first argument Used when a metaclass class method has a first argument named differently than the value specified in valid-metaclass-classmethod-first-arg option (default to "mcs"), recommended to easily differentiate them from regular instance methods. - Note: Actually `cls` is the default first arg for `__new__`. - `consider-using-get` (R1715): Consider using dict.get for getting values from a dict if a key is present or a default if not Using the builtin dict.get for getting a value from a dictionary if a key is present or a default if not, is simpler and considered more idiomatic, although sometimes a bit slower Issue: https://pagure.io/freeipa/issue/7614 Signed-off-by: Armando Neto <abiagion@redhat.com> Reviewed-By: Christian Heimes <cheimes@redhat.com>
2018-07-12 15:20:59 -05:00
return super(InnerClassMeta, cls).__new__(cls, name, bases, class_dict)
def __get__(cls, obj, obj_type):
outer_class, outer_name = cls.__bind(obj_type)
if obj is None:
return cls
assert isinstance(obj, outer_class)
try:
return obj.__dict__[outer_name]
except KeyError:
inner = cls(obj)
try:
getter = inner.__get__
except AttributeError:
return inner
else:
return getter(obj, obj_type)
def __set__(cls, obj, value):
outer_class, outer_name = cls.__bind(obj.__class__)
assert isinstance(obj, outer_class)
inner = cls(obj)
try:
setter = inner.__set__
except AttributeError:
try:
inner.__delete__
except AttributeError:
obj.__dict__[outer_name] = value
else:
raise AttributeError('__set__')
else:
setter(obj, value)
def __delete__(cls, obj):
outer_class, outer_name = cls.__bind(obj.__class__)
assert isinstance(obj, outer_class)
inner = cls(obj)
try:
deleter = inner.__delete__
except AttributeError:
try:
inner.__set__
except AttributeError:
try:
del obj.__dict__[outer_name]
except KeyError:
raise AttributeError(outer_name)
else:
raise AttributeError('__delete__')
else:
deleter(obj)
def __bind(cls, obj_type):
try:
outer_class = cls.__dict__['__outer_class__']
name = cls.__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:
break
if value is cls:
break
assert value is cls
cls.__outer_class__ = outer_class
cls.__outer_name__ = name
cls.__name__ = '.'.join((outer_class.__name__, name))
cls.__qualname__ = cls.__name__
return outer_class, name