fabiommendes/sidekick

View on GitHub
sidekick-api/sidekick/api/_operators.py

Summary

Maintainability
A
3 hrs
Test Coverage
import operator as op

#  Lists of methods
METHODS = [op.getitem, op.contains, op.delitem, op.getitem, getattr]
UNARY = [op.abs, op.invert, op.neg, op.pos, op.index, hash, len, str, int, repr]
BINARY = [
    op.add,
    op.and_,
    op.floordiv,
    op.lshift,
    op.matmul,
    op.mod,
    op.mul,
    op.or_,
    op.pow,
    op.rshift,
    op.sub,
    op.truediv,
    op.xor,
]
COMPARISON = [op.eq, op.ge, op.gt, op.le, op.lt, op.ne]
KEYWORDS = [op.is_, op.is_not, op.not_]

# Map symbol functions to their respective names
OP_SYMBOLS = {
    op.invert: "~",
    op.neg: "-",
    op.pos: "+",
    op.add: "+",
    op.floordiv: "//",
    op.lshift: "<<",
    op.matmul: "@",
    op.mod: "%",
    op.mul: "*",
    op.pow: "**",
    op.rshift: ">>",
    op.sub: "-",
    op.truediv: "/",
    op.and_: "&",
    op.xor: "^ ",
    op.or_: "|",
    op.eq: "==",
    op.ge: ">=",
    op.gt: ">",
    op.le: "<=",
    op.lt: "<",
    op.ne: "!=",
}

IRREGULAR = {"invert": "not"}


def normalize(name):
    if name.endswith("___"):
        return name[:-1]
    else:
        return IRREGULAR.get(name, name)


OP_NAMES = {
    **{f: normalize(f.__name__) for f in UNARY},
    **{f: normalize(f.__name__) for f in BINARY},
    **{f: normalize(f.__name__) for f in COMPARISON},
    **{f: normalize(f.__name__) for f in METHODS},
}
FROM_NAMES = {v: k for k, v in OP_NAMES.items()}
FROM_SYMBOLS = {v: k for k, v in OP_SYMBOLS.items()}


def op_wrapper_class(
    binary,
    rbinary=None,
    unary=None,
    arithmetic=True,
    bitwise=False,
    logic=True,
    inplace=True,
    sequence=True,
    operator=op,
) -> type:
    """
    Create a wrapper class that implements the given operations as a
    transformation from operator functions or equivalent.

    Args:
        binary:
            Function of operator -> method that creates binary operator.
            Operator is extracted from the given operator namespace.
        rbinary:
            Function of operator -> method that creates binary operator. For
            reversed operations (e.g., __radd__, __rmul__, etc).
        unary:
            Function of operator -> method that creates unary binary operator.
        arithmetic:
            If False, disable arithmetic operators.
        bitwise:
            If False, disable bitwise operators.
        logic:
            If False, disable logic operators.
        inplace:
            If False, disable inplace operators.
        sequence:
            If False, disable sequencing operators.
        operator:
            The operator namespace used to fetch operator functions that will be
            passed to constructors. Defaults to Python stdlib's operator module.
    """

    # noinspection DuplicatedCode
    class Base:
        if arithmetic:
            __add__ = binary(operator.add)
            __floordiv__ = binary(operator.floordiv)
            __mod__ = binary(operator.mod)
            __mul__ = binary(operator.mul)
            __pow__ = binary(operator.pow)
            __sub__ = binary(operator.sub)
            __truediv__ = binary(operator.truediv)
            if rbinary:
                __radd__ = rbinary(operator.add)
                __rfloordiv__ = rbinary(operator.floordiv)
                __rmod__ = rbinary(operator.mod)
                __rmul__ = rbinary(operator.mul)
                __rpow__ = rbinary(operator.pow)
                __rsub__ = rbinary(operator.sub)
                __rtruediv__ = rbinary(operator.truediv)
            if inplace:
                __iadd__ = binary(operator.iadd)
                __ifloordiv__ = binary(operator.ifloordiv)
                __imod__ = binary(operator.imod)
                __imul__ = binary(operator.imul)
                __ipow__ = binary(operator.ipow)
                __isub__ = binary(operator.isub)
                __itruediv__ = binary(operator.itruediv)
            if unary:
                __abs__ = unary(getattr(operator, "abs", abs))
                __int__ = unary(getattr(operator, "int", int))
                __float__ = unary(getattr(operator, "float", float))
                __complex__ = unary(getattr(operator, "complex", complex))
                __index__ = unary(operator.index)
                __neg__ = unary(operator.neg)
                __pos__ = unary(operator.pos)

        if bitwise:
            __and__ = binary(operator.and_)
            __lshift__ = binary(operator.lshift)
            __rshift__ = binary(operator.rshift)
            __or__ = binary(operator.or_)
            __xor__ = binary(operator.xor)
            if rbinary:
                __rand__ = rbinary(operator.and_)
                __rlshift__ = rbinary(operator.lshift)
                __rrshift__ = rbinary(operator.rshift)
                __ror__ = rbinary(operator.or_)
                __rxor__ = rbinary(operator.xor)
            if inplace:
                __iand__ = binary(operator.iand)
                __ilshift__ = binary(operator.ilshift)
                __ior__ = binary(operator.ior)
                __irshift__ = binary(operator.irshift)
                __ixor__ = binary(operator.ixor)
            if unary:
                __invert__ = unary(operator.invert)

        if logic:
            __eq__ = binary(operator.eq)
            __ge__ = binary(operator.ge)
            __gt__ = binary(operator.gt)
            __le__ = binary(operator.le)
            __lt__ = binary(operator.lt)
            __ne__ = binary(operator.ne)

        if sequence:
            __contains__ = binary(operator.contains)
            __delitem__ = binary(operator.delitem)
            __getitem__ = binary(operator.getitem)

        if unary:
            __hash__ = unary(getattr(operator, "hash", hash))
            __len__ = unary(getattr(operator, "len", len))
            __str__ = unary(getattr(operator, "str", str))
            __repr__ = unary(getattr(operator, "repr", repr))

    return Base