{"record":{"id":"70d758a5034cad54","repo":"cocoindex-io/cocoindex","slug":"overriding-the-default-lifespan-function-self-li","errorCode":null,"errorMessage":"Overriding the default lifespan function {self._lifespan_fn} with {fn}.","messagePattern":"Overriding the default lifespan function (.+?) with (.+?)\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"python/cocoindex/_internal/environment.py","lineNumber":332,"sourceCode":"        self._lifespan_fn = None\n        self._exit_stack = None\n        self._env = None\n        self._info = EnvironmentInfo(self)\n\n    def _get_start_stop_lock(self) -> asyncio.Lock:\n        \"\"\"Get or create the start/stop lock (must be called from async context).\"\"\"\n        if self._start_stop_lock is None:\n            self._start_stop_lock = asyncio.Lock()\n        return self._start_stop_lock\n\n    @property\n    def name(self) -> str:\n        return self._name\n\n    def lifespan(self, fn: LifespanFn) -> None:\n        with self._lifespan_fn_lock:\n            if self._lifespan_fn is not None:\n                warnings.warn(\n                    f\"Overriding the default lifespan function {self._lifespan_fn} with {fn}.\"\n                )\n            self._lifespan_fn = fn\n\n    async def _reset(self) -> None:\n        await self.stop()\n        with self._lifespan_fn_lock:\n            self._lifespan_fn = None\n\n    async def _get_env(self) -> Environment:\n        \"\"\"\n        Start the default environment (executes on the default environment's event loop).\n        \"\"\"\n        async with self._get_start_stop_lock():\n            if self._env is not None:\n                return self._env\n            with self._lifespan_fn_lock:\n                fn = self._lifespan_fn or _noop_lifespan_fn","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/_internal/environment.py#L314-L350","documentation":"Environment.lifespan() registers the lifespan function; if one was already registered, the old one is overwritten and a warning (not an error) is emitted showing both functions. This usually means two parts of the program each tried to set the app's lifespan, so one provider silently stops being used.","triggerScenarios":"Calling env.lifespan(fn) twice on the same Environment — e.g. a library sets a default lifespan and app code sets another, or a setup helper is invoked twice (common with test fixtures and app re-initialization).","commonSituations":"Combining cocoindex helpers that internally call lifespan() with your own lifespan registration; running tests where a module-level setup and a per-test fixture both configure the same Environment; duplicated app bootstrap code after a refactor.","solutions":["Register the lifespan only once per Environment; consolidate the two functions into one.","Compose lifespans: call the first function inside the second (or use a stacking helper) instead of overwriting.","If overwriting is intentional, suppress/handle the warning explicitly and document why.","Ensure setup helpers are idempotent (guard with a flag) so lifespan isn't set twice."],"exampleFix":"# before\nenv.lifespan(setup_db)\nenv.lifespan(setup_cache)  # overwrites setup_db\n\n# after\nasync def combined():\n    await setup_db()\n    await setup_cache()\nenv.lifespan(combined)","handlingStrategy":"validation","validationCode":"assert env._lifespan_fn is None, \"lifespan already registered; compose instead of overriding\"","typeGuard":null,"tryCatchPattern":"import warnings\nwith warnings.catch_warnings(record=True) as w:\n    warnings.simplefilter(\"always\")\n    env.lifespan(fn)\n    if any(\"Overriding the default lifespan\" in str(x.message) for x in w):\n        raise RuntimeError(\"lifespan registered twice — compose the functions instead\")","preventionTips":["Register lifespan exactly once per Environment, ideally in a single bootstrap function.","Compose multiple setup steps into one lifespan function.","Treat this warning as an error in CI with `-W error::UserWarning` filters."],"tags":["python","lifespan","environment","warning"],"backgroundTag":"conflicting-config-options","analyzedSha":"e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b","analyzedAt":"2026-09-08T15:59:19.997Z","contentChangedAt":"2026-09-08T15:59:19.997Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}