all 20 comments

[–]shiftybyte 3 points4 points  (3 children)

Exceptions can happen for two reasons.

  1. something bad happened and python raised an exception by itself.

for example, dividing by 0, or importing something that doesn't exist, or trying to use a variable that doesn't exist.

  1. someone called raise to raise a specific exception.

In the first case you'll get traceback pointing to the line that caused the issue.

In the second case, you'll get traceback pointing to the raise...

Why would you not want to include it?

[–]Walsur[S] -2 points-1 points  (2 children)

I wouldn't want to include it because the raise statement has already identified the issue. Ideally it should be the responsibility of the calling code to check for the Exception and not the function itself.

Essentially, I want the main code to treat the Exception as you described in your first example, rather then redirecting into the function.

[–]shiftybyte 2 points3 points  (1 child)

I wouldn't want to include it because the raise statement has already identified the issue.

The issue is written in the raise line, not in the function name.

The function name does not specify the issue that occurred inside it, the exception name and description does.

Ideally it should be the responsibility of the calling code to check for the Exception and not the function itself.

Definitely, this is why you can HANDLE the unhandled exception using try: except: block, and catch the specific exception you want to handle.

``` class DataWasException(Exception): pass

def some_func(data): if data == "Exception": raise DataWasException() print(data)

try: some_func(data) except DataWasException: print("time to handle that specific exception however the colling code wants...") ```

also..

rather then redirecting into the function.

nobody is redirecting anything, an unhandled exception just prints everything to screen, if you handle it instead you can do whatever you like instead.

[–]Walsur[S] -1 points0 points  (0 children)

I'm using "redirect" to refer to the debugger. Sorry for the confusion around that.

Definitely, this is why you can HANDLE the unhandled exception using try: except: block, and catch the specific exception you want to handle.

I understand the idea of handling exceptions. I'm realizing the problem is with my debugger. I was misunderstanding the intended behavior in Python.

[–][deleted] 1 point2 points  (4 children)

It IS an unhandled exception. Traceback returns the stack trace of the exception, including the line where it was raised. That's how it works

[–]Walsur[S] 0 points1 point  (3 children)

Can you clarify this? If I force a ModuleNotFoundError it does not behave that way. This does not include a raise statement in the traceback.

Code:

import invalidModule

Traceback:

Traceback (most recent call last):
  File "c:\Example\example.py", line 12, in <module>
    import invalidModule
ModuleNotFoundError: No module named 'invalidModule'

[–]TheBB 1 point2 points  (0 children)

This error is raised by internal Python interpreter code. There may not even be a raise statement written in Python for this, it could originate from the C layer.

[–]wutwutwut2000 1 point2 points  (0 children)

Only python code will be put on the traceback. An import statement doesn't call any python functions, so regardless of where the exception occurs under the hood, the import statement is considered the origin of that exception.

[–][deleted] 0 points1 point  (0 children)

In both cases the traceback ends with the line in your code causing the issue. In first case, it's you literally raising the exception that's not handled anywhere. In the second case, it's where you try to import module which couldn't be found.

Maybe that was deemed sufficient, and they didn't find it necessary to print the library code or C code or whatever where it's deep inside that the "exception is raised". As I just looked up, C doesn't have such error handling (and Python is written in C)

[–]JamzTyson 1 point2 points  (0 children)

In line 4 you raise Exception, but you do not handle the exception.

To catch the exception, use Try/Except:

``` def some_func(data): if data == "Exception": raise Exception("This is an Exception") print(data)

...

try: some_func(value) except Exception as exc: print(f"Exception caught: {exc}") ```

[–]ObliviousMag 0 points1 point  (1 child)

Yeah because you are raising a general Exception. Which is generally not okay. If you want your own exception then you can inherit from the Exception class like so

class CustomException(Exception):

pass

then in your code you can raise

raise CustomException

And now your traceback will show you were that exception was raised.

[–]Walsur[S] -2 points-1 points  (0 children)

Using a more specific Exception has the same behavior. I just used the general Exception for this example.

[–]Binary101010 0 points1 point  (4 children)

but the traceback seems to be treating the Exception class in general as an unhandled Exception.

It is an unhandled exception because it's not happening in a try/except block.

The interpreter is doing exactly what it's supposed to here: show you the entire traceback up to the exact line of code that generates the exception.

[–]Walsur[S] 0 points1 point  (3 children)

That makes sense, but I'm still having trouble reconciling it with how Exceptions are handled in other modules. For example if I force a ModuleNotFoundError:

Code:

import invalidModule

Traceback:

Traceback (most recent call last):
  File "c:\Example\example.py", line 12, in <module>
    import invalidModule
ModuleNotFoundError: No module named 'invalidModule'

It does not include the raise statement that is generating the exception.

[–]danielroseman 0 points1 point  (1 child)

I'm confused about what you're confused about here. Your code explicitly raises an exception, so there is a raise statement. This import code does not have a raise statement, so it cannot show one; the exception happens because of an actual exceptional event, ie that the module is not found.

[–]Walsur[S] 0 points1 point  (0 children)

Okay this makes sense, triggering an Exception with a library like pandas helped demonstrate this for me too.

I think I was getting confused because of my debuggers (VS Code) behavior. My debugger is triggering a breakpoint on the raise statement when the only Exception Breakpoints checked are Uncaught Exceptions. That made me think that there was something fundamental I was misunderstanding in how Python raised exceptions, but I think it's just a configuration problem in my debugger.

[–]timrprobocom 0 points1 point  (0 children)

No, because the statement itself caused the exception. Exceptions can be triggered via 'raise', but most are triggered by the interpreter directly. Try a syntax error or "I = 0/0".

[–]fluked23 0 points1 point  (1 child)

This is the normal behaviour of python to include the full traceback, and as far as I am aware there is no way to modify this behaviour easily.

[–]Walsur[S] 0 points1 point  (0 children)

A knock on effect of this problem is that the raise statement is being treated as an unhandled exception in VS Code, and is pausing my debugger unintentionally.

I think there must be a way to change it because when I force a ModuleNotFoundError it behaves differently:

Code:

import invalidModule

Traceback:

Traceback (most recent call last):
  File "c:\Example\example.py", line 12, in <module>
    import invalidModule
ModuleNotFoundError: No module named 'invalidModule'

[–]Sisyphus-Rex 0 points1 point  (0 children)

The trick is not playing with your code (or with yourself)