From e61ad52b67b0ff57a462e160832e2adfe625ef2c Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Mon, 12 Oct 2026 14:54:50 -0500 Subject: [PATCH] move env helpers out of the config package (#24648) go2rtc's create_config.py imported frigate.config.env, which ran the frigate.config package init and loaded every detector plugin. Since the onnxruntime 1.30 bump, importing onnxruntime takes about 5s on some hosts, so go2rtc started after Frigate's cameras and every ffmpeg process failed to connect to the restream until it came up. The env helpers now live in frigate.util.env, so go2rtc's config script no longer imports onnxruntime. --- .../rootfs/usr/local/go2rtc/create_config.py | 2 +- frigate/api/camera.py | 2 +- frigate/config/camera/ffmpeg.py | 2 +- frigate/config/camera/genai.py | 3 ++- frigate/config/camera/notification.py | 3 ++- frigate/config/camera/onvif.py | 3 ++- frigate/config/config.py | 2 +- frigate/config/mqtt.py | 2 +- frigate/config/proxy.py | 3 ++- frigate/test/http_api/test_http_app.py | 2 +- frigate/test/test_env.py | 21 ++++++++++--------- frigate/test/test_proxy_auth.py | 2 +- frigate/{config => util}/env.py | 0 13 files changed, 26 insertions(+), 21 deletions(-) rename frigate/{config => util}/env.py (100%) diff --git a/docker/main/rootfs/usr/local/go2rtc/create_config.py b/docker/main/rootfs/usr/local/go2rtc/create_config.py index afbd30424a..51c155075c 100644 --- a/docker/main/rootfs/usr/local/go2rtc/create_config.py +++ b/docker/main/rootfs/usr/local/go2rtc/create_config.py @@ -8,13 +8,13 @@ from typing import Any from ruamel.yaml import YAML sys.path.insert(0, "/opt/frigate") -from frigate.config.env import apply_config_env_vars, substitute_frigate_vars from frigate.const import ( BIRDSEYE_PIPE, LIBAVFORMAT_VERSION_MAJOR, ) from frigate.ffmpeg_presets import parse_preset_hardware_acceleration_encode from frigate.util.config import find_config_file, resolve_ffmpeg_path +from frigate.util.env import apply_config_env_vars, substitute_frigate_vars from frigate.util.live_streams import raw_transcode_streams from frigate.util.services import ( is_go2rtc_arbitrary_exec_allowed, diff --git a/frigate/api/camera.py b/frigate/api/camera.py index 94eb632c80..0f5280f8c6 100644 --- a/frigate/api/camera.py +++ b/frigate/api/camera.py @@ -33,11 +33,11 @@ from frigate.config.camera.updater import ( CameraConfigUpdateEnum, CameraConfigUpdateTopic, ) -from frigate.config.env import UnknownVariableError, substitute_frigate_vars from frigate.models import User from frigate.util.builtin import clean_camera_user_pass, get_record_segment_time from frigate.util.camera_cleanup import cleanup_camera_db, cleanup_camera_files from frigate.util.config import find_config_file +from frigate.util.env import UnknownVariableError, substitute_frigate_vars from frigate.util.image import run_ffmpeg_snapshot from frigate.util.live_streams import ( generated_transcode_streams, diff --git a/frigate/config/camera/ffmpeg.py b/frigate/config/camera/ffmpeg.py index fa690df237..445525cd0c 100644 --- a/frigate/config/camera/ffmpeg.py +++ b/frigate/config/camera/ffmpeg.py @@ -3,9 +3,9 @@ from enum import Enum from pydantic import Field, field_validator from frigate.util.config import resolve_ffmpeg_path +from frigate.util.env import EnvString from ..base import FrigateBaseModel -from ..env import EnvString __all__ = [ "CameraFfmpegConfig", diff --git a/frigate/config/camera/genai.py b/frigate/config/camera/genai.py index 2437c7f5e3..5d4ef9a0cf 100644 --- a/frigate/config/camera/genai.py +++ b/frigate/config/camera/genai.py @@ -3,8 +3,9 @@ from typing import Any, Self from pydantic import Field, model_validator +from frigate.util.env import EnvString + from ..base import FrigateBaseModel -from ..env import EnvString __all__ = ["GenAIConfig", "GenAIProviderEnum", "GenAIRoleEnum"] diff --git a/frigate/config/camera/notification.py b/frigate/config/camera/notification.py index 4f5e246b48..5fe6a57834 100644 --- a/frigate/config/camera/notification.py +++ b/frigate/config/camera/notification.py @@ -1,7 +1,8 @@ from pydantic import Field +from frigate.util.env import EnvString + from ..base import FrigateBaseModel -from ..env import EnvString __all__ = ["NotificationConfig"] diff --git a/frigate/config/camera/onvif.py b/frigate/config/camera/onvif.py index 01c9432819..1db7bdf900 100644 --- a/frigate/config/camera/onvif.py +++ b/frigate/config/camera/onvif.py @@ -2,8 +2,9 @@ from enum import Enum from pydantic import Field, field_validator +from frigate.util.env import EnvString + from ..base import FrigateBaseModel -from ..env import EnvString from .objects import DEFAULT_TRACKED_OBJECTS __all__ = ["OnvifConfig", "PtzAutotrackConfig", "ZoomingModeEnum"] diff --git a/frigate/config/config.py b/frigate/config/config.py index 267db86e9a..1b2c2f029c 100644 --- a/frigate/config/config.py +++ b/frigate/config/config.py @@ -34,6 +34,7 @@ from frigate.util.config import ( get_relative_coordinates, migrate_frigate_config, ) +from frigate.util.env import EnvVars, reload_sources from frigate.util.image import create_mask from frigate.util.live_streams import ( default_transcode_source, @@ -71,7 +72,6 @@ from .classification import ( SemanticSearchModelEnum, ) from .database import DatabaseConfig -from .env import EnvVars, reload_sources from .logger import LoggerConfig from .mqtt import MqttConfig from .network import NetworkingConfig diff --git a/frigate/config/mqtt.py b/frigate/config/mqtt.py index 28c002a0de..5815a99f6f 100644 --- a/frigate/config/mqtt.py +++ b/frigate/config/mqtt.py @@ -3,9 +3,9 @@ from typing import Self from pydantic import Field, ValidationInfo, model_validator from frigate.const import FREQUENCY_STATS_POINTS +from frigate.util.env import EnvString from .base import FrigateBaseModel -from .env import EnvString __all__ = ["MqttConfig"] diff --git a/frigate/config/proxy.py b/frigate/config/proxy.py index 939585f62c..baaa8f0304 100644 --- a/frigate/config/proxy.py +++ b/frigate/config/proxy.py @@ -1,7 +1,8 @@ from pydantic import Field, field_validator +from frigate.util.env import EnvString + from .base import FrigateBaseModel -from .env import EnvString __all__ = ["ProxyConfig", "HeaderMappingConfig"] diff --git a/frigate/test/http_api/test_http_app.py b/frigate/test/http_api/test_http_app.py index ad13b3e145..686b50f303 100644 --- a/frigate/test/http_api/test_http_app.py +++ b/frigate/test/http_api/test_http_app.py @@ -4,12 +4,12 @@ from unittest.mock import Mock, patch import frigate.genai from frigate.config import GenAIProviderEnum -from frigate.config.env import FRIGATE_ENV_VARS from frigate.const import MODEL_CACHE_DIR, REDACTED_CREDENTIAL_SENTINEL from frigate.genai import GenAIClient from frigate.models import Event, Recordings, ReviewSegment from frigate.stats.emitter import StatsEmitter from frigate.test.http_api.base_http_test import AuthTestClient, BaseTestHttp +from frigate.util.env import FRIGATE_ENV_VARS class TestHttpApp(BaseTestHttp): diff --git a/frigate/test/test_env.py b/frigate/test/test_env.py index 66143c03ee..45af4764de 100644 --- a/frigate/test/test_env.py +++ b/frigate/test/test_env.py @@ -7,8 +7,9 @@ from unittest.mock import MagicMock, patch from pydantic import ValidationError -from frigate.config import FrigateConfig, env -from frigate.config.env import ( +from frigate.config import FrigateConfig +from frigate.util import env +from frigate.util.env import ( FRIGATE_ENV_VARS, validate_env_string, validate_env_vars, @@ -301,14 +302,14 @@ class TestVariableSources(unittest.TestCase): def test_collision_warns_once_naming_the_winner(self): """A key from two sources logs one warning naming the source that won.""" with patch.dict(env._CONTAINER_ENV, {"FRIGATE_MQTT_HOST": "from_env"}): - with self.assertLogs("frigate.config.env", level="WARNING") as logs: + with self.assertLogs("frigate.util.env", level="WARNING") as logs: env.apply_config_env_vars({"FRIGATE_MQTT_HOST": "from_config"}) self.assertEqual(len(logs.output), 1) self.assertIn("FRIGATE_MQTT_HOST", logs.output[0]) self.assertIn("using the value from container environment", logs.output[0]) self.assertNotIn("environment_vars", logs.output[0]) - with self.assertNoLogs("frigate.config.env", level="WARNING"): + with self.assertNoLogs("frigate.util.env", level="WARNING"): env._rebuild() @@ -360,7 +361,7 @@ class TestSecretsFile(unittest.TestCase): def test_unprefixed_key_is_ignored_with_a_warning(self): """Names must start with FRIGATE_, matching the credentials directory.""" self._write("cam_pass: hunter2\nFRIGATE_CAM_PASS: hunter2\n") - with self.assertLogs("frigate.config.env", level="WARNING") as logs: + with self.assertLogs("frigate.util.env", level="WARNING") as logs: values = env._load_secrets_file() self.assertEqual(values, {"FRIGATE_CAM_PASS": "hunter2"}) self.assertIn("cam_pass", logs.output[0]) @@ -483,10 +484,10 @@ class TestSourceRobustness(unittest.TestCase): env.reload_sources() env._CONFIG_ENV_VARS["FRIGATE_DUPE"] = "from_config" - with self.assertNoLogs("frigate.config.env", level="WARNING"): + with self.assertNoLogs("frigate.util.env", level="WARNING"): env._rebuild(warn=False) - with self.assertLogs("frigate.config.env", level="WARNING") as logs: + with self.assertLogs("frigate.util.env", level="WARNING") as logs: env._rebuild() self.assertIn("FRIGATE_DUPE", logs.output[0]) @@ -496,7 +497,7 @@ class TestSourceRobustness(unittest.TestCase): env.reload_sources() self._write_secrets("FRIGATE_CAMS:\n alley: hunter2\n") - with self.assertLogs("frigate.config.env", level="ERROR") as logs: + with self.assertLogs("frigate.util.env", level="ERROR") as logs: env.reload_sources() self.assertIn("FRIGATE_CAMS", logs.output[0]) @@ -505,7 +506,7 @@ class TestSourceRobustness(unittest.TestCase): def test_duplicate_key_error_does_not_log_the_values(self): """ruamel's duplicate key message quotes both values; the log must not.""" self._write_secrets("FRIGATE_CAM_PASS: hunter2\nFRIGATE_CAM_PASS: hunter3\n") - with self.assertLogs("frigate.config.env", level="ERROR") as logs: + with self.assertLogs("frigate.util.env", level="ERROR") as logs: env.reload_sources() self.assertNotIn("hunter2", logs.output[0]) @@ -535,7 +536,7 @@ class TestSourceRobustness(unittest.TestCase): with open(os.path.join(self._creds.name, "FRIGATE_CRED"), "w") as f: f.write("from_creds\n") - with self.assertLogs("frigate.config.env", level="WARNING") as logs: + with self.assertLogs("frigate.util.env", level="WARNING") as logs: env.reload_sources() self.assertIn("FRIGATE_NOT_A_FILE", logs.output[0]) diff --git a/frigate/test/test_proxy_auth.py b/frigate/test/test_proxy_auth.py index 1833fd26b7..1a486ac9ee 100644 --- a/frigate/test/test_proxy_auth.py +++ b/frigate/test/test_proxy_auth.py @@ -4,7 +4,7 @@ from pydantic import ValidationError from frigate.api.auth import resolve_role from frigate.config import AuthConfig, HeaderMappingConfig, ProxyConfig -from frigate.config.env import FRIGATE_ENV_VARS +from frigate.util.env import FRIGATE_ENV_VARS class TestProxyRoleResolution(unittest.TestCase): diff --git a/frigate/config/env.py b/frigate/util/env.py similarity index 100% rename from frigate/config/env.py rename to frigate/util/env.py