gh-90300: split --help output into separate options (#30331)

Make --help output shorter and add new help options.

--help-env, --help-xoptions and --help-all command-line options are
added to complement --help.
This commit is contained in:
Éric 2022-06-01 05:50:01 -04:00 committed by GitHub
parent 132e563703
commit 8aa9d40b00
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 211 additions and 81 deletions

View file

@ -183,6 +183,8 @@ automatically enabled, if available on your platform (see
Automatic enabling of tab-completion and history editing.
.. _using-on-generic-options:
Generic options
~~~~~~~~~~~~~~~
@ -190,8 +192,28 @@ Generic options
-h
--help
Print a short description of all command line options.
Print a short description of all command line options and corresponding
environment variables and exit.
.. cmdoption:: --help-env
Print a short description of Python-specific environment variables
and exit.
.. versionadded:: 3.11
.. cmdoption:: --help-xoptions
Print a description of implementation-specific :option:`-X` options
and exit.
.. versionadded:: 3.11
.. cmdoption:: --help-all
Print complete usage information and exit.
.. versionadded:: 3.11
.. cmdoption:: -V
--version
@ -212,6 +234,7 @@ Generic options
.. versionadded:: 3.6
The ``-VV`` option.
.. _using-on-misc-options:
Miscellaneous options
@ -460,6 +483,7 @@ Miscellaneous options
See :ref:`warning-filter` and :ref:`describing-warning-filters` for more
details.
.. cmdoption:: -x
Skip the first line of the source, allowing use of non-Unix forms of
@ -553,6 +577,7 @@ Miscellaneous options
The ``-X frozen_modules`` option.
Options you shouldn't use
~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -25,16 +25,47 @@ def _kill_python_and_exit_code(p):
returncode = p.wait()
return data, returncode
class CmdLineTest(unittest.TestCase):
def test_directories(self):
assert_python_failure('.')
assert_python_failure('< .')
def verify_valid_flag(self, cmd_line):
rc, out, err = assert_python_ok(*cmd_line)
rc, out, err = assert_python_ok(cmd_line)
self.assertTrue(out == b'' or out.endswith(b'\n'))
self.assertNotIn(b'Traceback', out)
self.assertNotIn(b'Traceback', err)
return out
def test_help(self):
self.verify_valid_flag('-h')
self.verify_valid_flag('-?')
out = self.verify_valid_flag('--help')
lines = out.splitlines()
self.assertIn(b'usage', lines[0])
self.assertNotIn(b'PYTHONHOME', out)
self.assertNotIn(b'-X dev', out)
self.assertLess(len(lines), 50)
def test_help_env(self):
out = self.verify_valid_flag('--help-env')
self.assertIn(b'PYTHONHOME', out)
def test_help_xoptions(self):
out = self.verify_valid_flag('--help-xoptions')
self.assertIn(b'-X dev', out)
def test_help_all(self):
out = self.verify_valid_flag('--help-all')
lines = out.splitlines()
self.assertIn(b'usage', lines[0])
self.assertIn(b'PYTHONHOME', out)
self.assertIn(b'-X dev', out)
# The first line contains the program name,
# but the rest should be ASCII-only
b''.join(lines[1:]).decode('ascii')
def test_optimize(self):
self.verify_valid_flag('-O')
@ -43,14 +74,6 @@ def test_optimize(self):
def test_site_flag(self):
self.verify_valid_flag('-S')
def test_usage(self):
rc, out, err = assert_python_ok('-h')
lines = out.splitlines()
self.assertIn(b'usage', lines[0])
# The first line contains the program name,
# but the rest should be ASCII-only
b''.join(lines[1:]).decode('ascii')
def test_version(self):
version = ('Python %d.%d' % sys.version_info[:2]).encode("ascii")
for switch in '-V', '--version', '-VV':
@ -90,7 +113,7 @@ def get_xoptions(*args):
def test_unknown_xoptions(self):
rc, out, err = assert_python_failure('-X', 'blech')
self.assertIn(b'Unknown value for option -X', err)
msg = b'Fatal Python error: Unknown value for option -X'
msg = b'Fatal Python error: Unknown value for option -X (see --help-xoptions)'
self.assertEqual(err.splitlines().count(msg), 1)
self.assertEqual(b'', out)
@ -134,7 +157,6 @@ def test_xoption_frozen_modules(self):
}
for raw, expected in tests:
cmd = ['-X', f'frozen_modules{raw}',
#'-c', 'import os; print(os.__spec__.loader.__name__, end="")']
'-c', 'import os; print(os.__spec__.loader, end="")']
with self.subTest(raw):
res = assert_python_ok(*cmd)
@ -167,7 +189,6 @@ def test_relativedir_bug46421(self):
# Test `python -m unittest` with a relative directory beginning with ./
# Note: We have to switch to the project's top module's directory, as per
# the python unittest wiki. We will switch back when we are done.
defaultwd = os.getcwd()
projectlibpath = os.path.dirname(__file__).removesuffix("test")
with os_helper.change_cwd(projectlibpath):
# Testing with and without ./
@ -247,7 +268,6 @@ def test_invalid_utf8_arg(self):
#
# Test with default config, in the C locale, in the Python UTF-8 Mode.
code = 'import sys, os; s=os.fsencode(sys.argv[1]); print(ascii(s))'
base_cmd = [sys.executable, '-c', code]
def run_default(arg):
cmd = [sys.executable, '-c', code, arg]
@ -892,6 +912,7 @@ def test_sys_flags_not_set(self):
PYTHONSAFEPATH="1",
)
class SyntaxErrorTests(unittest.TestCase):
def check_string(self, code):
proc = subprocess.run([sys.executable, "-"], input=code,

View file

@ -0,0 +1,3 @@
Make ``--help`` output shorter by moving some info to the new
``--help-env`` and ``--help-xoptions`` command-line options.
Also add ``--help-all`` option to print complete usage.

View file

@ -84,6 +84,19 @@ python \- an interpreted, interactive, object-oriented programming language
|
.I never
]
.br
[
.B \--help
]
[
.B \--help-env
]
[
.B \--help-xoptions
]
[
.B \--help-all
]
.br
[
.B \-c
@ -149,6 +162,16 @@ the behavior of the interpreter.
.B \-h ", " \-? ", "\-\-help
Prints the usage for the interpreter executable and exits.
.TP
.B "\-\-help\-env"
Prints help about Python-specific environment variables and exits.
.TP
.B "\-\-help\-xoptions"
Prints help about implementation-specific \fB\-X\fP options and exits.
.TP
.TP
.B "\-\-help\-all"
Prints complete usage information and exits.
.TP
.B \-i
When a script is passed as first argument or the \fB\-c\fP option is
used, enter interactive mode after executing the script or the
@ -287,7 +310,7 @@ a regular expression on the warning message.
.TP
.BI "\-X " option
Set implementation specific option. The following options are available:
Set implementation-specific option. The following options are available:
-X faulthandler: enable faulthandler
@ -332,7 +355,7 @@ Set implementation specific option. The following options are available:
files are desired as well as suppressing the extra visual location indicators
when the interpreter displays tracebacks.
-X frozen_modules=[on|off]: whether or not frozen modules should be used
-X frozen_modules=[on|off]: whether or not frozen modules should be used.
The default is "on" (or "off" if you are running a local build).
.TP

View file

@ -44,8 +44,12 @@ static const wchar_t *opt_ptr = L"";
#define SHORT_OPTS L"bBc:dEhiIJm:OPqRsStuvVW:xX:?"
static const _PyOS_LongOption longopts[] = {
/* name, has_arg, val (used in switch in initconfig.c) */
{L"check-hash-based-pycs", 1, 0},
{NULL, 0, 0},
{L"help-all", 0, 1},
{L"help-env", 0, 2},
{L"help-xoptions", 0, 3},
{NULL, 0, -1}, /* sentinel */
};

View file

@ -28,9 +28,10 @@
static const char usage_line[] =
"usage: %ls [option] ... [-c cmd | -m mod | file | -] [arg] ...\n";
/* Long usage message, split into parts < 512 bytes */
static const char usage_1[] = "\
Options and arguments (and corresponding environment variables):\n\
/* Long help message */
/* Lines sorted by option name; keep in sync with usage_envvars* below */
static const char usage_help[] = "\
Options (and corresponding environment variables):\n\
-b : issue warnings about str(bytes_instance), str(bytearray_instance)\n\
and comparing bytes/bytearray with str. (-bb: issue errors)\n\
-B : don't write .pyc files on import; also PYTHONDONTWRITEBYTECODE=x\n\
@ -39,8 +40,6 @@ Options and arguments (and corresponding environment variables):\n\
debug builds); also PYTHONDEBUG=x\n\
-E : ignore PYTHON* environment variables (such as PYTHONPATH)\n\
-h : print this help message and exit (also -? or --help)\n\
";
static const char usage_2[] = "\
-i : inspect interactively after running script; forces a prompt even\n\
if stdin does not appear to be a terminal; also PYTHONINSPECT=x\n\
-I : isolate Python from the user's environment (implies -E and -s)\n\
@ -53,8 +52,6 @@ static const char usage_2[] = "\
-q : don't print version and copyright messages on interactive startup\n\
-s : don't add user site directory to sys.path; also PYTHONNOUSERSITE\n\
-S : don't imply 'import site' on initialization\n\
";
static const char usage_3[] = "\
-u : force the stdout and stderr streams to be unbuffered;\n\
this option has no effect on stdin; also PYTHONUNBUFFERED=x\n\
-v : verbose (trace import statements); also PYTHONVERBOSE=x\n\
@ -64,55 +61,72 @@ static const char usage_3[] = "\
-W arg : warning control; arg is action:message:category:module:lineno\n\
also PYTHONWARNINGS=arg\n\
-x : skip first line of source, allowing use of non-Unix forms of #!cmd\n\
-X opt : set implementation-specific option. The following options are available:\n\
-X faulthandler: enable faulthandler\n\
-X showrefcount: output the total reference count and number of used\n\
memory blocks when the program finishes or after each statement in the\n\
interactive interpreter. This only works on debug builds\n\
-X tracemalloc: start tracing Python memory allocations using the\n\
tracemalloc module. By default, only the most recent frame is stored in a\n\
traceback of a trace. Use -X tracemalloc=NFRAME to start tracing with a\n\
traceback limit of NFRAME frames\n\
-X importtime: show how long each import takes. It shows module name,\n\
cumulative time (including nested imports) and self time (excluding\n\
nested imports). Note that its output may be broken in multi-threaded\n\
application. Typical usage is python3 -X importtime -c 'import asyncio'\n\
-X dev: enable CPython's \"development mode\", introducing additional runtime\n\
checks which are too expensive to be enabled by default. Effect of the\n\
developer mode:\n\
* Add default warning filter, as -W default\n\
* Install debug hooks on memory allocators: see the PyMem_SetupDebugHooks()\n\
C function\n\
* Enable the faulthandler module to dump the Python traceback on a crash\n\
* Enable asyncio debug mode\n\
* Set the dev_mode attribute of sys.flags to True\n\
* io.IOBase destructor logs close() exceptions\n\
-X utf8: enable UTF-8 mode for operating system interfaces, overriding the default\n\
locale-aware mode. -X utf8=0 explicitly disables UTF-8 mode (even when it would\n\
otherwise activate automatically)\n\
-X pycache_prefix=PATH: enable writing .pyc files to a parallel tree rooted at the\n\
given directory instead of to the code tree\n\
-X warn_default_encoding: enable opt-in EncodingWarning for 'encoding=None'\n\
-X no_debug_ranges: disable the inclusion of the tables mapping extra location \n\
information (end line, start column offset and end column offset) to every \n\
instruction in code objects. This is useful when smaller code objects and pyc \n\
files are desired as well as suppressing the extra visual location indicators \n\
when the interpreter displays tracebacks.\n\
-X frozen_modules=[on|off]: whether or not frozen modules should be used.\n\
The default is \"on\" (or \"off\" if you are running a local build).\n\
-X opt : set implementation-specific option\n\
--check-hash-based-pycs always|default|never:\n\
control how Python invalidates hash-based .pyc files\n\
";
static const char usage_4[] = "\
control how Python invalidates hash-based .pyc files\n\
--help-env : print help about Python environment variables and exit\n\
--help-xoptions : print help about implementation-specific -X options and exit\n\
--help-all : print complete help information and exit\n\
Arguments:\n\
file : program read from script file\n\
- : program read from stdin (default; interactive mode if a tty)\n\
arg ...: arguments passed to program in sys.argv[1:]\n\n\
Other environment variables:\n\
PYTHONSTARTUP: file executed on interactive startup (no default)\n\
PYTHONPATH : '%lc'-separated list of directories prefixed to the\n\
default module search path. The result is sys.path.\n\
arg ...: arguments passed to program in sys.argv[1:]\n\
";
static const char usage_5[] =
static const char usage_xoptions[] = "\
The following implementation-specific options are available:\n\
\n\
-X faulthandler: enable faulthandler\n\
\n\
-X showrefcount: output the total reference count and number of used\n\
memory blocks when the program finishes or after each statement in the\n\
interactive interpreter. This only works on debug builds\n\
\n\
-X tracemalloc: start tracing Python memory allocations using the\n\
tracemalloc module. By default, only the most recent frame is stored in a\n\
traceback of a trace. Use -X tracemalloc=NFRAME to start tracing with a\n\
traceback limit of NFRAME frames\n\
\n\
-X importtime: show how long each import takes. It shows module name,\n\
cumulative time (including nested imports) and self time (excluding\n\
nested imports). Note that its output may be broken in multi-threaded\n\
application. Typical usage is python3 -X importtime -c 'import asyncio'\n\
\n\
-X dev: enable CPython's \"development mode\", introducing additional runtime\n\
checks which are too expensive to be enabled by default. Effect of the\n\
developer mode:\n\
* Add default warning filter, as -W default\n\
* Install debug hooks on memory allocators: see the PyMem_SetupDebugHooks()\n\
C function\n\
* Enable the faulthandler module to dump the Python traceback on a crash\n\
* Enable asyncio debug mode\n\
* Set the dev_mode attribute of sys.flags to True\n\
* io.IOBase destructor logs close() exceptions\n\
\n\
-X utf8: enable UTF-8 mode for operating system interfaces, overriding the default\n\
locale-aware mode. -X utf8=0 explicitly disables UTF-8 mode (even when it would\n\
otherwise activate automatically)\n\
\n\
-X pycache_prefix=PATH: enable writing .pyc files to a parallel tree rooted at the\n\
given directory instead of to the code tree\n\
\n\
-X warn_default_encoding: enable opt-in EncodingWarning for 'encoding=None'\n\
\n\
-X no_debug_ranges: disable the inclusion of the tables mapping extra location \n\
information (end line, start column offset and end column offset) to every \n\
instruction in code objects. This is useful when smaller code objects and pyc \n\
files are desired as well as suppressing the extra visual location indicators \n\
when the interpreter displays tracebacks.\n\
\n\
-X frozen_modules=[on|off]: whether or not frozen modules should be used.\n\
The default is \"on\" (or \"off\" if you are running a local build).";
/* Envvars that don't have equivalent command-line options are listed first */
static const char usage_envvars[] =
"Environment variables that change behavior:\n"
"PYTHONSTARTUP: file executed on interactive startup (no default)\n"
"PYTHONPATH : '%lc'-separated list of directories prefixed to the\n"
" default module search path. The result is sys.path.\n"
"PYTHONSAFEPATH: don't prepend a potentially unsafe path to sys.path.\n"
"PYTHONHOME : alternate <prefix> directory (or <prefix>%lc<exec_prefix>).\n"
" The default module search path uses %s.\n"
@ -120,8 +134,7 @@ static const char usage_5[] =
"PYTHONCASEOK : ignore case in 'import' statements (Windows).\n"
"PYTHONUTF8: if set to 1, enable the UTF-8 mode.\n"
"PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.\n"
"PYTHONFAULTHANDLER: dump the Python traceback on fatal errors.\n";
static const char usage_6[] =
"PYTHONFAULTHANDLER: dump the Python traceback on fatal errors.\n"
"PYTHONHASHSEED: if this variable is set to 'random', a random value is used\n"
" to seed the hashes of str and bytes objects. It can also be set to an\n"
" integer in the range [0,4294967295] to get hash values with a\n"
@ -141,7 +154,16 @@ static const char usage_6[] =
" tables mapping extra location information (end line, start column offset \n"
" and end column offset) to every instruction in code objects. This is useful \n"
" when smaller code objects and pyc files are desired as well as suppressing the \n"
" extra visual location indicators when the interpreter displays tracebacks.\n";
" extra visual location indicators when the interpreter displays tracebacks.\n"
"These variables have equivalent command-line parameters (see --help for details):\n"
"PYTHONDEBUG : enable parser debug mode (-d)\n"
"PYTHONDONTWRITEBYTECODE : don't write .pyc files (-B)\n"
"PYTHONINSPECT : inspect interactively after running script (-i)\n"
"PYTHONNOUSERSITE : disable user site directory (-s)\n"
"PYTHONOPTIMIZE : enable level 1 optimizations (-O)\n"
"PYTHONUNBUFFERED : disable stdout/stderr buffering (-u)\n"
"PYTHONVERBOSE : trace import statements (-v)\n"
"PYTHONWARNINGS=arg : warning control (-W arg)\n";
#if defined(MS_WINDOWS)
# define PYTHONHOMEHELP "<prefix>\\python{major}{minor}"
@ -2084,7 +2106,7 @@ config_read(PyConfig *config, int compute_path_config)
/* -X options */
const wchar_t* option = _Py_check_xoptions(&config->xoptions, known_xoptions);
if (option != NULL) {
return PyStatus_Error("Unknown value for option -X");
return PyStatus_Error("Unknown value for option -X (see --help-xoptions)");
}
if (config_get_xoption(config, L"showrefcount")) {
@ -2237,15 +2259,32 @@ config_usage(int error, const wchar_t* program)
if (error)
fprintf(f, "Try `python -h' for more information.\n");
else {
fputs(usage_1, f);
fputs(usage_2, f);
fputs(usage_3, f);
fprintf(f, usage_4, (wint_t)DELIM);
fprintf(f, usage_5, (wint_t)DELIM, PYTHONHOMEHELP);
fputs(usage_6, f);
fputs(usage_help, f);
}
}
static void
config_envvars_usage()
{
printf(usage_envvars, (wint_t)DELIM, (wint_t)DELIM, PYTHONHOMEHELP);
}
static void
config_xoptions_usage()
{
puts(usage_xoptions);
}
static void
config_complete_usage(const wchar_t* program)
{
config_usage(0, program);
puts("\n");
config_envvars_usage();
puts("\n");
config_xoptions_usage();
}
/* Parse the command line arguments */
static PyStatus
@ -2297,9 +2336,9 @@ config_parse_cmdline(PyConfig *config, PyWideStringList *warnoptions,
}
switch (c) {
// Integers represent long options, see Python/getopt.c
case 0:
// Handle long option.
assert(longindex == 0); // Only one long option now.
// check-hash-based-pycs
if (wcscmp(_PyOS_optarg, L"always") == 0
|| wcscmp(_PyOS_optarg, L"never") == 0
|| wcscmp(_PyOS_optarg, L"default") == 0)
@ -2317,6 +2356,21 @@ config_parse_cmdline(PyConfig *config, PyWideStringList *warnoptions,
}
break;
case 1:
// help-all
config_complete_usage(program);
return _PyStatus_EXIT(0);
case 2:
// help-env
config_envvars_usage();
return _PyStatus_EXIT(0);
case 3:
// help-xoptions
config_xoptions_usage();
return _PyStatus_EXIT(0);
case 'b':
config->bytes_warning++;
break;