| 378 | return False |
| 379 | |
| 380 | def run(self, dash_app: Dash, host, port, debug, **kwargs): # pylint: disable=R0912 |
| 381 | frame = inspect.stack()[2] |
| 382 | if debug and kwargs.get("reload") is None: |
| 383 | kwargs["reload"] = True |
| 384 | |
| 385 | # Check if we're running in a thread (e.g., from testing framework) |
| 386 | # If so, run uvicorn directly instead of spawning a subprocess |
| 387 | is_threaded = threading.current_thread() != threading.main_thread() |
| 388 | |
| 389 | if is_threaded: |
| 390 | # Running in a thread (testing context) - use uvicorn.Server |
| 391 | # This allows graceful shutdown via should_exit flag |
| 392 | kwargs.pop("reload", None) # Reload not supported in threaded mode |
| 393 | config = uvicorn.Config(self.server, host=host, port=port, **kwargs) |
| 394 | server = uvicorn.Server(config) |
| 395 | # Store server reference on the app for graceful shutdown |
| 396 | dash_app._uvicorn_server = server # pylint: disable=protected-access |
| 397 | server.run() |
| 398 | else: |
| 399 | # Running in main thread (normal context) - use subprocess |
| 400 | file_path = frame.filename |
| 401 | rel_path = os.path.relpath(file_path, os.getcwd()) |
| 402 | |
| 403 | # Check if the file is outside the current working directory |
| 404 | if rel_path.startswith(".."): |
| 405 | # File is outside cwd, try to find the module name from sys.modules |
| 406 | module_name = None |
| 407 | for mod_name, mod in sys.modules.items(): |
| 408 | if hasattr(mod, "__file__") and mod.__file__: |
| 409 | if os.path.abspath(mod.__file__) == os.path.abspath(file_path): |
| 410 | module_name = mod_name |
| 411 | break |
| 412 | |
| 413 | # If we still can't find it, raise an error |
| 414 | if not module_name: |
| 415 | raise RuntimeError( |
| 416 | f"Cannot determine module name for {file_path}. " |
| 417 | "The file is outside the current working directory and not found in sys.modules. " |
| 418 | "Please ensure the FastAPI app is being run from a file within the current working directory." |
| 419 | ) |
| 420 | else: |
| 421 | # File is within cwd, use relative path |
| 422 | module_name = os.path.splitext(rel_path)[0].replace(os.sep, ".") |
| 423 | |
| 424 | # Find the Dash app variable name by inspecting the calling frame |
| 425 | dash_var_name = None |
| 426 | calling_frame = frame.frame |
| 427 | for var_name, var_value in calling_frame.f_locals.items(): |
| 428 | if var_value is dash_app: |
| 429 | dash_var_name = var_name |
| 430 | break |
| 431 | |
| 432 | # If not found in locals, check globals |
| 433 | if not dash_var_name: |
| 434 | for var_name, var_value in calling_frame.f_globals.items(): |
| 435 | if var_value is dash_app: |
| 436 | dash_var_name = var_name |
| 437 | break |