pyegeria 5.4.0.4__py3-none-any.whl → 5.4.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- commands/cat/list_collections.py +37 -41
- commands/cat/list_format_set.py +6 -6
- pyegeria/__init__.py +1 -1
- pyegeria/_exceptions_new.py +29 -0
- pyegeria/_output_formats.py +30 -4
- pyegeria/collection_manager_omvs.py +25 -24
- pyegeria/data_designer_omvs.py +123 -106
- pyegeria/load_config.py +418 -144
- pyegeria/load_config_orig.py +218 -0
- pyegeria/logging_configuration.py +21 -15
- pyegeria/output_formatter.py +2 -0
- {pyegeria-5.4.0.4.dist-info → pyegeria-5.4.0.5.dist-info}/METADATA +2 -1
- {pyegeria-5.4.0.4.dist-info → pyegeria-5.4.0.5.dist-info}/RECORD +16 -15
- {pyegeria-5.4.0.4.dist-info → pyegeria-5.4.0.5.dist-info}/LICENSE +0 -0
- {pyegeria-5.4.0.4.dist-info → pyegeria-5.4.0.5.dist-info}/WHEEL +0 -0
- {pyegeria-5.4.0.4.dist-info → pyegeria-5.4.0.5.dist-info}/entry_points.txt +0 -0
pyegeria/load_config.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
"""
|
3
2
|
SPDX-License-Identifier: Apache-2.0
|
4
3
|
Copyright Contributors to the ODPi Egeria project.
|
@@ -6,212 +5,487 @@ Copyright Contributors to the ODPi Egeria project.
|
|
6
5
|
This module manages configuration information for pyegeria and pyegeria clients.
|
7
6
|
|
8
7
|
The load_app_config() function loads configuration information:
|
9
|
-
1) Default configuration variables are specified in the
|
10
|
-
2)
|
11
|
-
|
12
|
-
|
13
|
-
3)
|
14
|
-
|
8
|
+
1) Default configuration variables are specified in the Pydantic models below.
|
9
|
+
2) Environment variables are loaded from the .env file using pydantic-settings.
|
10
|
+
- PYEGERIA_ROOT_PATH and PYEGERIA_CONFIG_FILE are loaded first to locate the config file
|
11
|
+
- Additional environment variables are loaded from the operating system
|
12
|
+
3) We construct a path to an external configuration JSON file from the Environment Variables
|
13
|
+
- PYEGERIA_ROOT_PATH
|
14
|
+
- PYEGERIA_CONFIG_FILE
|
15
|
+
4) If a valid configuration file is found, the configuration will be loaded on top of the default configuration.
|
16
|
+
5) We then update the in-memory configuration from Environment Variables, if set.
|
15
17
|
|
16
18
|
The result is that Environment Variable values take priority over configuration file values which override the defaults.
|
17
19
|
|
18
20
|
The get_app_config() function is used by other modules to get configuration information from the configuration structure
|
19
|
-
and makes it available as a
|
21
|
+
and makes it available as a Pydantic model.
|
20
22
|
|
21
23
|
"""
|
22
24
|
import inspect
|
23
25
|
import os
|
24
26
|
import json
|
27
|
+
from typing import List, Optional, Union, Dict, Any
|
28
|
+
|
25
29
|
from loguru import logger
|
26
|
-
from
|
30
|
+
from pydantic import BaseModel, Field, validator
|
31
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
32
|
+
|
33
|
+
from pyegeria._exceptions_new import PyegeriaInvalidParameterException, PyegeriaException
|
34
|
+
|
35
|
+
logger.disable("pyegeria")
|
36
|
+
# --- Pydantic Settings for Environment Variables ---
|
37
|
+
|
38
|
+
class PyegeriaSettings(BaseSettings):
|
39
|
+
"""
|
40
|
+
Settings loaded from environment variables using pydantic-settings.
|
41
|
+
This class is used to load environment variables from the .env file.
|
42
|
+
|
43
|
+
The .env file path can be specified when creating an instance of this class
|
44
|
+
by passing the `_env_file` parameter. If not specified, it defaults to ".env"
|
45
|
+
in the current directory.
|
46
|
+
"""
|
47
|
+
# Core settings needed to locate the config file
|
48
|
+
pyegeria_root_path: str = ""
|
49
|
+
pyegeria_config_file: str = "config.json"
|
50
|
+
|
51
|
+
# Additional settings that can be loaded from .env
|
52
|
+
pyegeria_console_width: int = 200
|
53
|
+
egeria_user_name: str = ""
|
54
|
+
egeria_user_password: str = ""
|
55
|
+
|
56
|
+
model_config = SettingsConfigDict(
|
57
|
+
env_file=".env",
|
58
|
+
env_file_encoding="utf-8",
|
59
|
+
extra="ignore",
|
60
|
+
case_sensitive=False
|
61
|
+
)
|
62
|
+
|
63
|
+
@classmethod
|
64
|
+
def with_env_file(cls, env_file: str):
|
65
|
+
"""
|
66
|
+
Create a PyegeriaSettings instance with a specific .env file.
|
67
|
+
|
68
|
+
Args:
|
69
|
+
env_file: Path to the .env file to load
|
70
|
+
|
71
|
+
Returns:
|
72
|
+
PyegeriaSettings: A new PyegeriaSettings instance
|
73
|
+
"""
|
74
|
+
# Create a new class with a custom model_config that specifies the env_file
|
75
|
+
class CustomSettings(cls):
|
76
|
+
model_config = SettingsConfigDict(
|
77
|
+
env_file=env_file,
|
78
|
+
env_file_encoding="utf-8",
|
79
|
+
extra="ignore",
|
80
|
+
case_sensitive=False
|
81
|
+
)
|
82
|
+
|
83
|
+
# Create and return an instance of the custom class
|
84
|
+
return CustomSettings()
|
85
|
+
|
86
|
+
|
87
|
+
# --- Pydantic Models for Configuration ---
|
27
88
|
|
89
|
+
class EnvironmentConfig(BaseModel):
|
90
|
+
"""Environment configuration settings"""
|
91
|
+
console_width: int = Field(default=200, alias="Console Width")
|
92
|
+
dr_egeria_inbox: str = Field(default="md_processing/dr-egeria-inbox", alias="Dr.Egeria Inbox")
|
93
|
+
dr_egeria_outbox: str = Field(default="md_processing/dr-egeria-outbox", alias="Dr.Egeria Outbox")
|
94
|
+
egeria_engine_host_url: str = Field(default="", alias="Egeria Engine Host URL")
|
95
|
+
egeria_engine_host: str = Field(default="qs-engine-host", alias="Egeria Engine Host")
|
96
|
+
egeria_glossary_path: str = Field(default="glossary", alias="Egeria Glossary Path")
|
97
|
+
egeria_integration_daemon_url: str = Field(default="https://localhost:9443", alias="Egeria Integration Daemon URL")
|
98
|
+
egeria_integration_daemon: str = Field(default="qs-integration-daemon", alias="Egeria Integration Daemon")
|
99
|
+
egeria_jupyter: bool = Field(default=True, alias="Egeria Jupyter")
|
100
|
+
egeria_kafka_endpoint: str = Field(default="localhost:9192", alias="Egeria Kafka Endpoint")
|
101
|
+
egeria_mermaid_folder: str = Field(default="mermaid_graphs", alias="Egeria Mermaid Folder")
|
102
|
+
egeria_metadata_store: str = Field(default="qs-metadata-store", alias="Egeria Metadata Store")
|
103
|
+
egeria_platform_url: str = Field(default="https://localhost:9443", alias="Egeria Platform URL")
|
104
|
+
egeria_view_server_url: str = Field(default="https://localhost:9443", alias="Egeria View Server URL")
|
105
|
+
egeria_view_server: str = Field(default="qs-view-server", alias="Egeria View Server")
|
106
|
+
pyegeria_root: str = Field(default="/Users/dwolfson/localGit/egeria-v5-3/egeria-python", alias="Pyegeria Root")
|
107
|
+
|
108
|
+
class Config:
|
109
|
+
populate_by_name = True
|
110
|
+
extra = "allow"
|
111
|
+
|
112
|
+
|
113
|
+
class DebugConfig(BaseModel):
|
114
|
+
"""Debug configuration settings"""
|
115
|
+
debug_mode: bool = False
|
116
|
+
enable_logger_catch: bool = False
|
117
|
+
timeout_seconds: int = 30
|
118
|
+
|
119
|
+
class Config:
|
120
|
+
populate_by_name = True
|
121
|
+
extra = "allow"
|
122
|
+
|
123
|
+
|
124
|
+
class LoggingConfig(BaseModel):
|
125
|
+
"""Logging configuration settings"""
|
126
|
+
console_filter_levels: List[str] = ["ERROR"]
|
127
|
+
console_logging_enabled: List[str] = ["_client_new", "_exceptions_new", "collections_manager_omvs", "tests"]
|
128
|
+
console_logging_level: str = "INFO"
|
129
|
+
enable_logging: bool = False
|
130
|
+
file_logging_level: str = "INFO"
|
131
|
+
log_directory: str = "logs"
|
132
|
+
logging_console_format: str = " <green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level}</level> | <cyan>{name}</cyan>:<cyan>{line}</cyan> - <level>{message}</level> -{extra}"
|
133
|
+
logging_file_format: str = " {time:YYYY-MM-DD HH:mm:ss} | {level} | {function}:{line} - {message }-{extra}"
|
134
|
+
|
135
|
+
class Config:
|
136
|
+
populate_by_name = True
|
137
|
+
extra = "allow"
|
138
|
+
|
139
|
+
|
140
|
+
class UserProfileConfig(BaseModel):
|
141
|
+
"""User profile configuration settings"""
|
142
|
+
egeria_home_collection: str = Field(default="MyHome", alias="Egeria Home Collection")
|
143
|
+
egeria_home_glossary_name: str = Field(default="Egeria-Markdown", alias="Egeria Home Glossary Name")
|
144
|
+
egeria_local_qualifier: str = Field(default="PDR", alias="Egeria Local Qualifier")
|
145
|
+
user_name: str = ""
|
146
|
+
user_pwd: str = ""
|
147
|
+
|
148
|
+
@validator('user_name')
|
149
|
+
def validate_user_name(cls, v):
|
150
|
+
if not v:
|
151
|
+
raise ValueError("Egeria user name is not found in the configuration")
|
152
|
+
return v
|
153
|
+
|
154
|
+
@validator('user_pwd')
|
155
|
+
def validate_user_pwd(cls, v):
|
156
|
+
if not v:
|
157
|
+
raise ValueError("Egeria user password is not found in the configuration")
|
158
|
+
return v
|
159
|
+
|
160
|
+
class Config:
|
161
|
+
populate_by_name = True
|
162
|
+
extra = "allow"
|
163
|
+
|
164
|
+
|
165
|
+
class AppConfig(BaseModel):
|
166
|
+
"""Main application configuration"""
|
167
|
+
Environment: EnvironmentConfig
|
168
|
+
Debug: DebugConfig
|
169
|
+
Logging: LoggingConfig
|
170
|
+
User_Profile: UserProfileConfig = Field(alias="User Profile")
|
171
|
+
feature_x_enabled: bool = False
|
172
|
+
|
173
|
+
def get(self, key, default=None):
|
174
|
+
"""
|
175
|
+
Dictionary-like get method for backward compatibility.
|
176
|
+
|
177
|
+
Args:
|
178
|
+
key: The key to look up
|
179
|
+
default: The default value to return if the key is not found
|
180
|
+
|
181
|
+
Returns:
|
182
|
+
The value for the key if found, otherwise the default value
|
183
|
+
"""
|
184
|
+
# First check if the key is a direct attribute of this model
|
185
|
+
if hasattr(self, key):
|
186
|
+
return getattr(self, key)
|
187
|
+
|
188
|
+
# Then check if it's in any of the nested models
|
189
|
+
for section in [self.Environment, self.Debug, self.Logging, self.User_Profile]:
|
190
|
+
if hasattr(section, key):
|
191
|
+
return getattr(section, key)
|
192
|
+
# Also check using the original field names (with aliases)
|
193
|
+
for field_name, field in section.model_fields.items():
|
194
|
+
# In Pydantic v2, the alias is stored in json_schema_extra
|
195
|
+
alias = None
|
196
|
+
if hasattr(field, 'alias'):
|
197
|
+
alias = field.alias
|
198
|
+
elif hasattr(field, 'json_schema_extra') and field.json_schema_extra:
|
199
|
+
alias = field.json_schema_extra.get('alias')
|
200
|
+
|
201
|
+
if alias == key:
|
202
|
+
return getattr(section, field_name)
|
203
|
+
|
204
|
+
# If not found, return the default
|
205
|
+
return default
|
206
|
+
|
207
|
+
class Config:
|
208
|
+
populate_by_name = True
|
209
|
+
extra = "allow"
|
28
210
|
|
29
|
-
# from dotenv import load_dotenv # pip install python-dotenv if you use .env files
|
30
211
|
|
31
212
|
# --- Configuration Loading Logic ---
|
32
213
|
|
33
214
|
# Private variable to hold the loaded configuration
|
34
215
|
_app_config = None
|
35
216
|
|
36
|
-
def load_app_config():
|
217
|
+
def load_app_config(env_file: str = None):
|
37
218
|
"""
|
38
219
|
Loads application configuration from files and environment variables.
|
39
220
|
This function should ideally be called only once at application startup.
|
221
|
+
|
222
|
+
The function follows this precedence order for configuration settings:
|
223
|
+
1. If env_file is passed in, it uses that file to load environment variables
|
224
|
+
2. Otherwise, it first checks if OS environment variables are set for PYEGERIA_ROOT_PATH and PYEGERIA_CONFIG_FILE
|
225
|
+
3. If they are not set, it checks for a .env file in the current directory
|
226
|
+
4. It then loads the configuration from the config file if available
|
227
|
+
5. Finally, it updates the configuration with environment variables from the operating system,
|
228
|
+
which take precedence over the config file values
|
229
|
+
|
230
|
+
Args:
|
231
|
+
env_file: Optional path to a specific .env file to load. If not specified,
|
232
|
+
the function follows the precedence order described above.
|
233
|
+
|
234
|
+
Returns:
|
235
|
+
AppConfig: The loaded configuration as a Pydantic model
|
40
236
|
"""
|
41
|
-
global _app_config
|
237
|
+
global _app_config # Declare intent to modify the global _app_config
|
42
238
|
|
43
239
|
if _app_config is not None:
|
44
240
|
# Configuration already loaded, return existing instance
|
45
241
|
return _app_config
|
46
242
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
"
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
"
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
243
|
+
# Start with default configuration from Pydantic models
|
244
|
+
config_dict = {
|
245
|
+
"Environment": {},
|
246
|
+
"Debug": {},
|
247
|
+
"Logging": {},
|
248
|
+
"User Profile": {},
|
249
|
+
"feature_x_enabled": False
|
250
|
+
}
|
251
|
+
|
252
|
+
# Initialize env_settings with default values
|
253
|
+
env_settings = PyegeriaSettings()
|
254
|
+
|
255
|
+
# First check if OS environment variables are set for PYEGERIA_ROOT_PATH and PYEGERIA_CONFIG_FILE
|
256
|
+
root_path = os.getenv("PYEGERIA_ROOT_PATH")
|
257
|
+
config_file = os.getenv("PYEGERIA_CONFIG_FILE")
|
258
|
+
|
259
|
+
logger.info(f"DEBUG: Initial root_path from OS env: {root_path}")
|
260
|
+
logger.info(f"DEBUG: Initial config_file from OS env: {config_file}")
|
261
|
+
logger.info(f"DEBUG: env_file parameter: {env_file}")
|
262
|
+
|
263
|
+
# If env_file is specified, use it to load environment variables
|
264
|
+
if env_file is not None:
|
265
|
+
print(f"DEBUG: Loading environment variables from {env_file}")
|
266
|
+
env_settings = PyegeriaSettings.with_env_file(env_file)
|
267
|
+
print(f"DEBUG: env_settings.pyegeria_root_path: {env_settings.pyegeria_root_path}")
|
268
|
+
print(f"DEBUG: env_settings.pyegeria_config_file: {env_settings.pyegeria_config_file}")
|
269
|
+
# If env_file is specified, always use its values, regardless of OS environment variables
|
270
|
+
root_path = env_settings.pyegeria_root_path
|
271
|
+
config_file = env_settings.pyegeria_config_file
|
272
|
+
# If config_file is set but root_path is not, we'll try to load the config file first
|
273
|
+
# and only check the .env file if we still don't have a root_path after loading the config file
|
274
|
+
elif config_file is not None and root_path is None:
|
275
|
+
# We'll check for a .env file later if needed
|
276
|
+
pass
|
277
|
+
# If neither config_file nor root_path is set, check for a .env file in the current directory
|
278
|
+
elif (root_path is None or config_file is None):
|
279
|
+
if os.path.exists(".env"):
|
280
|
+
logger.info("Found .env file")
|
281
|
+
logger.debug(f"DEBUG: Loading environment variables from .env in current directory")
|
282
|
+
env_settings = PyegeriaSettings()
|
283
|
+
logger.debug(f"DEBUG: env_settings.pyegeria_root_path: {env_settings.pyegeria_root_path}")
|
284
|
+
logger.debug(f"DEBUG: env_settings.pyegeria_config_file: {env_settings.pyegeria_config_file}")
|
285
|
+
if root_path is None:
|
286
|
+
root_path = env_settings.pyegeria_root_path
|
287
|
+
if config_file is None:
|
288
|
+
config_file = env_settings.pyegeria_config_file
|
289
|
+
else:
|
290
|
+
logger.error(f"The .env file at {env_file} wasn't found")
|
291
|
+
|
292
|
+
# Use default values if still not set
|
293
|
+
if root_path is None:
|
294
|
+
root_path = ""
|
295
|
+
if config_file is None:
|
296
|
+
config_file = "config.json"
|
297
|
+
|
298
|
+
# Construct the config file path
|
299
|
+
config_file_path = os.path.join(root_path, config_file)
|
300
|
+
|
105
301
|
if os.path.exists(config_file_path):
|
106
302
|
try:
|
107
303
|
with open(config_file_path, 'r') as f:
|
108
304
|
file_config = json.load(f)
|
109
|
-
|
110
|
-
|
305
|
+
config_dict.update(file_config) # Merge/override defaults
|
306
|
+
|
307
|
+
# If root_path is not set from environment variables or .env file,
|
308
|
+
# set it from the config file if available
|
309
|
+
if not root_path and "Environment" in file_config and "Pyegeria Root" in file_config["Environment"]:
|
310
|
+
root_path = file_config["Environment"]["Pyegeria Root"]
|
311
|
+
logger.debug(f"DEBUG: Setting root_path from config file: {root_path}")
|
111
312
|
except json.JSONDecodeError:
|
112
|
-
|
313
|
+
logger.warning(f"Warning: Could not parse {config_file_path}. Using defaults/env vars.")
|
113
314
|
except Exception as e:
|
114
|
-
|
315
|
+
logger.warning(f"Warning: Error reading {config_file_path}: {e}. Using defaults/env vars.")
|
115
316
|
else:
|
116
317
|
logger.warning(f"Warning: Could not find {config_file_path}. Using defaults/env vars.")
|
117
318
|
|
118
|
-
|
119
|
-
|
120
|
-
|
319
|
+
# The root_path has already been set with the correct precedence order:
|
320
|
+
# 1. If env_file is passed in, use that file to load environment variables
|
321
|
+
# 2. Otherwise, first check OS environment variables
|
322
|
+
# 3. If not set, check for a .env file in the current directory
|
323
|
+
# 4. If still not set, use the default value
|
324
|
+
# We don't need to set it again here, as that would override the precedence order
|
325
|
+
env = config_dict.get("Environment", {})
|
326
|
+
|
327
|
+
# Update configuration from environment variables
|
328
|
+
# Debug section
|
329
|
+
debug = config_dict["Debug"]
|
330
|
+
debug['debug_mode'] = _parse_bool_env("PYEGERIA_DEBUG_MODE", debug.get("debug_mode", False))
|
331
|
+
debug["enable_logger_catch"] = _parse_bool_env("PYEGERIA_ENABLE_LOGGER_CATCH", debug.get("enable_logger_catch", False))
|
121
332
|
debug["timeout_seconds"] = int(os.getenv("PYEGERIA_TIMEOUT_SECONDS", debug.get("timeout_seconds", 30)))
|
122
333
|
|
123
|
-
|
124
|
-
env
|
125
|
-
|
126
|
-
|
127
|
-
env["Dr.Egeria
|
128
|
-
|
334
|
+
# Environment section
|
335
|
+
env = config_dict["Environment"]
|
336
|
+
# Use the settings from .env file, but allow OS environment variables to override them
|
337
|
+
env["Console Width"] = int(os.getenv("PYEGERIA_CONSOLE_WIDTH", env.get("Console Width", env_settings.pyegeria_console_width)))
|
338
|
+
env["Dr.Egeria Inbox"] = os.getenv("DR_EGERIA_INBOX_PATH", env.get("Dr.Egeria Inbox", "md_processing/dr-egeria-inbox"))
|
339
|
+
env["Dr.Egeria Outbox"] = os.getenv("DR_EGERIA_OUTBOX_PATH", env.get("Dr.Egeria Outbox", "md_processing/dr-egeria-outbox"))
|
129
340
|
env["Egeria Engine Host"] = os.getenv("EGERIA_ENGINE_HOST", env.get("Egeria Engine Host", "qs-engine-host"))
|
130
|
-
env["Egeria Engine Host URL"] = os.getenv("EGERIA_ENGINE_HOST_URL",env.get("Egeria Engine Host URL", "https://localhost:9443"))
|
131
|
-
|
341
|
+
env["Egeria Engine Host URL"] = os.getenv("EGERIA_ENGINE_HOST_URL", env.get("Egeria Engine Host URL", "https://localhost:9443"))
|
132
342
|
env["Egeria Glossary Path"] = os.getenv("EGERIA_GLOSSARY_PATH", env.get("Egeria Glossary Path", "glossary"))
|
133
343
|
env["Egeria Integration Daemon"] = os.getenv("EGERIA_INTEGRATION_DAEMON", env.get("Egeria Integration Daemon", "qs-integration-daemon"))
|
134
|
-
env["Egeria Integration Daemon URL"] = os.getenv("EGERIA_INTEGRATION_DAEMON_URL",env.get("Egeria Integration Daemon URL", "https://localhost:9443"))
|
135
|
-
env["Egeria Jupyter"] =
|
136
|
-
env["Egeria Kafka"] = os.getenv("EGERIA_KAFKA", env.get("Egeria Kafka", "
|
137
|
-
env["Egeria Mermaid Folder"] = os.getenv("EGERIA_MERMAID_FOLDER", env.get("Egeria Mermaid Folder","mermaid_graphs"))
|
138
|
-
env["Egeria Metadata Store"] = os.getenv("EGERIA_METADATA_STORE", env.get("Egeria Metadata Store","qs-metadata-store"))
|
139
|
-
env["Egeria Platform URL"] = os.getenv("EGERIA_PLATFORM_URL", env.get("Egeria Platform URL","https://localhost:9443"))
|
140
|
-
env["Egeria View Server"] = os.getenv("EGERIA_VIEW_SERVER", env.get("Egeria View Server","qs-view-server"))
|
141
|
-
env["Egeria
|
344
|
+
env["Egeria Integration Daemon URL"] = os.getenv("EGERIA_INTEGRATION_DAEMON_URL", env.get("Egeria Integration Daemon URL", "https://localhost:9443"))
|
345
|
+
env["Egeria Jupyter"] = _parse_bool_env("EGERIA_JUPYTER", env.get("Egeria Jupyter", True))
|
346
|
+
env["Egeria Kafka Endpoint"] = os.getenv("EGERIA_KAFKA", env.get("Egeria Kafka Endpoint", "localhost:9192"))
|
347
|
+
env["Egeria Mermaid Folder"] = os.getenv("EGERIA_MERMAID_FOLDER", env.get("Egeria Mermaid Folder", "mermaid_graphs"))
|
348
|
+
env["Egeria Metadata Store"] = os.getenv("EGERIA_METADATA_STORE", env.get("Egeria Metadata Store", "qs-metadata-store"))
|
349
|
+
env["Egeria Platform URL"] = os.getenv("EGERIA_PLATFORM_URL", env.get("Egeria Platform URL", "https://localhost:9443"))
|
350
|
+
env["Egeria View Server"] = os.getenv("EGERIA_VIEW_SERVER", env.get("Egeria View Server", "qs-view-server"))
|
351
|
+
env["Egeria View Server URL"] = os.getenv("EGERIA_VIEW_SERVER_URL", env.get("Egeria View Server URL", "https://localhost:9443"))
|
352
|
+
# Set Pyegeria Root to the root_path value we've already determined with the correct precedence order
|
142
353
|
env["Pyegeria Root"] = root_path
|
143
354
|
|
144
|
-
|
145
|
-
log
|
146
|
-
|
147
|
-
log["console_logging_enabled"] =
|
148
|
-
|
149
|
-
log["
|
150
|
-
log["
|
151
|
-
log["
|
152
|
-
log["log_directory"] = os.getenv("PYEGERIA_LOG_DIRECTORY", log.get("log_directory",'logs'))
|
355
|
+
# Logging section
|
356
|
+
log = config_dict["Logging"]
|
357
|
+
log["console_filter_levels"] = _parse_list_env("PYEGERIA_CONSOLE_FILTER_LEVELS", log.get("console_filter_levels", ["ERROR"]))
|
358
|
+
log["console_logging_enabled"] = _parse_list_env("PYEGERIA_CONSOLE_LOGGING_ENABLED", log.get("console_logging_enabled", ["tests"]))
|
359
|
+
log["console_logging_level"] = os.getenv("PYEGERIA_CONSOLE_LOG_LVL", log.get("console_logging_level", "INFO"))
|
360
|
+
log["enable_logging"] = _parse_bool_env("PYEGERIA_ENABLE_LOGGING", log.get("enable_logging", False))
|
361
|
+
log["file_logging_level"] = os.getenv("PYEGERIA_FILE_LOG_LVL", log.get("file_logging_level", "INFO"))
|
362
|
+
log["log_directory"] = os.getenv("PYEGERIA_LOG_DIRECTORY", log.get("log_directory", 'logs'))
|
153
363
|
log["logging_console_format"] = os.getenv("PYEGERIA_LOGGING_CONSOLE_FORMAT", log.get("logging_console_format",
|
154
364
|
" <green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level}</level> | "
|
155
365
|
"<cyan>{name}</cyan>:<cyan>{line}</cyan> - "
|
156
|
-
"<level>{message}</level> -{extra}"
|
157
|
-
log["logging_file_format"] = os.getenv("PYEGERIA_LOGGING_FILE_FORMAT",log.get("logging_file_format",
|
366
|
+
"<level>{message}</level> -{extra}"))
|
367
|
+
log["logging_file_format"] = os.getenv("PYEGERIA_LOGGING_FILE_FORMAT", log.get("logging_file_format",
|
158
368
|
" {time:YYYY-MM-DD HH:mm:ss} | {level} | {function}:{line} "
|
159
|
-
"- {message }-{extra}"
|
369
|
+
"- {message }-{extra}"))
|
160
370
|
|
161
|
-
|
371
|
+
# User Profile section
|
372
|
+
user = config_dict["User Profile"]
|
162
373
|
user["Egeria Home Collection"] = os.getenv("EGERIA_HOME_COLLECTION", user.get("Egeria Home Collection", "myHome"))
|
163
374
|
user["Egeria Home Glossary Name"] = os.getenv("EGERIA_HOME_GLOSSARY_NAME", user.get("Egeria Home Glossary Name", "Egeria-Markdown"))
|
164
375
|
user["Egeria Local Qualifier"] = os.getenv("EGERIA_LOCAL_QUALIFIER", user.get("Egeria Local Qualifier", "myLocal"))
|
165
|
-
|
166
|
-
user
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
376
|
+
|
377
|
+
# Load user credentials with proper precedence
|
378
|
+
# 1. Check OS environment variables
|
379
|
+
user_name = os.getenv("EGERIA_USER_NAME")
|
380
|
+
user_pwd = os.getenv("EGERIA_USER_PASSWORD")
|
381
|
+
|
382
|
+
# 2. If not set, check config file
|
383
|
+
if user_name is None:
|
384
|
+
user_name = user.get("user_name")
|
385
|
+
if user_pwd is None:
|
386
|
+
user_pwd = user.get("user_pwd")
|
387
|
+
|
388
|
+
# 3. If still not set and we have env_settings, check .env file
|
389
|
+
if (user_name is None or user_pwd is None) and 'env_settings' in locals():
|
390
|
+
if user_name is None:
|
391
|
+
user_name = env_settings.egeria_user_name
|
392
|
+
if user_pwd is None:
|
393
|
+
user_pwd = env_settings.egeria_user_password
|
394
|
+
|
395
|
+
# Set the values in the config dictionary
|
396
|
+
user["user_name"] = user_name
|
397
|
+
user["user_pwd"] = user_pwd
|
174
398
|
|
399
|
+
# Feature flags
|
400
|
+
# Ensure feature_x_enabled is a boolean, regardless of what's in the config file
|
401
|
+
feature_x = config_dict.get("feature_x_enabled", False)
|
402
|
+
feature_x = _parse_bool_value(feature_x)
|
403
|
+
# Check if the environment variable is set, which would override the config value
|
404
|
+
if "FEATURE_X_ENABLED" in os.environ:
|
405
|
+
feature_x = _parse_bool_value(os.getenv("FEATURE_X_ENABLED"))
|
406
|
+
config_dict["feature_x_enabled"] = feature_x
|
175
407
|
|
408
|
+
try:
|
409
|
+
# Create Pydantic model from the configuration dictionary
|
410
|
+
_app_config = AppConfig(**config_dict)
|
411
|
+
except ValueError as e:
|
412
|
+
# Handle validation errors from Pydantic
|
413
|
+
context = {"caller method": inspect.currentframe().f_back.f_code.co_name}
|
414
|
+
additional_info = {"reason": str(e)}
|
415
|
+
raise PyegeriaInvalidParameterException(None, context, additional_info)
|
176
416
|
|
177
|
-
# Handle type conversion for env vars (they are always strings)
|
178
|
-
# if "TIMEOUT_SECONDS" in os.environ:
|
179
|
-
# try:
|
180
|
-
# config["timeout_seconds"] = int(os.getenv("TIMEOUT_SECONDS"))
|
181
|
-
# except ValueError:
|
182
|
-
# print("Warning: TIMEOUT_SECONDS environment variable is not an integer. Using default.")
|
183
|
-
#
|
184
|
-
# if "DEBUG_MODE" in os.environ:
|
185
|
-
# # Convert string "True", "False", "1", "0" to boolean
|
186
|
-
# debug_str = os.getenv("DEBUG_MODE").lower()
|
187
|
-
# config["debug_mode"] = debug_str in ('True', '1', 't', 'y', 'yes', 'on')
|
188
|
-
#
|
189
|
-
# if "FEATURE_X_ENABLED" in os.environ:
|
190
|
-
# feature_x_str = os.getenv("FEATURE_X_ENABLED").lower()
|
191
|
-
# config["feature_x_enabled"] = feature_x_str in ('True', '1', 't', 'y', 'yes', 'on')
|
192
|
-
|
193
|
-
# 4. Handle sensitive API key (only from environment variable)
|
194
|
-
# config["api_key"] = os.getenv("MY_SERVICE_API_KEY")
|
195
|
-
# if config["api_key"] is None:
|
196
|
-
# print("Error: MY_SERVICE_API_KEY environment variable is critical and not set.")
|
197
|
-
# # In a production application, you might raise a critical exception here:
|
198
|
-
# # raise ValueError("MY_SERVICE_API_KEY is not set!")
|
199
|
-
|
200
|
-
_app_config = config # Store the final loaded configuration
|
201
417
|
return _app_config
|
202
418
|
|
203
|
-
|
419
|
+
|
420
|
+
def get_app_config(env_file: str = None):
|
204
421
|
"""
|
205
422
|
Provides access to the loaded application configuration.
|
206
423
|
Ensures config is loaded if not already (useful for testing or simple scripts).
|
207
424
|
For structured apps, load_app_config() should be called explicitly once at startup.
|
425
|
+
|
426
|
+
Args:
|
427
|
+
env_file: Optional path to a specific .env file to load. If not specified,
|
428
|
+
the default .env file in the current directory is used.
|
429
|
+
|
430
|
+
Returns:
|
431
|
+
AppConfig: The loaded configuration as a Pydantic model
|
208
432
|
"""
|
209
433
|
if _app_config is None:
|
210
434
|
# If get_app_config is called before load_app_config, load it now.
|
211
435
|
# This can be convenient but explicit loading is generally better.
|
212
|
-
|
436
|
+
logger.info(f"The env_file {env_file} is being passed in")
|
437
|
+
return load_app_config(env_file)
|
213
438
|
return _app_config
|
214
439
|
|
215
|
-
|
216
|
-
|
217
|
-
|
440
|
+
|
441
|
+
def _parse_bool_env(env_var: str, default: bool) -> bool:
|
442
|
+
"""
|
443
|
+
Parse a boolean environment variable.
|
444
|
+
|
445
|
+
Args:
|
446
|
+
env_var: The name of the environment variable
|
447
|
+
default: The default value if the environment variable is not set
|
448
|
+
|
449
|
+
Returns:
|
450
|
+
bool: The parsed boolean value
|
451
|
+
"""
|
452
|
+
if env_var in os.environ:
|
453
|
+
value = os.getenv(env_var).lower()
|
454
|
+
return value in ('true', '1', 't', 'y', 'yes', 'on')
|
455
|
+
return default
|
456
|
+
|
457
|
+
def _parse_bool_value(value: Any) -> bool:
|
458
|
+
"""
|
459
|
+
Parse a boolean value from any type.
|
460
|
+
|
461
|
+
Args:
|
462
|
+
value: The value to parse
|
463
|
+
|
464
|
+
Returns:
|
465
|
+
bool: The parsed boolean value
|
466
|
+
"""
|
467
|
+
if isinstance(value, bool):
|
468
|
+
return value
|
469
|
+
if isinstance(value, str):
|
470
|
+
return value.lower() in ('true', '1', 't', 'y', 'yes', 'on')
|
471
|
+
if isinstance(value, (int, float)):
|
472
|
+
return bool(value)
|
473
|
+
return False
|
474
|
+
|
475
|
+
|
476
|
+
def _parse_list_env(env_var: str, default: List[str]) -> List[str]:
|
477
|
+
"""
|
478
|
+
Parse a list environment variable (comma-separated).
|
479
|
+
|
480
|
+
Args:
|
481
|
+
env_var: The name of the environment variable
|
482
|
+
default: The default value if the environment variable is not set
|
483
|
+
|
484
|
+
Returns:
|
485
|
+
List[str]: The parsed list value
|
486
|
+
"""
|
487
|
+
if env_var in os.environ:
|
488
|
+
value = os.getenv(env_var)
|
489
|
+
if value:
|
490
|
+
return [item.strip() for item in value.split(',')]
|
491
|
+
return default
|