66These models can be extended to create specific configurations for different types of connectors.
77"""
88
9- import os
9+ import sys
1010from abc import ABC
1111from copy import deepcopy
1212from datetime import timedelta
1313from pathlib import Path
1414from typing import Any , Literal , Self
1515
16- import __main__
1716from connectors_sdk .settings .annotated_types import ListFromString
1817from connectors_sdk .settings .exceptions import ConfigValidationError
1918from pydantic import (
@@ -87,6 +86,47 @@ class _SettingsLoader(BaseSettings):
8786 enable_decoding = False ,
8887 )
8988
89+ @classmethod
90+ def _get_connector_main_path (cls ) -> Path :
91+ """Locate the main module of the running connector.
92+ This method is used to locate configuration files relative to connector's entrypoint.
93+
94+ Notes:
95+ - This method assumes that the connector is launched using a file-backed entrypoint
96+ (i.e., `python -m <module>` or `python <file>`).
97+ - At module import time, `__main__.__file__` might not be available yet,
98+ thus this method should be called at runtime only.
99+ """
100+ main = sys .modules .get ("__main__" )
101+ if main and getattr (main , "__file__" , None ):
102+ return Path (main .__file__ ).resolve () # type: ignore
103+
104+ raise RuntimeError (
105+ "Cannot determine connector's location: __main__.__file__ is not available. "
106+ "Ensure the connector is launched using `python -m <module>` or a file-backed entrypoint."
107+ )
108+
109+ @classmethod
110+ def _get_config_yml_file_path (cls ) -> Path | None :
111+ """Locate the `config.yml` file of the running connector."""
112+ main_path = cls ._get_connector_main_path ()
113+ config_yml_legacy_file_path = main_path .parent / "config.yml"
114+ config_yml_file_path = main_path .parent .parent / "config.yml"
115+
116+ if config_yml_legacy_file_path .is_file ():
117+ return config_yml_legacy_file_path
118+ elif config_yml_file_path .is_file ():
119+ return config_yml_file_path
120+ return None
121+
122+ @classmethod
123+ def _get_dot_env_file_path (cls ) -> Path | None :
124+ """Locate the `.env` file of the running connector."""
125+ main_path = cls ._get_connector_main_path ()
126+ dot_env_file_path = main_path .parent .parent / ".env"
127+
128+ return dot_env_file_path if dot_env_file_path .is_file () else None
129+
90130 @classmethod
91131 def settings_customise_sources (
92132 cls ,
@@ -109,26 +149,22 @@ def settings_customise_sources(
109149 1. If a config.yml file is found, the order will be: `ENV VAR` → config.yml → default value
110150 2. If a .env file is found, the order will be: `ENV VAR` → .env → default value
111151 """
112- _main_path = os .path .dirname (os .path .abspath (__main__ .__file__ ))
113-
114- settings_cls .model_config ["env_file" ] = f"{ _main_path } /../.env"
115-
116- if not settings_cls .model_config ["yaml_file" ]:
117- if Path (f"{ _main_path } /config.yml" ).is_file ():
118- settings_cls .model_config ["yaml_file" ] = f"{ _main_path } /config.yml"
119- if Path (f"{ _main_path } /../config.yml" ).is_file ():
120- settings_cls .model_config ["yaml_file" ] = f"{ _main_path } /../config.yml"
121-
122- if Path (settings_cls .model_config ["yaml_file" ] or "" ).is_file (): # type: ignore
152+ config_yml_file_path = cls ._get_config_yml_file_path ()
153+ if config_yml_file_path :
154+ settings_cls .model_config ["yaml_file" ] = config_yml_file_path
123155 return (
124156 env_settings ,
125157 YamlConfigSettingsSource (settings_cls ),
126158 )
127- if Path (settings_cls .model_config ["env_file" ] or "" ).is_file (): # type: ignore
159+
160+ dot_env_file_path = cls ._get_dot_env_file_path ()
161+ if dot_env_file_path :
162+ settings_cls .model_config ["env_file" ] = dot_env_file_path
128163 return (
129164 env_settings ,
130165 DotEnvSettingsSource (settings_cls ),
131166 )
167+
132168 return (env_settings ,)
133169
134170 @classmethod
0 commit comments