Why do script entrypoints require a function be specified?

The entrypoints spec 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

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?

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

Just for others’ reference, this concern was originally raised on the PEP 621 Setuptools testing thread, and discussed at some length there, which readers here might want to peruse first, particularly @abravalheri 's detailed response 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,

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

instead of the following,

[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?

4 Likes

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

1 Like

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.

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?)

1 Like

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 :smile:

Do we need a PEP for that?

The text in the living standard 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.
2 Likes

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? :person_shrugging:

2 Likes

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?

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.

2 Likes