From where is your main function being called? That is what really matters. In general, no, wirting sys.exit() and return are not the same thing. But yes, putting anything in a function after a return statement is completely useless and that code will never be executed. Answer from MegaIng on discuss.python.org
Discussions

What is the best way to exit a function (which has no return value) in python before the function ends (e.g. a check fails)? - Stack Overflow
Returning nothing is the same as returning None in Python. ... Sign up to request clarification or add additional context in comments. ... return doesn't work, if i set a = method() , inside method i use return, it still keep running code behind a. exit should be like php exit(), it breaks ... More on stackoverflow.com
🌐 stackoverflow.com
Is it ever OK to manually call __exit__?
No, that's not usual. I suspect that the reason they've done this is that there's a lot of cleanup code in that method that they want to call manually in some cases. But if so they should extract that into a separate method and call it from both places. More on reddit.com
🌐 r/learnpython
7
11
June 1, 2023
exit - Python, __exit__ method should it use return False or no return at all - Stack Overflow
What's the preferred way and why for __exit__ method when it comes to returning value? Should I write return False or no return at all since that will return None, I think behaviour in both cases w... More on stackoverflow.com
🌐 stackoverflow.com
Whether to use return or break
I would use "return" if I want to make clear that the code is not only breaking out of the loop but that it as well ends the function. More on reddit.com
🌐 r/learnpython
7
1
April 4, 2021
🌐
Python.org
discuss.python.org › ideas
Return statement outside function could do same as exit function - Ideas - Discussions on Python.org
May 11, 2021 - statement return x outside a function could have same effect as exit(x) This would be intuitive, because exit makes program end its execution and return value same way like return makes function end its execution and return value.
🌐
Quora
quora.com › What-is-the-difference-between-return-and-exit-in-a-function
What is the difference between “return” and “exit” in a function? - Quora
After exit () all memory and temporary storage areas are all flushed out and control goes out of program. In contrast the return statement is used to return from a function and return control to the calling function.
🌐
Reddit
reddit.com › r/learnpython › is it ever ok to manually call __exit__?
r/learnpython on Reddit: Is it ever OK to manually call __exit__?
June 1, 2023 -

I saw this following in some python code from a friend:

class Foo:
  #...
  def _exit(self):
    self.__exit__(None, None, None)
  #...

#...

foo._exit()

I thought that __exit__() was always supposed to be used in conjunction with the 'with' statement. Is my friend's code using a common practice?

Top answer
1 of 5
11
The __enter__ and __exit__ methods in Python are special methods that are utilized in the concept of context management. This concept revolves around the proper handling of resources such as files or network connections, ensuring they're used efficiently and cleaned up after usage. When you see a Python with statement, you're seeing a context manager in action. It's using the __enter__ and __exit__ methods to manage resources. The __enter__ method is automatically called at the beginning of the with block. Here, resources can be set up and then returned to be used within the with block. The __exit__ method is called at the end of the *with block, even if an exception occurs within the block. This method takes care of cleanup duties, such as closing a file or a network connection, ensuring no loose ends are left open. Here's a simple example using a file: class ManagedFile: def __init__(self, filename): self.filename = filename def __enter__(self): self.file = open(self.filename, 'r') return self.file def __exit__(self, exc_type, exc_val, exc_tb): if self.file: self.file.close() with ManagedFile('hello.txt') as f: content = f.read() print(content) In this case, when Python executes the with statement, it calls ManagedFile.__enter__, which opens the file and returns it. After the block of code under with is executed, ManagedFile.__exit__ is called, which takes care of closing the file, even if an exception occurred inside the block. I hope this helps clarify the role of __enter__ and __exit__ in context management.
2 of 5
8
No, that's not usual. I suspect that the reason they've done this is that there's a lot of cleanup code in that method that they want to call manually in some cases. But if so they should extract that into a separate method and call it from both places.
Find elsewhere
🌐
Delft Stack
delftstack.com › home › howto › python › python exit function
How to Exit a Function in Python | Delft Stack
February 2, 2024 - If it matches, we print the value of the name variable and then exit the function; otherwise, if the string doesn’t match, we will simply exit it without doing anything. Here, you might think that since there is no return statement written in the code, there is no return statement present.
Top answer
1 of 2
9

Your doing it in the past way is probably fine for quick& dirty scripts or for mini tools used by other developers. There’s often no need to be fancy. On the contrary, Python’s default traceback output can be an appropriate or even desired form of error reporting. A script falling off its end exits with code 0, an unhandled exception exits the script with a non-zero code, so you’re covered there, too:

def main() -> None:
    # here would be code raising exceptions on error
    # note the `None` return type

if __name__ == '__main__':
    main()

When you need something more fancy working with sys.exit() explicitly is a good idea. That’s basically your future approach. But don’t only think of scripts called directly, also think of installed packages. Setuptools has a cross-platform mechanism to define functions as entry points for scripts. If you have this in your setup.py:

setuptools.setup(
        ...
        entry_points={
            'console_scripts': ['your_script=your_package.your_module:main'],
    },
)

and install that package you can run your_script from the command line.

A console_scripts function must be callable without any arguments. So, as long as all parameters have default values you’re fine. For longer running scripts consider explicitly handling KeyboardInterrupt to print a nice message when the user aborts with Ctrl+C. In the end the relevant part of your your_package/your_module.py might look something like this:

def main(cli_args: List[str] = None) -> int:
    """
    `cli_args` makes it possible to call this function command-line-style
    from other Python code without touching sys.argv.
    """
    try:
        # Parsing with `argparse` and additional processing 
        # is usually lenghty enough to extract into separate functions.
        raw_config = _parse_cli(
            sys.argv[1:] if cli_args is None else cli_args)
        config = _validate_and_sanitize(raw_config)

        # Same exception raising idea as the simple approach
        do_real_work(config.foo, config.bar)

    except KeyboardInterrupt:
        print('Aborted manually.', file=sys.stderr)
        return 1

    except Exception as err:
        # (in real code the `except` would probably be less broad)
        # Turn exceptions into appropriate logs and/or console output.

        # non-zero return code to signal error
        # Can of course be more fine grained than this general
        # "something went wrong" code.
        return 1

    return 0  # success

# __main__ support is still here to make this file executable without
# installing the package first.
if __name__ == '__main__':
    sys.exit(main())
2 of 2
1

I have worked on this template and use-case a bit more and here's the more refined structure for files containing a main function that I currently use:

#!/usr/bin/env python3

import sys
from argparse import ArgumentParser, Namespace
from typing import Dict, List

import yaml  # just used as an example here for loading more configs, optional


def parse_arguments(cli_args: List[str] = None) -> Namespace:
    parser = ArgumentParser()
    # parser.add_argument()
    # ...
    return parser.parse_args(args=cli_args)  # None defaults to sys.argv[1:]


def load_configs(args: Namespace) -> Dict:
    try:
        with open(args.config_path, 'r') as file_pointer:
            config = yaml.safe_load(file_pointer)

        # arrange and check configs here

        return config
    except Exception as err:
        # log errors
        print(err)
        if err == "Really Bad":
            raise err

        # potentionally return some sane fallback defaults if desired/reasonable
        sane_defaults = []
        return sane_defaults


def main(args: Namespace = parse_arguments()) -> int:
    try:
        # maybe load some additional config files here or in a function called here
        # e.g. args contains a path to a config folder; or use sane defaults
        # if the config files are missing(if that is your desired behavior)
        config = load_configs(args)
        do_real_work(args, config)

    except KeyboardInterrupt:
        print("Aborted manually.", file=sys.stderr)
        return 1

    except Exception as err:
        # (in real code the `except` would probably be less broad)
        # Turn exceptions into appropriate logs and/or console output.

        # log err
        print("An unhandled exception crashed the application!", err)

        # non-zero return code to signal error
        # Can of course be more fine grained than this general
        # "something went wrong" code.
        return 1

    return 0  # success


# __main__ support is still here to make this file executable without
# installing the package first.
if __name__ == "__main__":
    sys.exit(main(parse_arguments()))

Having the parse_arguments function makes integration tests much more readable, as you then can just call that function to generate the desired namespace object for you, using the same string you'd use on the cli. Then as suggested in the accepted answer handle errors to give the output you'd want and pass the arguments to the function(s) doing the work. I also load and arrange configs in this context, as necessary.

🌐
Python
docs.python.org › 3 › library › contextlib.html
contextlib — Utilities for with-statement contexts — Python ...
An abstract base class for classes that implement __enter__() and __exit__(). A default implementation for __enter__() is provided which returns self while __exit__() is an abstract method which by default returns None.
🌐
Scaler
scaler.com › home › topics › exit() in python
exit() in Python - Scaler Topics
May 4, 2023 - It does not return anything and exits the process with status n, without calling cleanup handlers, flushing stdio buffers, etc. The sys.exit() function is responsible for throwing the SystemExit exception. To avoid being unintentionally caught by code that catches the exception, it inherits from BaseException rather than the exception. This enables the exception to ascend and results in the interpreter quitting correctly. The Python interpreter terminates if the exception is not handled, but no stack traceback is displayed.
Top answer
1 of 7
694

Using these magic methods (__enter__, __exit__) allows you to implement objects which can be used easily with the with statement.

The idea is that it makes it easy to build code which needs some 'cleandown' code executed (think of it as a try-finally block). Some more explanation here.

A useful example could be a database connection object (which then automagically closes the connection once the corresponding 'with'-statement goes out of scope):

class DatabaseConnection(object):

    def __enter__(self):
        # make a database connection and return it
        ...
        return self.dbconn

    def __exit__(self, exc_type, exc_val, exc_tb):
        # make sure the dbconnection gets closed
        self.dbconn.close()
        ...

As explained above, use this object with the with statement (you may need to do from __future__ import with_statement at the top of the file if you're on Python 2.5).

with DatabaseConnection() as mydbconn:
    # do stuff

PEP343 -- The 'with' statement' has a nice writeup as well.

2 of 7
150

If you know what context managers are then you need nothing more to understand __enter__ and __exit__ magic methods. Lets see a very simple example.

In this example I am opening the myfile.txt file with help of open function. The try/finally block ensures that even if an unexpected exception occurs myfile.txt will be closed.

fp=open(r"C:\Users\SharpEl\Desktop\myfile.txt")
try:
    for line in fp:
        print(line)
finally:
    fp.close()

Now I am opening same file with with statement:

with open(r"C:\Users\SharpEl\Desktop\myfile.txt") as fp:
    for line in fp:
        print(line) 

If you look at the code, I didn't close the file & there is no try/finally block. Because with statement automatically closes myfile.txt . You can even check it by calling print(fp.closed) attribute -- which returns True.

This is because the file objects (fp in my example) returned by open function has two built-in methods __enter__ and __exit__. It is also known as context manager. __enter__ method is called at the start of with block and __exit__ method is called at the end.

Note: with statement only works with objects that support the context management protocol (i.e. they have __enter__ and __exit__ methods). A class which implement both methods is known as context manager class.

Now lets define our own context manager class.

 class Log:
    def __init__(self,filename):
        self.filename=filename
        self.fp=None    
    def logging(self,text):
        self.fp.write(text+'\n')
    def __enter__(self):
        print("__enter__")
        self.fp=open(self.filename,"a+")
        return self    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("__exit__")
        self.fp.close()

with Log(r"C:\Users\SharpEl\Desktop\myfile.txt") as logfile:
    print("Main")
    logfile.logging("Test1")
    logfile.logging("Test2")

I hope now you have basic understanding of both __enter__ and __exit__ magic methods.

Top answer
1 of 4
1006

Let me give some information on them:

  1. quit() simply raises the SystemExit exception.

Furthermore, if you print it, it will give a message:

    >>> print (quit)
    Use quit() or Ctrl-Z plus Return to exit
    >>>

This functionality was included to help people who do not know Python. After all, one of the most likely things a newbie will try to exit Python is typing in quit.

Nevertheless, quit should not be used in production code. This is because it only works if the site module is loaded. Instead, this function should only be used in the interpreter.

  1. exit() is an alias for quit (or vice-versa). They exist together simply to make Python more user-friendly.

Furthermore, it too gives a message when printed:

    >>> print (exit)
    Use exit() or Ctrl-Z plus Return to exit
    >>>

However, like quit, exit is considered bad to use in production code and should be reserved for use in the interpreter. This is because it too relies on the site module.

  1. sys.exit() also raises the SystemExit exception. This means that it is the same as quit and exit in that respect.

Unlike those two however, sys.exit is considered good to use in production code. This is because the sys module will always be there.

  1. os._exit() exits the program without calling cleanup handlers, flushing stdio buffers, etc. Thus, it is not a standard way to exit and should only be used in special cases. The most common of these is in the child process(es) created by os.fork.

Note that, of the four methods given, only this one is unique in what it does.

Summed up, all four methods exit the program. However, the first two are considered bad to use in production code and the last is a non-standard, dirty way that is only used in special scenarios. So, if you want to exit a program normally, go with the third method: sys.exit.


Or, even better in my opinion, you can just do directly what sys.exit does behind the scenes and run:

raise SystemExit

This way, you do not need to import sys first.

However, this choice is simply one on style and is purely up to you.

2 of 4
156

The functions* quit(), exit(), and sys.exit() function in the same way: they raise the SystemExit exception. So there is no real difference, except that sys.exit() is always available but exit() and quit() are only available if the site module is imported (docs).

The os._exit() function is special, it exits immediately without calling any cleanup functions (it doesn't flush buffers, for example). This is designed for highly specialized use cases... basically, only in the child after an os.fork() call.

Conclusion

  • Use exit() or quit() in the REPL.

  • Use sys.exit() in scripts, or raise SystemExit() if you prefer.

  • Use os._exit() for child processes to exit after a call to os.fork().

All of these can be called without arguments, or you can specify the exit status, e.g., exit(1) or raise SystemExit(1) to exit with status 1. Note that portable programs are limited to exit status codes in the range 0-255, if you raise SystemExit(256) on many systems this will get truncated and your process will actually exit with status 0.

Footnotes

* Actually, quit() and exit() are callable instance objects, but I think it's okay to call them functions.

🌐
Notes
henryleach.com › 2025 › 02 › controlling-python-exit-codes-and-shell-scripts
Controlling Python Exit Codes and Shell Scripts - Henry Leach
February 9, 2025 - You can make the return from Python something that's not an integer, like a string, but the shell will interpret that as a 1. One final warning: out of range exit codes are interpreted as modulo 256, that mean after 255, you start going round again.
🌐
DigitalOcean
digitalocean.com › community › tutorials › how-to-use-break-continue-and-pass-statements-when-working-with-loops-in-python-3
How to Break Out of Multiple Loops in Python | DigitalOcean
August 7, 2025 - The break statement in Python always exits only the innermost loop in which it is used. To exit from multiple levels of nesting, you need to use: A flag to signal the outer loop. A function with return.
🌐
Reddit
reddit.com › r/python › is sys.exit() bad practice?
Is sys.exit() bad practice? : r/Python
February 13, 2018 - Well if you use exit or sys.exit in a library that's on you. If you have main in your in code, sys.exit is useful when a module raises a exception. After exit I can use $_ to check the exit code, which return and exit won't do.
🌐
freeCodeCamp
freecodecamp.org › news › python-exit-how-to-use-an-exit-function-in-python-to-stop-a-program
Python Exit – How to Use an Exit Function in Python to Stop a Program
June 5, 2023 - By Shittu Olumide The exit() function in Python is used to exit or terminate the current running script or program. You can use it to stop the execution of the program at any point. When the exit() function is called, the program will immediately sto...