# Why do script entrypoints require a function be specified?

**URL:** <https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090>\
**Category:** Packaging\
**Created:** [March 6, 2022, 7:08pm UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090 "2022-03-06T19:08:57Z")\
**Posts on this page:** 19\
**Page:** 1

<div class="post-metadata">

**Author:** ![LewisGaul](https://avatars.discourse-cdn.com/v4/letter/l/bc79bd/32.png) [@LewisGaul](https://discuss.python.org/u/LewisGaul)\
**Post date:** [March 6, 2022, 7:08pm UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/1 "2022-03-06T19:08:58Z")

</div>

The [entrypoints spec](https://packaging.python.org/en/latest/specifications/entry-points/) describes entrypoints as so:

> The **object reference** points to a Python object. It is either in the form `importable.module` , or `importable.module:object.attr`.

where for scripts

> [@](#):
>
> The object reference points to a function which will be called with no arguments when this command is run. The function may return an integer to be used as a process exit code, and returning `None` is equivalent to returning `0`.
> 
> For instance, the entry point `mycmd = mymod:main` would create a command `mycmd` launching a script like this:
> 
> ```python
> import sys
> from mymod import main
> sys.exit(main())
> 
> ```

My question is: **why must a function be given, rather than just executing a module/package?**

I have an entrypoint that looks like `minegauler = minegauler.app. __main__ :main`, which is normally run with `python -m minegauler.app` - why can’t I just specify `minegauler = minegauler.app`?

I’m assuming this case could be handled fairly easily using something like the following?

```python
import runpy
runpy.run_module("minegauler.app", run_name=" __main__")

```

---

<div class="post-metadata">

**Author:** ![CAM-Gerlach](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/cam-gerlach/32/3688_2.png) [@CAM-Gerlach](https://discuss.python.org/u/CAM-Gerlach)\
**Post date:** [March 6, 2022, 7:46pm UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/2 "2022-03-06T19:46:24Z")

</div>

Just for others’ reference, this concern was [originally raised](https://discuss.python.org/t/help-testing-experimental-features-in-setuptools/13821/24) on the [PEP 621 Setuptools testing thread](https://discuss.python.org/t/help-testing-experimental-features-in-setuptools/13821), and discussed at some length there, which readers here might want to peruse first, particularly @abravalheri 's [detailed response](https://discuss.python.org/t/help-testing-experimental-features-in-setuptools/13821/30) giving some additional background on the current situation.

In any case, at least to my naive analysis, it would seem quite possible and desirable, either via a specification update PR or a short PEP (which I’m happy to help with), to modify this section to allow the behavior implied above (i.e. that of `runpy`)—specifically, if a module is specified, execute it as a script with ` __name__ == " __main__"`, and if a package, execute ` __main__ ,py` inside of it. This would simplify and, thusly, encourage a consistent best-practice approach to declaring a package’s main entrypoint, if it has one; namely, for package `spam`,

```ini
[project.scripts]
spam-cli = "spam"

```

instead of the following,

```ini
[project.scripts]
spam-cli = "spam. __main__.main"

```

for an entry point that would work equally by executing it via `spam-cli` and via `python -m spam`.

@pf_moore what do you think about this?

---

<div class="post-metadata">

**Author:** ![kpfleming](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/kpfleming/32/2761_2.png) [@kpfleming](https://discuss.python.org/u/kpfleming)\
**Post date:** [March 7, 2022, 11:22am UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/3 "2022-03-07T11:22:54Z")

</div>

You might need to edit that post to move the last bit of text out of the preformatted block 🙂

---

<div class="post-metadata">

**Author:** ![CAM-Gerlach](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/cam-gerlach/32/3688_2.png) [@CAM-Gerlach](https://discuss.python.org/u/CAM-Gerlach)\
**Post date:** [March 7, 2022, 8:02pm UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/4 "2022-03-07T20:02:57Z")

</div>

Oops, thanks! Not sure how I missed seeing that; I usually carefully check the preview and/or the posted output for such mistakes.

Pinging @pf_moore again in case he didn’t see it before due to that.

---

<div class="post-metadata">

**Author:** ![pf\_moore](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pf_moore/32/35_2.png) [@pf\_moore](https://discuss.python.org/u/pf_moore)\
**Post date:** [March 7, 2022, 8:56pm UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/5 "2022-03-07T20:56:42Z")

</div>

Not sure why you’re asking me in particular, but I have no real opinion on the matter here. Allowing a module seems reasonable, given that it’s allowable for other types of entry point, but I’ve no idea what the trade-offs are.

The script usage was, as far as I’m aware, originated by setuptools, so I’d look into the history of that feature to see if there’s any indication of why it doesn’t handle modules (maybe it’s just because runpy wasn’t available then?)

---

<div class="post-metadata">

**Author:** ![CAM-Gerlach](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/cam-gerlach/32/3688_2.png) [@CAM-Gerlach](https://discuss.python.org/u/CAM-Gerlach)\
**Post date:** [March 7, 2022, 9:35pm UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/6 "2022-03-07T21:35:51Z")

</div>

> [@pf\_moore](#):
>
> Not sure why you’re asking me in particular

Because you’re the package metadata czar who would presumably be PEP-Delegate for any such PEP on this, and it would involve a change to several of the various specs you currently administer, alongside your role with `pip` 😄

---

<div class="post-metadata">

**Author:** ![abravalheri](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/abravalheri/32/4990_2.png) [@abravalheri](https://discuss.python.org/u/abravalheri)\
**Post date:** [March 7, 2022, 10:09pm UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/7 "2022-03-07T22:09:43Z")

</div>

Do we need a PEP for that?

The text in the [living standard](https://packaging.python.org/en/latest/specifications/entry-points/) is not explicit about disallowing the `:obj.attr` part of an entry-point string from being omitted in this scenario.  
Moreover, in the end of the day, this change would be backward-compatible right?

If someone is interested in pushing this behaviour forward, I think they could do:

1. Coordinate with `pip` and `distlib` maintainers if they are supportive of this change.
2. Create a PR in `distlib` implementing this new behaviour (I am assuming `pip` uses `distlib` to creating the script wrappers)
3. Wait until this is released in `distlib` and vendored back into `pip`
4. Submit a “clarification” PR to the living standard.

---

<div class="post-metadata">

**Author:** ![pf\_moore](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pf_moore/32/35_2.png) [@pf\_moore](https://discuss.python.org/u/pf_moore)\
**Post date:** [March 7, 2022, 11:22pm UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/8 "2022-03-07T23:22:32Z")

</div>

> [@abravalheri](#):
>
> If someone is interested in pushing this behaviour forward, I think they could do:
> 
> 1. Coordinate with `pip` and `distlib` maintainers if they are supportive of this change.
> 2. Create a PR in `distlib` implementing this new behaviour (I am assuming `pip` uses `distlib` to creating the script wrappers)
> 3. Wait until this is released in `distlib` and vendored back into `pip`
> 4. Submit a “clarification” PR to the living standard.

Precisely this, although whoever wants to do this should also co-ordinate with, and create a PR for, `installer`. And I can confirm that pip just calls distlib here, so there would be no pip change needed (apart from maybe adding a test to ensure that the new-style script specifications work as intended).

Personally, I think this seems like it would be a fair bit of work for a relatively minor benefit, so I wonder whether it’s really a good use of the community’s time? 🤷

---

<div class="post-metadata">

**Author:** ![CAM-Gerlach](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/cam-gerlach/32/3688_2.png) [@CAM-Gerlach](https://discuss.python.org/u/CAM-Gerlach)\
**Post date:** [March 7, 2022, 11:52pm UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/9 "2022-03-07T23:52:40Z")

</div>

Thanks; that’s the other main reason I was calling upon you in that capacity, which I meant to go back here and clarify, was to your take on whether this should just be a spec update, or require a PEP.

It’s certainly non-trivial, but while the benefit is not huge, neither seems to be the work, so long as there are a few motivated people to share the load. The clarification PR would be a cinch for me, and I could try to help with the implementation, given some pointers as to what code would need to be modified and any specific guidance as to how, and perhaps @LewisGaul could too?

@vsajip / @vsajip1 and @pradyunsg , what are your thoughts on this?

---

<div class="post-metadata">

**Author:** ![LewisGaul](https://avatars.discourse-cdn.com/v4/letter/l/bc79bd/32.png) [@LewisGaul](https://discuss.python.org/u/LewisGaul)\
**Post date:** [March 8, 2022, 12:08am UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/10 "2022-03-08T00:08:04Z")

</div>

I’m happy to help out with the implementation/testing/docs if someone can point in the right direction, it might be a nice way for me to gain some familiarity with the packaging projects.

I can understand the perspective that it’s only a small gain, but from a user’s perspective this just seems like an obvious “why isn’t this supported” quality of life thing, so I’m definitely in favour of it being done.

---

<div class="post-metadata">

**Author:** ![itcarroll](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/itcarroll/32/18244_2.png) [@itcarroll](https://discuss.python.org/u/itcarroll)\
**Post date:** [March 1, 2024, 12:49am UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/11 "2024-03-01T00:49:28Z")

</div>

This discussion looks like it was really going somewhere … did it? I still seem to need `abc. __main__ :main` as an entry point, which requires an `if __name__ == " __main__"` in ` __main__.py`. This seems like an obvious artifact of previous “best practices” that would _mainly_ be nice to tidy up. Where else can I throw encouragement?

---

<div class="post-metadata">

**Author:** ![merwok](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/merwok/32/29_2.png) [@merwok](https://discuss.python.org/u/merwok)\
**Post date:** [March 1, 2024, 1:48am UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/12 "2024-03-01T01:48:32Z")

</div>

That doesn’t seem accurate. We just have two mecanisms for the script need.

A module with a block `if __name__ == ' __main__':` provides a way to execute it as a script, either because the file is installed as a script or it’s a module and people invoke it with `python3 -m mod`. But if it’s only installed as a script, or is a ` __main__ ` module in a package, then the check can be omitted.  
[edit: this is indeed recommended in the docs: [\_\_main\_\_ — Top-level code environment — Python 3.12.2 documentation](https://docs.python.org/3/library/ __main__.html#id1) ]

Entry-points are a more complicated system, with executable wrapping on windows, that require a function definition but no ` __name__ ` check.

---

<div class="post-metadata">

**Author:** ![fungi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/fungi/32/5963_2.png) [@fungi](https://discuss.python.org/u/fungi)\
**Post date:** [March 1, 2024, 2:48am UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/13 "2024-03-01T02:48:56Z")

</div>

If you install the resulting package into a venv, you’ll find a  
separate entrypoint script in its bin subdirectory which can be  
executed directly to call the function identified in your entrypoint  
configuration. That is independent of and does not require the name  
conditional in your .py file.

---

<div class="post-metadata">

**Author:** ![itcarroll](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/itcarroll/32/18244_2.png) [@itcarroll](https://discuss.python.org/u/itcarroll)\
**Post date:** [April 4, 2024, 9:06pm UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/14 "2024-04-04T21:06:46Z")

</div>

@merwok I am sorry to have not replied! I guess I don’t get notifications here. I was brought back to this thread though by recent comments on [pypa/setuptools#1995](https://github.com/pypa/setuptools/issues/1995).

I think I was not clear. The `[project.scripts]` table of a `pyproject.yaml` is what I believe this thread was about modifying. Currently, you cannot get away with `project.scripts."spam-cli" = spam` for a project that contains a `spam/ __main__.py` module. This module would not need to define any functions nor need the `if __name__ == " __main__"` block. I know this block also isn’t needed when you call `python -m spam`, but I’m interested in the tooling/ability to automatically generate that `spam-cli` script.

Over in setuptools, @abravalheri, indicated this isn’t just a tooling change. I’m obviously neither involved in core development, standards, or setuptools … just thought this was a good idea that needed a second hearing.

---

<div class="post-metadata">

**Author:** ![kpfleming](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/kpfleming/32/2761_2.png) [@kpfleming](https://discuss.python.org/u/kpfleming)\
**Post date:** [April 7, 2024, 10:16am UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/15 "2024-04-07T10:16:11Z")

</div>

I suspect you meant `pyproject.toml` 🙂

Your proposal gives special meaning to a module named ` __main__.py`, but there doesn’t seem to be any need for that since ` __init__.py` already has special meaning when a package is imported. If the package is intended to be an application, and not a library, then ` __init__.py` could be the actual ‘startup’ code for the application.

---

<div class="post-metadata">

**Author:** ![pf\_moore](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pf_moore/32/35_2.png) [@pf\_moore](https://discuss.python.org/u/pf_moore)\
**Post date:** [April 7, 2024, 11:16am UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/16 "2024-04-07T11:16:33Z")

</div>

> [@kpfleming](#):
>
> Your proposal gives special meaning to a module named ` __main__.py`

` __main__.py` is already special - see [the documentation](https://docs.python.org/3.12/using/cmdline.html#interface-options) (specifically, the discussion of the `<script>` argument) and [the documentation of the `runpy.run_module` function](https://docs.python.org/3.12/library/runpy.html#runpy.run_module).

The proposal is that

```python
[project.scripts]
foo_cmd = foo

```

will create an entry point that’s equivalent to running `python -m foo`.

The specific specification change would be to [this text](https://packaging.python.org/en/latest/specifications/entry-points/#use-for-scripts)

> The object reference points to a function which will be called with no arguments when this command is run.

which would need to be amended to say

> The object reference points to a function which will be called with no arguments when this command is run, or a module which will be run as if invoked via `python -m module`.

The spec change would require a change to any code that currently creates entry point wrappers - that’s at least [`distlib`](https://github.com/pypa/distlib/blob/master/distlib/scripts.py#L224) (which would handle pip as well), [`installer`](https://github.com/pypa/installer/blob/main/src/installer/scripts.py#L93), and [`uv`](https://github.com/astral-sh/uv/blob/main/crates/install-wheel-rs/src/wheel.rs#L45), and possibly others. It might also need changes to build backends to allow the module-only form of the script specification - the [`pyproject.toml` specification for entry points](https://packaging.python.org/en/latest/specifications/pyproject-toml/#entry-points) doesn’t prohibit the module form for scripts, but backends might do so, given that it’s not currently possible to generate a script wrapper for that form.

It mostly just needs someone to do the work to (a) create PRs implementing this proposal, both to the spec and to the installers, and (b) collect agreement to the change from installers and any affected backends, as evidence that the proposal change is acceptable.

---

<div class="post-metadata">

**Author:** ![b11c](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/b11c/32/2350_2.png) [@b11c](https://discuss.python.org/u/b11c)\
**Post date:** [August 7, 2025, 9:04am UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/17 "2025-08-07T09:04:05Z")

</div>

Just out of curiosity, has there been any movement on this proposal?

---

<div class="post-metadata">

**Author:** ![pf\_moore](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pf_moore/32/35_2.png) [@pf\_moore](https://discuss.python.org/u/pf_moore)\
**Post date:** [August 7, 2025, 9:23am UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/18 "2025-08-07T09:23:35Z")

</div>

Not to my knowledge. Someone needs to do the work of writing PRs, collecting feedback to confirm that this change is acceptable to the community etc. No-one seems to have been motivated enough to do that work so far.

---

<div class="post-metadata">

**Author:** ![itcarroll](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/itcarroll/32/18244_2.png) [@itcarroll](https://discuss.python.org/u/itcarroll)\
**Post date:** [February 2, 2026, 4:15am UTC](https://discuss.python.org/t/why-do-script-entrypoints-require-a-function-be-specified/14090/19 "2026-02-02T04:15:27Z")

</div>

Though a somebody with no connection to PyPA, I’ve started an attempt to do the work in [pypa/distlib#257](https://github.com/pypa/distlib/issues/257). Comments appreciated.
