We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
cocotb.hook does not do anything unless in a module present in COCOTB_HOOKS
Marking a function with `@cocotb.hook` is insufficient for it to do anything - you also have to set the `COCOTB_HOOKS` environment variable to contain the module name where the decorated function resides.

Either we should document this, or we should just remove `cocotb.hook`. None of our tests test `cocotb.hook`. Is anyone using it?

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `cocotb/xunit_reporter.py`
Content:
```
1 # Copyright (c) 2013 Potential Ventures Ltd
2 # Copyright (c) 2013 SolarFlare Communications Inc
3 # All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are met:
7 #     * Redistributions of source code must retain the above copyright
8 #       notice, this list of conditions and the following disclaimer.
9 #     * Redistributions in binary form must reproduce the above copyright
10 #       notice, this list of conditions and the following disclaimer in the
11 #       documentation and/or other materials provided with the distribution.
12 #     * Neither the name of Potential Ventures Ltd,
13 #       SolarFlare Communications Inc nor the
14 #       names of its contributors may be used to endorse or promote products
15 #       derived from this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 # DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY
21 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 from xml.etree.ElementTree import Element, SubElement
29 import xml.etree.ElementTree as ET
30 
31 import mmap
32 from io import StringIO
33 
34 TRUNCATE_LINES = 100
35 
36 
37 # file from  http://stackoverflow.com/questions/136168/get-last-n-lines-of-a-file-with-python-similar-to-tail
38 class File(StringIO):
39 
40     def countlines(self):
41         buf = mmap.mmap(self.fileno(), 0)
42         lines = 0
43         while buf.readline():
44             lines += 1
45         return lines
46 
47     def head(self, lines_2find=1):
48         self.seek(0)                            # Rewind file
49         return [self.next() for x in range(lines_2find)]
50 
51     def tail(self, lines_2find=1):
52         self.seek(0, 2)                         # go to end of file
53         bytes_in_file = self.tell()
54         lines_found, total_bytes_scanned = 0, 0
55         while (lines_2find+1 > lines_found and
56                bytes_in_file > total_bytes_scanned):
57             byte_block = min(1024, bytes_in_file-total_bytes_scanned)
58             self.seek(-(byte_block+total_bytes_scanned), 2)
59             total_bytes_scanned += byte_block
60             lines_found += self.read(1024).count('\n')
61         self.seek(-total_bytes_scanned, 2)
62         line_list = list(self.readlines())
63         return line_list[-lines_2find:]
64 
65 
66 class XUnitReporter:
67 
68     def __init__(self, filename="results.xml"):
69         self.results = Element("testsuites", name="results")
70         self.filename = filename
71 
72     def add_testsuite(self, **kwargs):
73         self.last_testsuite = SubElement(self.results, "testsuite", **kwargs)
74         return self.last_testsuite
75 
76     def add_testcase(self, testsuite=None, **kwargs):
77         if testsuite is None:
78             testsuite = self.last_testsuite
79         self.last_testcase = SubElement(testsuite, "testcase", **kwargs)
80         return self.last_testcase
81 
82     def add_property(self, testsuite=None, **kwargs):
83         if testsuite is None:
84             testsuite = self.last_testsuite
85         self.last_property = SubElement(testsuite, "property", **kwargs)
86         return self.last_property
87 
88     def update_testsuite(self, testsuite=None, **kwargs):
89         if testsuite is None:
90             testsuite = self.last_testsuite
91         for k in kwargs:
92             testsuite.set(k, str(kwargs[k]))
93 
94     def update_testsuites(self, **kwargs):
95         for k in kwargs:
96             self.results.set(k, str(kwargs[k]))
97 
98     def add_log(self, logfile, testcase=None):
99         if testcase is None:
100             testcase = self.last_testcase
101         log = SubElement(testcase, "system-out")
102         f = File(logfile, 'r+')
103         lines = f.countlines()
104         if lines > (TRUNCATE_LINES * 2):
105             head = f.head(TRUNCATE_LINES)
106             tail = f.tail(TRUNCATE_LINES)
107             log.text = "".join(head + list("[...truncated %d lines...]\n" %
108                                ((lines - (TRUNCATE_LINES*2)))) + tail)
109         else:
110             log.text = "".join(f.readlines())
111 
112     def add_failure(self, testcase=None, **kwargs):
113         if testcase is None:
114             testcase = self.last_testcase
115         SubElement(testcase, "failure", **kwargs)
116 
117     def add_skipped(self, testcase=None, **kwargs):
118         if testcase is None:
119             testcase = self.last_testcase
120         SubElement(testcase, "skipped", **kwargs)
121 
122     def indent(self, elem, level=0):
123         i = "\n" + level*"  "
124         if len(elem):
125             if not elem.text or not elem.text.strip():
126                 elem.text = i + "  "
127             if not elem.tail or not elem.tail.strip():
128                 elem.tail = i
129             for elem in elem:
130                 self.indent(elem, level+1)
131             if not elem.tail or not elem.tail.strip():
132                 elem.tail = i
133         else:
134             if level and (not elem.tail or not elem.tail.strip()):
135                 elem.tail = i
136 
137     def write(self):
138         self.indent(self.results)
139         ET.ElementTree(self.results).write(self.filename, encoding="UTF-8")
140
```
--- 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/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py
--- a/cocotb/xunit_reporter.py
+++ b/cocotb/xunit_reporter.py
@@ -28,40 +28,6 @@
 from xml.etree.ElementTree import Element, SubElement
 import xml.etree.ElementTree as ET
 
-import mmap
-from io import StringIO
-
-TRUNCATE_LINES = 100
-
-
-# file from  http://stackoverflow.com/questions/136168/get-last-n-lines-of-a-file-with-python-similar-to-tail
-class File(StringIO):
-
-    def countlines(self):
-        buf = mmap.mmap(self.fileno(), 0)
-        lines = 0
-        while buf.readline():
-            lines += 1
-        return lines
-
-    def head(self, lines_2find=1):
-        self.seek(0)                            # Rewind file
-        return [self.next() for x in range(lines_2find)]
-
-    def tail(self, lines_2find=1):
-        self.seek(0, 2)                         # go to end of file
-        bytes_in_file = self.tell()
-        lines_found, total_bytes_scanned = 0, 0
-        while (lines_2find+1 > lines_found and
-               bytes_in_file > total_bytes_scanned):
-            byte_block = min(1024, bytes_in_file-total_bytes_scanned)
-            self.seek(-(byte_block+total_bytes_scanned), 2)
-            total_bytes_scanned += byte_block
-            lines_found += self.read(1024).count('\n')
-        self.seek(-total_bytes_scanned, 2)
-        line_list = list(self.readlines())
-        return line_list[-lines_2find:]
-
 
 class XUnitReporter:
 
@@ -85,30 +51,6 @@
         self.last_property = SubElement(testsuite, "property", **kwargs)
         return self.last_property
 
-    def update_testsuite(self, testsuite=None, **kwargs):
-        if testsuite is None:
-            testsuite = self.last_testsuite
-        for k in kwargs:
-            testsuite.set(k, str(kwargs[k]))
-
-    def update_testsuites(self, **kwargs):
-        for k in kwargs:
-            self.results.set(k, str(kwargs[k]))
-
-    def add_log(self, logfile, testcase=None):
-        if testcase is None:
-            testcase = self.last_testcase
-        log = SubElement(testcase, "system-out")
-        f = File(logfile, 'r+')
-        lines = f.countlines()
-        if lines > (TRUNCATE_LINES * 2):
-            head = f.head(TRUNCATE_LINES)
-            tail = f.tail(TRUNCATE_LINES)
-            log.text = "".join(head + list("[...truncated %d lines...]\n" %
-                               ((lines - (TRUNCATE_LINES*2)))) + tail)
-        else:
-            log.text = "".join(f.readlines())
-
     def add_failure(self, testcase=None, **kwargs):
         if testcase is None:
             testcase = self.last_testcase

{"golden_diff": "diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py\n--- a/cocotb/xunit_reporter.py\n+++ b/cocotb/xunit_reporter.py\n@@ -28,40 +28,6 @@\n from xml.etree.ElementTree import Element, SubElement\n import xml.etree.ElementTree as ET\n \n-import mmap\n-from io import StringIO\n-\n-TRUNCATE_LINES = 100\n-\n-\n-# file from  http://stackoverflow.com/questions/136168/get-last-n-lines-of-a-file-with-python-similar-to-tail\n-class File(StringIO):\n-\n-    def countlines(self):\n-        buf = mmap.mmap(self.fileno(), 0)\n-        lines = 0\n-        while buf.readline():\n-            lines += 1\n-        return lines\n-\n-    def head(self, lines_2find=1):\n-        self.seek(0)                            # Rewind file\n-        return [self.next() for x in range(lines_2find)]\n-\n-    def tail(self, lines_2find=1):\n-        self.seek(0, 2)                         # go to end of file\n-        bytes_in_file = self.tell()\n-        lines_found, total_bytes_scanned = 0, 0\n-        while (lines_2find+1 > lines_found and\n-               bytes_in_file > total_bytes_scanned):\n-            byte_block = min(1024, bytes_in_file-total_bytes_scanned)\n-            self.seek(-(byte_block+total_bytes_scanned), 2)\n-            total_bytes_scanned += byte_block\n-            lines_found += self.read(1024).count('\\n')\n-        self.seek(-total_bytes_scanned, 2)\n-        line_list = list(self.readlines())\n-        return line_list[-lines_2find:]\n-\n \n class XUnitReporter:\n \n@@ -85,30 +51,6 @@\n         self.last_property = SubElement(testsuite, \"property\", **kwargs)\n         return self.last_property\n \n-    def update_testsuite(self, testsuite=None, **kwargs):\n-        if testsuite is None:\n-            testsuite = self.last_testsuite\n-        for k in kwargs:\n-            testsuite.set(k, str(kwargs[k]))\n-\n-    def update_testsuites(self, **kwargs):\n-        for k in kwargs:\n-            self.results.set(k, str(kwargs[k]))\n-\n-    def add_log(self, logfile, testcase=None):\n-        if testcase is None:\n-            testcase = self.last_testcase\n-        log = SubElement(testcase, \"system-out\")\n-        f = File(logfile, 'r+')\n-        lines = f.countlines()\n-        if lines > (TRUNCATE_LINES * 2):\n-            head = f.head(TRUNCATE_LINES)\n-            tail = f.tail(TRUNCATE_LINES)\n-            log.text = \"\".join(head + list(\"[...truncated %d lines...]\\n\" %\n-                               ((lines - (TRUNCATE_LINES*2)))) + tail)\n-        else:\n-            log.text = \"\".join(f.readlines())\n-\n     def add_failure(self, testcase=None, **kwargs):\n         if testcase is None:\n             testcase = self.last_testcase\n", "issue": "cocotb.hook does not do anything unless in a module present in COCOTB_HOOKS\nMarking a function with `@cocotb.hook` is insufficient for it to do anything - you also have to set the `COCOTB_HOOKS` environment variable to contain the module name where the decorated function resides.\r\n\r\nEither we should document this, or we should just remove `cocotb.hook`. None of our tests test `cocotb.hook`. Is anyone using it?\n", "before_files": [{"content": "# Copyright (c) 2013 Potential Ventures Ltd\n# Copyright (c) 2013 SolarFlare Communications Inc\n# All rights reserved.\n#\n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions are met:\n#     * Redistributions of source code must retain the above copyright\n#       notice, this list of conditions and the following disclaimer.\n#     * Redistributions in binary form must reproduce the above copyright\n#       notice, this list of conditions and the following disclaimer in the\n#       documentation and/or other materials provided with the distribution.\n#     * Neither the name of Potential Ventures Ltd,\n#       SolarFlare Communications Inc nor the\n#       names of its contributors may be used to endorse or promote products\n#       derived from this software without specific prior written permission.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY\n# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nfrom xml.etree.ElementTree import Element, SubElement\nimport xml.etree.ElementTree as ET\n\nimport mmap\nfrom io import StringIO\n\nTRUNCATE_LINES = 100\n\n\n# file from  http://stackoverflow.com/questions/136168/get-last-n-lines-of-a-file-with-python-similar-to-tail\nclass File(StringIO):\n\n    def countlines(self):\n        buf = mmap.mmap(self.fileno(), 0)\n        lines = 0\n        while buf.readline():\n            lines += 1\n        return lines\n\n    def head(self, lines_2find=1):\n        self.seek(0)                            # Rewind file\n        return [self.next() for x in range(lines_2find)]\n\n    def tail(self, lines_2find=1):\n        self.seek(0, 2)                         # go to end of file\n        bytes_in_file = self.tell()\n        lines_found, total_bytes_scanned = 0, 0\n        while (lines_2find+1 > lines_found and\n               bytes_in_file > total_bytes_scanned):\n            byte_block = min(1024, bytes_in_file-total_bytes_scanned)\n            self.seek(-(byte_block+total_bytes_scanned), 2)\n            total_bytes_scanned += byte_block\n            lines_found += self.read(1024).count('\\n')\n        self.seek(-total_bytes_scanned, 2)\n        line_list = list(self.readlines())\n        return line_list[-lines_2find:]\n\n\nclass XUnitReporter:\n\n    def __init__(self, filename=\"results.xml\"):\n        self.results = Element(\"testsuites\", name=\"results\")\n        self.filename = filename\n\n    def add_testsuite(self, **kwargs):\n        self.last_testsuite = SubElement(self.results, \"testsuite\", **kwargs)\n        return self.last_testsuite\n\n    def add_testcase(self, testsuite=None, **kwargs):\n        if testsuite is None:\n            testsuite = self.last_testsuite\n        self.last_testcase = SubElement(testsuite, \"testcase\", **kwargs)\n        return self.last_testcase\n\n    def add_property(self, testsuite=None, **kwargs):\n        if testsuite is None:\n            testsuite = self.last_testsuite\n        self.last_property = SubElement(testsuite, \"property\", **kwargs)\n        return self.last_property\n\n    def update_testsuite(self, testsuite=None, **kwargs):\n        if testsuite is None:\n            testsuite = self.last_testsuite\n        for k in kwargs:\n            testsuite.set(k, str(kwargs[k]))\n\n    def update_testsuites(self, **kwargs):\n        for k in kwargs:\n            self.results.set(k, str(kwargs[k]))\n\n    def add_log(self, logfile, testcase=None):\n        if testcase is None:\n            testcase = self.last_testcase\n        log = SubElement(testcase, \"system-out\")\n        f = File(logfile, 'r+')\n        lines = f.countlines()\n        if lines > (TRUNCATE_LINES * 2):\n            head = f.head(TRUNCATE_LINES)\n            tail = f.tail(TRUNCATE_LINES)\n            log.text = \"\".join(head + list(\"[...truncated %d lines...]\\n\" %\n                               ((lines - (TRUNCATE_LINES*2)))) + tail)\n        else:\n            log.text = \"\".join(f.readlines())\n\n    def add_failure(self, testcase=None, **kwargs):\n        if testcase is None:\n            testcase = self.last_testcase\n        SubElement(testcase, \"failure\", **kwargs)\n\n    def add_skipped(self, testcase=None, **kwargs):\n        if testcase is None:\n            testcase = self.last_testcase\n        SubElement(testcase, \"skipped\", **kwargs)\n\n    def indent(self, elem, level=0):\n        i = \"\\n\" + level*\"  \"\n        if len(elem):\n            if not elem.text or not elem.text.strip():\n                elem.text = i + \"  \"\n            if not elem.tail or not elem.tail.strip():\n                elem.tail = i\n            for elem in elem:\n                self.indent(elem, level+1)\n            if not elem.tail or not elem.tail.strip():\n                elem.tail = i\n        else:\n            if level and (not elem.tail or not elem.tail.strip()):\n                elem.tail = i\n\n    def write(self):\n        self.indent(self.results)\n        ET.ElementTree(self.results).write(self.filename, encoding=\"UTF-8\")\n", "path": "cocotb/xunit_reporter.py"}], "after_files": [{"content": "# Copyright (c) 2013 Potential Ventures Ltd\n# Copyright (c) 2013 SolarFlare Communications Inc\n# All rights reserved.\n#\n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions are met:\n#     * Redistributions of source code must retain the above copyright\n#       notice, this list of conditions and the following disclaimer.\n#     * Redistributions in binary form must reproduce the above copyright\n#       notice, this list of conditions and the following disclaimer in the\n#       documentation and/or other materials provided with the distribution.\n#     * Neither the name of Potential Ventures Ltd,\n#       SolarFlare Communications Inc nor the\n#       names of its contributors may be used to endorse or promote products\n#       derived from this software without specific prior written permission.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY\n# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nfrom xml.etree.ElementTree import Element, SubElement\nimport xml.etree.ElementTree as ET\n\n\nclass XUnitReporter:\n\n    def __init__(self, filename=\"results.xml\"):\n        self.results = Element(\"testsuites\", name=\"results\")\n        self.filename = filename\n\n    def add_testsuite(self, **kwargs):\n        self.last_testsuite = SubElement(self.results, \"testsuite\", **kwargs)\n        return self.last_testsuite\n\n    def add_testcase(self, testsuite=None, **kwargs):\n        if testsuite is None:\n            testsuite = self.last_testsuite\n        self.last_testcase = SubElement(testsuite, \"testcase\", **kwargs)\n        return self.last_testcase\n\n    def add_property(self, testsuite=None, **kwargs):\n        if testsuite is None:\n            testsuite = self.last_testsuite\n        self.last_property = SubElement(testsuite, \"property\", **kwargs)\n        return self.last_property\n\n    def add_failure(self, testcase=None, **kwargs):\n        if testcase is None:\n            testcase = self.last_testcase\n        SubElement(testcase, \"failure\", **kwargs)\n\n    def add_skipped(self, testcase=None, **kwargs):\n        if testcase is None:\n            testcase = self.last_testcase\n        SubElement(testcase, \"skipped\", **kwargs)\n\n    def indent(self, elem, level=0):\n        i = \"\\n\" + level*\"  \"\n        if len(elem):\n            if not elem.text or not elem.text.strip():\n                elem.text = i + \"  \"\n            if not elem.tail or not elem.tail.strip():\n                elem.tail = i\n            for elem in elem:\n                self.indent(elem, level+1)\n            if not elem.tail or not elem.tail.strip():\n                elem.tail = i\n        else:\n            if level and (not elem.tail or not elem.tail.strip()):\n                elem.tail = i\n\n    def write(self):\n        self.indent(self.results)\n        ET.ElementTree(self.results).write(self.filename, encoding=\"UTF-8\")\n", "path": "cocotb/xunit_reporter.py"}]}