mirror of https://github.com/python/cpython
gh-118131: Command-line interface for the `random` module (#118132)
This commit is contained in:
parent
fed8d73fde
commit
3b32575ed6
|
@ -36,6 +36,7 @@ The following modules have a command-line interface.
|
||||||
* :mod:`pyclbr`
|
* :mod:`pyclbr`
|
||||||
* :mod:`pydoc`
|
* :mod:`pydoc`
|
||||||
* :mod:`quopri`
|
* :mod:`quopri`
|
||||||
|
* :ref:`random <random-cli>`
|
||||||
* :mod:`runpy`
|
* :mod:`runpy`
|
||||||
* :ref:`site <site-commandline>`
|
* :ref:`site <site-commandline>`
|
||||||
* :ref:`sqlite3 <sqlite3-cli>`
|
* :ref:`sqlite3 <sqlite3-cli>`
|
||||||
|
|
|
@ -706,3 +706,83 @@ positive unnormalized float and is equal to ``math.ulp(0.0)``.)
|
||||||
<https://allendowney.com/research/rand/downey07randfloat.pdf>`_ a
|
<https://allendowney.com/research/rand/downey07randfloat.pdf>`_ a
|
||||||
paper by Allen B. Downey describing ways to generate more
|
paper by Allen B. Downey describing ways to generate more
|
||||||
fine-grained floats than normally generated by :func:`.random`.
|
fine-grained floats than normally generated by :func:`.random`.
|
||||||
|
|
||||||
|
.. _random-cli:
|
||||||
|
|
||||||
|
Command-line usage
|
||||||
|
------------------
|
||||||
|
|
||||||
|
.. versionadded:: 3.13
|
||||||
|
|
||||||
|
The :mod:`!random` module can be executed from the command line.
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
python -m random [-h] [-c CHOICE [CHOICE ...] | -i N | -f N] [input ...]
|
||||||
|
|
||||||
|
The following options are accepted:
|
||||||
|
|
||||||
|
.. program:: random
|
||||||
|
|
||||||
|
.. option:: -h, --help
|
||||||
|
|
||||||
|
Show the help message and exit.
|
||||||
|
|
||||||
|
.. option:: -c CHOICE [CHOICE ...]
|
||||||
|
--choice CHOICE [CHOICE ...]
|
||||||
|
|
||||||
|
Print a random choice, using :meth:`choice`.
|
||||||
|
|
||||||
|
.. option:: -i <N>
|
||||||
|
--integer <N>
|
||||||
|
|
||||||
|
Print a random integer between 1 and N inclusive, using :meth:`randint`.
|
||||||
|
|
||||||
|
.. option:: -f <N>
|
||||||
|
--float <N>
|
||||||
|
|
||||||
|
Print a random floating point number between 1 and N inclusive,
|
||||||
|
using :meth:`uniform`.
|
||||||
|
|
||||||
|
If no options are given, the output depends on the input:
|
||||||
|
|
||||||
|
* String or multiple: same as :option:`--choice`.
|
||||||
|
* Integer: same as :option:`--integer`.
|
||||||
|
* Float: same as :option:`--float`.
|
||||||
|
|
||||||
|
.. _random-cli-example:
|
||||||
|
|
||||||
|
Command-line example
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Here are some examples of the :mod:`!random` command-line interface:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ # Choose one at random
|
||||||
|
$ python -m random egg bacon sausage spam "Lobster Thermidor aux crevettes with a Mornay sauce"
|
||||||
|
Lobster Thermidor aux crevettes with a Mornay sauce
|
||||||
|
|
||||||
|
$ # Random integer
|
||||||
|
$ python -m random 6
|
||||||
|
6
|
||||||
|
|
||||||
|
$ # Random floating-point number
|
||||||
|
$ python -m random 1.8
|
||||||
|
1.7080016272295635
|
||||||
|
|
||||||
|
$ # With explicit arguments
|
||||||
|
$ python -m random --choice egg bacon sausage spam "Lobster Thermidor aux crevettes with a Mornay sauce"
|
||||||
|
egg
|
||||||
|
|
||||||
|
$ python -m random --integer 6
|
||||||
|
3
|
||||||
|
|
||||||
|
$ python -m random --float 1.8
|
||||||
|
1.5666339105010318
|
||||||
|
|
||||||
|
$ python -m random --integer 6
|
||||||
|
5
|
||||||
|
|
||||||
|
$ python -m random --float 6
|
||||||
|
3.1942323316565915
|
||||||
|
|
|
@ -722,6 +722,12 @@ queue
|
||||||
termination.
|
termination.
|
||||||
(Contributed by Laurie Opperman and Yves Duprat in :gh:`104750`.)
|
(Contributed by Laurie Opperman and Yves Duprat in :gh:`104750`.)
|
||||||
|
|
||||||
|
random
|
||||||
|
------
|
||||||
|
|
||||||
|
* Add a :ref:`command-line interface <random-cli>`.
|
||||||
|
(Contributed by Hugo van Kemenade in :gh:`54321`.)
|
||||||
|
|
||||||
re
|
re
|
||||||
--
|
--
|
||||||
* Rename :exc:`!re.error` to :exc:`re.PatternError` for improved clarity.
|
* Rename :exc:`!re.error` to :exc:`re.PatternError` for improved clarity.
|
||||||
|
|
|
@ -996,5 +996,75 @@ if hasattr(_os, "fork"):
|
||||||
_os.register_at_fork(after_in_child=_inst.seed)
|
_os.register_at_fork(after_in_child=_inst.seed)
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------
|
||||||
|
# -------------- command-line interface ----------------
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_args(arg_list: list[str] | None):
|
||||||
|
import argparse
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
formatter_class=argparse.RawTextHelpFormatter)
|
||||||
|
group = parser.add_mutually_exclusive_group()
|
||||||
|
group.add_argument(
|
||||||
|
"-c", "--choice", nargs="+",
|
||||||
|
help="print a random choice")
|
||||||
|
group.add_argument(
|
||||||
|
"-i", "--integer", type=int, metavar="N",
|
||||||
|
help="print a random integer between 1 and N inclusive")
|
||||||
|
group.add_argument(
|
||||||
|
"-f", "--float", type=float, metavar="N",
|
||||||
|
help="print a random floating point number between 1 and N inclusive")
|
||||||
|
group.add_argument(
|
||||||
|
"--test", type=int, const=10_000, nargs="?",
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
parser.add_argument("input", nargs="*",
|
||||||
|
help="""\
|
||||||
|
if no options given, output depends on the input
|
||||||
|
string or multiple: same as --choice
|
||||||
|
integer: same as --integer
|
||||||
|
float: same as --float""")
|
||||||
|
args = parser.parse_args(arg_list)
|
||||||
|
return args, parser.format_help()
|
||||||
|
|
||||||
|
|
||||||
|
def main(arg_list: list[str] | None = None) -> int | str:
|
||||||
|
args, help_text = _parse_args(arg_list)
|
||||||
|
|
||||||
|
# Explicit arguments
|
||||||
|
if args.choice:
|
||||||
|
return choice(args.choice)
|
||||||
|
|
||||||
|
if args.integer is not None:
|
||||||
|
return randint(1, args.integer)
|
||||||
|
|
||||||
|
if args.float is not None:
|
||||||
|
return uniform(1, args.float)
|
||||||
|
|
||||||
|
if args.test:
|
||||||
|
_test(args.test)
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# No explicit argument, select based on input
|
||||||
|
if len(args.input) == 1:
|
||||||
|
val = args.input[0]
|
||||||
|
try:
|
||||||
|
# Is it an integer?
|
||||||
|
val = int(val)
|
||||||
|
return randint(1, val)
|
||||||
|
except ValueError:
|
||||||
|
try:
|
||||||
|
# Is it a float?
|
||||||
|
val = float(val)
|
||||||
|
return uniform(1, val)
|
||||||
|
except ValueError:
|
||||||
|
# Split in case of space-separated string: "a b c"
|
||||||
|
return choice(val.split())
|
||||||
|
|
||||||
|
if len(args.input) >= 2:
|
||||||
|
return choice(args.input)
|
||||||
|
|
||||||
|
return help_text
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
_test()
|
print(main())
|
||||||
|
|
|
@ -4,6 +4,7 @@ import random
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import pickle
|
import pickle
|
||||||
|
import shlex
|
||||||
import warnings
|
import warnings
|
||||||
import test.support
|
import test.support
|
||||||
|
|
||||||
|
@ -1397,5 +1398,47 @@ class TestModule(unittest.TestCase):
|
||||||
support.wait_process(pid, exitcode=0)
|
support.wait_process(pid, exitcode=0)
|
||||||
|
|
||||||
|
|
||||||
|
class CommandLineTest(unittest.TestCase):
|
||||||
|
def test_parse_args(self):
|
||||||
|
args, help_text = random._parse_args(shlex.split("--choice a b c"))
|
||||||
|
self.assertEqual(args.choice, ["a", "b", "c"])
|
||||||
|
self.assertTrue(help_text.startswith("usage: "))
|
||||||
|
|
||||||
|
args, help_text = random._parse_args(shlex.split("--integer 5"))
|
||||||
|
self.assertEqual(args.integer, 5)
|
||||||
|
self.assertTrue(help_text.startswith("usage: "))
|
||||||
|
|
||||||
|
args, help_text = random._parse_args(shlex.split("--float 2.5"))
|
||||||
|
self.assertEqual(args.float, 2.5)
|
||||||
|
self.assertTrue(help_text.startswith("usage: "))
|
||||||
|
|
||||||
|
args, help_text = random._parse_args(shlex.split("a b c"))
|
||||||
|
self.assertEqual(args.input, ["a", "b", "c"])
|
||||||
|
self.assertTrue(help_text.startswith("usage: "))
|
||||||
|
|
||||||
|
args, help_text = random._parse_args(shlex.split("5"))
|
||||||
|
self.assertEqual(args.input, ["5"])
|
||||||
|
self.assertTrue(help_text.startswith("usage: "))
|
||||||
|
|
||||||
|
args, help_text = random._parse_args(shlex.split("2.5"))
|
||||||
|
self.assertEqual(args.input, ["2.5"])
|
||||||
|
self.assertTrue(help_text.startswith("usage: "))
|
||||||
|
|
||||||
|
def test_main(self):
|
||||||
|
for command, expected in [
|
||||||
|
("--choice a b c", "b"),
|
||||||
|
('"a b c"', "b"),
|
||||||
|
("a b c", "b"),
|
||||||
|
("--choice 'a a' 'b b' 'c c'", "b b"),
|
||||||
|
("'a a' 'b b' 'c c'", "b b"),
|
||||||
|
("--integer 5", 4),
|
||||||
|
("5", 4),
|
||||||
|
("--float 2.5", 2.266632777287572),
|
||||||
|
("2.5", 2.266632777287572),
|
||||||
|
]:
|
||||||
|
random.seed(0)
|
||||||
|
self.assertEqual(random.main(shlex.split(command)), expected)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Add command-line interface for the :mod:`random` module. Patch by Hugo van
|
||||||
|
Kemenade.
|
Loading…
Reference in New Issue