Just to give a full picture of what megazord.py would look like, using @Jeffrey Harris suggestion to use a nice library for parsing the inputs.

import argparse

def main():
    ''' Example of taking inputs for megazord bin'''
    parser = argparse.ArgumentParser(prog='my_megazord_program')
    parser.add_argument('-i', nargs='?', help='help for -i blah')
    parser.add_argument('-d', nargs='?', help='help for -d blah')
    parser.add_argument('-v', nargs='?', help='help for -v blah')
    parser.add_argument('-w', nargs='?', help='help for -w blah')

    args = parser.parse_args()

    collected_inputs = {'i': args.i,
                    'd': args.d,
                    'v': args.v,
                    'w': args.w}
    print 'got input: ', collected_inputs 

And with using it like in the above, one would get

$ megazord -i input -d database -v xx-xx -w yy-yy
got input: {'i': 'input', 'd': 'database', 'w': 'yy-yy', 'v': 'xx-xx'}

And since they are all optional arguments,

$ megazord
got input: {'i': None, 'd': None, 'w': None, 'v': None}
Answer from HeyWatchThis 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', ] } )
๐ŸŒ
Readthedocs
python-packaging.readthedocs.io โ€บ en โ€บ latest โ€บ command-line-scripts.html
Command Line Scripts โ€” Python Packaging Tutorial
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'], } ... ) Again, once the package has been installed, we can use it in the same way. Setuptools will generate a standalone script โ€˜shimโ€™ which imports your module and calls the registered function.
Discussions

python - Setuptools not passing arguments for entry_points - Stack Overflow
The setuptools console_scripts entry point wants a function of no arguments. Happily, optparse (Parser for command line options) doesn't need to be passed any arguments, it will read in sys.argv[1:] and use that as it's input. ... Side note that as of Python 2.7, optparse is depracated in favor ... More on stackoverflow.com
๐ŸŒ stackoverflow.com
May 20, 2014
setuptools - Explain Python entry points? - Stack Overflow
An "entry point" is typically a ... of your Python package might want to use, though a non-callable object can be supplied as an entry point as well (as correctly pointed out in the comments!). The most popular kind of entry point is the console_scripts entry point, which points to a function that you want made available as a command-line tool to whoever installs your package. This goes into your setup.py script ... More on stackoverflow.com
๐ŸŒ stackoverflow.com
argparse - Preferred way to expand a command line script to be used as a library in Python - Software Engineering Stack Exchange
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 ... More on softwareengineering.stackexchange.com
๐ŸŒ softwareengineering.stackexchange.com
March 18, 2015
python - How to add an entry_point/console script to setup.py that uses arguments - Stack Overflow
I want to add an entry point to setup.py and pass in an argument. Is that possible, and if so how? I've tried, and looked up documentation, but can not figure out how to do it. Example: Package fo... More on stackoverflow.com
๐ŸŒ stackoverflow.com
๐ŸŒ
Python Packaging
packaging.python.org โ€บ specifications โ€บ entry-points
Entry points specification - Python Packaging User Guide
[console_scripts] foo = foomod:main ... system shell after the package is installed. The object reference points to a function which will be called with no arguments when this command is run....
๐ŸŒ
DEV Community
dev.to โ€บ demianbrecht โ€บ entry-points-in-python-34i3
Entry points in Python - DEV Community
April 9, 2020 - This is achieved by specifying a callable entry points in the setuptools config: Noting that we're back to the original directory structure: $ tree . . โ”œโ”€โ”€ hello_world.py โ””โ”€โ”€ setup.py
๐ŸŒ
DEV Community
dev.to โ€บ thefern โ€บ argparse-and-script-entry-points-4cde
Part 4 Argparse and Script Entry Points - DEV Community
August 4, 2019 - You donโ€™t have to worry about calling a wrong argument, argparse will handle that nicely. Documentation for entry points. You can have multiple entry points, but we will only use one. ... entry_points={ 'console_scripts': [ # command = package.module:function 'my_cli = another_package.another_package:say_hello', ], }, At this point we can uninstall our current utility since weโ€™ve modified setup.py: pip3 uninstall another-package cd git/another-package python3 setup.py develop
๐ŸŒ
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 - The main() function must not take any arguments, because thatโ€™s how entry_points executes things. This is the real deal: create the entry points in your setup.py file.
Find elsewhere
Top answer
1 of 3
297

An "entry point" is typically a function (or other callable function-like object) that a developer or user of your Python package might want to use, though a non-callable object can be supplied as an entry point as well (as correctly pointed out in the comments!).

The most popular kind of entry point is the console_scripts entry point, which points to a function that you want made available as a command-line tool to whoever installs your package. This goes into your setup.py script like:

entry_points={
    'console_scripts': [
        'cursive = cursive.tools.cmd:cursive_command',
    ],
},

I have a package I've just deployed called cursive.tools, and I wanted it to make available a "cursive" command that someone could run from the command line, like:

$ cursive --help
usage: cursive ...

The way to do this is define a function, like maybe a cursive_command function in the file cursive/tools/cmd.py that looks like:

def cursive_command():
    args = sys.argv[1:]
    if len(args) < 1:
        print "usage: ..."

and so forth; it should assume that it's been called from the command line, parse the arguments that the user has provided, and ... well, do whatever the command is designed to do.

Install the docutils package for a great example of entry-point use: it will install something like a half-dozen useful commands for converting Python documentation to other formats.

2 of 3
234

EntryPoints provide a persistent, filesystem-based object name registration and name-based direct object import mechanism (implemented by the setuptools package).

They associate names of Python objects with free-form identifiers. So any other code using the same Python installation and knowing the identifier can access an object with the associated name, no matter where the object is defined. The associated names can be any names existing in a Python module; for example name of a class, function or variable. The entry point mechanism does not care what the name refers to, as long as it is importable.

As an example, let's use (the name of) a function, and an imaginary python module with a fully-qualified name 'myns.mypkg.mymodule':

def the_function():
   "function whose name is 'the_function', in 'mymodule' module"
   print "hello from the_function"

Entry points are registered via an entry points declaration in setup.py. To register the_function under entrypoint called 'my_ep_func':

    entry_points = {
        'my_ep_group_id': [
            'my_ep_func = myns.mypkg.mymodule:the_function'
        ]
    },

As the example shows, entry points are grouped; there's corresponding API to look up all entry points belonging to a group (example below).

Upon a package installation (ie. running 'python setup.py install'), the above declaration is parsed by setuptools. It then writes the parsed information in special file. After that, the pkg_resources API (part of setuptools) can be used to look up the entry point and access the object(s) with the associated name(s):

import pkg_resources

named_objects = {}
for ep in pkg_resources.iter_entry_points(group='my_ep_group_id'):
   named_objects.update({ep.name: ep.load()})

Here, setuptools read the entry point information that was written in special files. It found the entry point, imported the module (myns.mypkg.mymodule), and retrieved the_function defined there, upon call to pkg_resources.load().

Calling the_function would then be simple:

>>> named_objects'my_ep_func'
hello from the_function

Thus, while perhaps a bit difficult to grasp at first, the entry point mechanism is actually quite simple to use. It provides an useful tool for pluggable Python software development.

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.

๐ŸŒ
Pylonsproject
docs.pylonsproject.org โ€บ projects โ€บ pylons-webframework โ€บ en โ€บ latest โ€บ advanced_pylons โ€บ entry_points_and_plugins.html
Using Entry Points to Write Plugins โ€” Pylons Framework 1.0.2 documentation
There are lots of other arguments such as author_email or url which you can add to the setup.py function too. We are interested in adding the entry points. We need to decide on a group name for the entry points. It is traditional to use the name of the package using the entry point, separated by a .
๐ŸŒ
Codefellows
codefellows.github.io โ€บ sea-python-401d4 โ€บ lectures โ€บ python_packaging_1.html
An Introduction to Python Packaging โ€” Python 401 2.1 documentation
In computer science, we refer to the functions in our that users interact with directly as entry points. They are the doorways that provide access to our code. In setup.py we can specify entry points to our code. The argument is called entry_points, and the value is a dict.
๐ŸŒ
Rachum
amir.rachum.com โ€บ amir rachum's blog โ€บ python entry points explained
Python Entry Points Explained - Amir Rachum's
July 28, 2017 - The call to pkg_resources.iter_entry_points('snek_types') iterates over all the entry points that were registered under the name "snek_types". So, external packages can define an entry point called "snek_types" in their setup.py, and snek will dynamically load it at runtime.
๐ŸŒ
GitHub
github.com โ€บ ninjaaron โ€บ fast-entry_points
GitHub - ninjaaron/fast-entry_points: make entry_points specified in setup.py load more quickly.
This monkey-patches setuptools.command.easy_install.ScriptWriter.get_args() in the background, which in turn produces simple entry scripts (like the one above) when you install the package. If you install fastentrypoints as a module, you have the fastep executable, which will copy fastentrypoints.py into the working directory (or into a list of directories you give it as arguments) and append include fastentrypoints.py to the MANIFEST.in file, and add an import statement to setup.py.
Starred by 122 users
Forked by 18 users
Languages: Python 100.0% | Python 100.0%
๐ŸŒ
Stack Overflow
stackoverflow.com โ€บ questions โ€บ 58426719 โ€บ building-a-python-cli-how-to-properly-declare-entry-points-in-setuptools-setup
Building a Python CLI: how to properly declare entry_points in setuptools.setup - Stack Overflow
The entry_points will call your main directly - that is the last main in 'rsearch=rsearch.__main__:main'. This means that the code that calls the args parser never gets called, and hence you get the problem with the arguments never being passed in.
๐ŸŒ
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 - When pytest is run, it will parse and import every entry point registered for its group and feed it with data via a hook mechanism based on certain events happening during program execution. # The example taken from pytest's documentation # sample ./setup.py file from setuptools import setup setup( name="myproject", packages = ['myproject'] # the following makes a plugin available to pytest entry_points = { 'pytest11': [ 'name_of_plugin = myproject.pluginmodule', ] }, # custom PyPI classifier for pytest plugins classifiers=[ "Framework :: Pytest", ], )
๐ŸŒ
GitHub
github.com โ€บ Pylons โ€บ pylons โ€บ blob โ€บ master โ€บ pylons โ€บ docs โ€บ en โ€บ advanced_pylons โ€บ entry_points_and_plugins.rst
pylons/pylons/docs/en/advanced_pylons/entry_points_and_plugins.rst at master ยท Pylons/pylons
An entry point is a Python object in a project's code that is identified by a string in the project's setup.py file. The entry point is referenced by a group and a name so that the object may be discoverable.
Author: Pylons
๐ŸŒ
Reddit
reddit.com โ€บ r/learnpython โ€บ how to specify entry points in "modern" python packages?
r/learnpython on Reddit: How to specify entry points in "modern" Python packages?
February 6, 2025 -

Instead of using the entry_points argument to setuptools.setup(), I'm as of recently supposed to put the file entry_points.txt into the .dist-info directory. But I don't understand how to do that because that directory gets automatically created during the package installation.

[EDIT]

I found that I have to make a [project.scripts] section in pyproject.toml, which upon installation creates the appropriate .dist-info/entry_points.txt. But it still doesn't install the actual callable command.

[EDIT 2]

I found that I made a totally stupid mistake and my executable ended up being named console_scripts. Please don't answer any more. I've downvoted my own OP.