The docs for the (awesome) Click package suggest a few reasons to use entry points instead of scripts, including

  1. cross-platform compatibility and
  2. avoiding having the interpreter assign __name__ to __main__, which could cause code to be imported twice (if another module imports your script)

Click is a nice way to implement functions for use as entry_points, btw.

Answer from jfeala on Stack Overflow
🌐
PyPA
setuptools.pypa.io › en › latest › userguide › entry_point.html
Entry Points - setuptools 84.0.0 documentation
Instead of this approach using __main__.py, you can also create a user-friendly CLI executable that can be called directly without python -m. In the above example, to create a command hello-world that invokes timmins.hello_world, add a console script entry point to your configuration: ... from setuptools import setup setup( # ..., entry_points={ 'console_scripts': [ 'hello-world = timmins:hello_world', ] } )
Discussions

python - How to set up entry_points in setup.cfg - Stack Overflow
I am moving my config from setup.py to setup.cfg and having issues setting up the entry_points parameter. At the moment I am using a hybrid approach which works, however, I would like to move the More on stackoverflow.com
🌐 stackoverflow.com
entry_points console_scripts not working in setuptools >=69 if `dynamic` is not configured
setuptools version setuptools>=69 Python version 3.8, 3.9, 3.10 OS ubuntu, Windows Additional environment information pyproject.toml: [build-system] requires = ["setuptools>=61.0", "pip >= 23.1", "... More on github.com
🌐 github.com
3
November 27, 2023
Console_scripts entrypoints hidden behind extras are always installed
pip version 21.0.1 Python version 3.9.2 OS Linux Additional information Python is being installed from conda-forge Description According to [1] it should be possible to define console_scripts which are only installed if an extra is speci... More on github.com
🌐 github.com
5
March 22, 2021
package - How to add console_script/entry point to setup.py that call a class function python - Stack Overflow
1 Can I put a Python console script in a submodule? 1 Calling a setuptools entry point from within the library More on stackoverflow.com
🌐 stackoverflow.com
February 27, 2020
🌐
Readthedocs
python-packaging.readthedocs.io › en › latest › command-line-scripts.html
Command Line Scripts — Python Packaging Tutorial
This allows Python functions (not scripts!) to be directly registered as command-line accessible tools. In this case, we’ll add a new file and function to support the command line tool: funniest/ funniest/ __init__.py command_line.py ... setup.py ... The command_line.py submodule exists only to service the command line tool (which is a convenient organization method): ... setup( ... entry_points = { 'console_scripts': ['funniest-joke=funniest.command_line:main'], } ...
🌐
Python Packaging
packaging.python.org › specifications › entry-points
Entry points specification - Python Packaging User Guide
Conceptually, an entry point is defined by three required properties: The group that an entry point belongs to indicates what sort of object it provides. For instance, the group console_scripts is for entry points referring to functions which can be used as a command, while pygments.styles ...
🌐
Tpleyer
blog.tpleyer.de › posts › 2017-12-13-Python-setup-py-entry-points.html
Python setup.py entry points - Tobi's blog
December 13, 2017 - entry_points = {'console_scripts': ['pytest=pytest:main']} Console scripts is a builtin feature of setuptools and will create a script on the system’s path with the name of the left hand side of the equal sign and make it a wrapper around the specified Python object (which should be callable).
🌐
Rachum
amir.rachum.com › amir rachum's blog › python entry points explained
Python Entry Points Explained - Amir Rachum's
July 28, 2017 - console_scripts, they say, is a special type of entry point. setuptools reads its content as "<console script name> = <python object path>" and creates an appropriate script when your package is installed.
🌐
Bouzekri
blog.bouzekri.net › 2017-10-08-bundle-cli-in-python-project.html
Bundle a CLI script in your Python project | Jonathan Bouzekri’s blog
October 8, 2017 - Each console script is defined with a string that follows this convention : <name of the executable> = <fullpath to a python module>:<name of the function to execute in the module> An example is worth all explanation, let’s continue to work with our previous basicpythonpackage: $ cat >setup.py <<EOL from setuptools import setup, find_packages setup( name="basicpythonpackage", version="0.1", packages=find_packages(), entry_points={ 'console_scripts': [ 'foo = basicpythonpackage.foo:my_func' ] } ) EOL $ cat >basicpythonpackage/foo.py <<EOL import sys def my_func(*args, **kwargs): print('args', args) print('kwargs', kwargs) EOL $ pip install .
🌐
Benjamintoll
benjamintoll.com › 2021 › 04 › 04 › on-python-entry_points
On Python entry_points - benjamintoll.com
April 4, 2021 - Instead of writing a custom module for your super-duper app that locates and loads third-party extensions and scripts at runtime, leverage the native tooling that Python offers to do this (primarily via the pkg_resources module), with minimum fuss and overhead on your end. As long as the group name is exposed in the third-party packages, the tooling will find them and automatically load them at runtime. Here is an example of the entry_points section of a setup.cfg file for a third-party extension: entry_points = [console_scripts] saddle = saddle:main [foo.extension] saddle = saddle
Find elsewhere
🌐
DEV Community
dev.to › demianbrecht › entry-points-in-python-34i3
Entry points in Python - DEV Community
April 9, 2020 - The observant reader will notice that as we're configuring the entry point to be a callable, this is now something that can easily be unit tested directly. The magic sauce that allows us to run the script directly looks a little different than the treatment it gets from scripts: # EASY-INSTALL-ENTRY-SCRIPT: 'hello-world','console_scripts','hello_world' __requires__ = 'hello-world' import re import sys from pkg_resources import load_entry_point if __name__ == '__main__': sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) sys.exit( load_entry_point('hello-world', 'console_scripts', 'hello_world')() )
🌐
Chriswarrick
chriswarrick.com › blog › 2014 › 09 › 15 › python-apps-the-right-way-entry_points-and-scripts
Python Apps the Right Way: entry points and scripts | Chris Warrick
September 15, 2014 - You must use setuptools, otherwise this won’t work. The most important piece of code is the entry_points declaration (unsurprisingly). ... If you are developing a GUI application (in Tkinter, PyQt/PySide, wxPython, PyGTK, PyGame…), you should change the declaration to gui_scripts. On *nix, this makes no difference, but on Windows, it means that running your script by opening the created .exe files does not show a console window.
🌐
ACM@UIUC TIL
til.acm.illinois.edu › python › setuptools_entry_points
Setuptools entry points | ACM@UIUC TIL
April 29, 2025 - When building a Python package, the most common way of exposing a command line interface (CLI) is to use the “scripts” keyword in setup.py. # file: setup.py from setuptools import setup setup( name='my_package', scripts=['scripts/my_cli'], ... ) However, entry points represents an improved and cross-platform approach to accomplish the same goal. # file: setup.py from setuptools import setup setup( name='my_package', entry_points={ 'console_scripts': [ 'my_cli': 'my_package.cli:main' ] }, ...
🌐
GitHub
github.com › pypa › setuptools › issues › 4141
entry_points console_scripts not working in setuptools >=69 if `dynamic` is not configured · Issue #4141 · pypa/setuptools
November 27, 2023 - from pathlib import Path from setuptools import setup class Setup: def __init__(self): self.conf = {} self.conf["entry_points"] = {"console_scripts": []} for exe in Path.cwd().glob("src/**/exe/*.py"): exe = exe.relative_to(Path.cwd()) if exe.name == "__init__.py": continue final_exe = ("-".join(x.replace("_", "-") for x in exe.parent.parent.parts[1:]) + "-" + exe.stem.replace("_", "-")) module = ".".join(exe.parent.parts[1:]) + "." + exe.stem self.conf["entry_points"]["console_scripts"].append( f"{final_exe} = {module}:main" ) def __str__(self): return str(self.conf) def __call__(self): setup(**self.conf) SETUP = Setup() if __name__ == "__main__": SETUP()
Author: pypa
🌐
GitHub
github.com › pypa › pip › issues › 9726
Console_scripts entrypoints hidden behind extras are always installed · Issue #9726 · pypa/pip
March 22, 2021 - According to [1] it should be possible to define console_scripts which are only installed if an extra is specified, i.e. example = example:my_func [bar]. Despite this is seems that pip ignores the section in square brackets and always installs all console_scripts. [1] https://packaging.python.org/specifications/entry-points/ The example command is only installed if the example package is installed with pip install .[bar]. ... $ cat pyproject.toml [build-system] requires = ["setuptools>=42", "wheel"] build-backend = "setuptools.build_meta" $ cat setup.cfg [metadata] name = example version = 1.0.1 [options] package_dir= =src packages=example [options.packages.find] where=src [options.entry_points] console_scripts = example = example:my_func [bar] $ cat src/example/__init__.py print("Hello world") def my_func(): print("Hello again!")
Author: pypa
🌐
Stack Overflow
stackoverflow.com › questions › 64878592 › how-to-set-the-bin-scripts-entry-point-in-setup-py
python - How to set the bin scripts entry point in `setup.py`? - Stack Overflow
.py is not needed if you want to follow this approach, just write python setup( # ... entry_points={'console_scripts': ['mycode=mycode.main:main']} # ... ) mycode folder will have a main.py with main function being called at entry this way! Better practice would be to create a setup.cfg file for config 2020-11-17T16:16:00.367Z+00:00 ·
🌐
Upmf-grenoble
transit.iut2.upmf-grenoble.fr › doc › python-setuptools-doc › html › userguide › entry_point.html
Entry Points — Python documentation
The syntax used for specifying the entry points remains the same as for console/GUI scripts, and is discussed in the last section. ... The recommended approach for loading and importing entry points is the importlib.metadata module, which is a part of the standard library since Python 3.8.
🌐
Xebia
xebia.com › home › blog › a practical guide to using setup.py
A Practical Guide To Using Setup.py | Xebia
June 22, 2026 - How do I create a command-line tool from my Python package using setup.py? Use the entry_points parameter with console_scripts to expose a function as a command-line command. For example: `entry_points={'console_scripts': ['my-command=examp...
Top answer
1 of 3
6

If there is not a good reason to not do so, I would definitely advocate a spin on option 3. As @jonsharp mentions, breaking up your utility into clean units of functionality is a good way to ensure testability. Even the smallest scripts can eventually morph into a much larger program and making sure that you have an extensible API sooner rather than later will alleviate much headache down the road.

The way I'd approach this is:

  1. Break up your code into logical methods with clean and clear I/O
  2. Add unit tests. Having them is never a bad thing.
  3. Rather than using if __name__ == '__main__', create a main() (or similar) method containing your entry point
  4. Use setuptool's setup() function to define the script entry point in your setup.py file.

For example:

from setuptools import setup
setup(
    name='mypackage',
    version='0.1',
    entry_points={
        'console_scripts': [ 'myscript = mypackage.mymodule:main' ],
    }
)

Now, not only is all of your code (including main()) is easily unit testable, but you can still have your console entry point once you've done a python setup.py install|develop.

Any validation I do using, say, ArgumentParser.add_mutually_exclusive_group will not apply when my tool is run as a library instead of a command line script.

Depending on how your API is designed, you may need to add some extra validation to input parameters, but that should likely be there to prevent unexpected input anyways.

Edit: The only time I would use generally use subprocess is when I'm calling into a non-Python application or another Python script that I don't own or have the time to refactor, but the latter only being as a last resort. Most well-written Python utilities will expose both command line utilities and internal API.

2 of 3
3

My guess is that you can pursue #3 by way of #2 and that it won't require anything close to a total refactor. Often in these situations, you just need to make a few adjustments at the entry points and (sometimes) exit points.

  • If needed, wrap your current script in a main() function.

    def main(args, stdin = None):
        if stdin is None:
            stdin = sys.stdin
    
        # full script here
    
    if __name__ == '__main__':
        main(sys.argv[1:])
    
  • That function should take a list of strings. When you parse command-line options, operate on args, not sys.argv.

  • Similarly, adjust other parts of your script to avoid direct operations on sys.stdin and related streams. Instead, use the arguments passed to your main() function.

Programatic users (as opposed to command-line users) will pass in a list of strings and any open file handles (or iterables) they want the code to use. Later, if needed, you can make the programatic use more natural by writing a function that knows how to convert typical positional and keyword-style arguments into the list of strings that your option parser needs.

🌐
JetBrains
youtrack.jetbrains.com › issue › PY-25219
setuptools entry_points/console_scripts with docker-compose
December 27, 2021 - Our website uses some cookies and records your IP address for the purposes of accessibility, security, and managing your access to the telecommunication network. You can disable data collection and cookies by changing your browser settings, but it may affect how this website functions.