Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IPython raises AttributeError when sys.exit() is called within a try:except block #13796

Open
davidrs06 opened this issue Oct 25, 2022 · 1 comment

Comments

@davidrs06
Copy link

When running the following code in IPython (version 8.3.0 with python 3.9.7)

import sys
def test_exit():
    for i in range(2,-2,-1):
        try:
            print(1/i)
        except ZeroDivisionError:
            sys.exit("ERROR: tried to divide by zero, exiting script.")

test_exit()

I get the following output, which I guess is not expected

Output
0.5
1.0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Input In [1], in test_exit()
      4 try:
----> 5     print(1/i)
      6 except ZeroDivisionError:

ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

SystemExit                                Traceback (most recent call last)
    [... skipping hidden 1 frame]

Input In [1], in <cell line: 9>()
      7             sys.exit("ERROR: tried to divide by zero, exiting script.")
----> 9 test_exit()

Input In [1], in test_exit()
      6 except ZeroDivisionError:
----> 7     sys.exit("ERROR: tried to divide by zero, exiting script.")

SystemExit: ERROR: tried to divide by zero, exiting script.

During handling of the above exception, another exception occurred:

AssertionError                            Traceback (most recent call last)
    [... skipping hidden 1 frame]

File ~/Software/anaconda3/envs/code_testing/lib/python3.9/site-packages/IPython/core/interactiveshell.py:1982, in InteractiveShell.showtraceback(self, exc_tuple, filename, tb_offset, exception_only, running_compiled_code)
   1979 if exception_only:
   1980     stb = ['An exception has occurred, use %tb to see '
   1981            'the full traceback.\n']
-> 1982     stb.extend(self.InteractiveTB.get_exception_only(etype,
   1983                                                      value))
   1984 else:
   1985     try:
   1986         # Exception classes can customise their traceback - we
   1987         # use this in IPython.parallel for exceptions occurring
   1988         # in the engines. This should return a list of strings.

File ~/Software/anaconda3/envs/code_testing/lib/python3.9/site-packages/IPython/core/ultratb.py:585, in ListTB.get_exception_only(self, etype, value)
    577 def get_exception_only(self, etype, value):
    578     """Only print the exception type and message, without a traceback.
    579 
    580     Parameters
   (...)
    583     value : exception value
    584     """
--> 585     return ListTB.structured_traceback(self, etype, value)

File ~/Software/anaconda3/envs/code_testing/lib/python3.9/site-packages/IPython/core/ultratb.py:443, in ListTB.structured_traceback(self, etype, evalue, etb, tb_offset, context)
    440     chained_exc_ids.add(id(exception[1]))
    441     chained_exceptions_tb_offset = 0
    442     out_list = (
--> 443         self.structured_traceback(
    444             etype, evalue, (etb, chained_exc_ids),
    445             chained_exceptions_tb_offset, context)
    446         + chained_exception_message
    447         + out_list)
    449 return out_list

File ~/Software/anaconda3/envs/code_testing/lib/python3.9/site-packages/IPython/core/ultratb.py:1118, in AutoFormattedTB.structured_traceback(self, etype, value, tb, tb_offset, number_of_lines_of_context)
   1116 else:
   1117     self.tb = tb
-> 1118 return FormattedTB.structured_traceback(
   1119     self, etype, value, tb, tb_offset, number_of_lines_of_context)

File ~/Software/anaconda3/envs/code_testing/lib/python3.9/site-packages/IPython/core/ultratb.py:1012, in FormattedTB.structured_traceback(self, etype, value, tb, tb_offset, number_of_lines_of_context)
   1009 mode = self.mode
   1010 if mode in self.verbose_modes:
   1011     # Verbose modes need a full traceback
-> 1012     return VerboseTB.structured_traceback(
   1013         self, etype, value, tb, tb_offset, number_of_lines_of_context
   1014     )
   1015 elif mode == 'Minimal':
   1016     return ListTB.get_exception_only(self, etype, value)

File ~/Software/anaconda3/envs/code_testing/lib/python3.9/site-packages/IPython/core/ultratb.py:865, in VerboseTB.structured_traceback(self, etype, evalue, etb, tb_offset, number_of_lines_of_context)
    856 def structured_traceback(
    857     self,
    858     etype: type,
   (...)
    862     number_of_lines_of_context: int = 5,
    863 ):
    864     """Return a nice text document describing the traceback."""
--> 865     formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
    866                                                            tb_offset)
    868     colors = self.Colors  # just a shorthand + quicker name lookup
    869     colorsnormal = colors.Normal  # used a lot

File ~/Software/anaconda3/envs/code_testing/lib/python3.9/site-packages/IPython/core/ultratb.py:799, in VerboseTB.format_exception_as_a_whole(self, etype, evalue, etb, number_of_lines_of_context, tb_offset)
    796 assert isinstance(tb_offset, int)
    797 head = self.prepare_header(etype, self.long_header)
    798 records = (
--> 799     self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else []
    800 )
    802 frames = []
    803 skipped = 0

File ~/Software/anaconda3/envs/code_testing/lib/python3.9/site-packages/IPython/core/ultratb.py:854, in VerboseTB.get_records(self, etb, number_of_lines_of_context, tb_offset)
    848     formatter = None
    849 options = stack_data.Options(
    850     before=before,
    851     after=after,
    852     pygments_formatter=formatter,
    853 )
--> 854 return list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]

File ~/Software/anaconda3/envs/code_testing/lib/python3.9/site-packages/stack_data/core.py:546, in FrameInfo.stack_data(cls, frame_or_tb, options, collapse_repeated_frames)
    530 @classmethod
    531 def stack_data(
    532         cls,
   (...)
    536         collapse_repeated_frames: bool = True
    537 ) -> Iterator[Union['FrameInfo', RepeatedFrames]]:
    538     """
    539     An iterator of FrameInfo and RepeatedFrames objects representing
    540     a full traceback or stack. Similar consecutive frames are collapsed into RepeatedFrames
   (...)
    544     and optionally an Options object to configure.
    545     """
--> 546     stack = list(iter_stack(frame_or_tb))
    548     # Reverse the stack from a frame so that it's in the same order
    549     # as the order from a traceback, which is the order of a printed
    550     # traceback when read top to bottom (most recent call last)
    551     if is_frame(frame_or_tb):

File ~/Software/anaconda3/envs/code_testing/lib/python3.9/site-packages/stack_data/utils.py:98, in iter_stack(frame_or_tb)
     96 while frame_or_tb:
     97     yield frame_or_tb
---> 98     if is_frame(frame_or_tb):
     99         frame_or_tb = frame_or_tb.f_back
    100     else:

File ~/Software/anaconda3/envs/code_testing/lib/python3.9/site-packages/stack_data/utils.py:91, in is_frame(frame_or_tb)
     90 def is_frame(frame_or_tb: Union[FrameType, TracebackType]) -> bool:
---> 91     assert_(isinstance(frame_or_tb, (types.FrameType, types.TracebackType)))
     92     return isinstance(frame_or_tb, (types.FrameType,))

File ~/Software/anaconda3/envs/code_testing/lib/python3.9/site-packages/stack_data/utils.py:172, in assert_(condition, error)
    170 if isinstance(error, str):
    171     error = AssertionError(error)
--> 172 raise error

AssertionError: 

Would anyone know what is the issue and how it could be solved ? I'm expecting this script to print the error message when i reaches 0, and simply interrupting the execution. I do get what I want if I use the following code instead:

import sys
def test_exit():
    for i in range(2,-2,-1):
        if i == 0:
            sys.exit("ERROR: tried to divide by zero, exiting script.")
        else:
            print(1/i)

test_exit()

Thanks in advance !

@davidrs06
Copy link
Author

Actually the issue might be related to the argument given to sys.exit(). I get a TypeError: object of type 'NoneType' has no len()if I call sys.exit() or sys.exit(1). However, the following code, which assigns the ZeroDivisionError to a variable to be printed inside the exceptblock works:

import sys
def test_exit():
    for i in range(2,-2,-1):
        try:
            print(1/i)
        except ZeroDivisionError as msg:
            sys.exit(msg)

test_exit()

Output:

0.5
1.0
An exception has occurred, use %tb to see the full traceback.

SystemExit: division by zero

/home/david/Software/anaconda3/lib/python3.9/site-packages/IPython/core/interactiveshell.py:3452: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.
  warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)

However, if I convert the msg variable to a string to append more information, things don't work anymore:

import sys
def test_exit():
    for i in range(2,-2,-1):
        try:
            print(1/i)
        except ZeroDivisionError as msg:
            sys.exit(str(msg)+'. Script stopped because of ZeroDivisionError. Please check your code to avoid zero division.')

test_exit()    

Is this something expected ? I could just print the complementary information before calling sys.exit(msg), but you might want to explore this in more detail if it turns out to be a a real issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant