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
Is it ever OK to manually call __exit__?
exit - Python, __exit__ method should it use return False or no return at all - Stack Overflow
Whether to use return or break
Videos
You could simply use
return
which does exactly the same as
return None
Your function will also return None if execution reaches the end of the function body without hitting a return statement. Returning nothing is the same as returning None in Python.
I would suggest:
def foo(element):
do something
if not check: return
do more (because check was succesful)
do much much more...
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?
def function():
while True:
result = do_a_thing()
if result == "all done":
returnOr
def function():
while True:
result = do_a_thing()
if result == "all done":
break?
They both have the same outcome. Would one be faster than the other? Which would the reader prefer?
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())
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.
0 and 1 are the exit codes.
exit(0) means a clean exit without any errors / problems
exit(1) means there was some issue / error / problem and that is why the program is exiting.
This is not Python specific and is pretty common. A non-zero exit code is treated as an abnormal exit, and at times, the error code indicates what the problem was. A zero error code means a successful exit.
This is useful for other programs, shell, caller etc. to know what happened with your program and proceed accordingly.
This determines the exit status of the program when it finishes running (generally, 0 for success and 1 for error).
It is not unique to Python, and the exact effect depends on your operating system and how the program is called (though 99% of the time, if you're just running Python scripts, it doesn't matter).
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.
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.
Let me give some information on them:
quit()simply raises theSystemExitexception.
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.
exit()is an alias forquit(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.
sys.exit()also raises theSystemExitexception. This means that it is the same asquitandexitin 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.
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 byos.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.
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()orquit()in the REPL.Use
sys.exit()in scripts, orraise SystemExit()if you prefer.Use
os._exit()for child processes to exit after a call toos.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.