docs: def wrapFQPN(fqpn):
      """
      Given an FQPN, retrieve the object via the global Python module
      namespace and wrap it with a L{PythonModule} or a
      L{twisted.python.modules.PythonAttribute}.
      """
      # largely cribbed from t.p.reflect.namedAny
  
      if not fqpn:
          raise InvalidFQPN("FQPN was empty")
  
      components = collections.deque(fqpn.split('.'))
  
      if '' in components:
          raise InvalidFQPN(
              "name must be a string giving a '.'-separated list of Python "
              "identifiers, not %r" % (fqpn,))
  
      component = components.popleft()
      try:
          module = getModule(component)
      except KeyError:
          raise NoModule(component)
  
      # find the bottom-most module
      while components:
          component = components.popleft()
          try:
              module = module[component]
          except KeyError:
              components.appendleft(component)
              break
          else:
              module.load()
      else:
          return module
  
      # find the bottom-most attribute
      attribute = module
      for component in components:
          try:
              attribute = next(child for child in attribute.iterAttributes()
                               if child.name.rsplit('.', 1)[-1] == component)
          except StopIteration:
              raise NoObject('{}.{}'.format(attribute.name, component))
  
      return attribute