prompt: 
  We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  [callbacks] call on_*_end events in reverse order
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  
  --- BEGIN FILE ---
  ```
  ### hydra/core/callbacks.py
  # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
  import warnings
  from typing import Any
  
  from omegaconf import DictConfig
  
  from hydra.core.utils import JobReturn
  from hydra.utils import instantiate
  
  
  class Callbacks:
      def __init__(self, config: DictConfig) -> None:
          self.callbacks = []
          for params in config.hydra.callbacks.values():
              self.callbacks.append(instantiate(params))
  
      def _notify(self, function_name: str, **kwargs: Any) -> None:
          for c in self.callbacks:
              try:
                  getattr(c, function_name)(**kwargs)
              except Exception as e:
                  warnings.warn(
                      f"Callback {type(c).__name__}.{function_name} raised {type(e).__name__}: {e}"
                  )
  
      def on_run_start(self, config: DictConfig, **kwargs: Any) -> None:
          self._notify(function_name="on_run_start", config=config, **kwargs)
  
      def on_run_end(self, config: DictConfig, **kwargs: Any) -> None:
          self._notify(function_name="on_run_end", config=config, **kwargs)
  
      def on_multirun_start(self, config: DictConfig, **kwargs: Any) -> None:
          self._notify(function_name="on_multirun_start", config=config, **kwargs)
  
      def on_multirun_end(self, config: DictConfig, **kwargs: Any) -> None:
          self._notify(function_name="on_multirun_end", config=config, **kwargs)
  
      def on_job_start(self, config: DictConfig, **kwargs: Any) -> None:
          self._notify(function_name="on_job_start", config=config, **kwargs)
  
      def on_job_end(
          self, config: DictConfig, job_return: JobReturn, **kwargs: Any
      ) -> None:
          self._notify(
              function_name="on_job_end", config=config, job_return=job_return, **kwargs
          )
  
  ```
  --- END FILE ---
  
  Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.
  
  Every *SEARCH/REPLACE* edit must use this format:
  1. The file path
  2. The start of search block: <<<<<<< SEARCH
  3. A contiguous chunk of lines to search for in the existing source code
  4. The dividing line: =======
  5. The lines to replace into the source code
  6. The end of the replace block: >>>>>>> REPLACE
  
  Here is an example:
  
  ```python
  ### mathweb/flask/app.py
  <<<<<<< SEARCH
  from flask import Flask
  =======
  import math
  from flask import Flask
  >>>>>>> REPLACE
  ```
  
  Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
  Wrap the *SEARCH/REPLACE* edit in blocks ```python...```.
  
instance: {"FAIL_TO_PASS": ["tests/test_callbacks.py::test_app_with_callbacks[args2-[HYDRA]"], "PASS_TO_PASS": ["tests/test_callbacks.py::test_app_with_callbacks[args1-[HYDRA]", "tests/test_callbacks.py::test_app_with_callbacks[args0-[HYDRA]"], "base_commit": "c6aeb2ea65892fd4af9f3a7d7795bab08ad7398d", "created_at": "2021-04-19T18:53:19Z", "hints_text": "", "instance_id": "facebookresearch__hydra-1560", "patch": "diff --git a/hydra/core/callbacks.py b/hydra/core/callbacks.py\n--- a/hydra/core/callbacks.py\n+++ b/hydra/core/callbacks.py\n@@ -14,8 +14,9 @@ def __init__(self, config: DictConfig) -> None:\n         for params in config.hydra.callbacks.values():\n             self.callbacks.append(instantiate(params))\n \n-    def _notify(self, function_name: str, **kwargs: Any) -> None:\n-        for c in self.callbacks:\n+    def _notify(self, function_name: str, reverse: bool = False, **kwargs: Any) -> None:\n+        callbacks = reversed(self.callbacks) if reverse else self.callbacks\n+        for c in callbacks:\n             try:\n                 getattr(c, function_name)(**kwargs)\n             except Exception as e:\n@@ -27,13 +28,15 @@ def on_run_start(self, config: DictConfig, **kwargs: Any) -> None:\n         self._notify(function_name=\"on_run_start\", config=config, **kwargs)\n \n     def on_run_end(self, config: DictConfig, **kwargs: Any) -> None:\n-        self._notify(function_name=\"on_run_end\", config=config, **kwargs)\n+        self._notify(function_name=\"on_run_end\", config=config, reverse=True, **kwargs)\n \n     def on_multirun_start(self, config: DictConfig, **kwargs: Any) -> None:\n         self._notify(function_name=\"on_multirun_start\", config=config, **kwargs)\n \n     def on_multirun_end(self, config: DictConfig, **kwargs: Any) -> None:\n-        self._notify(function_name=\"on_multirun_end\", config=config, **kwargs)\n+        self._notify(\n+            function_name=\"on_multirun_end\", reverse=True, config=config, **kwargs\n+        )\n \n     def on_job_start(self, config: DictConfig, **kwargs: Any) -> None:\n         self._notify(function_name=\"on_job_start\", config=config, **kwargs)\n@@ -42,5 +45,9 @@ def on_job_end(\n         self, config: DictConfig, job_return: JobReturn, **kwargs: Any\n     ) -> None:\n         self._notify(\n-            function_name=\"on_job_end\", config=config, job_return=job_return, **kwargs\n+            function_name=\"on_job_end\",\n+            config=config,\n+            job_return=job_return,\n+            reverse=True,\n+            **kwargs,\n         )\n", "problem_statement": "[callbacks] call on_*_end events in reverse order\n\n", "repo": "facebookresearch/hydra", "test_patch": "diff --git a/tests/test_apps/app_with_callbacks/custom_callback/config.yaml b/tests/test_apps/app_with_callbacks/custom_callback/config.yaml\n--- a/tests/test_apps/app_with_callbacks/custom_callback/config.yaml\n+++ b/tests/test_apps/app_with_callbacks/custom_callback/config.yaml\n@@ -4,5 +4,4 @@ hydra:\n   callbacks:\n     custom_callback:\n       _target_: my_app.CustomCallback\n-      param1: value1\n-      param2: value2\n+      callback_name: custom_callback\ndiff --git a/tests/test_apps/app_with_callbacks/custom_callback/config_with_two_callbacks.yaml b/tests/test_apps/app_with_callbacks/custom_callback/config_with_two_callbacks.yaml\nnew file mode 100644\n--- /dev/null\n+++ b/tests/test_apps/app_with_callbacks/custom_callback/config_with_two_callbacks.yaml\n@@ -0,0 +1,8 @@\n+hydra:\n+  callbacks:\n+    callback_1:\n+      _target_: my_app.CustomCallback\n+      callback_name: callback_1\n+    callback_2:\n+      _target_: my_app.CustomCallback\n+      callback_name: callback_2\ndiff --git a/tests/test_apps/app_with_callbacks/custom_callback/my_app.py b/tests/test_apps/app_with_callbacks/custom_callback/my_app.py\n--- a/tests/test_apps/app_with_callbacks/custom_callback/my_app.py\n+++ b/tests/test_apps/app_with_callbacks/custom_callback/my_app.py\n@@ -12,13 +12,9 @@\n \n \n class CustomCallback(Callback):\n-    def __init__(self, param1, param2):\n-        self.param1 = param1\n-        self.param2 = param2\n-        self.name = self.__class__.__name__\n-        log.info(\n-            f\"Init {self.name} with self.param1={self.param1}, self.param2={self.param2}\"\n-        )\n+    def __init__(self, callback_name):\n+        self.name = callback_name\n+        log.info(f\"Init {self.name}\")\n \n     def on_job_start(self, config: DictConfig, **kwargs) -> None:\n         log.info(f\"{self.name} on_job_start\")\n@@ -41,7 +37,7 @@ def on_multirun_end(self, config: DictConfig, **kwargs) -> None:\n \n @hydra.main(config_path=\".\", config_name=\"config\")\n def my_app(cfg: DictConfig) -> None:\n-    print(OmegaConf.to_yaml(cfg))\n+    log.info(OmegaConf.to_yaml(cfg))\n \n \n if __name__ == \"__main__\":\ndiff --git a/tests/test_callbacks.py b/tests/test_callbacks.py\n--- a/tests/test_callbacks.py\n+++ b/tests/test_callbacks.py\n@@ -15,51 +15,72 @@\n \n \n @mark.parametrize(\n-    \"app_path,args,expected\",\n+    \"args,expected\",\n     [\n         (\n-            \"tests/test_apps/app_with_callbacks/custom_callback/my_app.py\",\n             [],\n             dedent(\n                 \"\"\"\\\n-                [HYDRA] Init CustomCallback with self.param1=value1, self.param2=value2\n-                [HYDRA] CustomCallback on_run_start\n-                [HYDRA] Init CustomCallback with self.param1=value1, self.param2=value2\n-                [JOB] CustomCallback on_job_start\n-                foo: bar\n+                [HYDRA] Init custom_callback\n+                [HYDRA] custom_callback on_run_start\n+                [HYDRA] Init custom_callback\n+                [JOB] custom_callback on_job_start\n+                [JOB] foo: bar\n \n-                [JOB] CustomCallback on_job_end\n-                [JOB] CustomCallback on_run_end\"\"\"\n+                [JOB] custom_callback on_job_end\n+                [JOB] custom_callback on_run_end\"\"\"\n             ),\n         ),\n         (\n-            \"tests/test_apps/app_with_callbacks/custom_callback/my_app.py\",\n             [\n                 \"foo=bar\",\n                 \"-m\",\n             ],\n             dedent(\n                 \"\"\"\\\n-                [HYDRA] Init CustomCallback with self.param1=value1, self.param2=value2\n-                [HYDRA] CustomCallback on_multirun_start\n+                [HYDRA] Init custom_callback\n+                [HYDRA] custom_callback on_multirun_start\n                 [HYDRA] Launching 1 jobs locally\n                 [HYDRA] \\t#0 : foo=bar\n-                [HYDRA] Init CustomCallback with self.param1=value1, self.param2=value2\n-                [JOB] CustomCallback on_job_start\n-                foo: bar\n+                [HYDRA] Init custom_callback\n+                [JOB] custom_callback on_job_start\n+                [JOB] foo: bar\n \n-                [JOB] CustomCallback on_job_end\n-                [HYDRA] CustomCallback on_multirun_end\"\"\"\n+                [JOB] custom_callback on_job_end\n+                [HYDRA] custom_callback on_multirun_end\"\"\"\n+            ),\n+        ),\n+        (\n+            [\n+                \"--config-name\",\n+                \"config_with_two_callbacks\",\n+            ],\n+            dedent(\n+                \"\"\"\\\n+                [HYDRA] Init callback_1\n+                [HYDRA] Init callback_2\n+                [HYDRA] callback_1 on_run_start\n+                [HYDRA] callback_2 on_run_start\n+                [HYDRA] Init callback_1\n+                [HYDRA] Init callback_2\n+                [JOB] callback_1 on_job_start\n+                [JOB] callback_2 on_job_start\n+                [JOB] {}\n+\n+                [JOB] callback_2 on_job_end\n+                [JOB] callback_1 on_job_end\n+                [JOB] callback_2 on_run_end\n+                [JOB] callback_1 on_run_end\"\"\"\n             ),\n         ),\n     ],\n )\n def test_app_with_callbacks(\n     tmpdir: Path,\n-    app_path: str,\n     args: List[str],\n     expected: str,\n ) -> None:\n+    app_path = \"tests/test_apps/app_with_callbacks/custom_callback/my_app.py\"\n \n     cmd = [\n         app_path,\n", "version": "1.1"}
parsed_commit_content: {"file_diffs":[{"old_file_content":"# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\nimport warnings\nfrom typing import Any\n\nfrom omegaconf import DictConfig\n\nfrom hydra.core.utils import JobReturn\nfrom hydra.utils import instantiate\n\n\nclass Callbacks:\n    def __init__(self, config: DictConfig) -> None:\n        self.callbacks = []\n        for params in config.hydra.callbacks.values():\n            self.callbacks.append(instantiate(params))\n\n    def _notify(self, function_name: str, **kwargs: Any) -> None:\n        for c in self.callbacks:\n            try:\n                getattr(c, function_name)(**kwargs)\n            except Exception as e:\n                warnings.warn(\n                    f\"Callback {type(c).__name__}.{function_name} raised {type(e).__name__}: {e}\"\n                )\n\n    def on_run_start(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_run_start\", config=config, **kwargs)\n\n    def on_run_end(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_run_end\", config=config, **kwargs)\n\n    def on_multirun_start(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_multirun_start\", config=config, **kwargs)\n\n    def on_multirun_end(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_multirun_end\", config=config, **kwargs)\n\n    def on_job_start(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_job_start\", config=config, **kwargs)\n\n    def on_job_end(\n        self, config: DictConfig, job_return: JobReturn, **kwargs: Any\n    ) -> None:\n        self._notify(\n            function_name=\"on_job_end\", config=config, job_return=job_return, **kwargs\n        )\n","new_file_content":"# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\nimport warnings\nfrom typing import Any\n\nfrom omegaconf import DictConfig\n\nfrom hydra.core.utils import JobReturn\nfrom hydra.utils import instantiate\n\n\nclass Callbacks:\n    def __init__(self, config: DictConfig) -> None:\n        self.callbacks = []\n        for params in config.hydra.callbacks.values():\n            self.callbacks.append(instantiate(params))\n\n    def _notify(self, function_name: str, reverse: bool = False, **kwargs: Any) -> None:\n        callbacks = reversed(self.callbacks) if reverse else self.callbacks\n        for c in callbacks:\n            try:\n                getattr(c, function_name)(**kwargs)\n            except Exception as e:\n                warnings.warn(\n                    f\"Callback {type(c).__name__}.{function_name} raised {type(e).__name__}: {e}\"\n                )\n\n    def on_run_start(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_run_start\", config=config, **kwargs)\n\n    def on_run_end(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_run_end\", config=config, reverse=True, **kwargs)\n\n    def on_multirun_start(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_multirun_start\", config=config, **kwargs)\n\n    def on_multirun_end(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(\n            function_name=\"on_multirun_end\", reverse=True, config=config, **kwargs\n        )\n\n    def on_job_start(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_job_start\", config=config, **kwargs)\n\n    def on_job_end(\n        self, config: DictConfig, job_return: JobReturn, **kwargs: Any\n    ) -> None:\n        self._notify(\n            function_name=\"on_job_end\",\n            config=config,\n            job_return=job_return,\n            reverse=True,\n            **kwargs,\n        )\n","header":{"file":{"path":"hydra/core/callbacks.py"},"misc_line":null},"index_line":null,"is_binary_file":false,"binary_line":null,"minus_file":{"path":"a/hydra/core/callbacks.py"},"plus_file":{"path":"b/hydra/core/callbacks.py"},"hunks":[{"descriptor":{"old_range":{"start":14,"length":8},"new_range":{"start":14,"length":9},"section":"def __init__(self, config: DictConfig) -> None:"},"line_group":{"all_lines":[{"content":"        for params in config.hydra.callbacks.values():","type":"context"},{"content":"            self.callbacks.append(instantiate(params))","type":"context"},{"content":"","type":"context"},{"content":"    def _notify(self, function_name: str, **kwargs: Any) -> None:","type":"deleted"},{"content":"        for c in self.callbacks:","type":"deleted"},{"content":"    def _notify(self, function_name: str, reverse: bool = False, **kwargs: Any) -> None:","type":"added"},{"content":"        callbacks = reversed(self.callbacks) if reverse else self.callbacks","type":"added"},{"content":"        for c in callbacks:","type":"added"},{"content":"            try:","type":"context"},{"content":"                getattr(c, function_name)(**kwargs)","type":"context"},{"content":"            except Exception as e:","type":"context"}]},"modified_entities":[],"added_entities":[],"deleted_entities":[]},{"descriptor":{"old_range":{"start":27,"length":13},"new_range":{"start":28,"length":15},"section":"def on_run_start(self, config: DictConfig, **kwargs: Any) -> None:"},"line_group":{"all_lines":[{"content":"        self._notify(function_name=\"on_run_start\", config=config, **kwargs)","type":"context"},{"content":"","type":"context"},{"content":"    def on_run_end(self, config: DictConfig, **kwargs: Any) -> None:","type":"context"},{"content":"        self._notify(function_name=\"on_run_end\", config=config, **kwargs)","type":"deleted"},{"content":"        self._notify(function_name=\"on_run_end\", config=config, reverse=True, **kwargs)","type":"added"},{"content":"","type":"context"},{"content":"    def on_multirun_start(self, config: DictConfig, **kwargs: Any) -> None:","type":"context"},{"content":"        self._notify(function_name=\"on_multirun_start\", config=config, **kwargs)","type":"context"},{"content":"","type":"context"},{"content":"    def on_multirun_end(self, config: DictConfig, **kwargs: Any) -> None:","type":"context"},{"content":"        self._notify(function_name=\"on_multirun_end\", config=config, **kwargs)","type":"deleted"},{"content":"        self._notify(","type":"added"},{"content":"            function_name=\"on_multirun_end\", reverse=True, config=config, **kwargs","type":"added"},{"content":"        )","type":"added"},{"content":"","type":"context"},{"content":"    def on_job_start(self, config: DictConfig, **kwargs: Any) -> None:","type":"context"},{"content":"        self._notify(function_name=\"on_job_start\", config=config, **kwargs)","type":"context"}]},"modified_entities":[],"added_entities":[],"deleted_entities":[]},{"descriptor":{"old_range":{"start":42,"length":5},"new_range":{"start":45,"length":9},"section":"def on_job_end("},"line_group":{"all_lines":[{"content":"        self, config: DictConfig, job_return: JobReturn, **kwargs: Any","type":"context"},{"content":"    ) -> None:","type":"context"},{"content":"        self._notify(","type":"context"},{"content":"            function_name=\"on_job_end\", config=config, job_return=job_return, **kwargs","type":"deleted"},{"content":"            function_name=\"on_job_end\",","type":"added"},{"content":"            config=config,","type":"added"},{"content":"            job_return=job_return,","type":"added"},{"content":"            reverse=True,","type":"added"},{"content":"            **kwargs,","type":"added"},{"content":"        )","type":"context"},{"content":"","type":"context"}]},"modified_entities":[],"added_entities":[],"deleted_entities":[]}]}],"old_commit_hash":"c6aeb2ea65892fd4af9f3a7d7795bab08ad7398d","new_commit_hash":"f953811f643800183769b1f144d7bf5172f2a584","commit_message":"commit","commit_date":"2021-04-19T18:08:56-07:00","metadata":{}}
problem_statement: [callbacks] call on_*_end events in reverse order
  
  
patch: diff --git a/hydra/core/callbacks.py b/hydra/core/callbacks.py
  --- a/hydra/core/callbacks.py
  +++ b/hydra/core/callbacks.py
  @@ -14,8 +14,9 @@ def __init__(self, config: DictConfig) -> None:
           for params in config.hydra.callbacks.values():
               self.callbacks.append(instantiate(params))
   
  -    def _notify(self, function_name: str, **kwargs: Any) -> None:
  -        for c in self.callbacks:
  +    def _notify(self, function_name: str, reverse: bool = False, **kwargs: Any) -> None:
  +        callbacks = reversed(self.callbacks) if reverse else self.callbacks
  +        for c in callbacks:
               try:
                   getattr(c, function_name)(**kwargs)
               except Exception as e:
  @@ -27,13 +28,15 @@ def on_run_start(self, config: DictConfig, **kwargs: Any) -> None:
           self._notify(function_name="on_run_start", config=config, **kwargs)
   
       def on_run_end(self, config: DictConfig, **kwargs: Any) -> None:
  -        self._notify(function_name="on_run_end", config=config, **kwargs)
  +        self._notify(function_name="on_run_end", config=config, reverse=True, **kwargs)
   
       def on_multirun_start(self, config: DictConfig, **kwargs: Any) -> None:
           self._notify(function_name="on_multirun_start", config=config, **kwargs)
   
       def on_multirun_end(self, config: DictConfig, **kwargs: Any) -> None:
  -        self._notify(function_name="on_multirun_end", config=config, **kwargs)
  +        self._notify(
  +            function_name="on_multirun_end", reverse=True, config=config, **kwargs
  +        )
   
       def on_job_start(self, config: DictConfig, **kwargs: Any) -> None:
           self._notify(function_name="on_job_start", config=config, **kwargs)
  @@ -42,5 +45,9 @@ def on_job_end(
           self, config: DictConfig, job_return: JobReturn, **kwargs: Any
       ) -> None:
           self._notify(
  -            function_name="on_job_end", config=config, job_return=job_return, **kwargs
  +            function_name="on_job_end",
  +            config=config,
  +            job_return=job_return,
  +            reverse=True,
  +            **kwargs,
           )
   
  
file_context: {"hydra/core/callbacks.py": "# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\nimport warnings\nfrom typing import Any\n\nfrom omegaconf import DictConfig\n\nfrom hydra.core.utils import JobReturn\nfrom hydra.utils import instantiate\n\n\nclass Callbacks:\n    def __init__(self, config: DictConfig) -> None:\n        self.callbacks = []\n        for params in config.hydra.callbacks.values():\n            self.callbacks.append(instantiate(params))\n\n    def _notify(self, function_name: str, **kwargs: Any) -> None:\n        for c in self.callbacks:\n            try:\n                getattr(c, function_name)(**kwargs)\n            except Exception as e:\n                warnings.warn(\n                    f\"Callback {type(c).__name__}.{function_name} raised {type(e).__name__}: {e}\"\n                )\n\n    def on_run_start(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_run_start\", config=config, **kwargs)\n\n    def on_run_end(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_run_end\", config=config, **kwargs)\n\n    def on_multirun_start(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_multirun_start\", config=config, **kwargs)\n\n    def on_multirun_end(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_multirun_end\", config=config, **kwargs)\n\n    def on_job_start(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_job_start\", config=config, **kwargs)\n\n    def on_job_end(\n        self, config: DictConfig, job_return: JobReturn, **kwargs: Any\n    ) -> None:\n        self._notify(\n            function_name=\"on_job_end\", config=config, job_return=job_return, **kwargs\n        )\n"}
filelevel_patches: {"hydra/core/callbacks.py": "@@ -14,8 +14,9 @@ def __init__(self, config: DictConfig) -> None:\n         for params in config.hydra.callbacks.values():\n             self.callbacks.append(instantiate(params))\n \n-    def _notify(self, function_name: str, **kwargs: Any) -> None:\n-        for c in self.callbacks:\n+    def _notify(self, function_name: str, reverse: bool = False, **kwargs: Any) -> None:\n+        callbacks = reversed(self.callbacks) if reverse else self.callbacks\n+        for c in callbacks:\n             try:\n                 getattr(c, function_name)(**kwargs)\n             except Exception as e:\n@@ -27,13 +28,15 @@ def on_run_start(self, config: DictConfig, **kwargs: Any) -> None:\n         self._notify(function_name=\"on_run_start\", config=config, **kwargs)\n \n     def on_run_end(self, config: DictConfig, **kwargs: Any) -> None:\n-        self._notify(function_name=\"on_run_end\", config=config, **kwargs)\n+        self._notify(function_name=\"on_run_end\", config=config, reverse=True, **kwargs)\n \n     def on_multirun_start(self, config: DictConfig, **kwargs: Any) -> None:\n         self._notify(function_name=\"on_multirun_start\", config=config, **kwargs)\n \n     def on_multirun_end(self, config: DictConfig, **kwargs: Any) -> None:\n-        self._notify(function_name=\"on_multirun_end\", config=config, **kwargs)\n+        self._notify(\n+            function_name=\"on_multirun_end\", reverse=True, config=config, **kwargs\n+        )\n \n     def on_job_start(self, config: DictConfig, **kwargs: Any) -> None:\n         self._notify(function_name=\"on_job_start\", config=config, **kwargs)\n@@ -42,5 +45,9 @@ def on_job_end(\n         self, config: DictConfig, job_return: JobReturn, **kwargs: Any\n     ) -> None:\n         self._notify(\n-            function_name=\"on_job_end\", config=config, job_return=job_return, **kwargs\n+            function_name=\"on_job_end\",\n+            config=config,\n+            job_return=job_return,\n+            reverse=True,\n+            **kwargs,\n         )"}
messages.content: 
  We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  [callbacks] call on_*_end events in reverse order
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  
  --- BEGIN FILE ---
  ```
  ### hydra/core/callbacks.py
  # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
  import warnings
  from typing import Any
  
  from omegaconf import DictConfig
  
  from hydra.core.utils import JobReturn
  from hydra.utils import instantiate
  
  
  class Callbacks:
      def __init__(self, config: DictConfig) -> None:
          self.callbacks = []
          for params in config.hydra.callbacks.values():
              self.callbacks.append(instantiate(params))
  
      def _notify(self, function_name: str, **kwargs: Any) -> None:
          for c in self.callbacks:
              try:
                  getattr(c, function_name)(**kwargs)
              except Exception as e:
                  warnings.warn(
                      f"Callback {type(c).__name__}.{function_name} raised {type(e).__name__}: {e}"
                  )
  
      def on_run_start(self, config: DictConfig, **kwargs: Any) -> None:
          self._notify(function_name="on_run_start", config=config, **kwargs)
  
      def on_run_end(self, config: DictConfig, **kwargs: Any) -> None:
          self._notify(function_name="on_run_end", config=config, **kwargs)
  
      def on_multirun_start(self, config: DictConfig, **kwargs: Any) -> None:
          self._notify(function_name="on_multirun_start", config=config, **kwargs)
  
      def on_multirun_end(self, config: DictConfig, **kwargs: Any) -> None:
          self._notify(function_name="on_multirun_end", config=config, **kwargs)
  
      def on_job_start(self, config: DictConfig, **kwargs: Any) -> None:
          self._notify(function_name="on_job_start", config=config, **kwargs)
  
      def on_job_end(
          self, config: DictConfig, job_return: JobReturn, **kwargs: Any
      ) -> None:
          self._notify(
              function_name="on_job_end", config=config, job_return=job_return, **kwargs
          )
  
  ```
  --- END FILE ---
  
  Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.
  
  Every *SEARCH/REPLACE* edit must use this format:
  1. The file path
  2. The start of search block: <<<<<<< SEARCH
  3. A contiguous chunk of lines to search for in the existing source code
  4. The dividing line: =======
  5. The lines to replace into the source code
  6. The end of the replace block: >>>>>>> REPLACE
  
  Here is an example:
  
  ```python
  ### mathweb/flask/app.py
  <<<<<<< SEARCH
  from flask import Flask
  =======
  import math
  from flask import Flask
  >>>>>>> REPLACE
  ```
  
  Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
  Wrap the *SEARCH/REPLACE* edit in blocks ```python...```.
  
messages.role: user
verification_info: {"file_context": {"hydra/core/callbacks.py": "# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\nimport warnings\nfrom typing import Any\n\nfrom omegaconf import DictConfig\n\nfrom hydra.core.utils import JobReturn\nfrom hydra.utils import instantiate\n\n\nclass Callbacks:\n    def __init__(self, config: DictConfig) -> None:\n        self.callbacks = []\n        for params in config.hydra.callbacks.values():\n            self.callbacks.append(instantiate(params))\n\n    def _notify(self, function_name: str, **kwargs: Any) -> None:\n        for c in self.callbacks:\n            try:\n                getattr(c, function_name)(**kwargs)\n            except Exception as e:\n                warnings.warn(\n                    f\"Callback {type(c).__name__}.{function_name} raised {type(e).__name__}: {e}\"\n                )\n\n    def on_run_start(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_run_start\", config=config, **kwargs)\n\n    def on_run_end(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_run_end\", config=config, **kwargs)\n\n    def on_multirun_start(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_multirun_start\", config=config, **kwargs)\n\n    def on_multirun_end(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_multirun_end\", config=config, **kwargs)\n\n    def on_job_start(self, config: DictConfig, **kwargs: Any) -> None:\n        self._notify(function_name=\"on_job_start\", config=config, **kwargs)\n\n    def on_job_end(\n        self, config: DictConfig, job_return: JobReturn, **kwargs: Any\n    ) -> None:\n        self._notify(\n            function_name=\"on_job_end\", config=config, job_return=job_return, **kwargs\n        )\n"}, "filelevel_patches": {"hydra/core/callbacks.py": "@@ -14,8 +14,9 @@ def __init__(self, config: DictConfig) -> None:\n         for params in config.hydra.callbacks.values():\n             self.callbacks.append(instantiate(params))\n \n-    def _notify(self, function_name: str, **kwargs: Any) -> None:\n-        for c in self.callbacks:\n+    def _notify(self, function_name: str, reverse: bool = False, **kwargs: Any) -> None:\n+        callbacks = reversed(self.callbacks) if reverse else self.callbacks\n+        for c in callbacks:\n             try:\n                 getattr(c, function_name)(**kwargs)\n             except Exception as e:\n@@ -27,13 +28,15 @@ def on_run_start(self, config: DictConfig, **kwargs: Any) -> None:\n         self._notify(function_name=\"on_run_start\", config=config, **kwargs)\n \n     def on_run_end(self, config: DictConfig, **kwargs: Any) -> None:\n-        self._notify(function_name=\"on_run_end\", config=config, **kwargs)\n+        self._notify(function_name=\"on_run_end\", config=config, reverse=True, **kwargs)\n \n     def on_multirun_start(self, config: DictConfig, **kwargs: Any) -> None:\n         self._notify(function_name=\"on_multirun_start\", config=config, **kwargs)\n \n     def on_multirun_end(self, config: DictConfig, **kwargs: Any) -> None:\n-        self._notify(function_name=\"on_multirun_end\", config=config, **kwargs)\n+        self._notify(\n+            function_name=\"on_multirun_end\", reverse=True, config=config, **kwargs\n+        )\n \n     def on_job_start(self, config: DictConfig, **kwargs: Any) -> None:\n         self._notify(function_name=\"on_job_start\", config=config, **kwargs)\n@@ -42,5 +45,9 @@ def on_job_end(\n         self, config: DictConfig, job_return: JobReturn, **kwargs: Any\n     ) -> None:\n         self._notify(\n-            function_name=\"on_job_end\", config=config, job_return=job_return, **kwargs\n+            function_name=\"on_job_end\",\n+            config=config,\n+            job_return=job_return,\n+            reverse=True,\n+            **kwargs,\n         )"}}