From 5fc54493b6f71a5339a7f12a42e9ed32c9cc2320 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Wed, 17 Apr 2024 17:50:52 +0300 Subject: [PATCH 1/3] Add command-line interface for the `random` module --- Doc/library/cmdline.rst | 1 + Doc/library/random.rst | 84 +++++++++++++++++++++++++++++++++++++++++ Doc/whatsnew/3.13.rst | 6 +++ Lib/random.py | 72 ++++++++++++++++++++++++++++++++++- Lib/test/test_random.py | 43 +++++++++++++++++++++ 5 files changed, 205 insertions(+), 1 deletion(-) diff --git a/Doc/library/cmdline.rst b/Doc/library/cmdline.rst index b2379befeffcba..5174515ffc23ed 100644 --- a/Doc/library/cmdline.rst +++ b/Doc/library/cmdline.rst @@ -36,6 +36,7 @@ The following modules have a command-line interface. * :mod:`pyclbr` * :mod:`pydoc` * :mod:`quopri` +* :ref:`random ` * :mod:`runpy` * :ref:`site ` * :ref:`sqlite3 ` diff --git a/Doc/library/random.rst b/Doc/library/random.rst index 8fbce18c56f17c..b06142a68effc2 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -700,3 +700,87 @@ positive unnormalized float and is equal to ``math.ulp(0.0)``.) `_ a paper by Allen B. Downey describing ways to generate more 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 | --test [N]] + +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 + --integer + + Print a random integer between 1 and N inclusive, using :meth:`randint`. + +.. option:: -f + --float + + Print a random floating point number between 1 and N inclusive, + using :meth:`uniform`. + +.. option:: --test + + Run a test N times. + +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 diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 5be562030b507b..cbf882d8bd2040 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -651,6 +651,12 @@ queue termination. (Contributed by Laurie Opperman and Yves Duprat in :gh:`104750`.) +random +------ + +* Add a :ref:`command-line interface `. + (Contributed by Hugo van Kemenade in :gh:`54321`.) + re -- * Rename :exc:`!re.error` to :exc:`re.PatternError` for improved clarity. diff --git a/Lib/random.py b/Lib/random.py index 875beb2f8cf41c..30ac80b77e07cf 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -996,5 +996,75 @@ def _test(N=10_000): _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__': - _test() + print(main()) diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index b1e4ef4197d130..589d1243a4cac1 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -4,6 +4,7 @@ import os import time import pickle +import shlex import warnings import test.support @@ -1397,5 +1398,47 @@ def test_after_fork(self): 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__": unittest.main() From 5a487bb75d451ae9ff6094b18aa168114d30462c Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 21 Apr 2024 19:05:59 +0300 Subject: [PATCH 2/3] Add blurb, update docs --- Doc/library/random.rst | 6 +----- .../Library/2024-04-21-18-55-42.gh-issue-118131.eAT0is.rst | 2 ++ 2 files changed, 3 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-04-21-18-55-42.gh-issue-118131.eAT0is.rst diff --git a/Doc/library/random.rst b/Doc/library/random.rst index b06142a68effc2..b3856c5aa44a20 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -712,7 +712,7 @@ 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 | --test [N]] + python -m random [-h] [-c CHOICE [CHOICE ...] | -i N | -f N] [input ...] The following options are accepted: @@ -738,10 +738,6 @@ The following options are accepted: Print a random floating point number between 1 and N inclusive, using :meth:`uniform`. -.. option:: --test - - Run a test N times. - If no options are given, the output depends on the input: * String or multiple: same as :option:`--choice`. diff --git a/Misc/NEWS.d/next/Library/2024-04-21-18-55-42.gh-issue-118131.eAT0is.rst b/Misc/NEWS.d/next/Library/2024-04-21-18-55-42.gh-issue-118131.eAT0is.rst new file mode 100644 index 00000000000000..83ed66cf82fc20 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-21-18-55-42.gh-issue-118131.eAT0is.rst @@ -0,0 +1,2 @@ +Add command-line interface for the :mod:`random` module. Patch by Hugo van +Kemenade. From 15dec310aebbd4907e8dda1838d26938637f6f77 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 2 May 2024 16:37:02 +0300 Subject: [PATCH 3/3] Rename parse_args to _parse_args --- Lib/random.py | 4 ++-- Lib/test/test_random.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Lib/random.py b/Lib/random.py index 30ac80b77e07cf..bcc11c7cd3c208 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -1000,7 +1000,7 @@ def _test(N=10_000): # -------------- command-line interface ---------------- -def parse_args(arg_list: list[str] | None): +def _parse_args(arg_list: list[str] | None): import argparse parser = argparse.ArgumentParser( formatter_class=argparse.RawTextHelpFormatter) @@ -1028,7 +1028,7 @@ def parse_args(arg_list: list[str] | None): def main(arg_list: list[str] | None = None) -> int | str: - args, help_text = parse_args(arg_list) + args, help_text = _parse_args(arg_list) # Explicit arguments if args.choice: diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 589d1243a4cac1..9a44ab1768656a 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -1400,27 +1400,27 @@ def test_after_fork(self): class CommandLineTest(unittest.TestCase): def test_parse_args(self): - args, help_text = random.parse_args(shlex.split("--choice a b c")) + 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")) + 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")) + 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")) + 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")) + 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")) + args, help_text = random._parse_args(shlex.split("2.5")) self.assertEqual(args.input, ["2.5"]) self.assertTrue(help_text.startswith("usage: ")) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy