Repository: cocotb/cocotb

Issue:
Deprecate TestError/expect_error
With the move to using assertions instead of raising exceptions, ``TestError``/``expect_error`` could/should be deprecated. https://github.com/cocotb/cocotb/pull/2170#discussion_r516712751

<!--
In case you are reporting a problem with running cocotb,
please remember to add relevant information about your environment, such as
* the cocotb version used,
* the operating system and version (32/64 bit),
* the simulator and version (32/64 bit),
* the Python version, and where it's coming from (e.g. system, Anaconda, self-installed, ...),
* (part of) the log file (cleaned of proprietary information).
If you are unsure about any of the above items, open the issue anyway and we will figure it out together.
Thanks for helping improve cocotb!
-->



Discussion:
There is one reference that is located in the core libraries [here](https://github.com/cocotb/cocotb/blob/4d4fffaeedd3557601f706e1b5d872b15b0a7a42/cocotb/handle.py#L223), but I'm not sure why. When would iterating over a sub handles result in a `TestError`? I will check coverage to see if this is dead code. [Seems dead](https://codecov.io/gh/cocotb/cocotb/src/master/cocotb/handle.py#L223) to me.
@ktbarrett Looks like it's catching this:
https://github.com/cocotb/cocotb/blob/4d4fffaeedd3557601f706e1b5d872b15b0a7a42/cocotb/handle.py#L959
Never trust the Github search feature. Lets me down every time.

Resolving patch:
diff --git a/cocotb/handle.py b/cocotb/handle.py
--- a/cocotb/handle.py
+++ b/cocotb/handle.py
@@ -36,7 +36,6 @@
 from cocotb import simulator
 from cocotb.binary import BinaryValue
 from cocotb.log import SimLog
-from cocotb.result import TestError
 
 # Only issue a warning for each deprecated attribute access
 _deprecation_warned = set()
@@ -218,9 +217,10 @@ def _discover_all(self):
         self._log.debug("Discovering all on %s", self._name)
         for thing in self._handle.iterate(simulator.OBJECTS):
             name = thing.get_name_string()
+            path = self._child_path(name)
             try:
-                hdl = SimHandle(thing, self._child_path(name))
-            except TestError as e:
+                hdl = SimHandle(thing, path)
+            except NotImplementedError as e:
                 self._log.debug("%s", e)
                 continue
 
@@ -919,7 +919,7 @@ def SimHandle(handle, path=None):
         The `SimHandle` object.
 
     Raises:
-        TestError: If no matching object for GPI type could be found.
+        NotImplementedError: If no matching object for GPI type could be found.
     """
     _type2cls = {
         simulator.MODULE:      HierarchyObject,
@@ -956,7 +956,7 @@ def SimHandle(handle, path=None):
         return obj
 
     if t not in _type2cls:
-        raise TestError("Couldn't find a matching object for GPI type %d (path=%s)" % (t, path))
+        raise NotImplementedError("Couldn't find a matching object for GPI type %s(%d) (path=%s)" % (handle.get_type_string(), t, path))
     obj = _type2cls[t](handle, path)
     _handle2obj[handle] = obj
     return obj
diff --git a/cocotb/result.py b/cocotb/result.py
--- a/cocotb/result.py
+++ b/cocotb/result.py
@@ -38,16 +38,15 @@ def raise_error(obj, msg):
     """Create a :exc:`TestError` exception and raise it after printing a traceback.
 
     .. deprecated:: 1.3
-        Use ``raise TestError(msg)`` instead of this function. A stacktrace will
-        be printed by cocotb automatically if the exception is unhandled.
+        Raise a standard Python exception instead of calling this function.
+        A stacktrace will be printed by cocotb automatically if the exception is unhandled.
 
     Args:
         obj: Object with a log method.
         msg (str): The log message.
     """
     warnings.warn(
-        "``raise_error`` is deprecated - use ``raise TestError(msg)`` (or any "
-        "other exception type) instead",
+        "``raise_error`` is deprecated - raise a standard Exception instead",
         DeprecationWarning, stacklevel=2)
     _raise_error(obj, msg)
 
@@ -67,15 +66,14 @@ def create_error(obj, msg):
     simply to avoid too many levels of nested `try/except` blocks.
 
     .. deprecated:: 1.3
-        Use ``TestError(msg)`` directly instead of this function.
+        Raise a standard Python exception instead of calling this function.
 
     Args:
         obj: Object with a log method.
         msg (str): The log message.
     """
     warnings.warn(
-        "``create_error`` is deprecated - use ``TestError(msg)`` directly "
-        "(or any other exception type) instead",
+        "``create_error`` is deprecated - raise a standard Exception instead",
         DeprecationWarning, stacklevel=2)
     try:
         # use the private version to avoid multiple warnings
@@ -118,8 +116,19 @@ def __init__(self, exception):
 
 
 class TestError(TestComplete):
-    """Exception showing that the test was completed with severity Error."""
-    pass
+    """
+    Exception showing that the test was completed with severity Error.
+
+    .. deprecated:: 1.5
+        Raise a standard Python exception instead.
+        A stacktrace will be printed by cocotb automatically if the exception is unhandled.
+    """
+
+    def __init__(self, *args, **kwargs):
+        warnings.warn(
+            "TestError is deprecated - raise a standard Exception instead",
+            DeprecationWarning, stacklevel=2)
+        super().__init__(*args, **kwargs)
 
 
 class TestFailure(TestComplete, AssertionError):


Test patch:
diff --git a/tests/test_cases/test_cocotb/test_deprecated.py b/tests/test_cases/test_cocotb/test_deprecated.py
--- a/tests/test_cases/test_cocotb/test_deprecated.py
+++ b/tests/test_cases/test_cocotb/test_deprecated.py
@@ -16,7 +16,7 @@ def assert_deprecated(warning_category=DeprecationWarning):
             warnings.simplefilter("always")
             yield warns  # note: not a cocotb yield, but a contextlib one!
     finally:
-        assert len(warns) == 1
+        assert len(warns) >= 1
         msg = "Expected {}".format(warning_category.__qualname__)
         assert issubclass(warns[0].category, warning_category), msg