fix: add launcher script to wrap langflow startup (#8884)

* Add launcher script to wrap langflow startup

* [autofix.ci] apply automated fixes

* Ruff

* [autofix.ci] apply automated fixes

* ruff

* [autofix.ci] apply automated fixes

* rut pusff

* [autofix.ci] apply automated fixes

* use exec instead of subprocess

* [autofix.ci] apply automated fixes

* Remove forced run addition to cli

* Integrate Typer for improved error handling in langflow launcher

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
This commit is contained in:
Jordan Frazier 2025-07-07 06:28:54 -07:00 • committed by GitHub
commit 56ff3e12f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 130 additions and 110 deletions

View file

@ -0,0 +1,51 @@
import os
import platform
import sys
import typer
def main():
"""Launches langflow with appropriate environment setup.
On macOS, sets required environment variables and replaces current process.
On other platforms, calls main function directly.
"""
if platform.system() == "Darwin": # macOS
_launch_with_exec()
else:
# On non-macOS systems, call the main function directly
from langflow.__main__ import main as langflow_main
langflow_main()
def _launch_with_exec():
"""Launch langflow by replacing current process with properly configured environment.
This approach is necessary because Objective-C libraries are preloaded by the Python
runtime before any Python code executes. Setting OBJC_DISABLE_INITIALIZE_FORK_SAFETY
within Python code is too late - it must be set in the parent process environment
before spawning Python.
Testing with OBJC_PRINT_INITIALIZE=YES confirms that NSCheapMutableString and
other Objective-C classes are initialized during Python startup, before any
user code runs. This causes fork safety issues when gunicorn or multiprocessing
attempts to fork the process.
The exec approach sets the environment variables and then replaces the current
process with a new Python process. This is more efficient than subprocess since
we don't need the launcher process to remain running, and signals are handled
directly by the target process.
"""
# Set environment variables before exec
os.environ["OBJC_DISABLE_INITIALIZE_FORK_SAFETY"] = "YES"
# Additional fix for gunicorn compatibility
os.environ["no_proxy"] = "*"
try:
os.execv(sys.executable, [sys.executable, "-m", "langflow.__main__"] + sys.argv[1:]) # noqa: S606
except OSError as e:
# If exec fails, we need to exit since the process replacement failed
typer.echo(f"Failed to exec langflow: {e}", file=sys.stderr)
sys.exit(1)