# Asserting exact exception instances

**URL:** <https://discuss.python.org/t/asserting-exact-exception-instances/83763>\
**Category:** Ideas\
**Created:** [March 9, 2025, 1:40pm UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763 "2025-03-09T13:40:29Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![mauve](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mauve/32/3257_2.png) [@mauve](https://discuss.python.org/u/mauve)\
**Post date:** [March 9, 2025, 1:40pm UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/1 "2025-03-09T13:40:29Z")

</div>

There are two helpers in `unittest.TestCase` for asserting that exceptions are raised and properties of them:

- `assertRaises` checks that an exception is raised and asserts its _type_
- `assertRaisesRegex` checks that an exception is raised and matches its _type_ and str

I generally try to assert something about the message just to make sure the test doesn’t accidentally pass by raising the same exception in a different way, so I almost always use `assertRaisesRegex`.

But it’s a bit awkward to convert the string I want to match to a regex (and even more clumsy to use `re.escape()` purely for this.

How about allowing `assertRaises` to take an exception instance for exact match?

```python
with self.assertRaises(ValueError("object is already real")):
    reify(x)

```

This is currently rejected with `TypeError`.

It’s comparatively awkward to use the context object. This doesn’t work

```python
with self.assertRaises(ValueError) as cm:
    reify(x)
self.assertEqual(cm.exception, ValueError("object is already real"))

```

because

```python
>>> ValueError('foo') == ValueError('foo')
False

```

This seems like an oversight? `Exception. __eq__ ` could return `type(a) == type(b) and a.args == b.args`.

The other path that might be helpful is something like `assertRaisesStartsWith()` or `assertRaises(exc, *, startswith: str = "")` because a lot of exception messages are like “something bad happened: exact details” so this would allow

```python
with self.assertRaises(Exception, startswith="something bad happened:"):
    unbundle_from(stream)

```

---

<div class="post-metadata">

**Author:** ![chepner](https://avatars.discourse-cdn.com/v4/letter/c/22d042/32.png) [@chepner](https://discuss.python.org/u/chepner)\
**Post date:** [March 9, 2025, 4:03pm UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/2 "2025-03-09T16:03:33Z")

</div>

It’s probably a better idea to define custom exception types that don’t need parseable error messages to carry the same information.

```python
class AlreadyRealError(ValueError):
    pass

...

with self.assertRaises(AlreadyRealError):
    reify(x)

```

---

<div class="post-metadata">

**Author:** ![mcepl](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mcepl/32/24095_2.png) [@mcepl](https://discuss.python.org/u/mcepl)\
**Post date:** [March 10, 2025, 7:53am UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/3 "2025-03-10T07:53:57Z")

</div>

This is just wrong. You probably meant something like

```python
with self.assertRaises(ValueError, errormsg="object is already real"):
    reify(x)

```

Right? Your code tries to create a new exception instance and use it as an argument of the `assertRaises` method. That’s certainly not what you wanted.

And even if we accept that, then the question is why not to do:

```python
with self.assertRaisesRegex(ValueError, "object is already real"):
    reify(x)

```

Remember, the old adage of vi users, “every string is also a regular expression”? What’s wrong with that?

And even if you need to do something fancy with the caught exception (and its message) see the second example in the documentation for `assertRaises` method. You have the caught exception available, so you can test on that.

Also, my experience with `assertRaisesRegex` for checking methods OVER WHICH YOU HAVE NO CONTROL is rather mixed. Their maintainers tend to change error messages, and then you get an incredibly brittle test suite (e.g., see for the similar situation `Pygments`). Quite often, I have to switch it off, [Fix etree XMLPullParser tests for Expat \>=2.6.0 with reparse deferral · openSUSE-Python/cpython@d7133c7 · GitHub](https://da.gd/t1Jwe).

---

<div class="post-metadata">

**Author:** ![mauve](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mauve/32/3257_2.png) [@mauve](https://discuss.python.org/u/mauve)\
**Post date:** [March 10, 2025, 9:27am UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/4 "2025-03-10T09:27:18Z")

</div>

> [@chepner](#):
>
> It’s probably a better idea to define custom exception types that don’t need parseable error messages

Sometimes, but in a test we want to validate that exception messages are constructed correctly as well as that the correct type is raised.

> [@mcepl](#):
>
> Remember, the old adage of vi users, “every string is also a regular expression”? What’s wrong with that?

Regular expressions have a lot of special characters that need escaping, and those are exactly the characters that appear in the repr of common Python objects. I don’t know about you but I always have to spend ages looking at a failing `assertRaisesRegex` test to work out what I’ve failed to escape. I know exactly the message that should be raised, the problem is regex. It’s less convenient about 80% of the time imho.

---

<div class="post-metadata">

**Author:** ![storchaka](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/storchaka/32/217_2.png) [@storchaka](https://discuss.python.org/u/storchaka)\
**Post date:** [March 10, 2025, 11:36am UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/5 "2025-03-10T11:36:29Z")

</div>

Testing against an exact error message is usually not a good ide, since error messages are intended to be read by humans. Readability is more important than stability. This is especially important if you have no control on generated error messages. For example, Tkinter raises TclError for all errors, but error messages can vary between versions of the underlying Tcl/Tk library. They can be changed even in a bugfix release.

It is more common to use regular expressions that contains important keywords and ignore the rest of the error message. You can also test other exception attributes (`errno`, `filename`, `line`, `offset`) to be less depending on error message.

So, this feature has not be implemented because it is less useful than you think.

There are also other problems with this idea. Exceptions usually do not implement ` __eq__ `, so different exceptions ara always not equal. How are you going to test the exception? If you only test the type and the string representation, take into account that the string representation is not always equal to the first string argument. It can be constructed from all aruments, and it can depends on the hidden attributes. It may be not stable – depending on the order of keyword arguments. It can include timestamp of the exception creation or IDs of internal objects, unique for every exception. The constructor of the exception can be private. So this solution is not universal.

---

<div class="post-metadata">

**Author:** ![mauve](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mauve/32/3257_2.png) [@mauve](https://discuss.python.org/u/mauve)\
**Post date:** [March 10, 2025, 12:29pm UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/6 "2025-03-10T12:29:51Z")

</div>

> [@storchaka](#):
>
> Testing against an exact error message is usually not a good ide, since error messages are intended to be read by humans. Readability is more important than stability. This is especially important if you have no control on generated error messages.

I’m exclusively talking about testing the code in which I construct the error messages, and I disagree that it’s not a good idea. I want to test that the error condition is reported correctly. And we have `assertRaisesRegex` to do this; my complaint is that writing regexes is often inconvenient.

But yes, I would not expect that users of my library make assertions about the text of errors that they don’t raise.

> [@storchaka](#):
>
> Exceptions usually do not implement ` __eq__ `, so different exceptions ara always not equal.

Exactly, let’s make them implement ` __eq__ `. I don’t see any reason why comparing exceptions could not compare their value part (`args`) and ignore contextual stuff, ` __traceback__ `, ` __cause__ ` etc.

> [@storchaka](#):
>
> How are you going to test the exception? If you only test the type and the string representation, take into account that the string representation is not always equal to the first string argument. It can be constructed from all aruments, and it can depends on the hidden attributes. It may be not stable – depending on the order of keyword arguments. It can include timestamp of the exception creation or IDs of internal objects, unique for every exception.

The same applies for `assertRaisesRegex()` but we have that, so…? `assertRaisesRegex()` does something like `assert re.match(pat, str(exc))` so `assertRaisesStartsWith()` would do `assert str(exc).startswith(prefix)`.

> [@storchaka](#):
>
> The constructor of the exception can be private. So this solution is not universal.

So? If it’s my exception type and I can’t test it with `assertRaises(MyException(blah))` then and it’s on me to find a way to test it, probably `assertRaises(MyException._private_keep_out(blah))`. Same if I don’t implement `_eq_()`.

---

<div class="post-metadata">

**Author:** ![peterc](https://avatars.discourse-cdn.com/v4/letter/p/f9ae1b/32.png) [@peterc](https://discuss.python.org/u/peterc)\
**Post date:** [March 10, 2025, 12:44pm UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/7 "2025-03-10T12:44:34Z")

</div>

with pytest I can use the following code:

```python
    with pytest.raises(
        TypeError,
        match="missing 1 required positional argument: 'url_websocket'",
    ):

```

which is useful.

If that’s currently hard to achieve with unittest, adding it as a feature sounds like a good idea.

---

<div class="post-metadata">

**Author:** ![mauve](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mauve/32/3257_2.png) [@mauve](https://discuss.python.org/u/mauve)\
**Post date:** [March 11, 2025, 9:08am UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/8 "2025-03-11T09:08:36Z")

</div>

Unfortunately `pytest.raises(match=...)` [also takes a regex pattern](https://docs.pytest.org/en/latest/reference/reference.html#pytest-raises), and asks you to use `re.escape()` if you don’t want that.

But I think you’re right, comparing exceptions directly might be more challenging and there’s an easy path to improve ergonomics by adding an assert method like

```python
def assertRaisesMessage(
    self,
    expected_exception: Type[BaseException],
    callable: Callable | None = None,
    args: tuple = (),
    kwargs: dict = {},
    msg: str | None = None,
    *,
    equals: str | None = None,
    startswith: str | None = None,
    endswith: str | None = None,
    contains: str | None = None,
) -> _AssertRaisesContext:

```

That could be used as:

```python
with assertRaisesMessage(ValueError, startswith="object is already real:"):
    reify(x)

```

or

```python
with assertRaisesMessage(ValueError, contains=repr(x)):
    reify(x)

```

`equals`, `startswith`, `endswith`, `contains` seem like a basic set of tests on strings but could be extended with `fnmatch` or even `regex`/`match` to unify and align with `assertRaisesRegex`/`pytest.raises`.

---

<div class="post-metadata">

**Author:** ![pf\_moore](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pf_moore/32/35_2.png) [@pf\_moore](https://discuss.python.org/u/pf_moore)\
**Post date:** [March 11, 2025, 10:18am UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/9 "2025-03-11T10:18:04Z")

</div>

This seems like a relatively easy helper to write (based on `assertRaisesRegex`). Is it a helper you write in your own code\[1\], or do you have evidence that people write similar things in other projects? That would be useful information, when deciding whether it’s worth adding it to the stdlib (if no-one else thinks it’s worth the effort of writing, why should we?)

* * *

1. as opposed to just writing the test inline

---

<div class="post-metadata">

**Author:** ![mcepl](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mcepl/32/24095_2.png) [@mcepl](https://discuss.python.org/u/mcepl)\
**Post date:** [March 11, 2025, 11:01am UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/10 "2025-03-11T11:01:43Z")

</div>

I don’t know what’s so complicated about something like this:

```python
with self.assertRaises(SomeException) as cm:
    do_something()

self.assertEqual(str(cm.exception), "something")

```

---

<div class="post-metadata">

**Author:** ![sirosen](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/sirosen/32/8478_2.png) [@sirosen](https://discuss.python.org/u/sirosen)\
**Post date:** [March 11, 2025, 6:02pm UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/11 "2025-03-11T18:02:18Z")

</div>

Notably, `pytest.raises` is nearly identical to the unittest behavior. I think that’s a compliment to unittest.

I sometimes find myself mildly annoyed because I forgot to escape some symbols in the regex match. And then I escape them and happily move on with my day. I’m not sure that there’s a demonstrated need in this thread, and if there is it would be interesting to discuss solutions with `pytest` before trying to change `unittest`.

---

<div class="post-metadata">

**Author:** ![mauve](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mauve/32/3257_2.png) [@mauve](https://discuss.python.org/u/mauve)\
**Post date:** [March 11, 2025, 6:35pm UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/12 "2025-03-11T18:35:51Z")

</div>

> [@pf\_moore](#):
>
> do you have evidence that people write similar things in other projects

There are a lot of occurrences in the stdlib tests, seemingly hundreds. Many of these hits seem correct:

> **[repo:^github\\.com/python… - Sourcegraph](https://sourcegraph.com/search?q=repo:%5Egithub%5C.com%2Fpython%2Fcpython%24%2Bpath:Lib%2Ftest%2BassertRaisesRegex%2Bre.escape&patternType=keyword&sm=0)**
>
> Search across 2 million+ open source repositories for free. Powered by Sourcegraph Code Search.

Beyond CPython there are more than 10k hits for `assertRaisesRegex` with `re.escape`:

> **[language:python assertRa… - Sourcegraph](https://sourcegraph.com/search?q=language:python%2BassertRaisesRegex%2Bre.escape&patternType=keyword&sm=0)**
>
> Search across 2 million+ open source repositories for free. Powered by Sourcegraph Code Search.

This is just cases that are literally using `re.escape()`. There may be cases that have a manually escaped pattern literal, at the expense of readability.

> [@pf\_moore](#):
>
> when deciding whether it’s worth adding it to the stdlib (if no-one else thinks it’s worth the effort of writing, why should we?)

I’m happy to write it; _I_ think it’s worth the effort. The question is whether it would be accepted, whether it’s worth the cost of maintaining it in the stdlib.

---

<div class="post-metadata">

**Author:** ![pf\_moore](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pf_moore/32/35_2.png) [@pf\_moore](https://discuss.python.org/u/pf_moore)\
**Post date:** [March 11, 2025, 7:25pm UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/13 "2025-03-11T19:25:54Z")

</div>

> [@mauve](#):
>
> I’m happy to write it; _I_ think it’s worth the effort. The question is whether it would be accepted, whether it’s worth the cost of maintaining it in the stdlib.

I think you’re missing my point. Why is it worth writing to put in the stdlib, but _not_ worth writing for all those cases you identified? It seems like most people (including you) are getting along just fine using `re.escape`…

---

<div class="post-metadata">

**Author:** ![mauve](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mauve/32/3257_2.png) [@mauve](https://discuss.python.org/u/mauve)\
**Post date:** [March 11, 2025, 8:38pm UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/14 "2025-03-11T20:38:01Z")

</div>

> [@pf\_moore](#):
>
> Why is it worth writing to put in the stdlib, but _not_ worth writing for all those cases you identified? It seems like most people (including you) are getting along just fine using `re.escape`…

That can be explained purely by rational application of

> **[Is It Worth the Time?](https://xkcd.com/1205/)**

For most individual cases it’s not worth writing the helper, it saves 5 seconds a day maybe. Put it in the stdlib and give it to a million Python programmers and it saves years.

---

<div class="post-metadata">

**Author:** ![mauve](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mauve/32/3257_2.png) [@mauve](https://discuss.python.org/u/mauve)\
**Post date:** [March 12, 2025, 9:21am UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/15 "2025-03-12T09:21:25Z")

</div>

> [@pf\_moore](#):
>
> Why is it worth writing to put in the stdlib, but _not_ worth writing for all those cases you identified? It seems like most people (including you) are getting along just fine using `re.escape`…

Here’s a different answer - somebody did write it _in the CPython repo_ (jeffrey.yasskin in [r59974](https://github.com/python/cpython/commit/7736b5becd273cb271c55fcce9155677a381c4f6) in 2008):

> <https://github.com/python/cpython/blob/155c44b9015089eacc6e2ace449391c12bfb8b8d/Lib/test/test_fractions.py#L305-L313>

It’s also in Django:

> **[Testing tools | Django documentation](https://docs.djangoproject.com/en/5.1/topics/testing/tools/#django.test.SimpleTestCase.assertRaisesMessage)**
>
> The web framework for perfectionists with deadlines.

---

<div class="post-metadata">

**Author:** ![maximlt](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/maximlt/32/7166_2.png) [@maximlt](https://discuss.python.org/u/maximlt)\
**Post date:** [March 12, 2025, 9:30am UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/16 "2025-03-12T09:30:28Z")

</div>

I want to express sympathy for this proposal! I very often would have preferred to test against `startswith` or `contains` rather than having to deal with regular expressions. I also resort to manually escaping characters or just `re.escape()`. Having to deal with regular expressions is a conceptual step up that I am sure brings discomfort to the many non-software-developers people that end up writing Python code.

---

<div class="post-metadata">

**Author:** ![wim](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/wim/32/1705_2.png) [@wim](https://discuss.python.org/u/wim)\
**Post date:** [March 14, 2025, 2:00pm UTC](https://discuss.python.org/t/asserting-exact-exception-instances/83763/17 "2025-03-14T14:00:17Z")

</div>

Take a look at my plugin pytest-raisin

> **[GitHub - wimglenn/pytest-raisin: Plugin enabling the use of exception instances...](https://github.com/wimglenn/pytest-raisin)**
>
> Plugin enabling the use of exception instances with pytest.raises context 🍇
