We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Revisit: Make KedroContext a dataclass and add `config_loader` as a property
# Description
As `KedroSession` now control the lifecycle of Kedro's run, `KedroContext` act like a container and it stores important attributes. Since we now dropped Python 3.6 support, we can make use of Python's `dataclass` to further tidy up `KedroContext`'s constructor code.

todo:
- [x] Make `KedroContext` a dataclass(https://github.com/kedro-org/kedro/pull/1465)
- [x] Add `config_loader` as @property instead of relying the private `context._get_config_loader()` https://github.com/kedro-org/kedro/pull/1505
- [x] Update corresponding document in Starter/Kedro


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `kedro/framework/context/context.py`
Content:
```
1 """This module provides context for Kedro project."""
2 from __future__ import annotations
3 
4 import logging
5 from copy import deepcopy
6 from pathlib import Path, PurePosixPath, PureWindowsPath
7 from typing import Any
8 from urllib.parse import urlparse
9 from warnings import warn
10 
11 from attrs import field, frozen
12 from omegaconf import OmegaConf
13 from pluggy import PluginManager
14 
15 from kedro.config import AbstractConfigLoader, MissingConfigException
16 from kedro.framework.project import settings
17 from kedro.io import DataCatalog
18 from kedro.pipeline.pipeline import _transcode_split
19 
20 
21 def _is_relative_path(path_string: str) -> bool:
22     """Checks whether a path string is a relative path.
23 
24     Example:
25     ::
26         >>> _is_relative_path("data/01_raw") == True
27         >>> _is_relative_path("info.log") == True
28         >>> _is_relative_path("/tmp/data/01_raw") == False
29         >>> _is_relative_path(r"C:\\info.log") == False
30         >>> _is_relative_path(r"\\'info.log") == False
31         >>> _is_relative_path("c:/info.log") == False
32         >>> _is_relative_path("s3://info.log") == False
33 
34     Args:
35         path_string: The path string to check.
36     Returns:
37         Whether the string is a relative path.
38     """
39     # os.path.splitdrive does not reliably work on non-Windows systems
40     # breaking the coverage, using PureWindowsPath instead
41     is_full_windows_path_with_drive = bool(PureWindowsPath(path_string).drive)
42     if is_full_windows_path_with_drive:
43         return False
44 
45     is_remote_path = bool(urlparse(path_string).scheme)
46     if is_remote_path:
47         return False
48 
49     is_absolute_path = PurePosixPath(path_string).is_absolute()
50     if is_absolute_path:
51         return False
52 
53     return True
54 
55 
56 def _convert_paths_to_absolute_posix(
57     project_path: Path, conf_dictionary: dict[str, Any]
58 ) -> dict[str, Any]:
59     """Turn all relative paths inside ``conf_dictionary`` into absolute paths by appending them
60     to ``project_path`` and convert absolute Windows paths to POSIX format. This is a hack to
61     make sure that we don't have to change user's working directory for logging and datasets to
62     work. It is important for non-standard workflows such as IPython notebook where users don't go
63     through `kedro run` or `__main__.py` entrypoints.
64 
65     Example:
66     ::
67         >>> conf = _convert_paths_to_absolute_posix(
68         >>>     project_path=Path("/path/to/my/project"),
69         >>>     conf_dictionary={
70         >>>         "handlers": {
71         >>>             "info_file_handler": {
72         >>>                 "filename": "info.log"
73         >>>             }
74         >>>         }
75         >>>     }
76         >>> )
77         >>> print(conf['handlers']['info_file_handler']['filename'])
78         "/path/to/my/project/info.log"
79 
80     Args:
81         project_path: The root directory to prepend to relative path to make absolute path.
82         conf_dictionary: The configuration containing paths to expand.
83     Returns:
84         A dictionary containing only absolute paths.
85     Raises:
86         ValueError: If the provided ``project_path`` is not an absolute path.
87     """
88     if not project_path.is_absolute():
89         raise ValueError(
90             f"project_path must be an absolute path. Received: {project_path}"
91         )
92 
93     # only check a few conf keys that are known to specify a path string as value
94     conf_keys_with_filepath = ("filename", "filepath", "path")
95 
96     for conf_key, conf_value in conf_dictionary.items():
97         # if the conf_value is another dictionary, absolutify its paths first.
98         if isinstance(conf_value, dict):
99             conf_dictionary[conf_key] = _convert_paths_to_absolute_posix(
100                 project_path, conf_value
101             )
102             continue
103 
104         # if the conf_value is not a dictionary nor a string, skip
105         if not isinstance(conf_value, str):
106             continue
107 
108         # if the conf_value is a string but the conf_key isn't one associated with filepath, skip
109         if conf_key not in conf_keys_with_filepath:
110             continue
111 
112         if _is_relative_path(conf_value):
113             # Absolute local path should be in POSIX format
114             conf_value_absolute_path = (project_path / conf_value).as_posix()
115             conf_dictionary[conf_key] = conf_value_absolute_path
116         elif PureWindowsPath(conf_value).drive:
117             # Convert absolute Windows path to POSIX format
118             conf_dictionary[conf_key] = PureWindowsPath(conf_value).as_posix()
119 
120     return conf_dictionary
121 
122 
123 def _validate_transcoded_datasets(catalog: DataCatalog):
124     """Validates transcoded datasets are correctly named
125 
126     Args:
127         catalog (DataCatalog): The catalog object containing the
128         datasets to be validated.
129 
130     Raises:
131         ValueError: If a dataset name does not conform to the expected
132         transcoding naming conventions,a ValueError is raised by the
133         `_transcode_split` function.
134 
135     """
136     # noqa: protected-access
137     for dataset_name in catalog._datasets.keys():
138         _transcode_split(dataset_name)
139 
140 
141 def _expand_full_path(project_path: str | Path) -> Path:
142     return Path(project_path).expanduser().resolve()
143 
144 
145 @frozen
146 class KedroContext:
147     """``KedroContext`` is the base class which holds the configuration and
148     Kedro's main functionality.
149     """
150 
151     _package_name: str
152     project_path: Path = field(converter=_expand_full_path)
153     config_loader: AbstractConfigLoader
154     _hook_manager: PluginManager
155     env: str | None = None
156     _extra_params: dict[str, Any] | None = field(default=None, converter=deepcopy)
157 
158     @property
159     def catalog(self) -> DataCatalog:
160         """Read-only property referring to Kedro's ``DataCatalog`` for this context.
161 
162         Returns:
163             DataCatalog defined in `catalog.yml`.
164         Raises:
165             KedroContextError: Incorrect ``DataCatalog`` registered for the project.
166 
167         """
168         return self._get_catalog()
169 
170     @property
171     def params(self) -> dict[str, Any]:
172         """Read-only property referring to Kedro's parameters for this context.
173 
174         Returns:
175             Parameters defined in `parameters.yml` with the addition of any
176                 extra parameters passed at initialization.
177         """
178         try:
179             params = self.config_loader["parameters"]
180         except MissingConfigException as exc:
181             warn(f"Parameters not found in your Kedro project config.\n{str(exc)}")
182             params = {}
183 
184         if self._extra_params:
185             # Merge nested structures
186             params = OmegaConf.merge(params, self._extra_params)
187 
188         return OmegaConf.to_container(params) if OmegaConf.is_config(params) else params
189 
190     def _get_catalog(
191         self,
192         save_version: str = None,
193         load_versions: dict[str, str] = None,
194     ) -> DataCatalog:
195         """A hook for changing the creation of a DataCatalog instance.
196 
197         Returns:
198             DataCatalog defined in `catalog.yml`.
199         Raises:
200             KedroContextError: Incorrect ``DataCatalog`` registered for the project.
201 
202         """
203         # '**/catalog*' reads modular pipeline configs
204         conf_catalog = self.config_loader["catalog"]
205         # turn relative paths in conf_catalog into absolute paths
206         # before initializing the catalog
207         conf_catalog = _convert_paths_to_absolute_posix(
208             project_path=self.project_path, conf_dictionary=conf_catalog
209         )
210         conf_creds = self._get_config_credentials()
211 
212         catalog = settings.DATA_CATALOG_CLASS.from_config(
213             catalog=conf_catalog,
214             credentials=conf_creds,
215             load_versions=load_versions,
216             save_version=save_version,
217         )
218 
219         feed_dict = self._get_feed_dict()
220         catalog.add_feed_dict(feed_dict)
221         _validate_transcoded_datasets(catalog)
222         self._hook_manager.hook.after_catalog_created(
223             catalog=catalog,
224             conf_catalog=conf_catalog,
225             conf_creds=conf_creds,
226             feed_dict=feed_dict,
227             save_version=save_version,
228             load_versions=load_versions,
229         )
230         return catalog
231 
232     def _get_feed_dict(self) -> dict[str, Any]:
233         """Get parameters and return the feed dictionary."""
234         params = self.params
235         feed_dict = {"parameters": params}
236 
237         def _add_param_to_feed_dict(param_name, param_value):
238             """This recursively adds parameter paths to the `feed_dict`,
239             whenever `param_value` is a dictionary itself, so that users can
240             specify specific nested parameters in their node inputs.
241 
242             Example:
243 
244                 >>> param_name = "a"
245                 >>> param_value = {"b": 1}
246                 >>> _add_param_to_feed_dict(param_name, param_value)
247                 >>> assert feed_dict["params:a"] == {"b": 1}
248                 >>> assert feed_dict["params:a.b"] == 1
249             """
250             key = f"params:{param_name}"
251             feed_dict[key] = param_value
252             if isinstance(param_value, dict):
253                 for key, val in param_value.items():
254                     _add_param_to_feed_dict(f"{param_name}.{key}", val)
255 
256         for param_name, param_value in params.items():
257             _add_param_to_feed_dict(param_name, param_value)
258 
259         return feed_dict
260 
261     def _get_config_credentials(self) -> dict[str, Any]:
262         """Getter for credentials specified in credentials directory."""
263         try:
264             conf_creds = self.config_loader["credentials"]
265         except MissingConfigException as exc:
266             logging.getLogger(__name__).debug(
267                 "Credentials not found in your Kedro project config.\n %s", str(exc)
268             )
269             conf_creds = {}
270         return conf_creds
271 
272 
273 class KedroContextError(Exception):
274     """Error occurred when loading project and running context pipeline."""
275
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/kedro/framework/context/context.py b/kedro/framework/context/context.py
--- a/kedro/framework/context/context.py
+++ b/kedro/framework/context/context.py
@@ -8,7 +8,7 @@
 from urllib.parse import urlparse
 from warnings import warn
 
-from attrs import field, frozen
+from attrs import define, field
 from omegaconf import OmegaConf
 from pluggy import PluginManager
 
@@ -142,18 +142,38 @@
     return Path(project_path).expanduser().resolve()
 
 
-@frozen
+@define(slots=False)  # Enable setting new attributes to `KedroContext`
 class KedroContext:
     """``KedroContext`` is the base class which holds the configuration and
     Kedro's main functionality.
+
+    Create a context object by providing the root of a Kedro project and
+    the environment configuration subfolders (see ``kedro.config.OmegaConfigLoader``)
+    Raises:
+        KedroContextError: If there is a mismatch
+            between Kedro project version and package version.
+    Args:
+        project_path: Project path to define the context for.
+        config_loader: Kedro's ``OmegaConfigLoader`` for loading the configuration files.
+        env: Optional argument for configuration default environment to be used
+            for running the pipeline. If not specified, it defaults to "local".
+        package_name: Package name for the Kedro project the context is
+            created for.
+        hook_manager: The ``PluginManager`` to activate hooks, supplied by the session.
+        extra_params: Optional dictionary containing extra project parameters.
+            If specified, will update (and therefore take precedence over)
+            the parameters retrieved from the project configuration.
+
     """
 
-    _package_name: str
-    project_path: Path = field(converter=_expand_full_path)
-    config_loader: AbstractConfigLoader
-    _hook_manager: PluginManager
-    env: str | None = None
-    _extra_params: dict[str, Any] | None = field(default=None, converter=deepcopy)
+    project_path: Path = field(init=True, converter=_expand_full_path)
+    config_loader: AbstractConfigLoader = field(init=True)
+    env: str | None = field(init=True)
+    _package_name: str = field(init=True)
+    _hook_manager: PluginManager = field(init=True)
+    _extra_params: dict[str, Any] | None = field(
+        init=True, default=None, converter=deepcopy
+    )
 
     @property
     def catalog(self) -> DataCatalog:

{"golden_diff": "diff --git a/kedro/framework/context/context.py b/kedro/framework/context/context.py\n--- a/kedro/framework/context/context.py\n+++ b/kedro/framework/context/context.py\n@@ -8,7 +8,7 @@\n from urllib.parse import urlparse\n from warnings import warn\n \n-from attrs import field, frozen\n+from attrs import define, field\n from omegaconf import OmegaConf\n from pluggy import PluginManager\n \n@@ -142,18 +142,38 @@\n     return Path(project_path).expanduser().resolve()\n \n \n-@frozen\n+@define(slots=False)  # Enable setting new attributes to `KedroContext`\n class KedroContext:\n     \"\"\"``KedroContext`` is the base class which holds the configuration and\n     Kedro's main functionality.\n+\n+    Create a context object by providing the root of a Kedro project and\n+    the environment configuration subfolders (see ``kedro.config.OmegaConfigLoader``)\n+    Raises:\n+        KedroContextError: If there is a mismatch\n+            between Kedro project version and package version.\n+    Args:\n+        project_path: Project path to define the context for.\n+        config_loader: Kedro's ``OmegaConfigLoader`` for loading the configuration files.\n+        env: Optional argument for configuration default environment to be used\n+            for running the pipeline. If not specified, it defaults to \"local\".\n+        package_name: Package name for the Kedro project the context is\n+            created for.\n+        hook_manager: The ``PluginManager`` to activate hooks, supplied by the session.\n+        extra_params: Optional dictionary containing extra project parameters.\n+            If specified, will update (and therefore take precedence over)\n+            the parameters retrieved from the project configuration.\n+\n     \"\"\"\n \n-    _package_name: str\n-    project_path: Path = field(converter=_expand_full_path)\n-    config_loader: AbstractConfigLoader\n-    _hook_manager: PluginManager\n-    env: str | None = None\n-    _extra_params: dict[str, Any] | None = field(default=None, converter=deepcopy)\n+    project_path: Path = field(init=True, converter=_expand_full_path)\n+    config_loader: AbstractConfigLoader = field(init=True)\n+    env: str | None = field(init=True)\n+    _package_name: str = field(init=True)\n+    _hook_manager: PluginManager = field(init=True)\n+    _extra_params: dict[str, Any] | None = field(\n+        init=True, default=None, converter=deepcopy\n+    )\n \n     @property\n     def catalog(self) -> DataCatalog:\n", "issue": "Revisit: Make KedroContext a dataclass and add `config_loader` as a property\n# Description\r\nAs `KedroSession` now control the lifecycle of Kedro's run, `KedroContext` act like a container and it stores important attributes. Since we now dropped Python 3.6 support, we can make use of Python's `dataclass` to further tidy up `KedroContext`'s constructor code.\r\n\r\ntodo:\r\n- [x] Make `KedroContext` a dataclass(https://github.com/kedro-org/kedro/pull/1465)\r\n- [x] Add `config_loader` as @property instead of relying the private `context._get_config_loader()` https://github.com/kedro-org/kedro/pull/1505\r\n- [x] Update corresponding document in Starter/Kedro\r\n\n", "before_files": [{"content": "\"\"\"This module provides context for Kedro project.\"\"\"\nfrom __future__ import annotations\n\nimport logging\nfrom copy import deepcopy\nfrom pathlib import Path, PurePosixPath, PureWindowsPath\nfrom typing import Any\nfrom urllib.parse import urlparse\nfrom warnings import warn\n\nfrom attrs import field, frozen\nfrom omegaconf import OmegaConf\nfrom pluggy import PluginManager\n\nfrom kedro.config import AbstractConfigLoader, MissingConfigException\nfrom kedro.framework.project import settings\nfrom kedro.io import DataCatalog\nfrom kedro.pipeline.pipeline import _transcode_split\n\n\ndef _is_relative_path(path_string: str) -> bool:\n    \"\"\"Checks whether a path string is a relative path.\n\n    Example:\n    ::\n        >>> _is_relative_path(\"data/01_raw\") == True\n        >>> _is_relative_path(\"info.log\") == True\n        >>> _is_relative_path(\"/tmp/data/01_raw\") == False\n        >>> _is_relative_path(r\"C:\\\\info.log\") == False\n        >>> _is_relative_path(r\"\\\\'info.log\") == False\n        >>> _is_relative_path(\"c:/info.log\") == False\n        >>> _is_relative_path(\"s3://info.log\") == False\n\n    Args:\n        path_string: The path string to check.\n    Returns:\n        Whether the string is a relative path.\n    \"\"\"\n    # os.path.splitdrive does not reliably work on non-Windows systems\n    # breaking the coverage, using PureWindowsPath instead\n    is_full_windows_path_with_drive = bool(PureWindowsPath(path_string).drive)\n    if is_full_windows_path_with_drive:\n        return False\n\n    is_remote_path = bool(urlparse(path_string).scheme)\n    if is_remote_path:\n        return False\n\n    is_absolute_path = PurePosixPath(path_string).is_absolute()\n    if is_absolute_path:\n        return False\n\n    return True\n\n\ndef _convert_paths_to_absolute_posix(\n    project_path: Path, conf_dictionary: dict[str, Any]\n) -> dict[str, Any]:\n    \"\"\"Turn all relative paths inside ``conf_dictionary`` into absolute paths by appending them\n    to ``project_path`` and convert absolute Windows paths to POSIX format. This is a hack to\n    make sure that we don't have to change user's working directory for logging and datasets to\n    work. It is important for non-standard workflows such as IPython notebook where users don't go\n    through `kedro run` or `__main__.py` entrypoints.\n\n    Example:\n    ::\n        >>> conf = _convert_paths_to_absolute_posix(\n        >>>     project_path=Path(\"/path/to/my/project\"),\n        >>>     conf_dictionary={\n        >>>         \"handlers\": {\n        >>>             \"info_file_handler\": {\n        >>>                 \"filename\": \"info.log\"\n        >>>             }\n        >>>         }\n        >>>     }\n        >>> )\n        >>> print(conf['handlers']['info_file_handler']['filename'])\n        \"/path/to/my/project/info.log\"\n\n    Args:\n        project_path: The root directory to prepend to relative path to make absolute path.\n        conf_dictionary: The configuration containing paths to expand.\n    Returns:\n        A dictionary containing only absolute paths.\n    Raises:\n        ValueError: If the provided ``project_path`` is not an absolute path.\n    \"\"\"\n    if not project_path.is_absolute():\n        raise ValueError(\n            f\"project_path must be an absolute path. Received: {project_path}\"\n        )\n\n    # only check a few conf keys that are known to specify a path string as value\n    conf_keys_with_filepath = (\"filename\", \"filepath\", \"path\")\n\n    for conf_key, conf_value in conf_dictionary.items():\n        # if the conf_value is another dictionary, absolutify its paths first.\n        if isinstance(conf_value, dict):\n            conf_dictionary[conf_key] = _convert_paths_to_absolute_posix(\n                project_path, conf_value\n            )\n            continue\n\n        # if the conf_value is not a dictionary nor a string, skip\n        if not isinstance(conf_value, str):\n            continue\n\n        # if the conf_value is a string but the conf_key isn't one associated with filepath, skip\n        if conf_key not in conf_keys_with_filepath:\n            continue\n\n        if _is_relative_path(conf_value):\n            # Absolute local path should be in POSIX format\n            conf_value_absolute_path = (project_path / conf_value).as_posix()\n            conf_dictionary[conf_key] = conf_value_absolute_path\n        elif PureWindowsPath(conf_value).drive:\n            # Convert absolute Windows path to POSIX format\n            conf_dictionary[conf_key] = PureWindowsPath(conf_value).as_posix()\n\n    return conf_dictionary\n\n\ndef _validate_transcoded_datasets(catalog: DataCatalog):\n    \"\"\"Validates transcoded datasets are correctly named\n\n    Args:\n        catalog (DataCatalog): The catalog object containing the\n        datasets to be validated.\n\n    Raises:\n        ValueError: If a dataset name does not conform to the expected\n        transcoding naming conventions,a ValueError is raised by the\n        `_transcode_split` function.\n\n    \"\"\"\n    # noqa: protected-access\n    for dataset_name in catalog._datasets.keys():\n        _transcode_split(dataset_name)\n\n\ndef _expand_full_path(project_path: str | Path) -> Path:\n    return Path(project_path).expanduser().resolve()\n\n\n@frozen\nclass KedroContext:\n    \"\"\"``KedroContext`` is the base class which holds the configuration and\n    Kedro's main functionality.\n    \"\"\"\n\n    _package_name: str\n    project_path: Path = field(converter=_expand_full_path)\n    config_loader: AbstractConfigLoader\n    _hook_manager: PluginManager\n    env: str | None = None\n    _extra_params: dict[str, Any] | None = field(default=None, converter=deepcopy)\n\n    @property\n    def catalog(self) -> DataCatalog:\n        \"\"\"Read-only property referring to Kedro's ``DataCatalog`` for this context.\n\n        Returns:\n            DataCatalog defined in `catalog.yml`.\n        Raises:\n            KedroContextError: Incorrect ``DataCatalog`` registered for the project.\n\n        \"\"\"\n        return self._get_catalog()\n\n    @property\n    def params(self) -> dict[str, Any]:\n        \"\"\"Read-only property referring to Kedro's parameters for this context.\n\n        Returns:\n            Parameters defined in `parameters.yml` with the addition of any\n                extra parameters passed at initialization.\n        \"\"\"\n        try:\n            params = self.config_loader[\"parameters\"]\n        except MissingConfigException as exc:\n            warn(f\"Parameters not found in your Kedro project config.\\n{str(exc)}\")\n            params = {}\n\n        if self._extra_params:\n            # Merge nested structures\n            params = OmegaConf.merge(params, self._extra_params)\n\n        return OmegaConf.to_container(params) if OmegaConf.is_config(params) else params\n\n    def _get_catalog(\n        self,\n        save_version: str = None,\n        load_versions: dict[str, str] = None,\n    ) -> DataCatalog:\n        \"\"\"A hook for changing the creation of a DataCatalog instance.\n\n        Returns:\n            DataCatalog defined in `catalog.yml`.\n        Raises:\n            KedroContextError: Incorrect ``DataCatalog`` registered for the project.\n\n        \"\"\"\n        # '**/catalog*' reads modular pipeline configs\n        conf_catalog = self.config_loader[\"catalog\"]\n        # turn relative paths in conf_catalog into absolute paths\n        # before initializing the catalog\n        conf_catalog = _convert_paths_to_absolute_posix(\n            project_path=self.project_path, conf_dictionary=conf_catalog\n        )\n        conf_creds = self._get_config_credentials()\n\n        catalog = settings.DATA_CATALOG_CLASS.from_config(\n            catalog=conf_catalog,\n            credentials=conf_creds,\n            load_versions=load_versions,\n            save_version=save_version,\n        )\n\n        feed_dict = self._get_feed_dict()\n        catalog.add_feed_dict(feed_dict)\n        _validate_transcoded_datasets(catalog)\n        self._hook_manager.hook.after_catalog_created(\n            catalog=catalog,\n            conf_catalog=conf_catalog,\n            conf_creds=conf_creds,\n            feed_dict=feed_dict,\n            save_version=save_version,\n            load_versions=load_versions,\n        )\n        return catalog\n\n    def _get_feed_dict(self) -> dict[str, Any]:\n        \"\"\"Get parameters and return the feed dictionary.\"\"\"\n        params = self.params\n        feed_dict = {\"parameters\": params}\n\n        def _add_param_to_feed_dict(param_name, param_value):\n            \"\"\"This recursively adds parameter paths to the `feed_dict`,\n            whenever `param_value` is a dictionary itself, so that users can\n            specify specific nested parameters in their node inputs.\n\n            Example:\n\n                >>> param_name = \"a\"\n                >>> param_value = {\"b\": 1}\n                >>> _add_param_to_feed_dict(param_name, param_value)\n                >>> assert feed_dict[\"params:a\"] == {\"b\": 1}\n                >>> assert feed_dict[\"params:a.b\"] == 1\n            \"\"\"\n            key = f\"params:{param_name}\"\n            feed_dict[key] = param_value\n            if isinstance(param_value, dict):\n                for key, val in param_value.items():\n                    _add_param_to_feed_dict(f\"{param_name}.{key}\", val)\n\n        for param_name, param_value in params.items():\n            _add_param_to_feed_dict(param_name, param_value)\n\n        return feed_dict\n\n    def _get_config_credentials(self) -> dict[str, Any]:\n        \"\"\"Getter for credentials specified in credentials directory.\"\"\"\n        try:\n            conf_creds = self.config_loader[\"credentials\"]\n        except MissingConfigException as exc:\n            logging.getLogger(__name__).debug(\n                \"Credentials not found in your Kedro project config.\\n %s\", str(exc)\n            )\n            conf_creds = {}\n        return conf_creds\n\n\nclass KedroContextError(Exception):\n    \"\"\"Error occurred when loading project and running context pipeline.\"\"\"\n", "path": "kedro/framework/context/context.py"}], "after_files": [{"content": "\"\"\"This module provides context for Kedro project.\"\"\"\nfrom __future__ import annotations\n\nimport logging\nfrom copy import deepcopy\nfrom pathlib import Path, PurePosixPath, PureWindowsPath\nfrom typing import Any\nfrom urllib.parse import urlparse\nfrom warnings import warn\n\nfrom attrs import define, field\nfrom omegaconf import OmegaConf\nfrom pluggy import PluginManager\n\nfrom kedro.config import AbstractConfigLoader, MissingConfigException\nfrom kedro.framework.project import settings\nfrom kedro.io import DataCatalog\nfrom kedro.pipeline.pipeline import _transcode_split\n\n\ndef _is_relative_path(path_string: str) -> bool:\n    \"\"\"Checks whether a path string is a relative path.\n\n    Example:\n    ::\n        >>> _is_relative_path(\"data/01_raw\") == True\n        >>> _is_relative_path(\"info.log\") == True\n        >>> _is_relative_path(\"/tmp/data/01_raw\") == False\n        >>> _is_relative_path(r\"C:\\\\info.log\") == False\n        >>> _is_relative_path(r\"\\\\'info.log\") == False\n        >>> _is_relative_path(\"c:/info.log\") == False\n        >>> _is_relative_path(\"s3://info.log\") == False\n\n    Args:\n        path_string: The path string to check.\n    Returns:\n        Whether the string is a relative path.\n    \"\"\"\n    # os.path.splitdrive does not reliably work on non-Windows systems\n    # breaking the coverage, using PureWindowsPath instead\n    is_full_windows_path_with_drive = bool(PureWindowsPath(path_string).drive)\n    if is_full_windows_path_with_drive:\n        return False\n\n    is_remote_path = bool(urlparse(path_string).scheme)\n    if is_remote_path:\n        return False\n\n    is_absolute_path = PurePosixPath(path_string).is_absolute()\n    if is_absolute_path:\n        return False\n\n    return True\n\n\ndef _convert_paths_to_absolute_posix(\n    project_path: Path, conf_dictionary: dict[str, Any]\n) -> dict[str, Any]:\n    \"\"\"Turn all relative paths inside ``conf_dictionary`` into absolute paths by appending them\n    to ``project_path`` and convert absolute Windows paths to POSIX format. This is a hack to\n    make sure that we don't have to change user's working directory for logging and datasets to\n    work. It is important for non-standard workflows such as IPython notebook where users don't go\n    through `kedro run` or `__main__.py` entrypoints.\n\n    Example:\n    ::\n        >>> conf = _convert_paths_to_absolute_posix(\n        >>>     project_path=Path(\"/path/to/my/project\"),\n        >>>     conf_dictionary={\n        >>>         \"handlers\": {\n        >>>             \"info_file_handler\": {\n        >>>                 \"filename\": \"info.log\"\n        >>>             }\n        >>>         }\n        >>>     }\n        >>> )\n        >>> print(conf['handlers']['info_file_handler']['filename'])\n        \"/path/to/my/project/info.log\"\n\n    Args:\n        project_path: The root directory to prepend to relative path to make absolute path.\n        conf_dictionary: The configuration containing paths to expand.\n    Returns:\n        A dictionary containing only absolute paths.\n    Raises:\n        ValueError: If the provided ``project_path`` is not an absolute path.\n    \"\"\"\n    if not project_path.is_absolute():\n        raise ValueError(\n            f\"project_path must be an absolute path. Received: {project_path}\"\n        )\n\n    # only check a few conf keys that are known to specify a path string as value\n    conf_keys_with_filepath = (\"filename\", \"filepath\", \"path\")\n\n    for conf_key, conf_value in conf_dictionary.items():\n        # if the conf_value is another dictionary, absolutify its paths first.\n        if isinstance(conf_value, dict):\n            conf_dictionary[conf_key] = _convert_paths_to_absolute_posix(\n                project_path, conf_value\n            )\n            continue\n\n        # if the conf_value is not a dictionary nor a string, skip\n        if not isinstance(conf_value, str):\n            continue\n\n        # if the conf_value is a string but the conf_key isn't one associated with filepath, skip\n        if conf_key not in conf_keys_with_filepath:\n            continue\n\n        if _is_relative_path(conf_value):\n            # Absolute local path should be in POSIX format\n            conf_value_absolute_path = (project_path / conf_value).as_posix()\n            conf_dictionary[conf_key] = conf_value_absolute_path\n        elif PureWindowsPath(conf_value).drive:\n            # Convert absolute Windows path to POSIX format\n            conf_dictionary[conf_key] = PureWindowsPath(conf_value).as_posix()\n\n    return conf_dictionary\n\n\ndef _validate_transcoded_datasets(catalog: DataCatalog):\n    \"\"\"Validates transcoded datasets are correctly named\n\n    Args:\n        catalog (DataCatalog): The catalog object containing the\n        datasets to be validated.\n\n    Raises:\n        ValueError: If a dataset name does not conform to the expected\n        transcoding naming conventions,a ValueError is raised by the\n        `_transcode_split` function.\n\n    \"\"\"\n    # noqa: protected-access\n    for dataset_name in catalog._datasets.keys():\n        _transcode_split(dataset_name)\n\n\ndef _expand_full_path(project_path: str | Path) -> Path:\n    return Path(project_path).expanduser().resolve()\n\n\n@define(slots=False)  # Enable setting new attributes to `KedroContext`\nclass KedroContext:\n    \"\"\"``KedroContext`` is the base class which holds the configuration and\n    Kedro's main functionality.\n\n    Create a context object by providing the root of a Kedro project and\n    the environment configuration subfolders (see ``kedro.config.OmegaConfigLoader``)\n    Raises:\n        KedroContextError: If there is a mismatch\n            between Kedro project version and package version.\n    Args:\n        project_path: Project path to define the context for.\n        config_loader: Kedro's ``OmegaConfigLoader`` for loading the configuration files.\n        env: Optional argument for configuration default environment to be used\n            for running the pipeline. If not specified, it defaults to \"local\".\n        package_name: Package name for the Kedro project the context is\n            created for.\n        hook_manager: The ``PluginManager`` to activate hooks, supplied by the session.\n        extra_params: Optional dictionary containing extra project parameters.\n            If specified, will update (and therefore take precedence over)\n            the parameters retrieved from the project configuration.\n\n    \"\"\"\n\n    project_path: Path = field(init=True, converter=_expand_full_path)\n    config_loader: AbstractConfigLoader = field(init=True)\n    env: str | None = field(init=True)\n    _package_name: str = field(init=True)\n    _hook_manager: PluginManager = field(init=True)\n    _extra_params: dict[str, Any] | None = field(\n        init=True, default=None, converter=deepcopy\n    )\n\n    @property\n    def catalog(self) -> DataCatalog:\n        \"\"\"Read-only property referring to Kedro's ``DataCatalog`` for this context.\n\n        Returns:\n            DataCatalog defined in `catalog.yml`.\n        Raises:\n            KedroContextError: Incorrect ``DataCatalog`` registered for the project.\n\n        \"\"\"\n        return self._get_catalog()\n\n    @property\n    def params(self) -> dict[str, Any]:\n        \"\"\"Read-only property referring to Kedro's parameters for this context.\n\n        Returns:\n            Parameters defined in `parameters.yml` with the addition of any\n                extra parameters passed at initialization.\n        \"\"\"\n        try:\n            params = self.config_loader[\"parameters\"]\n        except MissingConfigException as exc:\n            warn(f\"Parameters not found in your Kedro project config.\\n{str(exc)}\")\n            params = {}\n\n        if self._extra_params:\n            # Merge nested structures\n            params = OmegaConf.merge(params, self._extra_params)\n\n        return OmegaConf.to_container(params) if OmegaConf.is_config(params) else params\n\n    def _get_catalog(\n        self,\n        save_version: str = None,\n        load_versions: dict[str, str] = None,\n    ) -> DataCatalog:\n        \"\"\"A hook for changing the creation of a DataCatalog instance.\n\n        Returns:\n            DataCatalog defined in `catalog.yml`.\n        Raises:\n            KedroContextError: Incorrect ``DataCatalog`` registered for the project.\n\n        \"\"\"\n        # '**/catalog*' reads modular pipeline configs\n        conf_catalog = self.config_loader[\"catalog\"]\n        # turn relative paths in conf_catalog into absolute paths\n        # before initializing the catalog\n        conf_catalog = _convert_paths_to_absolute_posix(\n            project_path=self.project_path, conf_dictionary=conf_catalog\n        )\n        conf_creds = self._get_config_credentials()\n\n        catalog = settings.DATA_CATALOG_CLASS.from_config(\n            catalog=conf_catalog,\n            credentials=conf_creds,\n            load_versions=load_versions,\n            save_version=save_version,\n        )\n\n        feed_dict = self._get_feed_dict()\n        catalog.add_feed_dict(feed_dict)\n        _validate_transcoded_datasets(catalog)\n        self._hook_manager.hook.after_catalog_created(\n            catalog=catalog,\n            conf_catalog=conf_catalog,\n            conf_creds=conf_creds,\n            feed_dict=feed_dict,\n            save_version=save_version,\n            load_versions=load_versions,\n        )\n        return catalog\n\n    def _get_feed_dict(self) -> dict[str, Any]:\n        \"\"\"Get parameters and return the feed dictionary.\"\"\"\n        params = self.params\n        feed_dict = {\"parameters\": params}\n\n        def _add_param_to_feed_dict(param_name, param_value):\n            \"\"\"This recursively adds parameter paths to the `feed_dict`,\n            whenever `param_value` is a dictionary itself, so that users can\n            specify specific nested parameters in their node inputs.\n\n            Example:\n\n                >>> param_name = \"a\"\n                >>> param_value = {\"b\": 1}\n                >>> _add_param_to_feed_dict(param_name, param_value)\n                >>> assert feed_dict[\"params:a\"] == {\"b\": 1}\n                >>> assert feed_dict[\"params:a.b\"] == 1\n            \"\"\"\n            key = f\"params:{param_name}\"\n            feed_dict[key] = param_value\n            if isinstance(param_value, dict):\n                for key, val in param_value.items():\n                    _add_param_to_feed_dict(f\"{param_name}.{key}\", val)\n\n        for param_name, param_value in params.items():\n            _add_param_to_feed_dict(param_name, param_value)\n\n        return feed_dict\n\n    def _get_config_credentials(self) -> dict[str, Any]:\n        \"\"\"Getter for credentials specified in credentials directory.\"\"\"\n        try:\n            conf_creds = self.config_loader[\"credentials\"]\n        except MissingConfigException as exc:\n            logging.getLogger(__name__).debug(\n                \"Credentials not found in your Kedro project config.\\n %s\", str(exc)\n            )\n            conf_creds = {}\n        return conf_creds\n\n\nclass KedroContextError(Exception):\n    \"\"\"Error occurred when loading project and running context pipeline.\"\"\"\n", "path": "kedro/framework/context/context.py"}]}