The program name (referred to as prog_name or info_name) is the name Click uses in usage help text. If it's not specified, Click tries to detect it by looking at sys.argv[0].
|
if prog_name is None: |
|
prog_name = make_str( |
|
os.path.basename(sys.argv[0] if sys.argv else __file__) |
|
) |
This works when the CLI is run as a script, either directly, or indirectly as an entry point, or as a module when the program is a single file, but fails when the program is a package and run with python -m. In that case, Python converts the -m name to the Python file within the package that would be executed. (The use of __file__ there is also incorrect, as that will always point to click/core.py. There should never be a case where sys.argv is empty. make_str is probably not needed either.)
This leads to the following names:
python example.py, executing a script directly - example.py
python -m example, the single file module example.py - example.py
python -m example, a package like example/__main__.py - __main__.py
python -m example.cli, a submodule like example/cli.py - cli.py
example, an entry point, with any of the layouts above - example
What's usually expected is that executing a script will print the Python file name (example.py), executing an entry point will print the entry point name (example), and executing a module or package will print the Python command (python -m example, python -m example.cli)
This leads to projects that expect to be run as either an entry point or -m writing their own wrapper around the CLI to control what name gets used. For example, Flask uses a wrapper that the entry point will call with no args, while __main__.py or __name__ == "__main__" will pass as_module=True.
def main(as_module=False):
cli.main(prog_name="python -m flask" if as_module else None)
Unfortunately, detecting whether python -m was used is really, really complicated. Luckily, I did the work already in Werkzeug's reloader: https://github.com/pallets/werkzeug/blob/102bcda52176db448d383a9ef1ccf7e406a379eb/src/werkzeug/_reloader.py#L59. It won't be quite the same in Click because we're not concerned with the exact path to the python command or script file, or with arguments, but that should be a good start. Write a function detect_program_name() to detect this and call it in place of the existing code.
The program name (referred to as
prog_nameorinfo_name) is the name Click uses in usage help text. If it's not specified, Click tries to detect it by looking atsys.argv[0].click/src/click/core.py
Lines 797 to 800 in dfb842d
This works when the CLI is run as a script, either directly, or indirectly as an entry point, or as a module when the program is a single file, but fails when the program is a package and run with
python -m. In that case, Python converts the-m nameto the Python file within the package that would be executed. (The use of__file__there is also incorrect, as that will always point toclick/core.py. There should never be a case wheresys.argvis empty.make_stris probably not needed either.)This leads to the following names:
python example.py, executing a script directly -example.pypython -m example, the single file moduleexample.py-example.pypython -m example, a package likeexample/__main__.py-__main__.pypython -m example.cli, a submodule likeexample/cli.py-cli.pyexample, an entry point, with any of the layouts above -exampleWhat's usually expected is that executing a script will print the Python file name (
example.py), executing an entry point will print the entry point name (example), and executing a module or package will print the Python command (python -m example,python -m example.cli)This leads to projects that expect to be run as either an entry point or
-mwriting their own wrapper around the CLI to control what name gets used. For example, Flask uses a wrapper that the entry point will call with no args, while__main__.pyor__name__ == "__main__"will passas_module=True.Unfortunately, detecting whether
python -mwas used is really, really complicated. Luckily, I did the work already in Werkzeug's reloader: https://github.com/pallets/werkzeug/blob/102bcda52176db448d383a9ef1ccf7e406a379eb/src/werkzeug/_reloader.py#L59. It won't be quite the same in Click because we're not concerned with the exact path to thepythoncommand or script file, or with arguments, but that should be a good start. Write a functiondetect_program_name()to detect this and call it in place of the existing code.