https://github.com/python/cpython/commit/77c391a1b178a35b0157e00689acb3904b77694d
commit: 77c391a1b178a35b0157e00689acb3904b77694d
branch: main
author: Harry <harry.l...@gmail.com>
committer: hugovk <1324225+hug...@users.noreply.github.com>
date: 2025-05-03T08:58:59Z
summary:

gh-131524: Update platform CLI to use argparse (#131542)

Co-authored-by: Hugo van Kemenade <1324225+hug...@users.noreply.github.com>
Co-authored-by: Bénédikt Tran <10796600+picn...@users.noreply.github.com>

files:
A Misc/NEWS.d/next/Library/2025-03-21-17-34-27.gh-issue-131524.Vj1pO_.rst
M Lib/platform.py
M Lib/test/test_platform.py

diff --git a/Lib/platform.py b/Lib/platform.py
index a62192589af8ff..507552f360ba59 100644
--- a/Lib/platform.py
+++ b/Lib/platform.py
@@ -1464,9 +1464,41 @@ def invalidate_caches():
 
 ### Command line interface
 
-if __name__ == '__main__':
-    # Default is to print the aliased verbose platform string
-    terse = ('terse' in sys.argv or '--terse' in sys.argv)
-    aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv)
+def _parse_args(args: list[str] | None):
+    import argparse
+
+    parser = argparse.ArgumentParser()
+    parser.add_argument("args", nargs="*", choices=["nonaliased", "terse"])
+    parser.add_argument(
+        "--terse",
+        action="store_true",
+        help=(
+            "return only the absolute minimum information needed "
+            "to identify the platform"
+        ),
+    )
+    parser.add_argument(
+        "--nonaliased",
+        dest="aliased",
+        action="store_false",
+        help=(
+            "disable system/OS name aliasing. If aliasing is enabled, "
+            "some platforms report system names different from "
+            "their common names, e.g. SunOS is reported as Solaris"
+        ),
+    )
+
+    return parser.parse_args(args)
+
+
+def _main(args: list[str] | None = None):
+    args = _parse_args(args)
+
+    terse = args.terse or ("terse" in args.args)
+    aliased = args.aliased and ('nonaliased' not in args.args)
+
     print(platform(aliased, terse))
-    sys.exit(0)
+
+
+if __name__ == "__main__":
+    _main()
diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py
index 6ba630ad527f91..b90edc05e0454e 100644
--- a/Lib/test/test_platform.py
+++ b/Lib/test/test_platform.py
@@ -1,5 +1,8 @@
-import os
+import contextlib
 import copy
+import io
+import itertools
+import os
 import pickle
 import platform
 import subprocess
@@ -741,5 +744,65 @@ def test_parse_os_release(self):
         self.assertEqual(len(info["SPECIALS"]), 5)
 
 
+class CommandLineTest(unittest.TestCase):
+    def setUp(self):
+        platform.invalidate_caches()
+        self.addCleanup(platform.invalidate_caches)
+
+    def invoke_platform(self, *flags):
+        output = io.StringIO()
+        with contextlib.redirect_stdout(output):
+            platform._main(args=flags)
+        return output.getvalue()
+
+    def test_unknown_flag(self):
+        with self.assertRaises(SystemExit):
+            output = io.StringIO()
+            # suppress argparse error message
+            with contextlib.redirect_stderr(output):
+                _ = self.invoke_platform('--unknown')
+            self.assertStartsWith(output, "usage: ")
+
+    def test_invocation(self):
+        flags = (
+            "--terse", "--nonaliased", "terse", "nonaliased"
+        )
+
+        for r in range(len(flags) + 1):
+            for combination in itertools.combinations(flags, r):
+                self.invoke_platform(*combination)
+
+    def test_arg_parsing(self):
+        # For backwards compatibility, the `aliased` and `terse` parameters are
+        # computed based on a combination of positional arguments and flags.
+        #
+        # Test that the arguments are correctly passed to the underlying
+        # `platform.platform()` call.
+        options = (
+            (["--nonaliased"], False, False),
+            (["nonaliased"], False, False),
+            (["--terse"], True, True),
+            (["terse"], True, True),
+            (["nonaliased", "terse"], False, True),
+            (["--nonaliased", "terse"], False, True),
+            (["--terse", "nonaliased"], False, True),
+        )
+
+        for flags, aliased, terse in options:
+            with self.subTest(flags=flags, aliased=aliased, terse=terse):
+                with mock.patch.object(platform, 'platform') as obj:
+                    self.invoke_platform(*flags)
+                    obj.assert_called_once_with(aliased, terse)
+
+    def test_help(self):
+        output = io.StringIO()
+
+        with self.assertRaises(SystemExit):
+            with contextlib.redirect_stdout(output):
+                platform._main(args=["--help"])
+
+        self.assertStartsWith(output.getvalue(), "usage:")
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git 
a/Misc/NEWS.d/next/Library/2025-03-21-17-34-27.gh-issue-131524.Vj1pO_.rst 
b/Misc/NEWS.d/next/Library/2025-03-21-17-34-27.gh-issue-131524.Vj1pO_.rst
new file mode 100644
index 00000000000000..28926d06ca4f93
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-03-21-17-34-27.gh-issue-131524.Vj1pO_.rst
@@ -0,0 +1,2 @@
+Add help message to :mod:`platform` command-line interface. Contributed by
+Harry Lees.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to