Decorator in charge of adding python properties to cls.

    {a/b}fget, {a/b}fset and {a/b}fdel are applied to all properties matching
    names in taking care to not forget default/existing properties. The
    prefixes *a* and *b* are respectively for after and before default/existing
    property getter/setter/deleter execution.

    These getter, setter and deleter functions are called before existing or
    default getter, setter and deleters. Default getter, setter and deleters
    are functions which uses an attribute with a name starting with '_' and
    finishing with the property name (like the python language convention).

    .. seealso::
        _protectedattrname(name)

    :param str(s) names: property name(s) to add.
    :param bfget: getter function to apply to all properties before
        default/existing getter execution. Parameters are a decorated cls
        instance and a property name.
    :param afget: fget function to apply to all properties after
        default/existing getter execution. Parameters are a decorated cls
        instance and a property name.
    :param bool enableget: if True (default), enable existing or default getter
        . Otherwise, use only fget if given.
    :param bfset: fset function to apply to all properties before
        default/existing setter execution. Parameters are a decorated cls
        instance and a property name.
    :param afset: fset function to apply to all properties after
        default/existing setter execution. Parameters are a decorated cls
        instance and a property name.
    :param bool enableset: if True (default), enable existing or default setter
        . Otherwise, use only fset if given.
    :param bfdel: fdel function to apply to all properties before
        default/existing deleter execution. Parameters are a decorated cls
        instance and a property name.
    :param bfdel: fdel function to apply to all properties after
        default/existing deleter execution. Parameters are a decorated cls
        instance and a property name.
    :param bool enabledel: if True (default), enable existing or default
        deleter. Otherwise, use only fdel if given.
    :return: cls decorator.

def addproperties(
        names,
        bfget=None, afget=None, enableget=True,
        bfset=None, afset=None, enableset=True,
        bfdel=None, afdel=None, enabledel=True
):
    """Decorator in charge of adding python properties to cls.

    {a/b}fget, {a/b}fset and {a/b}fdel are applied to all properties matching
    names in taking care to not forget default/existing properties. The
    prefixes *a* and *b* are respectively for after and before default/existing
    property getter/setter/deleter execution.

    These getter, setter and deleter functions are called before existing or
    default getter, setter and deleters. Default getter, setter and deleters
    are functions which uses an attribute with a name starting with '_' and
    finishing with the property name (like the python language convention).

    .. seealso::
        _protectedattrname(name)

    :param str(s) names: property name(s) to add.
    :param bfget: getter function to apply to all properties before
        default/existing getter execution. Parameters are a decorated cls
        instance and a property name.
    :param afget: fget function to apply to all properties after
        default/existing getter execution. Parameters are a decorated cls
        instance and a property name.
    :param bool enableget: if True (default), enable existing or default getter
        . Otherwise, use only fget if given.
    :param bfset: fset function to apply to all properties before
        default/existing setter execution. Parameters are a decorated cls
        instance and a property name.
    :param afset: fset function to apply to all properties after
        default/existing setter execution. Parameters are a decorated cls
        instance and a property name.
    :param bool enableset: if True (default), enable existing or default setter
        . Otherwise, use only fset if given.
    :param bfdel: fdel function to apply to all properties before
        default/existing deleter execution. Parameters are a decorated cls
        instance and a property name.
    :param bfdel: fdel function to apply to all properties after
        default/existing deleter execution. Parameters are a decorated cls
        instance and a property name.
    :param bool enabledel: if True (default), enable existing or default
        deleter. Otherwise, use only fdel if given.
    :return: cls decorator.
    """

    # ensure names is a list
    names = ensureiterable(names, exclude=string_types)

    if isinstance(bfget, MethodType):
        finalbfget = lambda self, name: bfget(name)
    else:
        finalbfget = bfget

    if isinstance(afget, MethodType):
        finalafget = lambda self, name: afget(name)
    else:
        finalafget = afget

    if isinstance(bfset, MethodType):
        finalbfset = lambda self, value, name: bfset(value, name)
    else:
        finalbfset = bfset

    if isinstance(afset, MethodType):
        finalafset = lambda self, value, name: afset(value, name)
    else:
        finalafset = afset

    if isinstance(bfdel, MethodType):
        finalbfdel = lambda self, name: bfdel(name)
    else:
        finalbfdel = bfdel

    if isinstance(afdel, MethodType):
        finalafdel = lambda self, name: afdel(name)
    else:
        finalafdel = afdel

    def _addproperties(cls):
        """Add properties to cls.

        :param type cls: cls on adding properties.
        :return: cls
        """

        for name in names:
            protectedattrname = _protectedattrname(name)
            # try to find an existing property
            existingproperty = getattr(cls, name, None)

            if isinstance(existingproperty, property):
                _fget = existingproperty.fget
                _fset = existingproperty.fset
                _fdel = existingproperty.fdel

            else:
                _fget, _fset, _fdel = None, None, None

            # construct existing/default getter
            if _fget is None:
                def _fget(protectedattrname):
                    """Simple getter wrapper."""
                    def _fget(self):
                        """Simple getter."""
                        return getattr(self, protectedattrname, None)
                    return _fget
                _fget = _fget(protectedattrname)
                _fget.__doc__ = 'Get this {0}.\n:return: this {0}.'.format(
                    name
                )
            # transform method to function in order to add self in parameters
            if isinstance(_fget, MethodType):
                final_fget = lambda self: _fget()

            else:
                final_fget = _fget

            # construct existing/default setter
            if _fset is None:
                def _fset(protectedattrname):
                    """Simple setter wrapper."""
                    def _fset(self, value):
                        """Simple setter."""
                        setattr(self, protectedattrname, value)
                    return _fset
                _fset = _fset(protectedattrname)
                _fset.__doc__ = (
                    'Change of {0}.\n:param {0}: {0} to use.'.format(name)
                )
            # transform method to function in order to add self in parameters
            if isinstance(_fset, MethodType):
                final_fset = lambda self, value: _fset(value)

            else:
                final_fset = _fset

            # construct existing/default deleter
            if _fdel is None:
                def _fdel(protectedattrname):
                    """Simple deleter wrapper."""
                    def _fdel(self):
                        """Simple deleter."""
                        if hasattr(self, protectedattrname):
                            delattr(self, protectedattrname)
                    return _fdel
                _fdel = _fdel(protectedattrname)
                _fdel.__doc__ = 'Delete this {0}.'.format(name)
            # transform method to function in order to add self in parameters
            if isinstance(_fdel, MethodType):
                final_fdel = lambda self: _fdel()
            else:
                final_fdel = _fdel

            def _getter(final_fget, name):
                """Property getter wrapper."""
                def _getter(self):
                    """Property getter."""
                    result = None
                    # start to process input bfget
                    if finalbfget is not None:
                        result = finalbfget(self, name)
                    # process cls getter
                    if enableget:
                        result = final_fget(self)
                    # finish to process afget
                    if finalafget is not None:
                        result = finalafget(self, name)
                    return result
                return _getter
            _getter = _getter(final_fget, name)
            _getter.__doc__ = final_fget.__doc__  # update doc

            def _setter(final_fset, name):
                """Property setter wrapper."""
                def _setter(self, value):
                    """Property setter."""
                    # start to process input bfset
                    if finalbfset is not None:
                        finalbfset(self, value, name)
                    # finish to process cls setter
                    if enableset:
                        final_fset(self, value)
                    # finish to process afset
                    if finalafset is not None:
                        finalafset(self, value, name)
                return _setter
            _setter = _setter(final_fset, name)
            _setter.__doc__ = final_fset.__doc__  # update doc

            def _deleter(final_fdel, name):
                """Property deleter wrapper."""
                def _deleter(self):
                    """Property deleter."""
                    # start to process input fdel
                    if finalbfdel is not None:
                        finalbfdel(self, name)
                    # finish to process cls deleter
                    if enabledel:
                        final_fdel(self)
                    # finish to process afget
                    if finalafdel is not None:
                        finalafdel(self, name)
                return _deleter
            _deleter = _deleter(final_fdel, name)
            _deleter.__doc__ = final_fdel.__doc__  # update doc

            # get property name
            doc = '{0} property.'.format(name)
            propertyfield = property(
                fget=_getter, fset=_setter, fdel=_deleter, doc=doc
            )

            # put property name in cls
            setattr(cls, name, propertyfield)

        return cls  # finish to return the cls

    return _addproperties