GH-88597: Added command line interface to UUID module. (#99463)
The `uuid` module now supports command line usage. ```python ❯ ./python.exe -m uuid 5f2d57b1-90e8-417c-ba5d-69b9b6f74289 ❯ ./python.exe -m uuid -h usage: uuid.py [-h] [-u {uuid1,uuid3,uuid4,uuid5}] [-ns NAMESPACE] [-n NAME] ... ```
This commit is contained in:
parent
c1c5882359
commit
95f5b05a8c
@ -261,6 +261,46 @@ of the :attr:`variant` attribute:
|
||||
internal format of UUIDs, and methods of generating UUIDs.
|
||||
|
||||
|
||||
.. _uuid-cli:
|
||||
|
||||
Command-Line Usage
|
||||
------------------
|
||||
|
||||
.. versionadded:: 3.12
|
||||
|
||||
The :mod:`uuid` module can be executed as a script from the command line.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
python -m uuid [-h] [-u {uuid1,uuid3,uuid4,uuid5}] [-ns NAMESPACE] [-n NAME]
|
||||
|
||||
The following options are accepted:
|
||||
|
||||
.. program:: uuid
|
||||
|
||||
.. cmdoption:: -h, --help
|
||||
|
||||
Show the help message and exit.
|
||||
|
||||
.. cmdoption:: -u <uuid>
|
||||
--uuid <uuid>
|
||||
|
||||
Specify the function name to use to generate the uuid. By default :func:`uuid4`
|
||||
is used.
|
||||
|
||||
.. cmdoption:: -ns <namespace>
|
||||
--namespace <namespace>
|
||||
|
||||
The namespace used as part of generating the uuid. Only required for
|
||||
:func:`uuid3` / :func:`uuid5` functions.
|
||||
|
||||
.. cmdoption:: -n <name>
|
||||
--name <name>
|
||||
|
||||
The name used as part of generating the uuid. Only required for
|
||||
:func:`uuid3` / :func:`uuid5` functions.
|
||||
|
||||
|
||||
.. _uuid-example:
|
||||
|
||||
Example
|
||||
@ -301,3 +341,22 @@ Here are some examples of typical usage of the :mod:`uuid` module::
|
||||
>>> uuid.UUID(bytes=x.bytes)
|
||||
UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')
|
||||
|
||||
|
||||
.. _uuid-cli-example:
|
||||
|
||||
Command-Line Example
|
||||
--------------------
|
||||
|
||||
Here are some examples of typical usage of the :mod:`uuid` command line interface:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
# generate a random uuid - by default uuid4() is used
|
||||
$ python -m uuid
|
||||
|
||||
# generate a uuid using uuid1()
|
||||
$ python -m uuid -u uuid1
|
||||
|
||||
# generate a uuid using uuid5
|
||||
$ python -m uuid -u uuid5 -ns NAMESPACE_URL -n example.com
|
||||
|
||||
|
@ -327,6 +327,12 @@ unicodedata
|
||||
* The Unicode database has been updated to version 15.0.0. (Contributed by
|
||||
Benjamin Peterson in :gh:`96734`).
|
||||
|
||||
uuid
|
||||
----
|
||||
|
||||
* Add a :ref:`command-line interface <uuid-cli>`.
|
||||
(Contributed by Adam Chhina in :gh:`88597`.)
|
||||
|
||||
tempfile
|
||||
--------
|
||||
|
||||
|
@ -675,6 +675,64 @@ class BaseTestUUID:
|
||||
weak = weakref.ref(strong)
|
||||
self.assertIs(strong, weak())
|
||||
|
||||
@mock.patch.object(sys, "argv", ["", "-u", "uuid3", "-ns", "NAMESPACE_DNS"])
|
||||
def test_cli_namespace_required_for_uuid3(self):
|
||||
with self.assertRaises(SystemExit) as cm:
|
||||
self.uuid.main()
|
||||
|
||||
# Check that exception code is the same as argparse.ArgumentParser.error
|
||||
self.assertEqual(cm.exception.code, 2)
|
||||
|
||||
@mock.patch.object(sys, "argv", ["", "-u", "uuid3", "-n", "python.org"])
|
||||
def test_cli_name_required_for_uuid3(self):
|
||||
with self.assertRaises(SystemExit) as cm:
|
||||
self.uuid.main()
|
||||
|
||||
# Check that exception code is the same as argparse.ArgumentParser.error
|
||||
self.assertEqual(cm.exception.code, 2)
|
||||
|
||||
@mock.patch.object(sys, "argv", [""])
|
||||
def test_cli_uuid4_outputted_with_no_args(self):
|
||||
stdout = io.StringIO()
|
||||
with contextlib.redirect_stdout(stdout):
|
||||
self.uuid.main()
|
||||
|
||||
output = stdout.getvalue().strip()
|
||||
uuid_output = self.uuid.UUID(output)
|
||||
|
||||
# Output uuid should be in the format of uuid4
|
||||
self.assertEqual(output, str(uuid_output))
|
||||
self.assertEqual(uuid_output.version, 4)
|
||||
|
||||
@mock.patch.object(sys, "argv",
|
||||
["", "-u", "uuid3", "-ns", "NAMESPACE_DNS", "-n", "python.org"])
|
||||
def test_cli_uuid3_ouputted_with_valid_namespace_and_name(self):
|
||||
stdout = io.StringIO()
|
||||
with contextlib.redirect_stdout(stdout):
|
||||
self.uuid.main()
|
||||
|
||||
output = stdout.getvalue().strip()
|
||||
uuid_output = self.uuid.UUID(output)
|
||||
|
||||
# Output should be in the form of uuid5
|
||||
self.assertEqual(output, str(uuid_output))
|
||||
self.assertEqual(uuid_output.version, 3)
|
||||
|
||||
@mock.patch.object(sys, "argv",
|
||||
["", "-u", "uuid5", "-ns", "NAMESPACE_DNS", "-n", "python.org"])
|
||||
def test_cli_uuid5_ouputted_with_valid_namespace_and_name(self):
|
||||
stdout = io.StringIO()
|
||||
with contextlib.redirect_stdout(stdout):
|
||||
self.uuid.main()
|
||||
|
||||
output = stdout.getvalue().strip()
|
||||
uuid_output = self.uuid.UUID(output)
|
||||
|
||||
# Output should be in the form of uuid5
|
||||
self.assertEqual(output, str(uuid_output))
|
||||
self.assertEqual(uuid_output.version, 5)
|
||||
|
||||
|
||||
class TestUUIDWithoutExtModule(BaseTestUUID, unittest.TestCase):
|
||||
uuid = py_uuid
|
||||
|
||||
|
49
Lib/uuid.py
49
Lib/uuid.py
@ -728,9 +728,58 @@ def uuid5(namespace, name):
|
||||
hash = sha1(namespace.bytes + bytes(name, "utf-8")).digest()
|
||||
return UUID(bytes=hash[:16], version=5)
|
||||
|
||||
|
||||
def main():
|
||||
"""Run the uuid command line interface."""
|
||||
uuid_funcs = {"uuid1": uuid1,
|
||||
"uuid3": uuid3,
|
||||
"uuid4": uuid4,
|
||||
"uuid5": uuid5}
|
||||
uuid_namespace_funcs = ("uuid3", "uuid5")
|
||||
namespaces = {
|
||||
"NAMESPACE_DNS": NAMESPACE_DNS,
|
||||
"NAMESPACE_URL": NAMESPACE_URL,
|
||||
"NAMESPACE_OID": NAMESPACE_OID,
|
||||
"NAMESPACE_X500": NAMESPACE_X500
|
||||
}
|
||||
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generates a uuid using the selected uuid function.")
|
||||
parser.add_argument("-u", "--uuid", choices=uuid_funcs.keys(), default="uuid4",
|
||||
help="The function to use to generate the uuid. "
|
||||
"By default uuid4 function is used.")
|
||||
parser.add_argument("-ns", "--namespace",
|
||||
help="The namespace used as part of generating the uuid. "
|
||||
"Only required for uuid3/uuid5 functions.")
|
||||
parser.add_argument("-n", "--name",
|
||||
help="The name used as part of generating the uuid. "
|
||||
"Only required for uuid3/uuid5 functions.")
|
||||
|
||||
args = parser.parse_args()
|
||||
uuid_func = uuid_funcs[args.uuid]
|
||||
namespace = args.namespace
|
||||
name = args.name
|
||||
|
||||
if args.uuid in uuid_namespace_funcs:
|
||||
if not namespace or not name:
|
||||
parser.error(
|
||||
"Incorrect number of arguments. "
|
||||
f"{args.uuid} requires a namespace and a name. "
|
||||
"Run 'python -m uuid -h' for more information."
|
||||
)
|
||||
namespace = namespaces[namespace] if namespace in namespaces else UUID(namespace)
|
||||
print(uuid_func(namespace, name))
|
||||
else:
|
||||
print(uuid_func())
|
||||
|
||||
|
||||
# The following standard UUIDs are for use with uuid3() or uuid5().
|
||||
|
||||
NAMESPACE_DNS = UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
|
||||
NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8')
|
||||
NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8')
|
||||
NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8')
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -315,6 +315,7 @@ Nicolas Chauvat
|
||||
Jerry Chen
|
||||
Michael Chermside
|
||||
Ingrid Cheung
|
||||
Adam Chhina
|
||||
Terry Chia
|
||||
Albert Chin-A-Young
|
||||
Adal Chiriliuc
|
||||
|
@ -0,0 +1 @@
|
||||
:mod:`uuid` now has a command line interface. Try ``python -m uuid -h``.
|
Loading…
x
Reference in New Issue
Block a user