pyegeria 5.4.2.3__py3-none-any.whl → 5.4.3.1__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/debug_log.2025-08-30_21-15-48_528443.log.zip +0 -0
- commands/cat/debug_log.log +9410 -1431
- commands/cat/dr_egeria_command_help.py +110 -7
- md_processing/__init__.py +6 -11
- md_processing/data/commands.json +150 -6
- md_processing/dr_egeria.py +7 -6
- md_processing/dr_egeria_inbox/glossary_test1.md +18 -3
- md_processing/dr_egeria_inbox/product.md +11 -11
- md_processing/dr_egeria_outbox/friday/processed-2025-08-31 20:57-glossary_test1.md +400 -0
- md_processing/dr_egeria_outbox/monday/processed-2025-09-01 09:26-product.md +210 -0
- md_processing/md_commands/glossary_commands.py +84 -459
- md_processing/md_commands/product_manager_commands.py +10 -7
- md_processing/md_commands/project_commands.py +2 -2
- md_processing/md_processing_utils/common_md_proc_utils.py +9 -6
- md_processing/md_processing_utils/common_md_utils.py +15 -13
- md_processing/md_processing_utils/extraction_utils.py +12 -3
- md_processing/md_processing_utils/md_processing_constants.py +5 -4
- pyegeria/__init__.py +2 -1
- pyegeria/config.py +292 -190
- pyegeria/glossary_manager.py +2 -2
- pyegeria/load_config.py +36 -0
- pyegeria-5.4.3.1.dist-info/METADATA +163 -0
- {pyegeria-5.4.2.3.dist-info → pyegeria-5.4.3.1.dist-info}/RECORD +26 -22
- pyegeria-5.4.2.3.dist-info/METADATA +0 -78
- {pyegeria-5.4.2.3.dist-info → pyegeria-5.4.3.1.dist-info}/LICENSE +0 -0
- {pyegeria-5.4.2.3.dist-info → pyegeria-5.4.3.1.dist-info}/WHEEL +0 -0
- {pyegeria-5.4.2.3.dist-info → pyegeria-5.4.3.1.dist-info}/entry_points.txt +0 -0
pyegeria/config.py
CHANGED
@@ -27,7 +27,7 @@ import json
|
|
27
27
|
from typing import List, Optional, Union, Dict, Any
|
28
28
|
|
29
29
|
from loguru import logger
|
30
|
-
from pydantic import BaseModel, Field, validator
|
30
|
+
from pydantic import BaseModel, Field, validator, ConfigDict
|
31
31
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
32
32
|
|
33
33
|
from pyegeria._exceptions_new import PyegeriaInvalidParameterException, PyegeriaException
|
@@ -105,15 +105,13 @@ class EnvironmentConfig(BaseModel):
|
|
105
105
|
egeria_platform_url: str = Field(default="https://localhost:9443", alias="Egeria Platform URL")
|
106
106
|
egeria_view_server_url: str = Field(default="https://localhost:9443", alias="Egeria View Server URL")
|
107
107
|
egeria_view_server: str = Field(default="qs-view-server", alias="Egeria View Server")
|
108
|
-
pyegeria_root: str = Field(default="
|
108
|
+
pyegeria_root: str = Field(default="", alias="Pyegeria Root")
|
109
109
|
pyegeria_config_directory: str = Field(default="", alias="Pyegeria Config Directory")
|
110
110
|
pyegeria_config_file: str = Field(default="config.json", alias="Egeria Config File")
|
111
111
|
pyegeria_publishing_root: str = Field(default="/dr-egeria-outbox", alias="Pyegeria Publishing Root")
|
112
112
|
pyegeria_user_format_sets_dir: str = Field(default="~/.pyegeria/format_sets", alias="Pyegeria User Format Sets Dir")
|
113
113
|
|
114
|
-
|
115
|
-
populate_by_name = True
|
116
|
-
extra = "allow"
|
114
|
+
model_config = ConfigDict(populate_by_name=True, extra='allow')
|
117
115
|
|
118
116
|
|
119
117
|
class DebugConfig(BaseModel):
|
@@ -122,9 +120,7 @@ class DebugConfig(BaseModel):
|
|
122
120
|
enable_logger_catch: bool = False
|
123
121
|
timeout_seconds: int = 30
|
124
122
|
|
125
|
-
|
126
|
-
populate_by_name = True
|
127
|
-
extra = "allow"
|
123
|
+
model_config = ConfigDict(populate_by_name=True, extra='allow')
|
128
124
|
|
129
125
|
|
130
126
|
class LoggingConfig(BaseModel):
|
@@ -138,9 +134,7 @@ class LoggingConfig(BaseModel):
|
|
138
134
|
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}"
|
139
135
|
logging_file_format: str = " {time:YYYY-MM-DD HH:mm:ss} | {level} | {function}:{line} - {message }-{extra}"
|
140
136
|
|
141
|
-
|
142
|
-
populate_by_name = True
|
143
|
-
extra = "allow"
|
137
|
+
model_config = ConfigDict(populate_by_name=True, extra='allow')
|
144
138
|
|
145
139
|
|
146
140
|
class UserProfileConfig(BaseModel):
|
@@ -151,21 +145,7 @@ class UserProfileConfig(BaseModel):
|
|
151
145
|
user_name: Optional[str] = "erinoverview"
|
152
146
|
user_pwd: Optional[str] = "secret"
|
153
147
|
|
154
|
-
|
155
|
-
# def validate_user_name(cls, v):
|
156
|
-
# if not v:
|
157
|
-
# raise ValueError("Egeria user name is not found in the configuration")
|
158
|
-
# return v
|
159
|
-
#
|
160
|
-
# @validator('user_pwd')
|
161
|
-
# def validate_user_pwd(cls, v):
|
162
|
-
# if not v:
|
163
|
-
# raise ValueError("Egeria user password is not found in the configuration")
|
164
|
-
# return v
|
165
|
-
|
166
|
-
class Config:
|
167
|
-
populate_by_name = True
|
168
|
-
extra = "allow"
|
148
|
+
model_config = ConfigDict(populate_by_name=True, extra='allow')
|
169
149
|
|
170
150
|
|
171
151
|
class AppConfig(BaseModel):
|
@@ -210,9 +190,7 @@ class AppConfig(BaseModel):
|
|
210
190
|
# If not found, return the default
|
211
191
|
return default
|
212
192
|
|
213
|
-
|
214
|
-
populate_by_name = True
|
215
|
-
extra = "allow"
|
193
|
+
model_config = ConfigDict(populate_by_name=True, extra='allow')
|
216
194
|
|
217
195
|
|
218
196
|
# --- Configuration Loading Logic ---
|
@@ -220,7 +198,31 @@ class AppConfig(BaseModel):
|
|
220
198
|
# Private variable to hold the loaded configuration
|
221
199
|
_app_config = None
|
222
200
|
|
223
|
-
def
|
201
|
+
def _resolve_env_settings(env_file: str | None) -> PyegeriaSettings:
|
202
|
+
if env_file:
|
203
|
+
return PyegeriaSettings.with_env_file(env_file)
|
204
|
+
return PyegeriaSettings()
|
205
|
+
|
206
|
+
|
207
|
+
def _find_config_file_path(settings: PyegeriaSettings) -> str | None:
|
208
|
+
config_dir = (settings.pyegeria_config_directory or "").strip()
|
209
|
+
root_path = (settings.pyegeria_root_path or "").strip()
|
210
|
+
config_file = (settings.pyegeria_config_file or "config.json").strip()
|
211
|
+
|
212
|
+
candidates = []
|
213
|
+
if config_dir:
|
214
|
+
candidates.append(os.path.join(config_dir, config_file))
|
215
|
+
if root_path:
|
216
|
+
candidates.append(os.path.join(root_path, config_file))
|
217
|
+
candidates.append(os.path.abspath(os.path.join(os.getcwd(), "config.json")))
|
218
|
+
|
219
|
+
for path in candidates:
|
220
|
+
if path and os.path.exists(path):
|
221
|
+
return path
|
222
|
+
return None
|
223
|
+
|
224
|
+
|
225
|
+
def load_app_config(env_file: str | None = None):
|
224
226
|
"""
|
225
227
|
Loads application configuration from files and environment variables.
|
226
228
|
This function should ideally be called only once at application startup.
|
@@ -246,118 +248,57 @@ def load_app_config(env_file: str = None):
|
|
246
248
|
# Configuration already loaded, return existing instance
|
247
249
|
return _app_config
|
248
250
|
|
249
|
-
#
|
250
|
-
config_dict = {
|
251
|
+
# 1) Defaults from models
|
252
|
+
config_dict: dict[str, Any] = {
|
251
253
|
"Environment": {},
|
252
254
|
"Debug": {},
|
253
255
|
"Logging": {},
|
254
256
|
"User Profile": {},
|
255
|
-
"feature_x_enabled": False
|
257
|
+
"feature_x_enabled": False,
|
256
258
|
}
|
257
|
-
|
258
|
-
#
|
259
|
-
env_settings =
|
260
|
-
|
261
|
-
#
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
logger.info(f"DEBUG: Initial config_directory from OS env: {config_directory}")
|
267
|
-
logger.info(f"DEBUG: Initial root_path from OS env: {root_path}")
|
268
|
-
logger.info(f"DEBUG: Initial config_file from OS env: {config_file}")
|
269
|
-
logger.info(f"DEBUG: env_file parameter: {env_file}")
|
270
|
-
|
271
|
-
# If env_file is specified, use it to load environment variables
|
272
|
-
if env_file is not None:
|
273
|
-
logger.trace(f"DEBUG: Loading environment variables from {env_file}")
|
274
|
-
env_settings = PyegeriaSettings.with_env_file(env_file)
|
275
|
-
|
276
|
-
# If env_file is specified, always use its values, regardless of OS environment variables
|
277
|
-
config_directory = env_settings.pyegeria_config_directory
|
278
|
-
root_path = env_settings.pyegeria_root_path
|
279
|
-
config_file = env_settings.pyegeria_config_file
|
280
|
-
# If config_file is set but config_directory and root_path are not, we'll try to load the config file first
|
281
|
-
# and only check the .env file if we still don't have a config_directory or root_path after loading the config file
|
282
|
-
elif config_file is not None and config_directory is None and root_path is None:
|
283
|
-
# We'll check for a .env file later if needed
|
284
|
-
pass
|
285
|
-
# If any of config_directory, root_path, or config_file is not set, check for a .env file in the current directory
|
286
|
-
elif (config_directory is None or root_path is None or config_file is None):
|
287
|
-
if os.path.exists(".env"):
|
288
|
-
logger.info("Found .env file")
|
289
|
-
logger.debug(f"DEBUG: Loading environment variables from .env in current directory")
|
290
|
-
env_settings = PyegeriaSettings()
|
291
|
-
logger.debug(f"DEBUG: env_settings.pyegeria_config_directory: {env_settings.pyegeria_config_directory}")
|
292
|
-
logger.debug(f"DEBUG: env_settings.pyegeria_root_path: {env_settings.pyegeria_root_path}")
|
293
|
-
logger.debug(f"DEBUG: env_settings.pyegeria_config_file: {env_settings.pyegeria_config_file}")
|
294
|
-
if config_directory is None:
|
295
|
-
config_directory = env_settings.pyegeria_config_directory
|
296
|
-
if root_path is None:
|
297
|
-
root_path = env_settings.pyegeria_root_path
|
298
|
-
if config_file is None:
|
299
|
-
config_file = env_settings.pyegeria_config_file
|
300
|
-
else:
|
301
|
-
logger.error(f"The .env file at {env_file} wasn't found")
|
302
|
-
else:
|
303
|
-
logger.error(f"The .env file at {env_file} wasn't found-outer")
|
304
|
-
# Use default values if still not set
|
305
|
-
if config_directory is None:
|
306
|
-
config_directory = ""
|
307
|
-
if root_path is None:
|
308
|
-
root_path = ""
|
309
|
-
if config_file is None:
|
310
|
-
config_file = "config.json"
|
311
|
-
|
312
|
-
# Construct the config file path - prefer config_directory over root_path
|
313
|
-
if config_directory:
|
314
|
-
config_file_path = os.path.join(config_directory, config_file)
|
315
|
-
else:
|
316
|
-
config_file_path = os.path.join(root_path, config_file)
|
317
|
-
|
318
|
-
if os.path.exists(config_file_path):
|
319
|
-
logger.info("Found config file at {}".format(config_file_path))
|
259
|
+
|
260
|
+
# 2) Load env settings from OS/.env according to env_file
|
261
|
+
env_settings = _resolve_env_settings(env_file)
|
262
|
+
|
263
|
+
# 3) Load config file if found
|
264
|
+
file_path = _find_config_file_path(env_settings)
|
265
|
+
if file_path:
|
266
|
+
logger.info(f"Using config file: {file_path}")
|
320
267
|
try:
|
321
|
-
with open(
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
if not config_directory and "Environment" in file_config and "Pyegeria Config Directory" in file_config["Environment"]:
|
328
|
-
config_directory = file_config["Environment"]["Pyegeria Config Directory"]
|
329
|
-
logger.debug(f"DEBUG: Setting config_directory from config file: {config_directory}")
|
330
|
-
# If config_directory is still not set and root_path is not set from environment variables or .env file,
|
331
|
-
# set root_path from the config file if available
|
332
|
-
if not config_directory and not root_path and "Environment" in file_config and "Pyegeria Root" in file_config["Environment"]:
|
333
|
-
root_path = file_config["Environment"]["Pyegeria Root"]
|
334
|
-
logger.debug(f"DEBUG: Setting root_path from config file: {root_path}")
|
335
|
-
except json.JSONDecodeError:
|
336
|
-
logger.warning(f"Warning: Could not parse {config_file_path}. Using defaults/env vars.")
|
268
|
+
with open(file_path, "r") as f:
|
269
|
+
file_cfg = json.load(f)
|
270
|
+
if isinstance(file_cfg, dict):
|
271
|
+
config_dict.update(file_cfg)
|
272
|
+
else:
|
273
|
+
logger.warning("Config file root is not an object; ignoring.")
|
337
274
|
except Exception as e:
|
338
|
-
logger.warning(f"
|
275
|
+
logger.warning(f"Could not read/parse config file '{file_path}': {e}. Continuing with defaults+env.")
|
339
276
|
else:
|
340
|
-
logger.
|
341
|
-
|
342
|
-
#
|
343
|
-
#
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
env
|
359
|
-
|
277
|
+
logger.debug("No config.json found; continuing with defaults + env.")
|
278
|
+
|
279
|
+
# 4) Overlay environment variables
|
280
|
+
# Debug
|
281
|
+
dbg = config_dict.setdefault("Debug", {})
|
282
|
+
dbg["debug_mode"] = _parse_bool_env("PYEGERIA_DEBUG_MODE", bool(dbg.get("debug_mode", False)))
|
283
|
+
dbg["enable_logger_catch"] = _parse_bool_env("PYEGERIA_ENABLE_LOGGER_CATCH", bool(dbg.get("enable_logger_catch", False)))
|
284
|
+
dbg["timeout_seconds"] = int(os.getenv("PYEGERIA_TIMEOUT_SECONDS", dbg.get("timeout_seconds", 30)))
|
285
|
+
|
286
|
+
# Environment
|
287
|
+
env = config_dict.setdefault("Environment", {})
|
288
|
+
default_root = env.get("Pyegeria Root") or os.getcwd()
|
289
|
+
env_config_dir = os.getenv("PYEGERIA_CONFIG_DIRECTORY", env_settings.pyegeria_config_directory or "")
|
290
|
+
env_root = os.getenv("PYEGERIA_ROOT_PATH", env_settings.pyegeria_root_path or default_root)
|
291
|
+
env_config_file = os.getenv("PYEGERIA_CONFIG_FILE", env_settings.pyegeria_config_file or "config.json")
|
292
|
+
|
293
|
+
env["Pyegeria Config Directory"] = env_config_dir
|
294
|
+
env["pyegeria_config_directory"] = env_config_dir
|
295
|
+
env["Pyegeria Root"] = env_root
|
296
|
+
env["pyegeria_root"] = env_root
|
297
|
+
env["Egeria Config File"] = env_config_file
|
298
|
+
env["pyegeria_config_file"] = env_config_file
|
299
|
+
|
360
300
|
env["Console Width"] = int(os.getenv("PYEGERIA_CONSOLE_WIDTH", env.get("Console Width", env_settings.pyegeria_console_width)))
|
301
|
+
env["console_width"] = env["Console Width"]
|
361
302
|
env["Dr.Egeria Inbox"] = os.getenv("DR_EGERIA_INBOX_PATH", env.get("Dr.Egeria Inbox", "md_processing/dr-egeria-inbox"))
|
362
303
|
env["Dr.Egeria Outbox"] = os.getenv("DR_EGERIA_OUTBOX_PATH", env.get("Dr.Egeria Outbox", "md_processing/dr-egeria-outbox"))
|
363
304
|
env["Egeria Engine Host"] = os.getenv("EGERIA_ENGINE_HOST", env.get("Egeria Engine Host", "qs-engine-host"))
|
@@ -365,79 +306,56 @@ def load_app_config(env_file: str = None):
|
|
365
306
|
env["Egeria Glossary Path"] = os.getenv("EGERIA_GLOSSARY_PATH", env.get("Egeria Glossary Path", "glossary"))
|
366
307
|
env["Egeria Integration Daemon"] = os.getenv("EGERIA_INTEGRATION_DAEMON", env.get("Egeria Integration Daemon", "qs-integration-daemon"))
|
367
308
|
env["Egeria Integration Daemon URL"] = os.getenv("EGERIA_INTEGRATION_DAEMON_URL", env.get("Egeria Integration Daemon URL", "https://localhost:9443"))
|
368
|
-
env["Egeria Jupyter"] = _parse_bool_env("EGERIA_JUPYTER", env.get("Egeria Jupyter", True))
|
309
|
+
env["Egeria Jupyter"] = _parse_bool_env("EGERIA_JUPYTER", bool(env.get("Egeria Jupyter", True)))
|
369
310
|
env["Egeria Kafka Endpoint"] = os.getenv("EGERIA_KAFKA", env.get("Egeria Kafka Endpoint", "localhost:9192"))
|
370
311
|
env["Egeria Mermaid Folder"] = os.getenv("EGERIA_MERMAID_FOLDER", env.get("Egeria Mermaid Folder", "mermaid_graphs"))
|
371
312
|
env["Egeria Metadata Store"] = os.getenv("EGERIA_METADATA_STORE", env.get("Egeria Metadata Store", "qs-metadata-store"))
|
372
313
|
env["Egeria Platform URL"] = os.getenv("EGERIA_PLATFORM_URL", env.get("Egeria Platform URL", "https://localhost:9443"))
|
373
314
|
env["Egeria View Server"] = os.getenv("EGERIA_VIEW_SERVER", env.get("Egeria View Server", "qs-view-server"))
|
374
315
|
env["Egeria View Server URL"] = os.getenv("EGERIA_VIEW_SERVER_URL", env.get("Egeria View Server URL", "https://localhost:9443"))
|
375
|
-
env[
|
376
|
-
env[
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
# Logging section
|
383
|
-
log = config_dict["Logging"]
|
384
|
-
log["console_filter_levels"] = _parse_list_env("PYEGERIA_CONSOLE_FILTER_LEVELS", log.get("console_filter_levels", ["ERROR","WARNING","INFO","SUCCESS"]))
|
316
|
+
env["Pyegeria Publishing Root"] = os.getenv("PYEGERIA_PUBLISHING_ROOT", env.get("Pyegeria Publishing Root", "/dr-egeria-outbox"))
|
317
|
+
env["Pyegeria User Format Sets Dir"] = os.getenv("PYEGERIA_USER_FORMAT_SETS_DIR", env.get("Pyegeria User Format Sets Dir", "~/.pyegeria/format_sets"))
|
318
|
+
|
319
|
+
# Logging
|
320
|
+
log = config_dict.setdefault("Logging", {})
|
321
|
+
log["console_filter_levels"] = _parse_list_env("PYEGERIA_CONSOLE_FILTER_LEVELS", log.get("console_filter_levels", ["ERROR", "WARNING", "INFO", "SUCCESS"]))
|
385
322
|
log["console_logging_enabled"] = _parse_list_env("PYEGERIA_CONSOLE_LOGGING_ENABLED", log.get("console_logging_enabled", ["pyegeria"]))
|
386
323
|
log["console_logging_level"] = os.getenv("PYEGERIA_CONSOLE_LOG_LVL", log.get("console_logging_level", "INFO"))
|
387
|
-
log["enable_logging"] = _parse_bool_env("PYEGERIA_ENABLE_LOGGING", log.get("enable_logging", False))
|
324
|
+
log["enable_logging"] = _parse_bool_env("PYEGERIA_ENABLE_LOGGING", bool(log.get("enable_logging", False)))
|
388
325
|
log["file_logging_level"] = os.getenv("PYEGERIA_FILE_LOG_LVL", log.get("file_logging_level", "INFO"))
|
389
|
-
log["log_directory"] = os.getenv("PYEGERIA_LOG_DIRECTORY", log.get("log_directory",
|
390
|
-
log["logging_console_format"] = os.getenv(
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
326
|
+
log["log_directory"] = os.getenv("PYEGERIA_LOG_DIRECTORY", log.get("log_directory", "logs"))
|
327
|
+
log["logging_console_format"] = os.getenv(
|
328
|
+
"PYEGERIA_LOGGING_CONSOLE_FORMAT",
|
329
|
+
log.get(
|
330
|
+
"logging_console_format",
|
331
|
+
" <green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level}</level> | <cyan>{name}</cyan>:<cyan>{line}</cyan> - <level>{message}</level> -{extra}",
|
332
|
+
),
|
333
|
+
)
|
334
|
+
log["logging_file_format"] = os.getenv(
|
335
|
+
"PYEGERIA_LOGGING_FILE_FORMAT",
|
336
|
+
log.get("logging_file_format", " {time:YYYY-MM-DD HH:mm:ss} | {level} | {function}:{line} - {message }-{extra}"),
|
337
|
+
)
|
338
|
+
|
339
|
+
# User Profile
|
340
|
+
user = config_dict.setdefault("User Profile", {})
|
341
|
+
user["Egeria Home Collection"] = os.getenv("EGERIA_HOME_COLLECTION", user.get("Egeria Home Collection", "MyHome"))
|
401
342
|
user["Egeria Home Glossary Name"] = os.getenv("EGERIA_HOME_GLOSSARY_NAME", user.get("Egeria Home Glossary Name", "Egeria-Markdown"))
|
402
|
-
user["Egeria Local Qualifier"] = os.getenv("EGERIA_LOCAL_QUALIFIER", user.get("Egeria Local Qualifier", "
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
user_name = os.getenv("EGERIA_USER")
|
407
|
-
user_pwd = os.getenv("EGERIA_USER_PASSWORD")
|
408
|
-
|
409
|
-
# 2. If not set, check config file
|
410
|
-
if user_name is None:
|
411
|
-
user_name = user.get("user_name")
|
412
|
-
if user_pwd is None:
|
413
|
-
user_pwd = user.get("user_pwd")
|
414
|
-
|
415
|
-
# 3. If still not set and we have env_settings, check .env file
|
416
|
-
if (user_name is None or user_pwd is None) and 'env_settings' in locals():
|
417
|
-
if user_name is None:
|
418
|
-
user_name = env_settings.egeria_user_name
|
419
|
-
if user_pwd is None:
|
420
|
-
user_pwd = env_settings.egeria_user_password
|
421
|
-
|
422
|
-
# Set the values in the config dictionary
|
343
|
+
user["Egeria Local Qualifier"] = os.getenv("EGERIA_LOCAL_QUALIFIER", user.get("Egeria Local Qualifier", "PDR"))
|
344
|
+
|
345
|
+
user_name = os.getenv("EGERIA_USER", user.get("user_name") or env_settings.egeria_user_name or None)
|
346
|
+
user_pwd = os.getenv("EGERIA_USER_PASSWORD", user.get("user_pwd") or env_settings.egeria_user_password or None)
|
423
347
|
user["user_name"] = user_name
|
424
348
|
user["user_pwd"] = user_pwd
|
425
349
|
|
426
350
|
# Feature flags
|
427
|
-
|
428
|
-
feature_x = config_dict.get("feature_x_enabled", False)
|
429
|
-
feature_x = _parse_bool_value(feature_x)
|
430
|
-
# Check if the environment variable is set, which would override the config value
|
431
|
-
if "FEATURE_X_ENABLED" in os.environ:
|
432
|
-
feature_x = _parse_bool_value(os.getenv("FEATURE_X_ENABLED"))
|
351
|
+
feature_x = _parse_bool_value(os.getenv("FEATURE_X_ENABLED", config_dict.get("feature_x_enabled", False)))
|
433
352
|
config_dict["feature_x_enabled"] = feature_x
|
434
353
|
|
354
|
+
# Debug print of env before model creation
|
435
355
|
try:
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
except ValueError as e:
|
440
|
-
# Handle validation errors from Pydantic
|
356
|
+
logger.info(f"DEBUG ENV SECTION: {config_dict.get('Environment')}")
|
357
|
+
_app_config = AppConfig(**config_dict)
|
358
|
+
except Exception as e:
|
441
359
|
context = {"caller method": inspect.currentframe().f_back.f_code.co_name}
|
442
360
|
additional_info = {"reason": str(e)}
|
443
361
|
raise PyegeriaInvalidParameterException(None, context, additional_info)
|
@@ -521,4 +439,188 @@ def _parse_list_env(env_var: str, default: List[str]) -> List[str]:
|
|
521
439
|
return default
|
522
440
|
|
523
441
|
|
524
|
-
settings
|
442
|
+
# Export a lazily-evaluated settings accessor to avoid import-time side effects
|
443
|
+
class _LazySettings:
|
444
|
+
def __getattr__(self, name):
|
445
|
+
cfg = get_app_config()
|
446
|
+
return getattr(cfg, name)
|
447
|
+
|
448
|
+
settings = _LazySettings()
|
449
|
+
|
450
|
+
|
451
|
+
def pretty_print_config(env_file: str | None = None, safe: bool = True, to_console: bool = True) -> Dict[str, Dict[str, Any]]:
|
452
|
+
"""
|
453
|
+
Pretty print the current configuration and indicate the source of each value
|
454
|
+
(Environment, .env file, config file, or default). Uses Rich if available.
|
455
|
+
|
456
|
+
Args:
|
457
|
+
env_file: Optional .env path to force loading before printing (if config not yet loaded).
|
458
|
+
safe: Mask sensitive values such as passwords/tokens.
|
459
|
+
to_console: If True, prints to console; function always returns a structured dict.
|
460
|
+
|
461
|
+
Returns:
|
462
|
+
dict mapping section -> { key -> {"value": ..., "source": ...} }
|
463
|
+
"""
|
464
|
+
# Ensure config is loaded
|
465
|
+
cfg = get_app_config(env_file)
|
466
|
+
|
467
|
+
# Try import rich lazily
|
468
|
+
try:
|
469
|
+
from rich.console import Console
|
470
|
+
from rich.table import Table
|
471
|
+
from rich import box
|
472
|
+
console = Console(width=getattr(cfg.Environment, 'console_width', 200))
|
473
|
+
use_rich = True
|
474
|
+
except Exception:
|
475
|
+
console = None
|
476
|
+
use_rich = False
|
477
|
+
|
478
|
+
# Helper to mask sensitive keys
|
479
|
+
def _mask(key: str, val: Any) -> Any:
|
480
|
+
if not safe:
|
481
|
+
return val
|
482
|
+
key_l = (key or "").lower()
|
483
|
+
if any(s in key_l for s in ["password", "pwd", "secret", "token", "apikey", "api_key"]):
|
484
|
+
if val is None:
|
485
|
+
return None
|
486
|
+
s = str(val)
|
487
|
+
if len(s) <= 4:
|
488
|
+
return "****"
|
489
|
+
return s[:2] + "****" + s[-2:]
|
490
|
+
return val
|
491
|
+
|
492
|
+
# Determine sources. Because we merge defaults, config.json, and env, we infer source:
|
493
|
+
# - If an OS env var exists for a specific setting name we used, it's "env".
|
494
|
+
# - Else if a config file was found (by our path resolver) and provided an override, mark "config".
|
495
|
+
# - Else if value equals model default and neither env nor config provided, "default".
|
496
|
+
|
497
|
+
# We need to reconstruct which keys are influenced by ENV VAR names.
|
498
|
+
env_var_map = {
|
499
|
+
# Debug
|
500
|
+
("Debug", "debug_mode"): "PYEGERIA_DEBUG_MODE",
|
501
|
+
("Debug", "enable_logger_catch"): "PYEGERIA_ENABLE_LOGGER_CATCH",
|
502
|
+
("Debug", "timeout_seconds"): "PYEGERIA_TIMEOUT_SECONDS",
|
503
|
+
# Environment
|
504
|
+
("Environment", "Console Width"): "PYEGERIA_CONSOLE_WIDTH",
|
505
|
+
("Environment", "Dr.Egeria Inbox"): "DR_EGERIA_INBOX_PATH",
|
506
|
+
("Environment", "Dr.Egeria Outbox"): "DR_EGERIA_OUTBOX_PATH",
|
507
|
+
("Environment", "Egeria Engine Host"): "EGERIA_ENGINE_HOST",
|
508
|
+
("Environment", "Egeria Engine Host URL"): "EGERIA_ENGINE_HOST_URL",
|
509
|
+
("Environment", "Egeria Glossary Path"): "EGERIA_GLOSSARY_PATH",
|
510
|
+
("Environment", "Egeria Integration Daemon"): "EGERIA_INTEGRATION_DAEMON",
|
511
|
+
("Environment", "Egeria Integration Daemon URL"): "EGERIA_INTEGRATION_DAEMON_URL",
|
512
|
+
("Environment", "Egeria Jupyter"): "EGERIA_JUPYTER",
|
513
|
+
("Environment", "Egeria Kafka Endpoint"): "EGERIA_KAFKA",
|
514
|
+
("Environment", "Egeria Mermaid Folder"): "EGERIA_MERMAID_FOLDER",
|
515
|
+
("Environment", "Egeria Metadata Store"): "EGERIA_METADATA_STORE",
|
516
|
+
("Environment", "Egeria Platform URL"): "EGERIA_PLATFORM_URL",
|
517
|
+
("Environment", "Egeria View Server"): "EGERIA_VIEW_SERVER",
|
518
|
+
("Environment", "Egeria View Server URL"): "EGERIA_VIEW_SERVER_URL",
|
519
|
+
("Environment", "Pyegeria Publishing Root"): "PYEGERIA_PUBLISHING_ROOT",
|
520
|
+
("Environment", "Pyegeria User Format Sets Dir"): "PYEGERIA_USER_FORMAT_SETS_DIR",
|
521
|
+
("Environment", "Pyegeria Root"): "PYEGERIA_ROOT_PATH",
|
522
|
+
("Environment", "Pyegeria Config Directory"): "PYEGERIA_CONFIG_DIRECTORY",
|
523
|
+
("Environment", "Egeria Config File"): "PYEGERIA_CONFIG_FILE",
|
524
|
+
# Logging
|
525
|
+
("Logging", "console_filter_levels"): "PYEGERIA_CONSOLE_FILTER_LEVELS",
|
526
|
+
("Logging", "console_logging_enabled"): "PYEGERIA_CONSOLE_LOGGING_ENABLED",
|
527
|
+
("Logging", "console_logging_level"): "PYEGERIA_CONSOLE_LOG_LVL",
|
528
|
+
("Logging", "enable_logging"): "PYEGERIA_ENABLE_LOGGING",
|
529
|
+
("Logging", "file_logging_level"): "PYEGERIA_FILE_LOG_LVL",
|
530
|
+
("Logging", "log_directory"): "PYEGERIA_LOG_DIRECTORY",
|
531
|
+
("Logging", "logging_console_format"): "PYEGERIA_LOGGING_CONSOLE_FORMAT",
|
532
|
+
("Logging", "logging_file_format"): "PYEGERIA_LOGGING_FILE_FORMAT",
|
533
|
+
# User profile
|
534
|
+
("User Profile", "Egeria Home Collection"): "EGERIA_HOME_COLLECTION",
|
535
|
+
("User Profile", "Egeria Home Glossary Name"): "EGERIA_HOME_GLOSSARY_NAME",
|
536
|
+
("User Profile", "Egeria Local Qualifier"): "EGERIA_LOCAL_QUALIFIER",
|
537
|
+
("User Profile", "user_name"): "EGERIA_USER",
|
538
|
+
("User Profile", "user_pwd"): "EGERIA_USER_PASSWORD",
|
539
|
+
# Feature flag example
|
540
|
+
(None, "feature_x_enabled"): "FEATURE_X_ENABLED",
|
541
|
+
}
|
542
|
+
|
543
|
+
# Attempt to detect if a config.json was used
|
544
|
+
env_settings = _resolve_env_settings(env_file)
|
545
|
+
config_file_path = _find_config_file_path(env_settings)
|
546
|
+
|
547
|
+
# Build a snapshot of defaults by instantiating empty models
|
548
|
+
defaults = {
|
549
|
+
"Environment": EnvironmentConfig().model_dump(by_alias=True),
|
550
|
+
"Debug": DebugConfig().model_dump(by_alias=True),
|
551
|
+
"Logging": LoggingConfig().model_dump(by_alias=False),
|
552
|
+
"User Profile": UserProfileConfig().model_dump(by_alias=True),
|
553
|
+
"feature_x_enabled": False,
|
554
|
+
}
|
555
|
+
|
556
|
+
# Current values from cfg
|
557
|
+
sections = [
|
558
|
+
("Environment", cfg.Environment.model_dump(by_alias=True)),
|
559
|
+
("Debug", cfg.Debug.model_dump(by_alias=False)),
|
560
|
+
("Logging", cfg.Logging.model_dump(by_alias=False)),
|
561
|
+
("User Profile", cfg.User_Profile.model_dump(by_alias=True)),
|
562
|
+
]
|
563
|
+
|
564
|
+
result: Dict[str, Dict[str, Any]] = {}
|
565
|
+
|
566
|
+
for section_name, values in sections:
|
567
|
+
section_out: Dict[str, Any] = {}
|
568
|
+
for key, val in values.items():
|
569
|
+
# Prefer alias keys in display; ensure key exists in defaults appropriately
|
570
|
+
default_section = defaults.get(section_name, {})
|
571
|
+
default_val = default_section.get(key, None)
|
572
|
+
|
573
|
+
# Identify env var used for this key if any
|
574
|
+
env_var = env_var_map.get((section_name, key))
|
575
|
+
source = "default"
|
576
|
+
if env_var and env_var in os.environ:
|
577
|
+
source = "env"
|
578
|
+
elif config_file_path and (key in (defaults.get(section_name, {}) or {}) or True):
|
579
|
+
# If a config file exists and value differs from default and no env var set
|
580
|
+
if val != default_val:
|
581
|
+
source = "config"
|
582
|
+
else:
|
583
|
+
source = "default"
|
584
|
+
else:
|
585
|
+
source = "default"
|
586
|
+
|
587
|
+
section_out[key] = {
|
588
|
+
"value": _mask(key, val),
|
589
|
+
"source": source,
|
590
|
+
}
|
591
|
+
result[section_name] = section_out
|
592
|
+
|
593
|
+
# Add top-level feature flags
|
594
|
+
feat_val = getattr(cfg, "feature_x_enabled", False)
|
595
|
+
source = "env" if ("FEATURE_X_ENABLED" in os.environ) else ("config" if config_file_path and feat_val != defaults["feature_x_enabled"] else "default")
|
596
|
+
result["feature_x_enabled"] = {"value": _mask("feature_x_enabled", feat_val), "source": source}
|
597
|
+
|
598
|
+
if to_console:
|
599
|
+
if use_rich and console:
|
600
|
+
for section_name in ["Environment", "Debug", "Logging", "User Profile"]:
|
601
|
+
table = Table(title=f"{section_name} Settings", box=box.SIMPLE_HEAVY)
|
602
|
+
table.add_column("Key")
|
603
|
+
table.add_column("Value")
|
604
|
+
table.add_column("Source")
|
605
|
+
for k, info in result[section_name].items():
|
606
|
+
table.add_row(str(k), str(info["value"]), info["source"])
|
607
|
+
console.print(table)
|
608
|
+
# Feature flags
|
609
|
+
table = Table(title="Feature Flags", box=box.SIMPLE_HEAVY)
|
610
|
+
table.add_column("Key")
|
611
|
+
table.add_column("Value")
|
612
|
+
table.add_column("Source")
|
613
|
+
ff = result["feature_x_enabled"]
|
614
|
+
table.add_row("feature_x_enabled", str(ff["value"]), ff["source"])
|
615
|
+
console.print(table)
|
616
|
+
else:
|
617
|
+
# Plain text fallback
|
618
|
+
print("Configuration:")
|
619
|
+
for section_name in ["Environment", "Debug", "Logging", "User Profile"]:
|
620
|
+
print(f"[{section_name}]")
|
621
|
+
for k, info in result[section_name].items():
|
622
|
+
print(f"- {k}: {info['value']} (source: {info['source']})")
|
623
|
+
ff = result["feature_x_enabled"]
|
624
|
+
print(f"feature_x_enabled: {ff['value']} (source: {ff['source']})")
|
625
|
+
|
626
|
+
return result
|
pyegeria/glossary_manager.py
CHANGED
@@ -2245,9 +2245,9 @@ class GlossaryManager(CollectionManager):
|
|
2245
2245
|
|
2246
2246
|
if body is None:
|
2247
2247
|
body = {
|
2248
|
-
"class": "
|
2248
|
+
"class": "NewRelationshipRequestBody",
|
2249
2249
|
"properties":
|
2250
|
-
{"class": "GlossaryTermRelationship"
|
2250
|
+
{"class": "GlossaryTermRelationship" }
|
2251
2251
|
}
|
2252
2252
|
|
2253
2253
|
|
pyegeria/load_config.py
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
"""
|
2
|
+
Compatibility shim for legacy imports.
|
3
|
+
Re-exports configuration loading utilities from pyegeria.config.
|
4
|
+
"""
|
5
|
+
# Import config lazily inside functions to avoid import-time side effects in tests
|
6
|
+
from importlib import import_module
|
7
|
+
|
8
|
+
def load_app_config(env_file: str | None = None):
|
9
|
+
return import_module('pyegeria.config').load_app_config(env_file)
|
10
|
+
|
11
|
+
def get_app_config(env_file: str | None = None):
|
12
|
+
return import_module('pyegeria.config').get_app_config(env_file)
|
13
|
+
|
14
|
+
class PyegeriaSettings:
|
15
|
+
def __new__(cls, *args, **kwargs):
|
16
|
+
# Construct a real settings instance from pyegeria.config
|
17
|
+
return import_module('pyegeria.config').PyegeriaSettings(*args, **kwargs)
|
18
|
+
|
19
|
+
@classmethod
|
20
|
+
def with_env_file(cls, env_file: str):
|
21
|
+
return import_module('pyegeria.config').PyegeriaSettings.with_env_file(env_file)
|
22
|
+
|
23
|
+
# expose the cached instance for tests that reset it directly
|
24
|
+
from . import config as _config
|
25
|
+
|
26
|
+
def __getattr__(name):
|
27
|
+
if name == "_app_config":
|
28
|
+
return _config._app_config
|
29
|
+
raise AttributeError(name)
|
30
|
+
|
31
|
+
|
32
|
+
def __setattr__(name, value):
|
33
|
+
if name == "_app_config":
|
34
|
+
_config._app_config = value
|
35
|
+
else:
|
36
|
+
globals()[name] = value
|