Mirrowel commited on
Commit
682bbea
Β·
1 Parent(s): f925da4

fix(env): πŸ› use explicit .env path for PyInstaller compatibility

Browse files

Replaced hardcoded `Path.cwd() / ".env"` references with centralized `_get_env_file()` helper function across launcher_tui.py, main.py, and settings_tool.py.

- Adds `_get_env_file()` helper that detects PyInstaller frozen state
- When running as EXE, resolves .env relative to executable directory
- When running as script, resolves .env relative to current working directory
- Updates all `load_dotenv()` calls to use explicit path instead of relying on implicit cwd resolution
- Ensures consistent .env file location regardless of execution context

This fixes issues where the application couldn't locate the .env file when running as a PyInstaller bundle and the working directory differed from the executable location.

src/proxy_app/launcher_tui.py CHANGED
@@ -16,6 +16,20 @@ from dotenv import load_dotenv, set_key
16
  console = Console()
17
 
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  def clear_screen():
20
  """
21
  Cross-platform terminal clear that works robustly on both
@@ -74,7 +88,7 @@ class LauncherConfig:
74
  @staticmethod
75
  def update_proxy_api_key(new_key: str):
76
  """Update PROXY_API_KEY in .env only"""
77
- env_file = Path.cwd() / ".env"
78
  set_key(str(env_file), "PROXY_API_KEY", new_key)
79
  load_dotenv(dotenv_path=env_file, override=True)
80
 
@@ -85,7 +99,7 @@ class SettingsDetector:
85
  @staticmethod
86
  def _load_local_env() -> dict:
87
  """Load environment variables from local .env file only"""
88
- env_file = Path.cwd() / ".env"
89
  env_dict = {}
90
  if not env_file.exists():
91
  return env_dict
@@ -271,7 +285,7 @@ class LauncherTUI:
271
  self.console = Console()
272
  self.config = LauncherConfig()
273
  self.running = True
274
- self.env_file = Path.cwd() / ".env"
275
  # Load .env file to ensure environment variables are available
276
  load_dotenv(dotenv_path=self.env_file, override=True)
277
 
@@ -428,7 +442,7 @@ class LauncherTUI:
428
  elif choice == "4":
429
  self.show_provider_settings_menu()
430
  elif choice == "5":
431
- load_dotenv(dotenv_path=Path.cwd() / ".env", override=True)
432
  self.config = LauncherConfig() # Reload config
433
  self.console.print("\n[green]βœ… Configuration reloaded![/green]")
434
  elif choice == "6":
@@ -824,7 +838,7 @@ class LauncherTUI:
824
  # Run the tool with from_launcher=True to skip duplicate loading screen
825
  run_credential_tool(from_launcher=True)
826
  # Reload environment after credential tool
827
- load_dotenv(dotenv_path=Path.cwd() / ".env", override=True)
828
 
829
  def launch_settings_tool(self):
830
  """Launch settings configuration tool"""
@@ -848,7 +862,7 @@ class LauncherTUI:
848
 
849
  run_settings_tool()
850
  # Reload environment after settings tool
851
- load_dotenv(dotenv_path=Path.cwd() / ".env", override=True)
852
 
853
  def show_about(self):
854
  """Display About page with project information"""
@@ -936,9 +950,9 @@ class LauncherTUI:
936
  )
937
 
938
  ensure_env_defaults()
939
- load_dotenv(dotenv_path=Path.cwd() / ".env", override=True)
940
  run_credential_tool()
941
- load_dotenv(dotenv_path=Path.cwd() / ".env", override=True)
942
 
943
  # Check again after credential tool
944
  if not os.getenv("PROXY_API_KEY"):
 
16
  console = Console()
17
 
18
 
19
+ def _get_env_file() -> Path:
20
+ """
21
+ Get .env file path (lightweight - no heavy imports).
22
+
23
+ Returns:
24
+ Path to .env file - EXE directory if frozen, else current working directory
25
+ """
26
+ if getattr(sys, "frozen", False):
27
+ # Running as PyInstaller EXE - use EXE's directory
28
+ return Path(sys.executable).parent / ".env"
29
+ # Running as script - use current working directory
30
+ return Path.cwd() / ".env"
31
+
32
+
33
  def clear_screen():
34
  """
35
  Cross-platform terminal clear that works robustly on both
 
88
  @staticmethod
89
  def update_proxy_api_key(new_key: str):
90
  """Update PROXY_API_KEY in .env only"""
91
+ env_file = _get_env_file()
92
  set_key(str(env_file), "PROXY_API_KEY", new_key)
93
  load_dotenv(dotenv_path=env_file, override=True)
94
 
 
99
  @staticmethod
100
  def _load_local_env() -> dict:
101
  """Load environment variables from local .env file only"""
102
+ env_file = _get_env_file()
103
  env_dict = {}
104
  if not env_file.exists():
105
  return env_dict
 
285
  self.console = Console()
286
  self.config = LauncherConfig()
287
  self.running = True
288
+ self.env_file = _get_env_file()
289
  # Load .env file to ensure environment variables are available
290
  load_dotenv(dotenv_path=self.env_file, override=True)
291
 
 
442
  elif choice == "4":
443
  self.show_provider_settings_menu()
444
  elif choice == "5":
445
+ load_dotenv(dotenv_path=_get_env_file(), override=True)
446
  self.config = LauncherConfig() # Reload config
447
  self.console.print("\n[green]βœ… Configuration reloaded![/green]")
448
  elif choice == "6":
 
838
  # Run the tool with from_launcher=True to skip duplicate loading screen
839
  run_credential_tool(from_launcher=True)
840
  # Reload environment after credential tool
841
+ load_dotenv(dotenv_path=_get_env_file(), override=True)
842
 
843
  def launch_settings_tool(self):
844
  """Launch settings configuration tool"""
 
862
 
863
  run_settings_tool()
864
  # Reload environment after settings tool
865
+ load_dotenv(dotenv_path=_get_env_file(), override=True)
866
 
867
  def show_about(self):
868
  """Display About page with project information"""
 
950
  )
951
 
952
  ensure_env_defaults()
953
+ load_dotenv(dotenv_path=_get_env_file(), override=True)
954
  run_credential_tool()
955
+ load_dotenv(dotenv_path=_get_env_file(), override=True)
956
 
957
  # Check again after credential tool
958
  if not os.getenv("PROXY_API_KEY"):
src/proxy_app/main.py CHANGED
@@ -326,7 +326,7 @@ litellm_logger.propagate = False
326
  logging.debug(f"Modules loaded in {_elapsed:.2f}s")
327
 
328
  # Load environment variables from .env file
329
- load_dotenv()
330
 
331
  # --- Configuration ---
332
  USE_EMBEDDING_BATCHER = False
@@ -1333,7 +1333,7 @@ if __name__ == "__main__":
1333
 
1334
  ensure_env_defaults()
1335
  # Reload environment variables after ensure_env_defaults creates/updates .env
1336
- load_dotenv(override=True)
1337
  run_credential_tool()
1338
  else:
1339
  # Check if onboarding is needed
@@ -1351,11 +1351,11 @@ if __name__ == "__main__":
1351
  from rotator_library.credential_tool import ensure_env_defaults
1352
 
1353
  ensure_env_defaults()
1354
- load_dotenv(override=True)
1355
  run_credential_tool()
1356
 
1357
  # After credential tool exits, reload and re-check
1358
- load_dotenv(override=True)
1359
  # Re-read PROXY_API_KEY from environment
1360
  PROXY_API_KEY = os.getenv("PROXY_API_KEY")
1361
 
 
326
  logging.debug(f"Modules loaded in {_elapsed:.2f}s")
327
 
328
  # Load environment variables from .env file
329
+ load_dotenv(_root_dir / ".env")
330
 
331
  # --- Configuration ---
332
  USE_EMBEDDING_BATCHER = False
 
1333
 
1334
  ensure_env_defaults()
1335
  # Reload environment variables after ensure_env_defaults creates/updates .env
1336
+ load_dotenv(ENV_FILE, override=True)
1337
  run_credential_tool()
1338
  else:
1339
  # Check if onboarding is needed
 
1351
  from rotator_library.credential_tool import ensure_env_defaults
1352
 
1353
  ensure_env_defaults()
1354
+ load_dotenv(ENV_FILE, override=True)
1355
  run_credential_tool()
1356
 
1357
  # After credential tool exits, reload and re-check
1358
+ load_dotenv(ENV_FILE, override=True)
1359
  # Re-read PROXY_API_KEY from environment
1360
  PROXY_API_KEY = os.getenv("PROXY_API_KEY")
1361
 
src/proxy_app/settings_tool.py CHANGED
@@ -64,7 +64,7 @@ class AdvancedSettings:
64
  """Load current .env values into env vars"""
65
  from dotenv import load_dotenv
66
 
67
- load_dotenv(override=True)
68
 
69
  def set(self, key: str, value: str):
70
  """Stage a change"""
 
64
  """Load current .env values into env vars"""
65
  from dotenv import load_dotenv
66
 
67
+ load_dotenv(self.env_file, override=True)
68
 
69
  def set(self, key: str, value: str):
70
  """Stage a change"""