docs: def attach(self, module_factory, name=None, **kwargs):
          """Attach a module to the pipeline system"""
          if self.anybar: self.anybar.change("yellow")
  
          fac = module_factory
  
          if name is None:
              name = fac.__name__
  
          log.info("Attaching module '{0}'".format(name))
  
          if (inspect.isclass(fac) and issubclass(fac, Module)) or \
                  name == 'GenericPump':
              log.debug("Attaching as regular module")
              if name in self.module_configuration:
                  log.debug(
                      "Applying pipeline configuration file for module '%s'" %
                      name
                  )
                  for key, value in self.module_configuration[name].items():
                      if key in kwargs:
                          self.log.info(
                              "Overwriting parameter '%s' in module '%s' from "
                              "the pipeline configuration file." % (key, name)
                          )
                      kwargs[key] = value
              module = fac(name=name, **kwargs)
              if hasattr(module, "provided_services"):
                  for service_name, obj in module.provided_services.items():
                      self.services.register(service_name, obj)
              if hasattr(module, "required_services"):
                  updated_required_services = {}
                  updated_required_services.update(self.required_services)
                  updated_required_services.update(module.required_services)
                  self.required_services = updated_required_services
              module.services = self.services
              module.pipeline = self
          else:
              if isinstance(fac, types.FunctionType):
                  log.debug("Attaching as function module")
              else:
                  log.critical(
                      "Don't know how to attach module '{0}'!\n"
                      "But I'll do my best".format(name)
                  )
              module = fac
              module.name = name
              module.timeit = self.timeit
  
          # Special parameters
          if 'only_if' in kwargs:
              required_keys = kwargs['only_if']
              if isinstance(required_keys, basestring):
                  required_keys = [required_keys]
              module.only_if = set(required_keys)
          else:
              module.only_if = set()
  
          if 'blob_keys' in kwargs:
              module.blob_keys = kwargs['blob_keys']
          else:
              module.blob_keys = None
  
          if 'every' in kwargs:
              module.every = kwargs['every']
          else:
              module.every = 1
  
          self._timeit[module] = {
              'process': deque(maxlen=STAT_LIMIT),
              'process_cpu': deque(maxlen=STAT_LIMIT),
              'finish': 0,
              'finish_cpu': 0
          }
  
          if hasattr(module, 'get_detector'):    # Calibration-like module
              self.log.deprecation(
                  "Calibration-like modules will not be supported in future "
                  "versions of KM3Pipe. Please use services instead.\n"
                  "If you are attaching the `calib.Calibration` as a module, "
                  "switch to `calib.CalibrationService` and use the "
                  "`self.services['calibrate']()` method in your modules "
                  "to apply calibration.\n\n"
                  "This means:\n\n"
                  "    pipe.attach(kp.calib.CalibrationService, ...)\n\n"
                  "And inside the attached modules, you can apply the "
                  "calibration with e.g.:\n\n"
                  "    cal_hits = self.services['calibrate'](blob['Hits'])\n"
              )
              self.calibration = module
              if module._should_apply:
                  self.modules.append(module)
          else:    # normal module
              module.calibration = self.calibration
              self.modules.append(module)