pyegeria 5.3.9.9.3__py3-none-any.whl → 5.5.3.3__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.

Potentially problematic release.


This version of pyegeria might be problematic. Click here for more details.

Files changed (272) hide show
  1. commands/__init__.py +24 -0
  2. commands/cat/Dr-Egeria_md-orig.py +2 -2
  3. commands/cat/__init__.py +1 -17
  4. commands/cat/collection_actions.py +197 -0
  5. commands/cat/dr_egeria_command_help.py +372 -0
  6. commands/cat/dr_egeria_jupyter.py +7 -7
  7. commands/cat/dr_egeria_md.py +27 -182
  8. commands/cat/exp_list_glossaries.py +11 -14
  9. commands/cat/get_asset_graph.py +37 -267
  10. commands/cat/{get_collection.py → get_collection_tree.py} +10 -18
  11. commands/cat/get_project_dependencies.py +14 -14
  12. commands/cat/get_project_structure.py +15 -14
  13. commands/cat/get_tech_type_elements.py +16 -116
  14. commands/cat/glossary_actions.py +145 -298
  15. commands/cat/list_assets.py +3 -11
  16. commands/cat/list_cert_types.py +17 -63
  17. commands/cat/list_collections.py +46 -138
  18. commands/cat/list_deployed_catalogs.py +15 -27
  19. commands/cat/list_deployed_database_schemas.py +27 -43
  20. commands/cat/list_deployed_databases.py +16 -31
  21. commands/cat/list_deployed_servers.py +35 -54
  22. commands/cat/list_glossaries.py +18 -17
  23. commands/cat/list_projects.py +10 -12
  24. commands/cat/list_tech_type_elements.py +21 -37
  25. commands/cat/list_tech_types.py +13 -25
  26. commands/cat/list_terms.py +38 -79
  27. commands/cat/list_todos.py +4 -11
  28. commands/cat/list_user_ids.py +3 -10
  29. commands/cat/my_reports.py +559 -0
  30. commands/cat/run_report.py +394 -0
  31. commands/cat/run_report_orig.py +528 -0
  32. commands/cli/egeria.py +222 -247
  33. commands/cli/egeria_cat.py +68 -81
  34. commands/cli/egeria_my.py +13 -0
  35. commands/cli/egeria_ops.py +69 -74
  36. commands/cli/egeria_tech.py +17 -93
  37. commands/cli/ops_config.py +3 -6
  38. commands/{cat/list_categories.py → deprecated/list_data_designer.py} +53 -64
  39. commands/{cat/list_data_structures.py → deprecated/list_data_structures_full.py} +3 -6
  40. commands/deprecated/old_get_asset_graph.py +315 -0
  41. commands/my/__init__.py +0 -2
  42. commands/my/list_my_profile.py +27 -34
  43. commands/my/list_my_roles.py +1 -7
  44. commands/my/monitor_my_todos.py +1 -7
  45. commands/my/monitor_open_todos.py +6 -7
  46. commands/my/todo_actions.py +4 -5
  47. commands/ops/__init__.py +0 -2
  48. commands/ops/gov_server_actions.py +17 -21
  49. commands/ops/list_archives.py +17 -38
  50. commands/ops/list_catalog_targets.py +33 -40
  51. commands/ops/load_archive.py +35 -26
  52. commands/ops/{monitor_engine_activity_c.py → monitor_active_engine_activity.py} +51 -82
  53. commands/ops/{monitor_integ_daemon_status.py → monitor_daemon_status.py} +35 -55
  54. commands/ops/monitor_engine_activity.py +79 -77
  55. commands/ops/{monitor_gov_eng_status.py → monitor_engine_status.py} +10 -7
  56. commands/ops/monitor_platform_status.py +38 -50
  57. commands/ops/monitor_server_startup.py +6 -11
  58. commands/ops/monitor_server_status.py +7 -11
  59. commands/ops/orig_monitor_server_list.py +8 -8
  60. commands/ops/orig_monitor_server_status.py +1 -5
  61. commands/ops/refresh_integration_daemon.py +5 -5
  62. commands/ops/restart_integration_daemon.py +5 -5
  63. commands/ops/table_integ_daemon_status.py +6 -6
  64. commands/ops/x_engine_actions.py +7 -7
  65. commands/tech/__init__.py +0 -2
  66. commands/tech/{generic_actions.py → element_actions.py} +6 -11
  67. commands/tech/get_element_info.py +20 -29
  68. commands/tech/get_guid_info.py +23 -42
  69. commands/tech/get_tech_details.py +20 -35
  70. commands/tech/get_tech_type_template.py +28 -39
  71. commands/tech/list_all_om_type_elements.py +24 -30
  72. commands/tech/list_all_om_type_elements_x.py +22 -28
  73. commands/tech/list_all_related_elements.py +19 -28
  74. commands/tech/list_anchored_elements.py +22 -30
  75. commands/tech/list_asset_types.py +19 -24
  76. commands/tech/list_elements_by_classification_by_property_value.py +26 -32
  77. commands/tech/list_elements_by_property_value.py +19 -25
  78. commands/tech/list_elements_by_property_value_x.py +20 -28
  79. commands/tech/list_elements_for_classification.py +28 -41
  80. commands/tech/list_gov_action_processes.py +16 -27
  81. commands/tech/list_information_supply_chains.py +22 -30
  82. commands/tech/list_registered_services.py +14 -26
  83. commands/tech/list_related_elements_with_prop_value.py +15 -25
  84. commands/tech/list_related_specification.py +1 -4
  85. commands/tech/list_relationship_types.py +15 -25
  86. commands/tech/list_relationships.py +20 -36
  87. commands/tech/list_solution_blueprints.py +28 -33
  88. commands/tech/list_solution_components.py +23 -29
  89. commands/tech/list_solution_roles.py +21 -32
  90. commands/tech/list_tech_templates.py +51 -54
  91. commands/tech/list_valid_metadata_values.py +5 -9
  92. commands/tech/table_tech_templates.py +2 -6
  93. commands/tech/x_list_related_elements.py +1 -4
  94. examples/GeoSpatial Products Example.py +524 -0
  95. examples/Jupyter Notebooks/P-egeria-server-config.ipynb +2137 -0
  96. examples/Jupyter Notebooks/README.md +2 -0
  97. examples/Jupyter Notebooks/common/P-environment-check.ipynb +115 -0
  98. examples/Jupyter Notebooks/common/__init__.py +14 -0
  99. examples/Jupyter Notebooks/common/common-functions.ipynb +4694 -0
  100. examples/Jupyter Notebooks/common/environment-check.ipynb +52 -0
  101. examples/Jupyter Notebooks/common/globals.ipynb +184 -0
  102. examples/Jupyter Notebooks/common/globals.py +154 -0
  103. examples/Jupyter Notebooks/common/orig_globals.py +152 -0
  104. examples/format_sets/all_format_sets.json +910 -0
  105. examples/format_sets/custom_format_sets.json +268 -0
  106. examples/format_sets/subset_format_sets.json +187 -0
  107. examples/format_sets_save_load_example.py +291 -0
  108. examples/jacquard_data_sets.py +129 -0
  109. examples/output_formats_example.py +193 -0
  110. examples/test_jacquard_data_sets.py +54 -0
  111. examples/test_jacquard_data_sets_scenarios.py +94 -0
  112. md_processing/__init__.py +90 -0
  113. md_processing/command_dispatcher.py +33 -0
  114. md_processing/command_mapping.py +221 -0
  115. md_processing/data/commands/commands_data_designer.json +537 -0
  116. md_processing/data/commands/commands_external_reference.json +733 -0
  117. md_processing/data/commands/commands_feedback.json +155 -0
  118. md_processing/data/commands/commands_general.json +204 -0
  119. md_processing/data/commands/commands_glossary.json +218 -0
  120. md_processing/data/commands/commands_governance.json +3678 -0
  121. md_processing/data/commands/commands_product_manager.json +865 -0
  122. md_processing/data/commands/commands_project.json +642 -0
  123. md_processing/data/commands/commands_solution_architect.json +366 -0
  124. md_processing/data/commands.json +17568 -0
  125. md_processing/data/commands_working.json +30641 -0
  126. md_processing/data/gened_report_specs.py +6584 -0
  127. md_processing/data/generated_format_sets.json +6533 -0
  128. md_processing/data/generated_format_sets_old.json +4137 -0
  129. md_processing/data/generated_format_sets_old.py +45 -0
  130. md_processing/dr_egeria.py +182 -0
  131. md_processing/md_commands/__init__.py +3 -0
  132. md_processing/md_commands/data_designer_commands.py +1276 -0
  133. md_processing/md_commands/ext_ref_commands.py +530 -0
  134. md_processing/md_commands/feedback_commands.py +726 -0
  135. md_processing/md_commands/glossary_commands.py +684 -0
  136. md_processing/md_commands/governance_officer_commands.py +600 -0
  137. md_processing/md_commands/product_manager_commands.py +1266 -0
  138. md_processing/md_commands/project_commands.py +383 -0
  139. md_processing/md_commands/solution_architect_commands.py +1184 -0
  140. md_processing/md_commands/view_commands.py +295 -0
  141. md_processing/md_processing_utils/__init__.py +4 -0
  142. md_processing/md_processing_utils/common_md_proc_utils.py +1249 -0
  143. md_processing/md_processing_utils/common_md_utils.py +578 -0
  144. md_processing/md_processing_utils/determine_width.py +103 -0
  145. md_processing/md_processing_utils/extraction_utils.py +547 -0
  146. md_processing/md_processing_utils/gen_report_specs.py +643 -0
  147. md_processing/md_processing_utils/generate_dr_help.py +193 -0
  148. md_processing/md_processing_utils/generate_md_cmd_templates.py +144 -0
  149. md_processing/md_processing_utils/generate_md_templates.py +83 -0
  150. md_processing/md_processing_utils/md_processing_constants.py +1228 -0
  151. md_processing/md_processing_utils/message_constants.py +19 -0
  152. pyegeria/__init__.py +201 -443
  153. pyegeria/core/__init__.py +40 -0
  154. pyegeria/core/_base_platform_client.py +574 -0
  155. pyegeria/core/_base_server_client.py +573 -0
  156. pyegeria/core/_exceptions.py +457 -0
  157. pyegeria/core/_globals.py +60 -0
  158. pyegeria/core/_server_client.py +6073 -0
  159. pyegeria/core/_validators.py +257 -0
  160. pyegeria/core/config.py +654 -0
  161. pyegeria/{create_tech_guid_lists.py → core/create_tech_guid_lists.py} +0 -1
  162. pyegeria/core/load_config.py +37 -0
  163. pyegeria/core/logging_configuration.py +207 -0
  164. pyegeria/core/mcp_adapter.py +144 -0
  165. pyegeria/core/mcp_server.py +212 -0
  166. pyegeria/core/utils.py +405 -0
  167. pyegeria/deprecated/__init__.py +0 -0
  168. pyegeria/{_client.py → deprecated/_client.py} +62 -24
  169. pyegeria/{_deprecated_gov_engine.py → deprecated/_deprecated_gov_engine.py} +16 -16
  170. pyegeria/{classification_manager_omvs.py → deprecated/classification_manager_omvs.py} +1988 -1878
  171. pyegeria/deprecated/output_formatter_with_machine_keys.py +1127 -0
  172. pyegeria/{runtime_manager_omvs.py → deprecated/runtime_manager_omvs.py} +216 -229
  173. pyegeria/{valid_metadata_omvs.py → deprecated/valid_metadata_omvs.py} +93 -93
  174. pyegeria/{x_action_author_omvs.py → deprecated/x_action_author_omvs.py} +2 -3
  175. pyegeria/egeria_cat_client.py +25 -51
  176. pyegeria/egeria_client.py +140 -98
  177. pyegeria/egeria_config_client.py +48 -24
  178. pyegeria/egeria_tech_client.py +170 -83
  179. pyegeria/models/__init__.py +150 -0
  180. pyegeria/models/collection_models.py +168 -0
  181. pyegeria/models/models.py +654 -0
  182. pyegeria/omvs/__init__.py +84 -0
  183. pyegeria/omvs/action_author.py +342 -0
  184. pyegeria/omvs/actor_manager.py +5980 -0
  185. pyegeria/omvs/asset_catalog.py +842 -0
  186. pyegeria/omvs/asset_maker.py +2736 -0
  187. pyegeria/omvs/automated_curation.py +4403 -0
  188. pyegeria/omvs/classification_manager.py +11213 -0
  189. pyegeria/omvs/collection_manager.py +5780 -0
  190. pyegeria/omvs/community_matters_omvs.py +468 -0
  191. pyegeria/{core_omag_server_config.py → omvs/core_omag_server_config.py} +157 -157
  192. pyegeria/{data_designer_omvs.py → omvs/data_designer.py} +1991 -1691
  193. pyegeria/omvs/data_discovery.py +869 -0
  194. pyegeria/omvs/data_engineer.py +372 -0
  195. pyegeria/omvs/digital_business.py +1133 -0
  196. pyegeria/omvs/external_links.py +1752 -0
  197. pyegeria/omvs/feedback_manager.py +834 -0
  198. pyegeria/{full_omag_server_config.py → omvs/full_omag_server_config.py} +73 -69
  199. pyegeria/omvs/glossary_manager.py +3231 -0
  200. pyegeria/omvs/governance_officer.py +3009 -0
  201. pyegeria/omvs/lineage_linker.py +314 -0
  202. pyegeria/omvs/location_arena.py +1525 -0
  203. pyegeria/omvs/metadata_expert.py +668 -0
  204. pyegeria/omvs/metadata_explorer_omvs.py +2943 -0
  205. pyegeria/omvs/my_profile.py +1042 -0
  206. pyegeria/omvs/notification_manager.py +358 -0
  207. pyegeria/omvs/people_organizer.py +394 -0
  208. pyegeria/{platform_services.py → omvs/platform_services.py} +113 -193
  209. pyegeria/omvs/product_manager.py +1825 -0
  210. pyegeria/omvs/project_manager.py +1907 -0
  211. pyegeria/omvs/reference_data.py +1140 -0
  212. pyegeria/omvs/registered_info.py +334 -0
  213. pyegeria/omvs/runtime_manager.py +2817 -0
  214. pyegeria/omvs/schema_maker.py +446 -0
  215. pyegeria/{server_operations.py → omvs/server_operations.py} +27 -26
  216. pyegeria/omvs/solution_architect.py +6490 -0
  217. pyegeria/omvs/specification_properties.py +37 -0
  218. pyegeria/omvs/subject_area.py +1042 -0
  219. pyegeria/omvs/template_manager_omvs.py +236 -0
  220. pyegeria/omvs/time_keeper.py +1761 -0
  221. pyegeria/omvs/valid_metadata.py +3221 -0
  222. pyegeria/omvs/valid_metadata_lists.py +37 -0
  223. pyegeria/omvs/valid_type_lists.py +37 -0
  224. pyegeria/view/__init__.py +28 -0
  225. pyegeria/view/_output_format_models.py +514 -0
  226. pyegeria/view/_output_formats.py +14 -0
  227. pyegeria/view/base_report_formats.py +2719 -0
  228. pyegeria/view/dr_egeria_reports.py +56 -0
  229. pyegeria/view/format_set_executor.py +397 -0
  230. pyegeria/{md_processing_utils.py → view/md_processing_utils.py} +5 -5
  231. pyegeria/{mermaid_utilities.py → view/mermaid_utilities.py} +2 -154
  232. pyegeria/view/output_formatter.py +1297 -0
  233. pyegeria-5.5.3.3.dist-info/METADATA +218 -0
  234. pyegeria-5.5.3.3.dist-info/RECORD +241 -0
  235. {pyegeria-5.3.9.9.3.dist-info → pyegeria-5.5.3.3.dist-info}/WHEEL +2 -1
  236. pyegeria-5.5.3.3.dist-info/entry_points.txt +103 -0
  237. pyegeria-5.5.3.3.dist-info/top_level.txt +4 -0
  238. commands/cat/.DS_Store +0 -0
  239. commands/cat/README.md +0 -16
  240. commands/cli/txt_custom_v2.tcss +0 -19
  241. commands/my/README.md +0 -17
  242. commands/ops/README.md +0 -24
  243. commands/ops/monitor_asset_events.py +0 -108
  244. commands/tech/README.md +0 -24
  245. pyegeria/.DS_Store +0 -0
  246. pyegeria/README.md +0 -35
  247. pyegeria/_globals.py +0 -47
  248. pyegeria/_validators.py +0 -385
  249. pyegeria/asset_catalog_omvs.py +0 -864
  250. pyegeria/automated_curation_omvs.py +0 -3765
  251. pyegeria/collection_manager_omvs.py +0 -2744
  252. pyegeria/dr.egeria spec.md +0 -9
  253. pyegeria/egeria_my_client.py +0 -56
  254. pyegeria/feedback_manager_omvs.py +0 -4573
  255. pyegeria/glossary_browser_omvs.py +0 -3728
  256. pyegeria/glossary_manager_omvs.py +0 -2440
  257. pyegeria/m_test.py +0 -118
  258. pyegeria/md_processing_helpers.py +0 -58
  259. pyegeria/md_processing_utils_orig.py +0 -1103
  260. pyegeria/metadata_explorer_omvs.py +0 -2326
  261. pyegeria/my_profile_omvs.py +0 -1022
  262. pyegeria/output_formatter.py +0 -389
  263. pyegeria/project_manager_omvs.py +0 -1933
  264. pyegeria/registered_info.py +0 -167
  265. pyegeria/solution_architect_omvs.py +0 -2156
  266. pyegeria/template_manager_omvs.py +0 -1414
  267. pyegeria/utils.py +0 -197
  268. pyegeria-5.3.9.9.3.dist-info/METADATA +0 -72
  269. pyegeria-5.3.9.9.3.dist-info/RECORD +0 -143
  270. pyegeria-5.3.9.9.3.dist-info/entry_points.txt +0 -99
  271. /pyegeria/{_exceptions.py → deprecated/_exceptions.py} +0 -0
  272. {pyegeria-5.3.9.9.3.dist-info → pyegeria-5.5.3.3.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,654 @@
1
+ """
2
+ SPDX-License-Identifier: Apache-2.0
3
+ Copyright Contributors to the ODPi Egeria project.
4
+
5
+ This module manages configuration information for pyegeria and pyegeria clients.
6
+
7
+ The load_app_config() function loads configuration information:
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.
17
+
18
+ The result is that Environment Variable values take priority over configuration file values which override the defaults.
19
+
20
+ The get_app_config() function is used by other modules to get configuration information from the configuration structure
21
+ and makes it available as a Pydantic model.
22
+
23
+ """
24
+ import inspect
25
+ import os
26
+ import json
27
+ from typing import List, Optional, Dict, Any
28
+
29
+ from loguru import logger
30
+ from pydantic import BaseModel, Field, ConfigDict
31
+ from pydantic_settings import BaseSettings, SettingsConfigDict
32
+
33
+ from pyegeria.core._exceptions import PyegeriaInvalidParameterException
34
+
35
+ logger.disable("pyegeria")
36
+ # --- Pydantic Settings for Environment Variables ---
37
+
38
+ class PyegeriaSettings(BaseSettings):
39
+ """Top-level environment settings for pyegeria.
40
+
41
+ This class centralizes discovery of important filesystem paths and default
42
+ configuration values. It can be constructed directly or via `with_env_file`
43
+ to load overrides from a specific .env-like file.
44
+ """
45
+ """
46
+ Settings loaded from environment variables using pydantic-settings.
47
+ This class is used to load environment variables from the .env file.
48
+
49
+ The .env file path can be specified when creating an instance of this class
50
+ by passing the `_env_file` parameter. If not specified, it defaults to ".env"
51
+ in the current directory.
52
+ """
53
+ # Core settings needed to locate the config file
54
+ pyegeria_root_path: str = ""
55
+ pyegeria_config_directory: str = ""
56
+ pyegeria_config_file: str = "config.json"
57
+
58
+ # Additional settings that can be loaded from .env
59
+ pyegeria_console_width: int = 200
60
+ # Renamed: format_sets -> report_specs
61
+ pyegeria_user_report_specs_dir: str = "~/.pyegeria/report_specs"
62
+ egeria_user_name: str = ""
63
+ egeria_user_password: str = ""
64
+
65
+ model_config = SettingsConfigDict(
66
+ env_file=".env",
67
+ env_file_encoding="utf-8",
68
+ extra="ignore",
69
+ case_sensitive=False
70
+ )
71
+
72
+ @classmethod
73
+ def with_env_file(cls, env_file: str):
74
+ """
75
+ Create a PyegeriaSettings instance with a specific .env file.
76
+
77
+ Args:
78
+ env_file: Path to the .env file to load
79
+
80
+ Returns:
81
+ PyegeriaSettings: A new PyegeriaSettings instance
82
+ """
83
+ # Create a new class with a custom model_config that specifies the env_file
84
+ class CustomSettings(cls):
85
+ model_config = SettingsConfigDict(
86
+ env_file=env_file,
87
+ env_file_encoding="utf-8",
88
+ extra="ignore",
89
+ case_sensitive=False
90
+ )
91
+
92
+ # Create and return an instance of the custom class
93
+ return CustomSettings()
94
+
95
+
96
+ # --- Pydantic Models for Configuration ---
97
+
98
+ class EnvironmentConfig(BaseModel):
99
+ """Runtime environment parameters that influence formatting and behavior."""
100
+ """Environment configuration settings"""
101
+ console_width: int = Field(default=200, alias="Console Width")
102
+ egeria_outbox: str = Field(default="egeria-outbox", alias="Egeria Outbox")
103
+ egeria_inbox: str = Field(default="egeria-inbox", alias="Egeria Inbox")
104
+ dr_egeria_inbox: str = Field(default="sample-data/egeria-inbox/dr-egeria-inbox", alias="Dr.Egeria Inbox")
105
+ dr_egeria_outbox: str = Field(default="sample-data/egeria-outbox/dr-egeria-outbox", alias="Dr.Egeria Outbox")
106
+ egeria_engine_host_url: str = Field(default="", alias="Egeria Engine Host URL")
107
+ egeria_engine_host: str = Field(default="qs-engine-host", alias="Egeria Engine Host")
108
+ egeria_glossary_path: str = Field(default="glossary", alias="Egeria Glossary Path")
109
+ egeria_integration_daemon_url: str = Field(default="https://localhost:9443", alias="Egeria Integration Daemon URL")
110
+ egeria_integration_daemon: str = Field(default="qs-integration-daemon", alias="Egeria Integration Daemon")
111
+ egeria_jupyter: bool = Field(default=True, alias="Egeria Jupyter")
112
+ egeria_kafka_endpoint: str = Field(default="localhost:9192", alias="Egeria Kafka Endpoint")
113
+ egeria_mermaid_folder: str = Field(default="egeria-outbox/mermaid-graphs", alias="Egeria Mermaid Folder")
114
+ egeria_metadata_store: str = Field(default="qs-metadata-store", alias="Egeria Metadata Store")
115
+ egeria_platform_url: str = Field(default="https://localhost:9443", alias="Egeria Platform URL")
116
+ egeria_view_server_url: str = Field(default="https://localhost:9443", alias="Egeria View Server URL")
117
+ egeria_view_server: str = Field(default="qs-view-server", alias="Egeria View Server")
118
+ pyegeria_root: str = Field(default="sample-data", alias="Pyegeria Root")
119
+ pyegeria_config_directory: str = Field(default="", alias="Pyegeria Config Directory")
120
+ pyegeria_config_file: str = Field(default="config.json", alias="Egeria Config File")
121
+ pyegeria_publishing_root: str = Field(default="/dr-egeria-outbox", alias="Pyegeria Publishing Root")
122
+ # Renamed: Format Sets -> Report Specs
123
+ pyegeria_user_report_specs_dir: str = Field(default="~/.pyegeria/report_specs", alias="Pyegeria User Report Specs Dir")
124
+
125
+ model_config = ConfigDict(populate_by_name=True, extra='allow')
126
+
127
+
128
+ class DebugConfig(BaseModel):
129
+ """Debug configuration settings"""
130
+ debug_mode: bool = False
131
+ enable_logger_catch: bool = False
132
+ timeout_seconds: int = 30
133
+
134
+ model_config = ConfigDict(populate_by_name=True, extra='allow')
135
+
136
+
137
+ class LoggingConfig(BaseModel):
138
+ """Logging configuration settings"""
139
+ console_filter_levels: List[str] = ["SUCCESS"]
140
+ console_logging_enabled: List[str] = [ "_exceptions_new", "dr_egeria_md", "tests"]
141
+ console_logging_level: str = "ERROR"
142
+ enable_logging: bool = False
143
+ file_logging_level: str = "INFO"
144
+ log_directory: str = "logs"
145
+ 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}"
146
+ logging_file_format: str = " {time:YYYY-MM-DD HH:mm:ss} | {level} | {function}:{line} - {message }-{extra}"
147
+
148
+ model_config = ConfigDict(populate_by_name=True, extra='allow')
149
+
150
+
151
+ class UserProfileConfig(BaseModel):
152
+ """User profile configuration settings"""
153
+ egeria_home_collection: str = Field(default="MyHome", alias="Egeria Home Collection")
154
+ egeria_home_glossary_name: str = Field(default="Egeria-Markdown", alias="Egeria Home Glossary Name")
155
+ egeria_local_qualifier: str = Field(default="PDR", alias="Egeria Local Qualifier")
156
+ egeria_usage_level: str = Field(default="Advanced", alias="Egeria Usage Level")
157
+ user_name: Optional[str] = "erinoverview"
158
+ user_pwd: Optional[str] = "secret"
159
+
160
+ model_config = ConfigDict(populate_by_name=True, extra='allow')
161
+
162
+
163
+ class AppConfig(BaseModel):
164
+ """Aggregated application configuration used by pyegeria components."""
165
+ """Main application configuration"""
166
+ Environment: EnvironmentConfig
167
+ Debug: DebugConfig
168
+ Logging: LoggingConfig
169
+ User_Profile: UserProfileConfig = Field(alias="User Profile")
170
+ feature_x_enabled: bool = False
171
+
172
+ def get(self, key, default=None):
173
+ """
174
+ Dictionary-like get method for backward compatibility.
175
+
176
+ Args:
177
+ key: The key to look up
178
+ default: The default value to return if the key is not found
179
+
180
+ Returns:
181
+ The value for the key if found, otherwise the default value
182
+ """
183
+ # First check if the key is a direct attribute of this model
184
+ if hasattr(self, key):
185
+ return getattr(self, key)
186
+
187
+ # Then check if it's in any of the nested models
188
+ for section in [self.Environment, self.Debug, self.Logging, self.User_Profile]:
189
+ if hasattr(section, key):
190
+ return getattr(section, key)
191
+ # Also check using the original field names (with aliases)
192
+ for field_name, field in section.model_fields.items():
193
+ # In Pydantic v2, the alias is stored in json_schema_extra
194
+ alias = None
195
+ if hasattr(field, 'alias'):
196
+ alias = field.alias
197
+ elif hasattr(field, 'json_schema_extra') and field.json_schema_extra:
198
+ alias = field.json_schema_extra.get('alias')
199
+
200
+ if alias == key:
201
+ return getattr(section, field_name)
202
+
203
+ # If not found, return the default
204
+ return default
205
+
206
+ model_config = ConfigDict(populate_by_name=True, extra='allow')
207
+
208
+
209
+ # --- Configuration Loading Logic ---
210
+
211
+ # Private variable to hold the loaded configuration
212
+ _app_config = None
213
+
214
+ def _resolve_env_settings(env_file: str | None) -> PyegeriaSettings:
215
+ if env_file:
216
+ return PyegeriaSettings.with_env_file(env_file)
217
+ return PyegeriaSettings()
218
+
219
+
220
+ def _find_config_file_path(settings: PyegeriaSettings) -> str | None:
221
+ config_dir = (settings.pyegeria_config_directory or "").strip()
222
+ root_path = (settings.pyegeria_root_path or "").strip()
223
+ config_file = (settings.pyegeria_config_file or "config.json").strip()
224
+
225
+ candidates = []
226
+ if config_dir:
227
+ candidates.append(os.path.join(config_dir, config_file))
228
+ if root_path:
229
+ candidates.append(os.path.join(root_path, config_file))
230
+ candidates.append(os.path.abspath(os.path.join(os.getcwd(), "config.json")))
231
+
232
+ for path in candidates:
233
+ if path and os.path.exists(path):
234
+ return path
235
+ return None
236
+
237
+
238
+ def load_app_config(env_file: str | None = None):
239
+ """
240
+ Loads application configuration from files and environment variables.
241
+ This function should ideally be called only once at application startup.
242
+
243
+ The function follows this precedence order for configuration settings:
244
+ 1. If env_file is passed in, it uses that file to load environment variables
245
+ 2. Otherwise, it first checks if OS environment variables are set for PYEGERIA_ROOT_PATH and PYEGERIA_CONFIG_FILE
246
+ 3. If they are not set, it checks for a .env file in the current directory
247
+ 4. It then loads the configuration from the config file if available
248
+ 5. Finally, it updates the configuration with environment variables from the operating system,
249
+ which take precedence over the config file values
250
+
251
+ Args:
252
+ env_file: Optional path to a specific .env file to load. If not specified,
253
+ the function follows the precedence order described above.
254
+
255
+ Returns:
256
+ AppConfig: The loaded configuration as a Pydantic model
257
+ """
258
+ global _app_config # Declare intent to modify the global _app_config
259
+
260
+ if _app_config is not None:
261
+ # Configuration already loaded, return existing instance
262
+ return _app_config
263
+
264
+ # 1) Defaults from models
265
+ config_dict: dict[str, Any] = {
266
+ "Environment": {},
267
+ "Debug": {},
268
+ "Logging": {},
269
+ "User Profile": {},
270
+ "feature_x_enabled": False,
271
+ }
272
+
273
+ # 2) Load env settings from OS/.env according to env_file
274
+ env_settings = _resolve_env_settings(env_file)
275
+
276
+ # 3) Load config file if found
277
+ file_path = _find_config_file_path(env_settings)
278
+ if file_path:
279
+ logger.info(f"Using config file: {file_path}")
280
+ try:
281
+ with open(file_path, "r") as f:
282
+ file_cfg = json.load(f)
283
+ if isinstance(file_cfg, dict):
284
+ config_dict.update(file_cfg)
285
+ else:
286
+ logger.warning("Config file root is not an object; ignoring.")
287
+ except Exception as e:
288
+ logger.warning(f"Could not read/parse config file '{file_path}': {e}. Continuing with defaults+env.")
289
+ else:
290
+ logger.debug("No config.json found; continuing with defaults + env.")
291
+
292
+ # 4) Overlay environment variables
293
+ # Debug
294
+ dbg = config_dict.setdefault("Debug", {})
295
+ dbg["debug_mode"] = _parse_bool_env("PYEGERIA_DEBUG_MODE", bool(dbg.get("debug_mode", False)))
296
+ dbg["enable_logger_catch"] = _parse_bool_env("PYEGERIA_ENABLE_LOGGER_CATCH", bool(dbg.get("enable_logger_catch", False)))
297
+ dbg["timeout_seconds"] = int(os.getenv("PYEGERIA_TIMEOUT_SECONDS", dbg.get("timeout_seconds", 30)))
298
+
299
+ # Environment
300
+ env = config_dict.setdefault("Environment", {})
301
+ default_root = env.get("Pyegeria Root") or os.getcwd()
302
+ env_config_dir = os.getenv("PYEGERIA_CONFIG_DIRECTORY", env_settings.pyegeria_config_directory or "")
303
+ env_root = os.getenv("PYEGERIA_ROOT_PATH", env_settings.pyegeria_root_path or default_root)
304
+ env_config_file = os.getenv("PYEGERIA_CONFIG_FILE", env_settings.pyegeria_config_file or "config.json")
305
+ env["Pyegeria Config Directory"] = env_config_dir
306
+ env["pyegeria_config_directory"] = env_config_dir
307
+ env["Pyegeria Root"] = env_root
308
+ env["pyegeria_root"] = env_root
309
+ env["Egeria Config File"] = env_config_file
310
+ env["pyegeria_config_file"] = env_config_file
311
+
312
+ env["Console Width"] = int(os.getenv("CONSOLE_WIDTH", env.get("Console Width", env_settings.pyegeria_console_width)))
313
+ env["console_width"] = env["Console Width"]
314
+ # Egeria Outbox (new)
315
+ env["Egeria Outbox"] = os.getenv("EGERIA_OUTBOX", env.get("Egeria Outbox", "egeria-outbox"))
316
+ env["Egeria Inbox"] = os.getenv("EGERIA_INBOX", env.get("Egeria Inbox", "egeria-inbox"))
317
+ env["Dr.Egeria Inbox"] = os.getenv("DR_EGERIA_INBOX_PATH", env.get("Dr.Egeria Inbox", "md_processing/dr-egeria-inbox"))
318
+ env["Dr.Egeria Outbox"] = os.getenv("DR_EGERIA_OUTBOX_PATH", env.get("Dr.Egeria Outbox", "md_processing/dr-egeria-outbox"))
319
+ env["Egeria Engine Host"] = os.getenv("EGERIA_ENGINE_HOST", env.get("Egeria Engine Host", "qs-engine-host"))
320
+ env["Egeria Engine Host URL"] = os.getenv("EGERIA_ENGINE_HOST_URL", env.get("Egeria Engine Host URL", "https://localhost:9443"))
321
+ env["Egeria Glossary Path"] = os.getenv("EGERIA_GLOSSARY_PATH", env.get("Egeria Glossary Path", "glossary"))
322
+ env["Egeria Integration Daemon"] = os.getenv("EGERIA_INTEGRATION_DAEMON", env.get("Egeria Integration Daemon", "qs-integration-daemon"))
323
+ env["Egeria Integration Daemon URL"] = os.getenv("EGERIA_INTEGRATION_DAEMON_URL", env.get("Egeria Integration Daemon URL", "https://localhost:9443"))
324
+ env["Egeria Jupyter"] = _parse_bool_env("EGERIA_JUPYTER", bool(env.get("Egeria Jupyter", True)))
325
+ env["Egeria Kafka Endpoint"] = os.getenv("EGERIA_KAFKA", env.get("Egeria Kafka Endpoint", "localhost:9192"))
326
+ # Normalize to hyphenated folder name; retain any explicit env override
327
+ env["Egeria Mermaid Folder"] = os.getenv(
328
+ "EGERIA_MERMAID_FOLDER",
329
+ env.get("Egeria Mermaid Folder", "egeria-outbox/mermaid-graphs"),
330
+ )
331
+ env["Egeria Metadata Store"] = os.getenv("EGERIA_METADATA_STORE", env.get("Egeria Metadata Store", "qs-metadata-store"))
332
+ env["Egeria Platform URL"] = os.getenv("EGERIA_PLATFORM_URL", env.get("Egeria Platform URL", "https://localhost:9443"))
333
+ env["Egeria View Server"] = os.getenv("EGERIA_VIEW_SERVER", env.get("Egeria View Server", "qs-view-server"))
334
+ env["Egeria View Server URL"] = os.getenv("EGERIA_VIEW_SERVER_URL", env.get("Egeria View Server URL", "https://localhost:9443"))
335
+ env["Pyegeria Publishing Root"] = os.getenv("PYEGERIA_PUBLISHING_ROOT", env.get("Pyegeria Publishing Root", "/dr-egeria-outbox"))
336
+ # New primary env var with fallback to old name for backward compatibility
337
+ env["Pyegeria User Report Specs Dir"] = (
338
+ os.getenv("PYEGERIA_USER_REPORT_SPECS_DIR")
339
+ or os.getenv("PYEGERIA_USER_FORMAT_SETS_DIR")
340
+ or env.get("Pyegeria User Report Specs Dir")
341
+ or env.get("Pyegeria User Format Sets Dir")
342
+ or "~/.pyegeria/report_specs"
343
+ )
344
+
345
+ # Logging
346
+ log = config_dict.setdefault("Logging", {})
347
+ log["console_filter_levels"] = _parse_list_env("PYEGERIA_CONSOLE_FILTER_LEVELS", log.get("console_filter_levels", ["ERROR", "WARNING", "INFO", "SUCCESS"]))
348
+ log["console_logging_enabled"] = _parse_list_env("PYEGERIA_CONSOLE_LOGGING_ENABLED", log.get("console_logging_enabled", ["pyegeria"]))
349
+ log["console_logging_level"] = os.getenv("PYEGERIA_CONSOLE_LOG_LVL", log.get("console_logging_level", "INFO"))
350
+ log["enable_logging"] = _parse_bool_env("PYEGERIA_ENABLE_LOGGING", bool(log.get("enable_logging", False)))
351
+ log["file_logging_level"] = os.getenv("PYEGERIA_FILE_LOG_LVL", log.get("file_logging_level", "INFO"))
352
+ log["log_directory"] = os.getenv("PYEGERIA_LOG_DIRECTORY", log.get("log_directory", "logs"))
353
+ log["logging_console_format"] = os.getenv(
354
+ "PYEGERIA_LOGGING_CONSOLE_FORMAT",
355
+ log.get(
356
+ "logging_console_format",
357
+ " <green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level}</level> | <cyan>{name}</cyan>:<cyan>{line}</cyan> - <level>{message}</level> -{extra}",
358
+ ),
359
+ )
360
+ log["logging_file_format"] = os.getenv(
361
+ "PYEGERIA_LOGGING_FILE_FORMAT",
362
+ log.get("logging_file_format", " {time:YYYY-MM-DD HH:mm:ss} | {level} | {function}:{line} - {message}-{extra}"),
363
+ )
364
+
365
+ # User Profile
366
+ user = config_dict.setdefault("User Profile", {})
367
+ user["Egeria Home Collection"] = os.getenv("EGERIA_HOME_COLLECTION", user.get("Egeria Home Collection", "MyHome"))
368
+ user["Egeria Home Glossary Name"] = os.getenv("EGERIA_HOME_GLOSSARY_NAME", user.get("Egeria Home Glossary Name", "Egeria-Markdown"))
369
+ user["Egeria Local Qualifier"] = os.getenv("EGERIA_LOCAL_QUALIFIER", user.get("Egeria Local Qualifier", "PDR"))
370
+
371
+ user_name = os.getenv("EGERIA_USER", user.get("user_name") or env_settings.egeria_user_name or None)
372
+ user_pwd = os.getenv("EGERIA_USER_PASSWORD", user.get("user_pwd") or env_settings.egeria_user_password or None)
373
+ user["user_name"] = user_name
374
+ user["user_pwd"] = user_pwd
375
+
376
+ # Feature flags
377
+ feature_x = _parse_bool_value(os.getenv("FEATURE_X_ENABLED", config_dict.get("feature_x_enabled", False)))
378
+ config_dict["feature_x_enabled"] = feature_x
379
+
380
+ # Debug print of env before model creation
381
+ try:
382
+ logger.info(f"DEBUG ENV SECTION: {config_dict.get('Environment')}")
383
+ _app_config = AppConfig(**config_dict)
384
+ except Exception as e:
385
+ context = {"caller method": inspect.currentframe().f_back.f_code.co_name}
386
+ additional_info = {"reason": str(e)}
387
+ raise PyegeriaInvalidParameterException(None, context, additional_info)
388
+
389
+ return _app_config
390
+
391
+
392
+ def get_app_config(env_file: str = None)-> AppConfig:
393
+ """
394
+ Provides access to the loaded application configuration.
395
+ Ensures config is loaded if not already (useful for testing or simple scripts).
396
+ For structured apps, load_app_config() should be called explicitly once at startup.
397
+
398
+ Args:
399
+ env_file: Optional path to a specific .env file to load. If not specified,
400
+ the default .env file in the current directory is used.
401
+
402
+ Returns:
403
+ AppConfig: The loaded configuration as a Pydantic model
404
+ """
405
+ if _app_config is None:
406
+ # If get_app_config is called before load_app_config, load it now.
407
+ # This can be convenient but explicit loading is generally better.
408
+ logger.info(f"The env_file {env_file} is being passed in")
409
+ return load_app_config(env_file)
410
+ return _app_config
411
+
412
+
413
+
414
+
415
+ def _parse_bool_env(env_var: str, default: bool) -> bool:
416
+ """
417
+ Parse a boolean environment variable.
418
+
419
+ Args:
420
+ env_var: The name of the environment variable
421
+ default: The default value if the environment variable is not set
422
+
423
+ Returns:
424
+ bool: The parsed boolean value
425
+ """
426
+ if env_var in os.environ:
427
+ value = os.getenv(env_var).lower()
428
+ return value in ('true', '1', 't', 'y', 'yes', 'on')
429
+ return default
430
+
431
+ def _parse_bool_value(value: Any) -> bool:
432
+ """
433
+ Parse a boolean value from any type.
434
+
435
+ Args:
436
+ value: The value to parse
437
+
438
+ Returns:
439
+ bool: The parsed boolean value
440
+ """
441
+ if isinstance(value, bool):
442
+ return value
443
+ if isinstance(value, str):
444
+ return value.lower() in ('true', '1', 't', 'y', 'yes', 'on')
445
+ if isinstance(value, (int, float)):
446
+ return bool(value)
447
+ return False
448
+
449
+
450
+ def _parse_list_env(env_var: str, default: List[str]) -> List[str]:
451
+ """
452
+ Parse a list environment variable (comma-separated).
453
+
454
+ Args:
455
+ env_var: The name of the environment variable
456
+ default: The default value if the environment variable is not set
457
+
458
+ Returns:
459
+ List[str]: The parsed list value
460
+ """
461
+ if env_var in os.environ:
462
+ value = os.getenv(env_var)
463
+ if value:
464
+ return [item.strip() for item in value.split(',')]
465
+ return default
466
+
467
+
468
+ # Export a lazily-evaluated settings accessor to avoid import-time side effects
469
+ class _LazySettings:
470
+ def __getattr__(self, name):
471
+ cfg = get_app_config()
472
+ return getattr(cfg, name)
473
+
474
+ settings = _LazySettings()
475
+
476
+
477
+ def pretty_print_config(env_file: str | None = None, safe: bool = True, to_console: bool = True) -> Dict[str, Dict[str, Any]]:
478
+ """
479
+ Pretty print the current configuration and indicate the source of each value
480
+ (Environment, .env file, config file, or default). Uses Rich if available.
481
+
482
+ Args:
483
+ env_file: Optional .env path to force loading before printing (if config not yet loaded).
484
+ safe: Mask sensitive values such as passwords/tokens.
485
+ to_console: If True, prints to console; function always returns a structured dict.
486
+
487
+ Returns:
488
+ dict mapping section -> { key -> {"value": ..., "source": ...} }
489
+ """
490
+ # Ensure config is loaded
491
+ cfg = get_app_config(env_file)
492
+
493
+ # Try import rich lazily
494
+ try:
495
+ from rich.console import Console
496
+ from rich.table import Table
497
+ from rich import box
498
+ console = Console(width=getattr(cfg.Environment, 'console_width', 200))
499
+ use_rich = True
500
+ except Exception:
501
+ console = None
502
+ use_rich = False
503
+
504
+ # Helper to mask sensitive keys
505
+ def _mask(key: str, val: Any) -> Any:
506
+ if not safe:
507
+ return val
508
+ key_l = (key or "").lower()
509
+ if any(s in key_l for s in ["password", "pwd", "secret", "token", "apikey", "api_key"]):
510
+ if val is None:
511
+ return None
512
+ s = str(val)
513
+ if len(s) <= 4:
514
+ return "****"
515
+ return s[:2] + "****" + s[-2:]
516
+ return val
517
+
518
+ # Determine sources. Because we merge defaults, config.json, and env, we infer source:
519
+ # - If an OS env var exists for a specific setting name we used, it's "env".
520
+ # - Else if a config file was found (by our path resolver) and provided an override, mark "config".
521
+ # - Else if value equals model default and neither env nor config provided, "default".
522
+
523
+ # We need to reconstruct which keys are influenced by ENV VAR names.
524
+ env_var_map = {
525
+ # Debug
526
+ ("Debug", "debug_mode"): "PYEGERIA_DEBUG_MODE",
527
+ ("Debug", "enable_logger_catch"): "PYEGERIA_ENABLE_LOGGER_CATCH",
528
+ ("Debug", "timeout_seconds"): "PYEGERIA_TIMEOUT_SECONDS",
529
+ # Environment
530
+ ("Environment", "Console Width"): "CONSOLE_WIDTH",
531
+ ("Environment", "Egeria Outbox"): "EGERIA_OUTBOX",
532
+ ("Environment", "Dr.Egeria Inbox"): "DR_EGERIA_INBOX_PATH",
533
+ ("Environment", "Dr.Egeria Outbox"): "DR_EGERIA_OUTBOX_PATH",
534
+ ("Environment", "Egeria Engine Host"): "EGERIA_ENGINE_HOST",
535
+ ("Environment", "Egeria Engine Host URL"): "EGERIA_ENGINE_HOST_URL",
536
+ ("Environment", "Egeria Glossary Path"): "EGERIA_GLOSSARY_PATH",
537
+ ("Environment", "Egeria Integration Daemon"): "EGERIA_INTEGRATION_DAEMON",
538
+ ("Environment", "Egeria Integration Daemon URL"): "EGERIA_INTEGRATION_DAEMON_URL",
539
+ ("Environment", "Egeria Jupyter"): "EGERIA_JUPYTER",
540
+ ("Environment", "Egeria Kafka Endpoint"): "EGERIA_KAFKA",
541
+ ("Environment", "Egeria Mermaid Folder"): "EGERIA_MERMAID_FOLDER",
542
+ ("Environment", "Egeria Metadata Store"): "EGERIA_METADATA_STORE",
543
+ ("Environment", "Egeria Platform URL"): "EGERIA_PLATFORM_URL",
544
+ ("Environment", "Egeria View Server"): "EGERIA_VIEW_SERVER",
545
+ ("Environment", "Egeria View Server URL"): "EGERIA_VIEW_SERVER_URL",
546
+ ("Environment", "Pyegeria Publishing Root"): "PYEGERIA_PUBLISHING_ROOT",
547
+ # Updated variable name
548
+ ("Environment", "Pyegeria User Report Specs Dir"): "PYEGERIA_USER_REPORT_SPECS_DIR",
549
+ ("Environment", "Pyegeria Root"): "PYEGERIA_ROOT_PATH",
550
+ ("Environment", "Pyegeria Config Directory"): "PYEGERIA_CONFIG_DIRECTORY",
551
+ ("Environment", "Egeria Config File"): "PYEGERIA_CONFIG_FILE",
552
+ # Logging
553
+ ("Logging", "console_filter_levels"): "PYEGERIA_CONSOLE_FILTER_LEVELS",
554
+ ("Logging", "console_logging_enabled"): "PYEGERIA_CONSOLE_LOGGING_ENABLED",
555
+ ("Logging", "console_logging_level"): "PYEGERIA_CONSOLE_LOG_LVL",
556
+ ("Logging", "enable_logging"): "PYEGERIA_ENABLE_LOGGING",
557
+ ("Logging", "file_logging_level"): "PYEGERIA_FILE_LOG_LVL",
558
+ ("Logging", "log_directory"): "PYEGERIA_LOG_DIRECTORY",
559
+ ("Logging", "logging_console_format"): "PYEGERIA_LOGGING_CONSOLE_FORMAT",
560
+ ("Logging", "logging_file_format"): "PYEGERIA_LOGGING_FILE_FORMAT",
561
+ # User profile
562
+ ("User Profile", "Egeria Home Collection"): "EGERIA_HOME_COLLECTION",
563
+ ("User Profile", "Egeria Home Glossary Name"): "EGERIA_HOME_GLOSSARY_NAME",
564
+ ("User Profile", "Egeria Local Qualifier"): "EGERIA_LOCAL_QUALIFIER",
565
+ ("User Profile", "user_name"): "EGERIA_USER",
566
+ ("User Profile", "user_pwd"): "EGERIA_USER_PASSWORD",
567
+ # Feature flag example
568
+ (None, "feature_x_enabled"): "FEATURE_X_ENABLED",
569
+ }
570
+
571
+ # Attempt to detect if a config.json was used
572
+ env_settings = _resolve_env_settings(env_file)
573
+ config_file_path = _find_config_file_path(env_settings)
574
+
575
+ # Build a snapshot of defaults by instantiating empty models
576
+ defaults = {
577
+ "Environment": EnvironmentConfig().model_dump(by_alias=True),
578
+ "Debug": DebugConfig().model_dump(by_alias=True),
579
+ "Logging": LoggingConfig().model_dump(by_alias=False),
580
+ "User Profile": UserProfileConfig().model_dump(by_alias=True),
581
+ "feature_x_enabled": False,
582
+ }
583
+
584
+ # Current values from cfg
585
+ sections = [
586
+ ("Environment", cfg.Environment.model_dump(by_alias=True)),
587
+ ("Debug", cfg.Debug.model_dump(by_alias=False)),
588
+ ("Logging", cfg.Logging.model_dump(by_alias=False)),
589
+ ("User Profile", cfg.User_Profile.model_dump(by_alias=True)),
590
+ ]
591
+
592
+ result: Dict[str, Dict[str, Any]] = {}
593
+
594
+ for section_name, values in sections:
595
+ section_out: Dict[str, Any] = {}
596
+ for key, val in values.items():
597
+ # Prefer alias keys in display; ensure key exists in defaults appropriately
598
+ default_section = defaults.get(section_name, {})
599
+ default_val = default_section.get(key, None)
600
+
601
+ # Identify env var used for this key if any
602
+ env_var = env_var_map.get((section_name, key))
603
+ source = "default"
604
+ if env_var and env_var in os.environ:
605
+ source = "env"
606
+ elif config_file_path and (key in (defaults.get(section_name, {}) or {}) or True):
607
+ # If a config file exists and value differs from default and no env var set
608
+ if val != default_val:
609
+ source = "config"
610
+ else:
611
+ source = "default"
612
+ else:
613
+ source = "default"
614
+
615
+ section_out[key] = {
616
+ "value": _mask(key, val),
617
+ "source": source,
618
+ }
619
+ result[section_name] = section_out
620
+
621
+ # Add top-level feature flags
622
+ feat_val = getattr(cfg, "feature_x_enabled", False)
623
+ source = "env" if ("FEATURE_X_ENABLED" in os.environ) else ("config" if config_file_path and feat_val != defaults["feature_x_enabled"] else "default")
624
+ result["feature_x_enabled"] = {"value": _mask("feature_x_enabled", feat_val), "source": source}
625
+
626
+ if to_console:
627
+ if use_rich and console:
628
+ for section_name in ["Environment", "Debug", "Logging", "User Profile"]:
629
+ table = Table(title=f"{section_name} Settings", box=box.SIMPLE_HEAVY)
630
+ table.add_column("Key")
631
+ table.add_column("Value")
632
+ table.add_column("Source")
633
+ for k, info in result[section_name].items():
634
+ table.add_row(str(k), str(info["value"]), info["source"])
635
+ console.print(table)
636
+ # Feature flags
637
+ table = Table(title="Feature Flags", box=box.SIMPLE_HEAVY)
638
+ table.add_column("Key")
639
+ table.add_column("Value")
640
+ table.add_column("Source")
641
+ ff = result["feature_x_enabled"]
642
+ table.add_row("feature_x_enabled", str(ff["value"]), ff["source"])
643
+ console.print(table)
644
+ else:
645
+ # Plain text fallback
646
+ print("Configuration:")
647
+ for section_name in ["Environment", "Debug", "Logging", "User Profile"]:
648
+ print(f"[{section_name}]")
649
+ for k, info in result[section_name].items():
650
+ print(f"- {k}: {info['value']} (source: {info['source']})")
651
+ ff = result["feature_x_enabled"]
652
+ print(f"feature_x_enabled: {ff['value']} (source: {ff['source']})")
653
+
654
+ return result
@@ -10,7 +10,6 @@ These GUIDS should be copied into the pyegeria/__init__.py.
10
10
  from datetime import datetime
11
11
  from rich.console import Console
12
12
  from pyegeria.egeria_tech_client import EgeriaTech
13
- from pyegeria._globals import NO_ELEMENTS_FOUND
14
13
 
15
14
  console = Console(width=200)
16
15
  integration_server = "qs-integration-daemon"