docs: 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