Apply pointcut on input target and returns final interception.

        The poincut respects all meta properties such as:
        - function signature,
        - module path,
        - file path,
        - __dict__ reference.

def apply_pointcut(self, target, function=None, ctx=None):
        """Apply pointcut on input target and returns final interception.

        The poincut respects all meta properties such as:
        - function signature,
        - module path,
        - file path,
        - __dict__ reference.
        """

        try:
            __file__ = getfile(target)
        except TypeError:
            __file__ = '<string>'

        if function is None:
            function = _get_function(target)

        # flag which indicates that the function is not a pure python function
        # and has to be wrapped
        wrap_function = not hasattr(function, '__code__')

        try:
            # get params from target
            args, varargs, kwargs, _ = getargspec(function)
        except TypeError:
            # in case of error, wrap the function
            wrap_function = True

        if wrap_function:
            # if function is not pure python, create a generic one
            # with assignments
            assigned = []
            for wrapper_assignment in WRAPPER_ASSIGNMENTS:
                if hasattr(function, wrapper_assignment):
                    assigned.append(wrapper_assignment)
            # and updates
            updated = []
            for wrapper_update in WRAPPER_UPDATES:
                if hasattr(function, wrapper_update):
                    updated.append(wrapper_update)

            @wraps(function, assigned=assigned, updated=updated)
            def wrapper(*args, **kwargs):
                """Default wrapper."""

            function = wrapper

            # get params from target wrapper
            args, varargs, kwargs, _ = getargspec(function)

        # get params from target
        name = function.__name__

        # if target has not name, use 'function'
        if name == Joinpoint.__LAMBDA_NAME__:
            name = Joinpoint.__INTERCEPTION__

        # get join method for reducing concatenation time execution
        join = "".join

        # default indentation
        indent = '    '

        newcodestr = "def {0}(".format(name)
        if args:
            newcodestr = join((newcodestr, "{0}".format(args[0])))
        for arg in args[1:]:
            newcodestr = join((newcodestr, ", {0}".format(arg)))

        if varargs is not None:
            if args:
                newcodestr = join((newcodestr, ", "))
            newcodestr = join((newcodestr, "*{0}".format(varargs)))

        if kwargs is not None:
            if args or varargs is not None:
                newcodestr = join((newcodestr, ", "))
            newcodestr = join((newcodestr, "**{0}".format(kwargs)))

        newcodestr = join((newcodestr, "):\n"))

        # unique id which will be used for advicesexecutor and kwargs
        generated_id = repr(time()).replace('.', '_')

        # if kwargs is None
        if kwargs is None and args:
            kwargs = "kwargs_{0}".format(generated_id)  # generate a name
            # initialize a new dict with args
            newcodestr = join(
                (newcodestr, "{0}{1} = {{\n".format(indent, kwargs)))
            for arg in args:
                newcodestr = join(
                    (newcodestr, "{0}{0}'{1}': {1},\n".format(indent, arg))
                )
            newcodestr = join((newcodestr, "{0}}}\n".format(indent)))

        else:
            # fill args in kwargs
            for arg in args:
                newcodestr = join(
                    (newcodestr, "{0}{1}['{2}'] = {2}\n".format(
                        indent, kwargs, arg))
                )

        # advicesexecutor name
        joinpoint = "joinpoint_{0}".format(generated_id)

        if varargs:
            newcodestr = join(
                (newcodestr, "{0}{1}.args = {2}\n".format(
                    indent, joinpoint, varargs))
            )

        # set kwargs in advicesexecutor
        if kwargs is not None:
            newcodestr = join(
                (newcodestr, "{0}{1}.kwargs = {2}\n".format(
                    indent, joinpoint, kwargs))
            )

        # return advicesexecutor proceed result
        start = "start_{0}".format(generated_id)
        newcodestr = join(
            (newcodestr, "{0}return {1}()\n".format(indent, start))
        )
        # compile newcodestr
        code = compile(newcodestr, __file__, 'single')

        _globals = {}

        # define the code with the new function
        exec(code, _globals)

        # get new code
        newco = _globals[name].__code__

        # get new consts list
        newconsts = list(newco.co_consts)

        if PY3:
            newcode = list(newco.co_code)
        else:
            newcode = map(ord, newco.co_code)

        consts_values = {joinpoint: self, start: self.start}

        # change LOAD_GLOBAL to LOAD_CONST
        index = 0
        newcodelen = len(newcode)
        while index < newcodelen:
            if newcode[index] == LOAD_GLOBAL:
                oparg = newcode[index + 1] + (newcode[index + 2] << 8)
                name = newco.co_names[oparg]
                if name in consts_values:
                    const_value = consts_values[name]
                    if const_value in newconsts:
                        pos = newconsts.index(const_value)
                    else:
                        pos = len(newconsts)
                        newconsts.append(consts_values[name])
                    newcode[index] = LOAD_CONST
                    newcode[index + 1] = pos & 0xFF
                    newcode[index + 2] = pos >> 8
                    if name == start:
                        break  # stop when start is encountered
            index += 1

        # get code string
        codestr = bytes(newcode) if PY3 else join(map(chr, newcode))

        # get vargs
        vargs = [
            newco.co_argcount, newco.co_nlocals, newco.co_stacksize,
            newco.co_flags, codestr, tuple(newconsts), newco.co_names,
            newco.co_varnames, newco.co_filename, newco.co_name,
            newco.co_firstlineno, newco.co_lnotab,
            getattr(function.__code__, 'co_freevars', ()),
            newco.co_cellvars
        ]
        if PY3:
            vargs.insert(1, newco.co_kwonlyargcount)

        # instanciate a new code object
        codeobj = type(newco)(*vargs)
        # instanciate a new function
        if function is None or isbuiltin(function):
            interception_fn = FunctionType(codeobj, {})

        else:
            interception_fn = type(function)(
                codeobj,
                {} if function.__globals__ is None else function.__globals__,
                function.__name__,
                function.__defaults__,
                function.__closure__
            )

        # set wrapping assignments
        for wrapper_assignment in WRAPPER_ASSIGNMENTS:
            try:
                value = getattr(function, wrapper_assignment)
            except AttributeError:
                pass
            else:
                setattr(interception_fn, wrapper_assignment, value)
        # update wrapping updating
        for wrapper_update in WRAPPER_UPDATES:
            try:
                value = getattr(function, wrapper_update)
            except AttributeError:
                pass
            else:
                getattr(interception_fn, wrapper_update).update(value)

        # set interception, target function and ctx
        self._interception, self.target, self.ctx = _apply_interception(
            target=target, interception_fn=interception_fn, ctx=ctx
        )

        return self._interception