Repository: facebookresearch/hydra

Issue:
Resetting the state of GlobalHydra
Hi,

I am trying to use the pytorch bottleneck profiler (`python -m torch.utils.bottleneck train.py`) which apparently has to run the hydra-decorated main() function twice and results in the error
`AssertionError: GlobalHydra is already initialized`

Is there a way to reset the state of the GlobalHydra singleton manually or another workaround to make the profiler work with hydra?

Thanks


Discussion:
You can try calling the [clear function](https://github.com/facebookresearch/hydra/blob/0.11_branch/hydra/_internal/hydra.py#L41-L42) at the end of your hydra.main function:


Ah cool. Had some trouble finding how to actually call this; in the end, `hydra._internal.hydra.GlobalHydra.get_state().clear()` worked.
Yup.
Note that GlobalHydra is being promoted out of _internal in the next version and will be [here](https://github.com/facebookresearch/hydra/blob/master/hydra/core/global_hydra.py).
Ah, you are using it wrong (I suspect you are clearing all signletons which may have incidentally been what you want but not what you asked for).

try, in 0.11, try:
`hydra._internal.hydra.GlobalHydra().clear()`

In the next version it will  be (I think):
`hydra.core.global_hydra.GlobalHydra.instance().clear()`
Okay, I also tried those two (I'm on version 0.11.3):  
- `hydra._internal.hydra.GlobalHydra().clear() ` gave me `TypeError: clear() missing 1 required positional argument: 'self'` (which makes sense I guess) and  
- `hydra._internal.hydra.GlobalHydra.instance().clear()` raises `AttributeError: type object 'GlobalHydra' has no attribute 'instance'`
Ah sorry, `hydra._internal.hydra.GlobalHydra.clear()` doesn't work, `hydra._internal.hydra.GlobalHydra().clear()` does.

Thanks again!

Resolving patch:
diff --git a/hydra/_internal/utils.py b/hydra/_internal/utils.py
--- a/hydra/_internal/utils.py
+++ b/hydra/_internal/utils.py
@@ -152,6 +152,8 @@ def run_hydra(
     config_path: Optional[str],
     strict: Optional[bool],
 ) -> None:
+    from hydra.core.global_hydra import GlobalHydra
+
     from .hydra import Hydra
 
     calling_file, calling_module = detect_calling_file_or_module(3)
@@ -165,44 +167,48 @@ def run_hydra(
     hydra = Hydra.create_main_hydra2(
         task_name=task_name, config_search_path=search_path, strict=strict
     )
-
-    args = args_parser.parse_args()
-    if args.help:
-        hydra.app_help(config_file=config_file, args_parser=args_parser, args=args)
-        sys.exit(0)
-    if args.hydra_help:
-        hydra.hydra_help(config_file=config_file, args_parser=args_parser, args=args)
-        sys.exit(0)
-
-    has_show_cfg = args.cfg is not None
-    num_commands = args.run + has_show_cfg + args.multirun + args.shell_completion
-    if num_commands > 1:
-        raise ValueError(
-            "Only one of --run, --multirun,  -cfg and --shell_completion can be specified"
-        )
-    if num_commands == 0:
-        args.run = True
-    if args.run:
-        hydra.run(
-            config_file=config_file,
-            task_function=task_function,
-            overrides=args.overrides,
-        )
-    elif args.multirun:
-        hydra.multirun(
-            config_file=config_file,
-            task_function=task_function,
-            overrides=args.overrides,
-        )
-    elif args.cfg:
-        hydra.show_cfg(
-            config_file=config_file, overrides=args.overrides, cfg_type=args.cfg
-        )
-    elif args.shell_completion:
-        hydra.shell_completion(config_file=config_file, overrides=args.overrides)
-    else:
-        print("Command not specified")
-        sys.exit(1)
+    try:
+        args = args_parser.parse_args()
+        if args.help:
+            hydra.app_help(config_file=config_file, args_parser=args_parser, args=args)
+            sys.exit(0)
+        if args.hydra_help:
+            hydra.hydra_help(
+                config_file=config_file, args_parser=args_parser, args=args
+            )
+            sys.exit(0)
+
+        has_show_cfg = args.cfg is not None
+        num_commands = args.run + has_show_cfg + args.multirun + args.shell_completion
+        if num_commands > 1:
+            raise ValueError(
+                "Only one of --run, --multirun,  -cfg and --shell_completion can be specified"
+            )
+        if num_commands == 0:
+            args.run = True
+        if args.run:
+            hydra.run(
+                config_file=config_file,
+                task_function=task_function,
+                overrides=args.overrides,
+            )
+        elif args.multirun:
+            hydra.multirun(
+                config_file=config_file,
+                task_function=task_function,
+                overrides=args.overrides,
+            )
+        elif args.cfg:
+            hydra.show_cfg(
+                config_file=config_file, overrides=args.overrides, cfg_type=args.cfg
+            )
+        elif args.shell_completion:
+            hydra.shell_completion(config_file=config_file, overrides=args.overrides)
+        else:
+            print("Command not specified")
+            sys.exit(1)
+    finally:
+        GlobalHydra.instance().clear()
 
 
 def _get_exec_command() -> str:
diff --git a/noxfile.py b/noxfile.py
--- a/noxfile.py
+++ b/noxfile.py
@@ -2,6 +2,7 @@
 # type: ignore
 import copy
 import os
+import platform
 
 import nox
 
@@ -218,6 +219,12 @@ def coverage(session):
 
 @nox.session(python=PYTHON_VERSIONS)
 def test_jupyter_notebook(session):
+    # TODO: disable this when https://github.com/zeromq/pyzmq/issues/1366 is resolved
+    if platform.system() == "Windows":
+        session.skip(
+            f"Not testing Jupyter notebook on Windows due to a temporary dependency issue"
+        )
+
     versions = copy.copy(DEFAULT_PYTHON_VERSIONS)
     if session.python not in versions:
         session.skip(


Test patch:
diff --git a/tests/test_apps/hydra_main_rerun/__init__.py b/tests/test_apps/hydra_main_rerun/__init__.py
new file mode 100644
--- /dev/null
+++ b/tests/test_apps/hydra_main_rerun/__init__.py
@@ -0,0 +1 @@
+# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
diff --git a/tests/test_apps/hydra_main_rerun/my_app.py b/tests/test_apps/hydra_main_rerun/my_app.py
new file mode 100644
--- /dev/null
+++ b/tests/test_apps/hydra_main_rerun/my_app.py
@@ -0,0 +1,14 @@
+# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
+from omegaconf import DictConfig
+
+import hydra
+
+
+@hydra.main()
+def my_app(_: DictConfig) -> None:
+    pass
+
+
+if __name__ == "__main__":
+    my_app()
+    my_app()
diff --git a/tests/test_hydra.py b/tests/test_hydra.py
--- a/tests/test_hydra.py
+++ b/tests/test_hydra.py
@@ -72,6 +72,23 @@ def test_app_without_config___no_overrides(
         assert task.job_ret.cfg == {}
 
 
+@pytest.mark.parametrize(  # type: ignore
+    "calling_file, calling_module",
+    [
+        ("tests/test_apps/hydra_main_rerun/my_app.py", None),
+        (None, "tests.test_apps.hydra_main_rerun.my_app"),
+    ],
+)
+def test_hydra_main_rerun(
+    task_runner: TTaskRunner, calling_file: str, calling_module: str  # noqa: F811
+) -> None:
+    with task_runner(
+        calling_file=calling_file, calling_module=calling_module, config_path=None
+    ) as task:
+        assert task.job_ret is not None
+        assert task.job_ret.cfg == {}
+
+
 @pytest.mark.parametrize(  # type: ignore
     "calling_file, calling_module",
     [