Repository: kedro-org/kedro
Pull request: Fix `OmegaConfigLoader` __repr__ and missing keys method
## Description
<!-- Why was this PR created? -->
Fix #3618, since we are trying to improve the interactive experience. While it's still a bit unclear as `AbstractConfigLoader` inherit from `UserDict`,  `OmegaConfigLoader` only presents traits for dictionary access pattern, i.e. dict["key"], but not the other API such as iterating the keys.



## Development notes
<!-- What have you changed, and how has this been tested? -->
The PR is not a perfect fix, but nonetheless address two issues:
1. `__repr__` is misleading as it is missing some arguments to start with, to do this I also have to save `self._custom_resolver`)
2. `keys` is implemented so that at least it is iterable, we can consider `__iter__` and `values` as well but I want to keep this PR minimal.

### Before change
![image](https://github.com/user-attachments/assets/b2375bb8-0d2a-407e-bfa1-1f325096d598)

### After change
![image](https://github.com/user-attachments/assets/2a40cb58-f50f-4cf2-93e4-0e0b145bc71a)


## Developer Certificate of Origin
We need all contributions to comply with the [Developer Certificate of Origin (DCO)](https://developercertificate.org/). All commits must be signed off by including a `Signed-off-by` line in the commit message. [See our wiki for guidance](https://github.com/kedro-org/kedro/wiki/Guidelines-for-contributing-developers/).

If your PR is blocked due to unsigned commits, then you must follow the instructions under "Rebase the branch" on the GitHub Checks page for your PR. This will retroactively add the sign-off to all unsigned commits and allow the DCO check to pass.

## Checklist

- [ ] Read the [contributing](https://github.com/kedro-org/kedro/blob/main/CONTRIBUTING.md) guidelines
- [ ] Signed off each commit with a [Developer Certificate of Origin (DCO)](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/managing-repository-settings/managing-the-commit-signoff-policy-for-your-repository)
- [ ] Opened this PR as a 'Draft Pull Request' if it is work-in-progress
- [ ] Updated the documentation to reflect the code changes
- [ ] Added a description of this change in the [`RELEASE.md`](https://github.com/kedro-org/kedro/blob/main/RELEASE.md) file
- [ ] Added tests to cover my changes
- [ ] Checked if this change will affect Kedro-Viz, and if so, communicated that with the Viz team


Problem statement:
OmegaConfigLoader's `__repr__` is not implemented correctly, and it does not behave similar to a `UserDict`
## Description
<!-- Short description of the problem here. -->
Currently, `base_env` and `default_run_env` is not included in the `__repr__`.

When it is printed, it shows something like this

> In [11]: c
> Out[11]: OmegaConfigLoader(conf_source=/Users/Nok_Lam_Chan/dev/kedro-inspect/demo-project/conf, env=None, config_patterns={'catalog': ['catalog*', 'catalog*/**', '**/catalog*'], 'parameters': ['parameters*', 'parameters*/**', '**/parameters*'], 'credentials': ['credentials*', 'credentials*/**', '**/credentials*'], 'globals': ['globals.yml']})
> 

## Context
<!-- How has this bug affected you? What were you trying to accomplish? -->
Even it inherits from `UserDict`, but the dictionary interface seems broken. For example:
```
for key in config_loader.keys():
  print(key)
```
will print nothing even though `config_loader["parameters"]` exist. For the context, I was trying to build a plugin that iterate the config loader and "compile" the configuration.

I need to use a workaround looks like this:
```python
keys = config_loader.config_patterns.keys()
for key in keys:
   config = config_loader[key] 
   ...
```

 In addition, if `__repr__` is removed, the config_loader will be printed as if it is a "dictionary". Right now it always print the `__repr__` regardless of the `keys`, `values`, `items` or `print` method.

Cc @merelcht

Hints from issue discussion:

Commit message: linter is not happy about this

Signed-off-by: Nok Lam Chan <nok.lam.chan@quantumblack.com>

Patch to review:
diff --git a/RELEASE.md b/RELEASE.md
index cc8b9032c9..cd1eb2b5ae 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -11,6 +11,8 @@
 * Made [kedro-telemetry](https://github.com/kedro-org/kedro-plugins/tree/main/kedro-telemetry) a core dependency.
 * Implemented dataset pretty printing.
 * Implemented `DataCatalog` pretty printing.
+* Fixed a bug when `OmegaConfigLoader` is printed, there are few missing arguments.
+* Fixed a bug when where iterating `OmegaConfigLoader`'s `keys` return empty dictionary.
 
 ## Breaking changes to the API
 
@@ -77,6 +79,7 @@ Many thanks to the following Kedroids for contributing PRs to this release:
 * Fixed error handling message for malformed yaml/json files in OmegaConfigLoader.
 * Fixed a bug in `node`-creation allowing self-dependencies when using transcoding, that is datasets named like `name@format`.
 * Improved error message when passing wrong value to node.
+* Fixed a bug when `OmegaConfigLoader` is printed, there is a few arguments missing.
 
 ## Breaking changes to the API
 * Methods `_is_project` and `_find_kedro_project` have been moved to `kedro.utils`. We recommend not using private methods in your code, but if you do, please update your code to use the new location.
diff --git a/kedro/config/omegaconf_config.py b/kedro/config/omegaconf_config.py
index 0afc43cfa4..a74c0a1351 100644
--- a/kedro/config/omegaconf_config.py
+++ b/kedro/config/omegaconf_config.py
@@ -7,6 +7,7 @@
 import logging
 import mimetypes
 import typing
+from collections.abc import KeysView
 from pathlib import Path
 from typing import Any, Callable, Iterable
 
@@ -124,6 +125,7 @@ def __init__(  # noqa: PLR0913
         # Deactivate oc.env built-in resolver for OmegaConf
         OmegaConf.clear_resolver("oc.env")
         # Register user provided custom resolvers
+        self._custom_resolvers = custom_resolvers
         if custom_resolvers:
             self._register_new_resolvers(custom_resolvers)
         # Register globals resolver
@@ -255,9 +257,17 @@ def __getitem__(self, key: str) -> dict[str, Any]:  # noqa: PLR0912
     def __repr__(self) -> str:  # pragma: no cover
         return (
             f"OmegaConfigLoader(conf_source={self.conf_source}, env={self.env}, "
-            f"config_patterns={self.config_patterns})"
+            f"runtime_params={self.runtime_params}, "
+            f"config_patterns={self.config_patterns}, "
+            f"base_env={self.base_env}), "
+            f"default_run_env={self.default_run_env}), "
+            f"custom_resolvers={self._custom_resolvers}), "
+            f"merge_strategy={self.merge_strategy})"
         )
 
+    def keys(self) -> KeysView:
+        return KeysView(self.config_patterns)
+
     @typing.no_type_check
     def load_and_merge_dir_config(  # noqa: PLR0913
         self,
diff --git a/tests/config/test_omegaconf_config.py b/tests/config/test_omegaconf_config.py
index 676963ee18..cf64c4c7c7 100644
--- a/tests/config/test_omegaconf_config.py
+++ b/tests/config/test_omegaconf_config.py
@@ -1482,3 +1482,7 @@ def test_runtime_params_default_global(self, tmp_path):
         conf = OmegaConfigLoader(tmp_path, runtime_params=runtime_params)
         # runtime params are resolved correctly in catalog using global default
         assert conf["catalog"]["companies"]["type"] == globals_config["dataset"]["type"]
+
+    def test_keys_exist(self):
+        conf = OmegaConfigLoader(conf_source="")
+        assert list(conf.keys()) == ["catalog", "parameters", "credentials", "globals"]


Code review comments:
diff_hunk: @@ -77,6 +79,7 @@ Many thanks to the following Kedroids for contributing PRs to this release:
 * Fixed error handling message for malformed yaml/json files in OmegaConfigLoader.
 * Fixed a bug in `node`-creation allowing self-dependencies when using transcoding, that is datasets named like `name@format`.
 * Improved error message when passing wrong value to node.
+* Fixed a bug when `OmegaConfigLoader` is printed, there is a few arguments missing.
path: RELEASE.md
text: @user1:
Is it here intentionally?

@user2:
+1

@author:
No idea how this get in😅 nice catch!