So, to rephrase your request,
you want argparse.ArgumentParser.prog to contain "{sys.executable} -m
{modulename}" so that the help text correctly indicates that it was invoked
that way?
PEP notes:
- POSIX only is maybe not a good strategy
- Can cmdline contain other unescaped \0's?
- This would be a sight performance regression for the most common cases.
(__main__.py is typically only used by stdlib modules; which is what it was
added for)
Solutions / workarounds:
- console_script entry_points
- How does pip make this work?
When I run `python -u -m pip -h` it prints `{sys.executable} -m pip` as
the progname under usage (with *optparse*)
- Is this platform portable? Does __package__ always contain the correct
module name?
prog = None
if os.path.basename(sys.argv[0]) == "__main__.py":
prog = f"{sys.executable} -m {__package__}"
...
- thecode.py (prog=...)
- output from `!examplecli -h` and `!python -m example -h`
# Python packaging, setup.py, console_scripts entrypoints, and \_\_main__.py
- Source:
https://github.com/westurner/setuptoolsexample
- Read-only GitHub notebook view:
https://github.com/westurner/setuptoolsexample/blob/master/examplenb.ipynb
- Read-only Nbviewer notebook view:
https://nbviewer.jupyter.org/github/westurner/setuptoolsexample/blob/master/examplenb.ipynb
- Read/Write mybinder.org (BinderHub (repo2docker)) notebook:
https://mybinder.org/v2/gh/westurner/setuptoolsexample/master?filepath=examplenb.ipynb
References:
-
https://python-packaging.readthedocs.io/en/latest/command-line-scripts.html#the-console-scripts-entry-point
```python
!rm -rf ./example.egg-info/
!ls **
```
examplenb.ipynb requirements.txt setup.py
example:
__init__.py __main__.py __pycache__ thecode.py
```python
!cat setup.py
```
from setuptools import setup
setup(name='example',
version='0.1',
description='Example',
url='http://github.com/.../...',
author='Example',
author_email='[email protected]',
license='...',
packages=['example'],
zip_safe=False,
entry_points={
'console_scripts': [
'examplecli = example.thecode:main'
]}
)
```python
!cat example/thecode.py
```
import argparse
import os
import sys
def main(argv=None):
if argv is None:
argv = sys.argv
print("argv:", argv)
prog = None
if os.path.basename(sys.argv[0]) == "__main__.py":
prog = f"{sys.executable} -m {__package__}"
prs = argparse.ArgumentParser(prog=prog)
prs.parse_args()
```python
!cat example/__main__.py
```
import sys
from .thecode import main
def othermain():
print("othermain")
return main(sys.argv)
if __name__ == "__main__":
othermain()
```python
!cat ./requirements.txt
```
-e .
```python
!pip install -r ./requirements.txt
# (this is equivalent to `pip install -e .`)
```
Obtaining file:///home/user/-wrk/-ve37/sexample/src/sexample (from -r
./requirements.txt (line 1))
Installing collected packages: example
Found existing installation: example 0.1
Uninstalling example-0.1:
Successfully uninstalled example-0.1
Running setup.py develop for example
Successfully installed example
```python
!ls **
```
examplenb.ipynb requirements.txt setup.py
example:
__init__.py __main__.py __pycache__ thecode.py
example.egg-info:
dependency_links.txt not-zip-safe SOURCES.txt
entry_points.txt PKG-INFO top_level.txt
```python
# This is generated when you `pip install -e`
!cat ./example.egg-info/entry_points.txt
```
[console_scripts]
examplecli = example.thecode:main
```python
!ls ../../bin/
```
activate jsonschema pip3
activate.csh jupyter pip3.7
activate.fish jupyter-bundlerextension postactivate
activate.ps1 jupyter-kernel postdeactivate
activate_this.py jupyter-kernelspec preactivate
activate.xsh jupyter-migrate predeactivate
easy_install jupyter-nbconvert pygmentize
easy_install-3.7 jupyter-nbextension python
examplecli jupyter-notebook python3
get_env_details jupyter-run python3.7
iptest jupyter-serverextension python-config
iptest3 jupyter-troubleshoot wheel
ipython jupyter-trust
ipython3 pip
```python
!cat ../../bin/examplecli
```
#!/home/user/-wrk/-ve37/sexample/bin/python3
# EASY-INSTALL-ENTRY-SCRIPT: 'example','console_scripts','examplecli'
__requires__ = 'example'
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('example', 'console_scripts', 'examplecli')()
)
```python
# this runs the 'example' console_scripts entry_point
!examplecli -h
```
argv: ['/home/user/-wrk/-ve37/sexample/bin/examplecli', '-h']
usage: examplecli [-h]
optional arguments:
-h, --help show this help message and exit
```python
# this runs example/__main__
!python -m example -h
```
othermain
argv:
['/home/user/-wrk/-ve37/sexample/src/sexample/example/__main__.py', '-h']
usage: /home/user/-wrk/-ve37/sexample/bin/python -m example [-h]
optional arguments:
-h, --help show this help message and exit
On Saturday, August 24, 2019, Michael Hooreman <[email protected]> wrote:
> Indeed.
>
> I usually do a workflow which makes my needs compatible with the
> standards, but I've started this when I was a python beginner.
>
> I'm aware since months that I should use setuptools, but it was not on my
> priority, because that bad decision had no impact. Now, time to change had
> come...
>
> Maybe one suggestion: I think it would be great to document it as a
> limitation in argparse. Don't you think?
>
> Regarding a "patching lib", I don't plan to add anything to pip until I
> found a portable solution. Sorry if my message was incorrect.
>
> Thanks again to everyone.
>
> Le sam. 24 août 2019 23:20, Andrew Barnert <[email protected]> a écrit :
>
>> On Aug 24, 2019, at 13:16, Michael Hooreman <[email protected]> wrote:
>> >
>> > I'll consider what you say, and try to understand what entrypoint is. I
>> have understood entrypoint as a wording for an entry point (main) script.
>> Sorry.
>>
>> Well, it _is_ that, but the point is that setuptools can auto-generate
>> those main scripts for you, without making you rearrange any of your code.
>> Which is very cool, and it’s a shame more people don’t know how to use it.
>>
>> > I just hope that I won't have to spend days on adapting my workflow,
>> but well, that's life.
>>
>> In your case, hours adapting your scripts to munge the usage output might
>> well be a better use of your time than days learning a new tool and
>> adapting your workflow to it.
>>
>> That doesn’t mean we should necessarily change the stdlib of Python to
>> encourage more people to build workflows like yours in the future instead
>> of more easily-manageable ones. But it does mean that if a lot of people
>> are already in your situation, and there’s something that would help all of
>> them, a change could well be worth doing. That’s why I responded
>> specifically to your proposal, and only added the stuff about setuptools
>> entrypoints as an afterthought.
>>
>> Your proposal might well be useful—but not if it breaks all POSIX systems
>> but Linux, gives a prog that isn’t usable as a prog, etc. I raised those
>> issues to see if you have a solution (or if anyone else does).
>>
>> > [Private joke]
>> > I must say that I find disappointing to receive a "tell me what you
>> need, I'll explain how to not need that". We are all IT guys, and I also do
>> that. Now, I see what it is from the other side of the court :-)
>> > [End of private joke]
>>
>> Personally, I generally try to explain “Here’s how to do that” before
>> “Here’s how _not_ to do that.” Especially since “Here’s how to do that”
>> often illustrates what a huge pain it is to do that, which convinces people
>> better than just saying “Don’t do that.” :) But in this case, you already
>> had a solution to offer, so it’s not really the same thing.
>>
>
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/C6XBDK52RBJLLCCTRM5MD4AELMZEJEZJ/
Code of Conduct: http://python.org/psf/codeofconduct/