Repository: kedro-org/kedro

Issue:
Remove logic to activate and deactivate `KedroSession`
KedroSession is still keeping a global for the currently active session, which causes problems when trying to create a session in a notebook with the Kedro IPython extension and makes it unable to create/run the session. Here's the functions we need to drop:
https://github.com/kedro-org/kedro/blob/main/kedro/framework/session/session.py#L34-L47

And here is where they are used: https://github.com/kedro-org/kedro/blob/main/kedro/framework/session/session.py#L274-L287

Need to add a regression test:
- Create a test that shows the bug: create a session, then try creating another session and see the failure
- Then fix it in the code
- Then this test should pass


Discussion:
I think we should take care here to understand exactly why this code exists in the first place and what would be the consequences of making this change, i.e. what will behave differently after removing this code. Presumably it was originally put there for a good reason to handle some particular cases. Maybe such cases are no longer possible now we have one run session, or maybe they're so marginal that we don't care about them. But it would be good to understand the context of why this logic is here currently.
@AntonyMilneQB 

I checked the repository the `_activate_session()` and the global `_active_session` are only used by `ipython.py` and the `KedroSession`'s context manager. It was also used by `get_current_session()` but it no longer exists now.

The activate/deactivate session mechanism doesn't look necessary to me, in fact, if you just do `session.run()` today it runs perfectly fine. This whole activate/deactivate thing only stops you if you are using context manager.

This will run
```
# Assume activate session exists but not closed
session.run() # this will run

with KedroSession.create('dummy') as new_session:
    new_session.run() # this will fail due to existence of unclosed session.
```

However, there is a `test_nested_sessions` test expecting this to fail.
https://github.com/kedro-org/kedro/blob/307783b7f48a6d986d0f9981077f71ebd1775429/tests/framework/session/test_session.py#L521-L532



It was created since Ipython extension exists. [KED-1996] Create an IPython extension for Kedro (#853) · kedro-org/kedro@80e2202 · GitHub](https://github.com/kedro-org/kedro/commit/80e220242b434af7c42c1c8b19e54675b3091013)

Resolving patch:
diff --git a/kedro/extras/extensions/ipython.py b/kedro/extras/extensions/ipython.py
--- a/kedro/extras/extensions/ipython.py
+++ b/kedro/extras/extensions/ipython.py
@@ -45,7 +45,6 @@ def reload_kedro(
     from kedro.framework.cli import load_entry_points
     from kedro.framework.project import configure_project, pipelines
     from kedro.framework.session import KedroSession
-    from kedro.framework.session.session import _activate_session
     from kedro.framework.startup import bootstrap_project
 
     # If a path is provided, set it as default for subsequent calls
@@ -64,7 +63,6 @@ def reload_kedro(
     session = KedroSession.create(
         metadata.package_name, default_project_path, env=env, extra_params=extra_params
     )
-    _activate_session(session, force=True)
     logger.debug("Loading the context from %s", default_project_path)
     context = session.load_context()
     catalog = context.catalog
diff --git a/kedro/framework/session/session.py b/kedro/framework/session/session.py
--- a/kedro/framework/session/session.py
+++ b/kedro/framework/session/session.py
@@ -28,24 +28,6 @@
 from kedro.io.core import generate_timestamp
 from kedro.runner import AbstractRunner, SequentialRunner
 
-_active_session = None
-
-
-def _activate_session(session: "KedroSession", force: bool = False) -> None:
-    global _active_session
-
-    if _active_session and not force and session is not _active_session:
-        raise RuntimeError(
-            "Cannot activate the session as another active session already exists."
-        )
-
-    _active_session = session
-
-
-def _deactivate_session() -> None:
-    global _active_session
-    _active_session = None
-
 
 def _describe_git(project_path: Path) -> Dict[str, Dict[str, Any]]:
     project_path = str(project_path)
@@ -282,12 +264,7 @@ def close(self):
         if self.save_on_close:
             self._store.save()
 
-        if _active_session is self:
-            _deactivate_session()
-
     def __enter__(self):
-        if _active_session is not self:
-            _activate_session(self)
         return self
 
     def __exit__(self, exc_type, exc_value, tb_):


Test patch:
diff --git a/tests/extras/extensions/test_ipython.py b/tests/extras/extensions/test_ipython.py
--- a/tests/extras/extensions/test_ipython.py
+++ b/tests/extras/extensions/test_ipython.py
@@ -2,7 +2,6 @@
 import pytest
 
 from kedro.extras.extensions.ipython import load_ipython_extension, reload_kedro
-from kedro.framework.session.session import _deactivate_session
 from kedro.framework.startup import ProjectMetadata
 from kedro.pipeline import Pipeline
 
@@ -23,7 +22,6 @@ def mocked_logging(mocker):
 @pytest.fixture(autouse=True)
 def cleanup_session():
     yield
-    _deactivate_session()
 
 
 @pytest.fixture()
diff --git a/tests/framework/session/test_session.py b/tests/framework/session/test_session.py
--- a/tests/framework/session/test_session.py
+++ b/tests/framework/session/test_session.py
@@ -17,7 +17,6 @@
     _HasSharedParentClassValidator,
     _IsSubclassValidator,
     _ProjectSettings,
-    configure_project,
 )
 from kedro.framework.session import KedroSession
 from kedro.framework.session.store import BaseSessionStore, ShelveStore
@@ -285,6 +284,12 @@ def test_create(
         assert session.load_context() is mock_context_class.return_value
         assert isinstance(session._get_config_loader(), ConfigLoader)
 
+    @pytest.mark.usefixtures("mock_settings")
+    def test_create_multiple_sessions(self, fake_project, mock_package_name):
+        with KedroSession.create(mock_package_name, fake_project):
+            with KedroSession.create(mock_package_name, fake_project):
+                pass
+
     @pytest.mark.usefixtures("mock_settings_context_class")
     def test_create_no_env_extra_params(
         self,
@@ -519,19 +524,6 @@ def test_log_error(self, fake_project, mock_package_name):
             "raise FakeException" in tb_line for tb_line in exception["traceback"]
         )
 
-    @pytest.mark.usefixtures("mock_settings")
-    def test_nested_sessions(self, fake_project, mock_package_name):
-        configure_project(mock_package_name)
-        session1 = KedroSession.create(mock_package_name, fake_project)
-        session2 = KedroSession.create(mock_package_name, fake_project)
-
-        with session1:
-            pattern = (
-                "Cannot activate the session as another active session already exists"
-            )
-            with pytest.raises(RuntimeError, match=pattern), session2:
-                pass  # pragma: no cover
-
     @pytest.mark.usefixtures("mock_settings_context_class")
     @pytest.mark.parametrize("fake_pipeline_name", [None, _FAKE_PIPELINE_NAME])
     def test_run(