mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Update uuid from v3.14.2-288-g06f9c8ca1c
This commit is contained in:
committed by
Jeong, YunWon
parent
babc3c634f
commit
15efc4a808
487
Lib/test/test_uuid.py
vendored
487
Lib/test/test_uuid.py
vendored
@@ -1,7 +1,3 @@
|
||||
import unittest
|
||||
from test import support
|
||||
from test.support import import_helper
|
||||
from test.support.script_helper import assert_python_ok
|
||||
import builtins
|
||||
import contextlib
|
||||
import copy
|
||||
@@ -9,10 +5,17 @@ import enum
|
||||
import io
|
||||
import os
|
||||
import pickle
|
||||
import random
|
||||
import sys
|
||||
import unittest
|
||||
import weakref
|
||||
from itertools import product
|
||||
from unittest import mock
|
||||
|
||||
from test import support
|
||||
from test.support import import_helper
|
||||
from test.support.script_helper import assert_python_ok
|
||||
|
||||
py_uuid = import_helper.import_fresh_module('uuid', blocked=['_uuid'])
|
||||
c_uuid = import_helper.import_fresh_module('uuid', fresh=['_uuid'])
|
||||
|
||||
@@ -33,6 +36,47 @@ def mock_get_command_stdout(data):
|
||||
class BaseTestUUID:
|
||||
uuid = None
|
||||
|
||||
def test_nil_uuid(self):
|
||||
nil_uuid = self.uuid.NIL
|
||||
|
||||
s = '00000000-0000-0000-0000-000000000000'
|
||||
i = 0
|
||||
self.assertEqual(nil_uuid, self.uuid.UUID(s))
|
||||
self.assertEqual(nil_uuid, self.uuid.UUID(int=i))
|
||||
self.assertEqual(nil_uuid.int, i)
|
||||
self.assertEqual(str(nil_uuid), s)
|
||||
# The Nil UUID falls within the range of the Apollo NCS variant as per
|
||||
# RFC 9562.
|
||||
# See https://www.rfc-editor.org/rfc/rfc9562.html#section-5.9-4
|
||||
self.assertEqual(nil_uuid.variant, self.uuid.RESERVED_NCS)
|
||||
# A version field of all zeros is "Unused" in RFC 9562, but the version
|
||||
# field also only applies to the 10xx variant, i.e. the variant
|
||||
# specified in RFC 9562. As such, because the Nil UUID falls under a
|
||||
# different variant, its version is considered undefined.
|
||||
# See https://www.rfc-editor.org/rfc/rfc9562.html#table2
|
||||
self.assertIsNone(nil_uuid.version)
|
||||
|
||||
def test_max_uuid(self):
|
||||
max_uuid = self.uuid.MAX
|
||||
|
||||
s = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
|
||||
i = (1 << 128) - 1
|
||||
self.assertEqual(max_uuid, self.uuid.UUID(s))
|
||||
self.assertEqual(max_uuid, self.uuid.UUID(int=i))
|
||||
self.assertEqual(max_uuid.int, i)
|
||||
self.assertEqual(str(max_uuid), s)
|
||||
# The Max UUID falls within the range of the "yet-to-be defined" future
|
||||
# UUID variant as per RFC 9562.
|
||||
# See https://www.rfc-editor.org/rfc/rfc9562.html#section-5.10-4
|
||||
self.assertEqual(max_uuid.variant, self.uuid.RESERVED_FUTURE)
|
||||
# A version field of all ones is "Reserved for future definition" in
|
||||
# RFC 9562, but the version field also only applies to the 10xx
|
||||
# variant, i.e. the variant specified in RFC 9562. As such, because the
|
||||
# Max UUID falls under a different variant, its version is considered
|
||||
# undefined.
|
||||
# See https://www.rfc-editor.org/rfc/rfc9562.html#table2
|
||||
self.assertIsNone(max_uuid.version)
|
||||
|
||||
def test_safe_uuid_enum(self):
|
||||
class CheckedSafeUUID(enum.Enum):
|
||||
safe = 0
|
||||
@@ -268,7 +312,7 @@ class BaseTestUUID:
|
||||
|
||||
# Version number out of range.
|
||||
badvalue(lambda: self.uuid.UUID('00'*16, version=0))
|
||||
badvalue(lambda: self.uuid.UUID('00'*16, version=6))
|
||||
badvalue(lambda: self.uuid.UUID('00'*16, version=42))
|
||||
|
||||
# Integer value out of range.
|
||||
badvalue(lambda: self.uuid.UUID(int=-1))
|
||||
@@ -682,6 +726,392 @@ class BaseTestUUID:
|
||||
equal(u, self.uuid.UUID(v))
|
||||
equal(str(u), v)
|
||||
|
||||
def test_uuid6(self):
|
||||
equal = self.assertEqual
|
||||
u = self.uuid.uuid6()
|
||||
equal(u.variant, self.uuid.RFC_4122)
|
||||
equal(u.version, 6)
|
||||
|
||||
fake_nanoseconds = 0x1571_20a1_de1a_c533
|
||||
fake_node_value = 0x54e1_acf6_da7f
|
||||
fake_clock_seq = 0x14c5
|
||||
with (
|
||||
mock.patch.object(self.uuid, '_last_timestamp_v6', None),
|
||||
mock.patch.object(self.uuid, 'getnode', return_value=fake_node_value),
|
||||
mock.patch('time.time_ns', return_value=fake_nanoseconds),
|
||||
mock.patch('random.getrandbits', return_value=fake_clock_seq)
|
||||
):
|
||||
u = self.uuid.uuid6()
|
||||
equal(u.variant, self.uuid.RFC_4122)
|
||||
equal(u.version, 6)
|
||||
|
||||
# 32 (top) | 16 (mid) | 12 (low) == 60 (timestamp)
|
||||
equal(u.time, 0x1e901fca_7a55_b92)
|
||||
equal(u.fields[0], 0x1e901fca) # 32 top bits of time
|
||||
equal(u.fields[1], 0x7a55) # 16 mid bits of time
|
||||
# 4 bits of version + 12 low bits of time
|
||||
equal((u.fields[2] >> 12) & 0xf, 6)
|
||||
equal((u.fields[2] & 0xfff), 0xb92)
|
||||
# 2 bits of variant + 6 high bits of clock_seq
|
||||
equal((u.fields[3] >> 6) & 0xf, 2)
|
||||
equal(u.fields[3] & 0x3f, fake_clock_seq >> 8)
|
||||
# 8 low bits of clock_seq
|
||||
equal(u.fields[4], fake_clock_seq & 0xff)
|
||||
equal(u.fields[5], fake_node_value)
|
||||
|
||||
def test_uuid6_uniqueness(self):
|
||||
# Test that UUIDv6-generated values are unique.
|
||||
|
||||
# Unlike UUIDv8, only 62 bits can be randomized for UUIDv6.
|
||||
# In practice, however, it remains unlikely to generate two
|
||||
# identical UUIDs for the same 60-bit timestamp if neither
|
||||
# the node ID nor the clock sequence is specified.
|
||||
uuids = {self.uuid.uuid6() for _ in range(1000)}
|
||||
self.assertEqual(len(uuids), 1000)
|
||||
versions = {u.version for u in uuids}
|
||||
self.assertSetEqual(versions, {6})
|
||||
|
||||
timestamp = 0x1ec9414c_232a_b00
|
||||
fake_nanoseconds = (timestamp - 0x1b21dd21_3814_000) * 100
|
||||
|
||||
with mock.patch('time.time_ns', return_value=fake_nanoseconds):
|
||||
def gen():
|
||||
with mock.patch.object(self.uuid, '_last_timestamp_v6', None):
|
||||
return self.uuid.uuid6(node=0, clock_seq=None)
|
||||
|
||||
# By the birthday paradox, sampling N = 1024 UUIDs with identical
|
||||
# node IDs and timestamps results in duplicates with probability
|
||||
# close to 1 (not having a duplicate happens with probability of
|
||||
# order 1E-15) since only the 14-bit clock sequence is randomized.
|
||||
N = 1024
|
||||
uuids = {gen() for _ in range(N)}
|
||||
self.assertSetEqual({u.node for u in uuids}, {0})
|
||||
self.assertSetEqual({u.time for u in uuids}, {timestamp})
|
||||
self.assertLess(len(uuids), N, 'collision property does not hold')
|
||||
|
||||
def test_uuid6_node(self):
|
||||
# Make sure the given node ID appears in the UUID.
|
||||
#
|
||||
# Note: when no node ID is specified, the same logic as for UUIDv1
|
||||
# is applied to UUIDv6. In particular, there is no need to test that
|
||||
# getnode() correctly returns positive integers of exactly 48 bits
|
||||
# since this is done in test_uuid1_eui64().
|
||||
self.assertLessEqual(self.uuid.uuid6().node.bit_length(), 48)
|
||||
|
||||
self.assertEqual(self.uuid.uuid6(0).node, 0)
|
||||
|
||||
# tests with explicit values
|
||||
max_node = 0xffff_ffff_ffff
|
||||
self.assertEqual(self.uuid.uuid6(max_node).node, max_node)
|
||||
big_node = 0xE_1234_5678_ABCD # 52-bit node
|
||||
res_node = 0x0_1234_5678_ABCD # truncated to 48 bits
|
||||
self.assertEqual(self.uuid.uuid6(big_node).node, res_node)
|
||||
|
||||
# randomized tests
|
||||
for _ in range(10):
|
||||
# node with > 48 bits is truncated
|
||||
for b in [24, 48, 72]:
|
||||
node = (1 << (b - 1)) | random.getrandbits(b)
|
||||
with self.subTest(node=node, bitlen=b):
|
||||
self.assertEqual(node.bit_length(), b)
|
||||
u = self.uuid.uuid6(node=node)
|
||||
self.assertEqual(u.node, node & 0xffff_ffff_ffff)
|
||||
|
||||
def test_uuid6_clock_seq(self):
|
||||
# Make sure the supplied clock sequence appears in the UUID.
|
||||
#
|
||||
# For UUIDv6, clock sequence bits are stored from bit 48 to bit 62,
|
||||
# with the convention that the least significant bit is bit 0 and
|
||||
# the most significant bit is bit 127.
|
||||
get_clock_seq = lambda u: (u.int >> 48) & 0x3fff
|
||||
|
||||
u = self.uuid.uuid6()
|
||||
self.assertLessEqual(get_clock_seq(u).bit_length(), 14)
|
||||
|
||||
# tests with explicit values
|
||||
big_clock_seq = 0xffff # 16-bit clock sequence
|
||||
res_clock_seq = 0x3fff # truncated to 14 bits
|
||||
u = self.uuid.uuid6(clock_seq=big_clock_seq)
|
||||
self.assertEqual(get_clock_seq(u), res_clock_seq)
|
||||
|
||||
# some randomized tests
|
||||
for _ in range(10):
|
||||
# clock_seq with > 14 bits is truncated
|
||||
for b in [7, 14, 28]:
|
||||
node = random.getrandbits(48)
|
||||
clock_seq = (1 << (b - 1)) | random.getrandbits(b)
|
||||
with self.subTest(node=node, clock_seq=clock_seq, bitlen=b):
|
||||
self.assertEqual(clock_seq.bit_length(), b)
|
||||
u = self.uuid.uuid6(node=node, clock_seq=clock_seq)
|
||||
self.assertEqual(get_clock_seq(u), clock_seq & 0x3fff)
|
||||
|
||||
def test_uuid6_test_vectors(self):
|
||||
equal = self.assertEqual
|
||||
# https://www.rfc-editor.org/rfc/rfc9562#name-test-vectors
|
||||
# (separators are put at the 12th and 28th bits)
|
||||
timestamp = 0x1ec9414c_232a_b00
|
||||
fake_nanoseconds = (timestamp - 0x1b21dd21_3814_000) * 100
|
||||
# https://www.rfc-editor.org/rfc/rfc9562#name-example-of-a-uuidv6-value
|
||||
node = 0x9f6bdeced846
|
||||
clock_seq = (3 << 12) | 0x3c8
|
||||
|
||||
with (
|
||||
mock.patch.object(self.uuid, '_last_timestamp_v6', None),
|
||||
mock.patch('time.time_ns', return_value=fake_nanoseconds)
|
||||
):
|
||||
u = self.uuid.uuid6(node=node, clock_seq=clock_seq)
|
||||
equal(str(u).upper(), '1EC9414C-232A-6B00-B3C8-9F6BDECED846')
|
||||
# 32 16 4 12 2 14 48
|
||||
# time_hi | time_mid | ver | time_lo | var | clock_seq | node
|
||||
equal(u.time, timestamp)
|
||||
equal(u.int & 0xffff_ffff_ffff, node)
|
||||
equal((u.int >> 48) & 0x3fff, clock_seq)
|
||||
equal((u.int >> 62) & 0x3, 0b10)
|
||||
equal((u.int >> 64) & 0xfff, 0xb00)
|
||||
equal((u.int >> 76) & 0xf, 0x6)
|
||||
equal((u.int >> 80) & 0xffff, 0x232a)
|
||||
equal((u.int >> 96) & 0xffff_ffff, 0x1ec9_414c)
|
||||
|
||||
def test_uuid7(self):
|
||||
equal = self.assertEqual
|
||||
u = self.uuid.uuid7()
|
||||
equal(u.variant, self.uuid.RFC_4122)
|
||||
equal(u.version, 7)
|
||||
|
||||
# 1 Jan 2023 12:34:56.123_456_789
|
||||
timestamp_ns = 1672533296_123_456_789 # ns precision
|
||||
timestamp_ms, _ = divmod(timestamp_ns, 1_000_000)
|
||||
|
||||
for _ in range(100):
|
||||
counter_hi = random.getrandbits(11)
|
||||
counter_lo = random.getrandbits(30)
|
||||
counter = (counter_hi << 30) | counter_lo
|
||||
|
||||
tail = random.getrandbits(32)
|
||||
# effective number of bits is 32 + 30 + 11 = 73
|
||||
random_bits = counter << 32 | tail
|
||||
|
||||
# set all remaining MSB of fake random bits to 1 to ensure that
|
||||
# the implementation correctly removes them
|
||||
random_bits = (((1 << 7) - 1) << 73) | random_bits
|
||||
random_data = random_bits.to_bytes(10)
|
||||
|
||||
with (
|
||||
mock.patch.multiple(
|
||||
self.uuid,
|
||||
_last_timestamp_v7=None,
|
||||
_last_counter_v7=0,
|
||||
),
|
||||
mock.patch('time.time_ns', return_value=timestamp_ns),
|
||||
mock.patch('os.urandom', return_value=random_data) as urand
|
||||
):
|
||||
u = self.uuid.uuid7()
|
||||
urand.assert_called_once_with(10)
|
||||
equal(u.variant, self.uuid.RFC_4122)
|
||||
equal(u.version, 7)
|
||||
|
||||
equal(self.uuid._last_timestamp_v7, timestamp_ms)
|
||||
equal(self.uuid._last_counter_v7, counter)
|
||||
|
||||
unix_ts_ms = timestamp_ms & 0xffff_ffff_ffff
|
||||
equal(u.time, unix_ts_ms)
|
||||
equal((u.int >> 80) & 0xffff_ffff_ffff, unix_ts_ms)
|
||||
|
||||
equal((u.int >> 75) & 1, 0) # check that the MSB is 0
|
||||
equal((u.int >> 64) & 0xfff, counter_hi)
|
||||
equal((u.int >> 32) & 0x3fff_ffff, counter_lo)
|
||||
equal(u.int & 0xffff_ffff, tail)
|
||||
|
||||
def test_uuid7_uniqueness(self):
|
||||
# Test that UUIDv7-generated values are unique.
|
||||
#
|
||||
# While UUIDv8 has an entropy of 122 bits, those 122 bits may not
|
||||
# necessarily be sampled from a PRNG. On the other hand, UUIDv7
|
||||
# uses os.urandom() as a PRNG which features better randomness.
|
||||
N = 1000
|
||||
uuids = {self.uuid.uuid7() for _ in range(N)}
|
||||
self.assertEqual(len(uuids), N)
|
||||
|
||||
versions = {u.version for u in uuids}
|
||||
self.assertSetEqual(versions, {7})
|
||||
|
||||
def test_uuid7_monotonicity(self):
|
||||
equal = self.assertEqual
|
||||
|
||||
us = [self.uuid.uuid7() for _ in range(10_000)]
|
||||
equal(us, sorted(us))
|
||||
|
||||
with mock.patch.multiple(
|
||||
self.uuid,
|
||||
_last_timestamp_v7=0,
|
||||
_last_counter_v7=0,
|
||||
):
|
||||
# 1 Jan 2023 12:34:56.123_456_789
|
||||
timestamp_ns = 1672533296_123_456_789 # ns precision
|
||||
timestamp_ms, _ = divmod(timestamp_ns, 1_000_000)
|
||||
|
||||
# counter_{hi,lo} are chosen so that "counter + 1" does not overflow
|
||||
counter_hi = random.getrandbits(11)
|
||||
counter_lo = random.getrandbits(29)
|
||||
counter = (counter_hi << 30) | counter_lo
|
||||
self.assertLess(counter + 1, 0x3ff_ffff_ffff)
|
||||
|
||||
tail = random.getrandbits(32)
|
||||
random_bits = counter << 32 | tail
|
||||
random_data = random_bits.to_bytes(10)
|
||||
|
||||
with (
|
||||
mock.patch('time.time_ns', return_value=timestamp_ns),
|
||||
mock.patch('os.urandom', return_value=random_data) as urand
|
||||
):
|
||||
u1 = self.uuid.uuid7()
|
||||
urand.assert_called_once_with(10)
|
||||
equal(self.uuid._last_timestamp_v7, timestamp_ms)
|
||||
equal(self.uuid._last_counter_v7, counter)
|
||||
equal(u1.time, timestamp_ms)
|
||||
equal((u1.int >> 64) & 0xfff, counter_hi)
|
||||
equal((u1.int >> 32) & 0x3fff_ffff, counter_lo)
|
||||
equal(u1.int & 0xffff_ffff, tail)
|
||||
|
||||
# 1 Jan 2023 12:34:56.123_457_032 (same millisecond but not same ns)
|
||||
next_timestamp_ns = 1672533296_123_457_032
|
||||
next_timestamp_ms, _ = divmod(timestamp_ns, 1_000_000)
|
||||
equal(timestamp_ms, next_timestamp_ms)
|
||||
|
||||
next_tail_bytes = os.urandom(4)
|
||||
next_fail = int.from_bytes(next_tail_bytes)
|
||||
|
||||
with (
|
||||
mock.patch('time.time_ns', return_value=next_timestamp_ns),
|
||||
mock.patch('os.urandom', return_value=next_tail_bytes) as urand
|
||||
):
|
||||
u2 = self.uuid.uuid7()
|
||||
urand.assert_called_once_with(4)
|
||||
# same milli-second
|
||||
equal(self.uuid._last_timestamp_v7, timestamp_ms)
|
||||
# 42-bit counter advanced by 1
|
||||
equal(self.uuid._last_counter_v7, counter + 1)
|
||||
equal(u2.time, timestamp_ms)
|
||||
equal((u2.int >> 64) & 0xfff, counter_hi)
|
||||
equal((u2.int >> 32) & 0x3fff_ffff, counter_lo + 1)
|
||||
equal(u2.int & 0xffff_ffff, next_fail)
|
||||
|
||||
self.assertLess(u1, u2)
|
||||
|
||||
def test_uuid7_timestamp_backwards(self):
|
||||
equal = self.assertEqual
|
||||
# 1 Jan 2023 12:34:56.123_456_789
|
||||
timestamp_ns = 1672533296_123_456_789 # ns precision
|
||||
timestamp_ms, _ = divmod(timestamp_ns, 1_000_000)
|
||||
fake_last_timestamp_v7 = timestamp_ms + 1
|
||||
|
||||
# counter_{hi,lo} are chosen so that "counter + 1" does not overflow
|
||||
counter_hi = random.getrandbits(11)
|
||||
counter_lo = random.getrandbits(29)
|
||||
counter = (counter_hi << 30) | counter_lo
|
||||
self.assertLess(counter + 1, 0x3ff_ffff_ffff)
|
||||
|
||||
tail_bytes = os.urandom(4)
|
||||
tail = int.from_bytes(tail_bytes)
|
||||
|
||||
with (
|
||||
mock.patch.multiple(
|
||||
self.uuid,
|
||||
_last_timestamp_v7=fake_last_timestamp_v7,
|
||||
_last_counter_v7=counter,
|
||||
),
|
||||
mock.patch('time.time_ns', return_value=timestamp_ns),
|
||||
mock.patch('os.urandom', return_value=tail_bytes) as urand
|
||||
):
|
||||
u = self.uuid.uuid7()
|
||||
urand.assert_called_once_with(4)
|
||||
equal(u.variant, self.uuid.RFC_4122)
|
||||
equal(u.version, 7)
|
||||
equal(self.uuid._last_timestamp_v7, fake_last_timestamp_v7 + 1)
|
||||
unix_ts_ms = (fake_last_timestamp_v7 + 1) & 0xffff_ffff_ffff
|
||||
equal(u.time, unix_ts_ms)
|
||||
equal((u.int >> 80) & 0xffff_ffff_ffff, unix_ts_ms)
|
||||
# 42-bit counter advanced by 1
|
||||
equal(self.uuid._last_counter_v7, counter + 1)
|
||||
equal((u.int >> 64) & 0xfff, counter_hi)
|
||||
# 42-bit counter advanced by 1 (counter_hi is untouched)
|
||||
equal((u.int >> 32) & 0x3fff_ffff, counter_lo + 1)
|
||||
equal(u.int & 0xffff_ffff, tail)
|
||||
|
||||
def test_uuid7_overflow_counter(self):
|
||||
equal = self.assertEqual
|
||||
# 1 Jan 2023 12:34:56.123_456_789
|
||||
timestamp_ns = 1672533296_123_456_789 # ns precision
|
||||
timestamp_ms, _ = divmod(timestamp_ns, 1_000_000)
|
||||
|
||||
new_counter_hi = random.getrandbits(11)
|
||||
new_counter_lo = random.getrandbits(30)
|
||||
new_counter = (new_counter_hi << 30) | new_counter_lo
|
||||
|
||||
tail = random.getrandbits(32)
|
||||
random_bits = (new_counter << 32) | tail
|
||||
random_data = random_bits.to_bytes(10)
|
||||
|
||||
with (
|
||||
mock.patch.multiple(
|
||||
self.uuid,
|
||||
_last_timestamp_v7=timestamp_ms,
|
||||
# same timestamp, but force an overflow on the counter
|
||||
_last_counter_v7=0x3ff_ffff_ffff,
|
||||
),
|
||||
mock.patch('time.time_ns', return_value=timestamp_ns),
|
||||
mock.patch('os.urandom', return_value=random_data) as urand
|
||||
):
|
||||
u = self.uuid.uuid7()
|
||||
urand.assert_called_with(10)
|
||||
equal(u.variant, self.uuid.RFC_4122)
|
||||
equal(u.version, 7)
|
||||
# timestamp advanced due to overflow
|
||||
equal(self.uuid._last_timestamp_v7, timestamp_ms + 1)
|
||||
unix_ts_ms = (timestamp_ms + 1) & 0xffff_ffff_ffff
|
||||
equal(u.time, unix_ts_ms)
|
||||
equal((u.int >> 80) & 0xffff_ffff_ffff, unix_ts_ms)
|
||||
# counter overflowed, so we picked a new one
|
||||
equal(self.uuid._last_counter_v7, new_counter)
|
||||
equal((u.int >> 64) & 0xfff, new_counter_hi)
|
||||
equal((u.int >> 32) & 0x3fff_ffff, new_counter_lo)
|
||||
equal(u.int & 0xffff_ffff, tail)
|
||||
|
||||
def test_uuid8(self):
|
||||
equal = self.assertEqual
|
||||
u = self.uuid.uuid8()
|
||||
|
||||
equal(u.variant, self.uuid.RFC_4122)
|
||||
equal(u.version, 8)
|
||||
|
||||
for (_, hi, mid, lo) in product(
|
||||
range(10), # repeat 10 times
|
||||
[None, 0, random.getrandbits(48)],
|
||||
[None, 0, random.getrandbits(12)],
|
||||
[None, 0, random.getrandbits(62)],
|
||||
):
|
||||
u = self.uuid.uuid8(hi, mid, lo)
|
||||
equal(u.variant, self.uuid.RFC_4122)
|
||||
equal(u.version, 8)
|
||||
if hi is not None:
|
||||
equal((u.int >> 80) & 0xffffffffffff, hi)
|
||||
if mid is not None:
|
||||
equal((u.int >> 64) & 0xfff, mid)
|
||||
if lo is not None:
|
||||
equal(u.int & 0x3fffffffffffffff, lo)
|
||||
|
||||
def test_uuid8_uniqueness(self):
|
||||
# Test that UUIDv8-generated values are unique (up to a negligible
|
||||
# probability of failure). There are 122 bits of entropy and assuming
|
||||
# that the underlying mt-19937-based random generator is sufficiently
|
||||
# good, it is unlikely to have a collision of two UUIDs.
|
||||
N = 1000
|
||||
uuids = {self.uuid.uuid8() for _ in range(N)}
|
||||
self.assertEqual(len(uuids), N)
|
||||
|
||||
versions = {u.version for u in uuids}
|
||||
self.assertSetEqual(versions, {8})
|
||||
|
||||
@support.requires_fork()
|
||||
def testIssue8621(self):
|
||||
# On at least some versions of OSX self.uuid.uuid4 generates
|
||||
@@ -710,6 +1140,23 @@ class BaseTestUUID:
|
||||
weak = weakref.ref(strong)
|
||||
self.assertIs(strong, weak())
|
||||
|
||||
|
||||
class CommandLineTestCases:
|
||||
uuid = None # to be defined in subclasses
|
||||
|
||||
def do_test_standalone_uuid(self, version):
|
||||
stdout = io.StringIO()
|
||||
with contextlib.redirect_stdout(stdout):
|
||||
self.uuid.main()
|
||||
output = stdout.getvalue().strip()
|
||||
u = self.uuid.UUID(output)
|
||||
self.assertEqual(output, str(u))
|
||||
self.assertEqual(u.version, version)
|
||||
|
||||
@mock.patch.object(sys, "argv", ["", "-u", "uuid1"])
|
||||
def test_cli_uuid1(self):
|
||||
self.do_test_standalone_uuid(1)
|
||||
|
||||
@mock.patch.object(sys, "argv", ["", "-u", "uuid3", "-n", "@dns"])
|
||||
@mock.patch('sys.stderr', new_callable=io.StringIO)
|
||||
def test_cli_namespace_required_for_uuid3(self, mock_err):
|
||||
@@ -742,6 +1189,20 @@ class BaseTestUUID:
|
||||
self.assertEqual(output, str(uuid_output))
|
||||
self.assertEqual(uuid_output.version, 4)
|
||||
|
||||
@mock.patch.object(sys, "argv", ["", "-C", "3"])
|
||||
def test_cli_uuid4_outputted_with_count(self):
|
||||
stdout = io.StringIO()
|
||||
with contextlib.redirect_stdout(stdout):
|
||||
self.uuid.main()
|
||||
|
||||
output = stdout.getvalue().strip().splitlines()
|
||||
|
||||
# Check that 3 UUIDs in the format of uuid4 have been generated
|
||||
self.assertEqual(len(output), 3)
|
||||
for o in output:
|
||||
uuid_output = self.uuid.UUID(o)
|
||||
self.assertEqual(uuid_output.version, 4)
|
||||
|
||||
@mock.patch.object(sys, "argv",
|
||||
["", "-u", "uuid3", "-n", "@dns", "-N", "python.org"])
|
||||
def test_cli_uuid3_ouputted_with_valid_namespace_and_name(self):
|
||||
@@ -770,13 +1231,25 @@ class BaseTestUUID:
|
||||
self.assertEqual(output, str(uuid_output))
|
||||
self.assertEqual(uuid_output.version, 5)
|
||||
|
||||
@mock.patch.object(sys, "argv", ["", "-u", "uuid6"])
|
||||
def test_cli_uuid6(self):
|
||||
self.do_test_standalone_uuid(6)
|
||||
|
||||
class TestUUIDWithoutExtModule(BaseTestUUID, unittest.TestCase):
|
||||
@mock.patch.object(sys, "argv", ["", "-u", "uuid7"])
|
||||
def test_cli_uuid7(self):
|
||||
self.do_test_standalone_uuid(7)
|
||||
|
||||
@mock.patch.object(sys, "argv", ["", "-u", "uuid8"])
|
||||
def test_cli_uuid8(self):
|
||||
self.do_test_standalone_uuid(8)
|
||||
|
||||
|
||||
class TestUUIDWithoutExtModule(CommandLineTestCases, BaseTestUUID, unittest.TestCase):
|
||||
uuid = py_uuid
|
||||
|
||||
|
||||
@unittest.skipUnless(c_uuid, 'requires the C _uuid module')
|
||||
class TestUUIDWithExtModule(BaseTestUUID, unittest.TestCase):
|
||||
class TestUUIDWithExtModule(CommandLineTestCases, BaseTestUUID, unittest.TestCase):
|
||||
uuid = c_uuid
|
||||
|
||||
def check_has_stable_libuuid_extractable_node(self):
|
||||
|
||||
341
Lib/uuid.py
vendored
341
Lib/uuid.py
vendored
@@ -1,8 +1,12 @@
|
||||
r"""UUID objects (universally unique identifiers) according to RFC 4122.
|
||||
r"""UUID objects (universally unique identifiers) according to RFC 4122/9562.
|
||||
|
||||
This module provides immutable UUID objects (class UUID) and the functions
|
||||
uuid1(), uuid3(), uuid4(), uuid5() for generating version 1, 3, 4, and 5
|
||||
UUIDs as specified in RFC 4122.
|
||||
This module provides immutable UUID objects (class UUID) and functions for
|
||||
generating UUIDs corresponding to a specific UUID version as specified in
|
||||
RFC 4122/9562, e.g., uuid1() for UUID version 1, uuid3() for UUID version 3,
|
||||
and so on.
|
||||
|
||||
Note that UUID version 2 is deliberately omitted as it is outside the scope
|
||||
of the RFC.
|
||||
|
||||
If all you want is a unique ID, you should probably call uuid1() or uuid4().
|
||||
Note that uuid1() may compromise privacy since it creates a UUID containing
|
||||
@@ -42,10 +46,19 @@ Typical usage:
|
||||
# make a UUID from a 16-byte string
|
||||
>>> uuid.UUID(bytes=x.bytes)
|
||||
UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')
|
||||
|
||||
# get the Nil UUID
|
||||
>>> uuid.NIL
|
||||
UUID('00000000-0000-0000-0000-000000000000')
|
||||
|
||||
# get the Max UUID
|
||||
>>> uuid.MAX
|
||||
UUID('ffffffff-ffff-ffff-ffff-ffffffffffff')
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
from enum import Enum, _simple_enum
|
||||
|
||||
@@ -85,6 +98,19 @@ class SafeUUID:
|
||||
unknown = None
|
||||
|
||||
|
||||
_UINT_128_MAX = (1 << 128) - 1
|
||||
# 128-bit mask to clear the variant and version bits of a UUID integral value
|
||||
_RFC_4122_CLEARFLAGS_MASK = ~((0xf000 << 64) | (0xc000 << 48))
|
||||
# RFC 4122 variant bits and version bits to activate on a UUID integral value.
|
||||
_RFC_4122_VERSION_1_FLAGS = ((1 << 76) | (0x8000 << 48))
|
||||
_RFC_4122_VERSION_3_FLAGS = ((3 << 76) | (0x8000 << 48))
|
||||
_RFC_4122_VERSION_4_FLAGS = ((4 << 76) | (0x8000 << 48))
|
||||
_RFC_4122_VERSION_5_FLAGS = ((5 << 76) | (0x8000 << 48))
|
||||
_RFC_4122_VERSION_6_FLAGS = ((6 << 76) | (0x8000 << 48))
|
||||
_RFC_4122_VERSION_7_FLAGS = ((7 << 76) | (0x8000 << 48))
|
||||
_RFC_4122_VERSION_8_FLAGS = ((8 << 76) | (0x8000 << 48))
|
||||
|
||||
|
||||
class UUID:
|
||||
"""Instances of the UUID class represent UUIDs as specified in RFC 4122.
|
||||
UUID objects are immutable, hashable, and usable as dictionary keys.
|
||||
@@ -108,7 +134,16 @@ class UUID:
|
||||
|
||||
fields a tuple of the six integer fields of the UUID,
|
||||
which are also available as six individual attributes
|
||||
and two derived attributes:
|
||||
and two derived attributes. Those attributes are not
|
||||
always relevant to all UUID versions:
|
||||
|
||||
The 'time_*' attributes are only relevant to version 1.
|
||||
|
||||
The 'clock_seq*' and 'node' attributes are only relevant
|
||||
to versions 1 and 6.
|
||||
|
||||
The 'time' attribute is only relevant to versions 1, 6
|
||||
and 7.
|
||||
|
||||
time_low the first 32 bits of the UUID
|
||||
time_mid the next 16 bits of the UUID
|
||||
@@ -117,19 +152,20 @@ class UUID:
|
||||
clock_seq_low the next 8 bits of the UUID
|
||||
node the last 48 bits of the UUID
|
||||
|
||||
time the 60-bit timestamp
|
||||
time the 60-bit timestamp for UUIDv1/v6,
|
||||
or the 48-bit timestamp for UUIDv7
|
||||
clock_seq the 14-bit sequence number
|
||||
|
||||
hex the UUID as a 32-character hexadecimal string
|
||||
|
||||
int the UUID as a 128-bit integer
|
||||
|
||||
urn the UUID as a URN as specified in RFC 4122
|
||||
urn the UUID as a URN as specified in RFC 4122/9562
|
||||
|
||||
variant the UUID variant (one of the constants RESERVED_NCS,
|
||||
RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE)
|
||||
|
||||
version the UUID version number (1 through 5, meaningful only
|
||||
version the UUID version number (1 through 8, meaningful only
|
||||
when the variant is RFC_4122)
|
||||
|
||||
is_safe An enum indicating whether the UUID has been generated in
|
||||
@@ -174,57 +210,69 @@ class UUID:
|
||||
if [hex, bytes, bytes_le, fields, int].count(None) != 4:
|
||||
raise TypeError('one of the hex, bytes, bytes_le, fields, '
|
||||
'or int arguments must be given')
|
||||
if hex is not None:
|
||||
if int is not None:
|
||||
pass
|
||||
elif hex is not None:
|
||||
hex = hex.replace('urn:', '').replace('uuid:', '')
|
||||
hex = hex.strip('{}').replace('-', '')
|
||||
if len(hex) != 32:
|
||||
raise ValueError('badly formed hexadecimal UUID string')
|
||||
int = int_(hex, 16)
|
||||
if bytes_le is not None:
|
||||
elif bytes_le is not None:
|
||||
if len(bytes_le) != 16:
|
||||
raise ValueError('bytes_le is not a 16-char string')
|
||||
assert isinstance(bytes_le, bytes_), repr(bytes_le)
|
||||
bytes = (bytes_le[4-1::-1] + bytes_le[6-1:4-1:-1] +
|
||||
bytes_le[8-1:6-1:-1] + bytes_le[8:])
|
||||
if bytes is not None:
|
||||
int = int_.from_bytes(bytes) # big endian
|
||||
elif bytes is not None:
|
||||
if len(bytes) != 16:
|
||||
raise ValueError('bytes is not a 16-char string')
|
||||
assert isinstance(bytes, bytes_), repr(bytes)
|
||||
int = int_.from_bytes(bytes) # big endian
|
||||
if fields is not None:
|
||||
elif fields is not None:
|
||||
if len(fields) != 6:
|
||||
raise ValueError('fields is not a 6-tuple')
|
||||
(time_low, time_mid, time_hi_version,
|
||||
clock_seq_hi_variant, clock_seq_low, node) = fields
|
||||
if not 0 <= time_low < 1<<32:
|
||||
if not 0 <= time_low < (1 << 32):
|
||||
raise ValueError('field 1 out of range (need a 32-bit value)')
|
||||
if not 0 <= time_mid < 1<<16:
|
||||
if not 0 <= time_mid < (1 << 16):
|
||||
raise ValueError('field 2 out of range (need a 16-bit value)')
|
||||
if not 0 <= time_hi_version < 1<<16:
|
||||
if not 0 <= time_hi_version < (1 << 16):
|
||||
raise ValueError('field 3 out of range (need a 16-bit value)')
|
||||
if not 0 <= clock_seq_hi_variant < 1<<8:
|
||||
if not 0 <= clock_seq_hi_variant < (1 << 8):
|
||||
raise ValueError('field 4 out of range (need an 8-bit value)')
|
||||
if not 0 <= clock_seq_low < 1<<8:
|
||||
if not 0 <= clock_seq_low < (1 << 8):
|
||||
raise ValueError('field 5 out of range (need an 8-bit value)')
|
||||
if not 0 <= node < 1<<48:
|
||||
if not 0 <= node < (1 << 48):
|
||||
raise ValueError('field 6 out of range (need a 48-bit value)')
|
||||
clock_seq = (clock_seq_hi_variant << 8) | clock_seq_low
|
||||
int = ((time_low << 96) | (time_mid << 80) |
|
||||
(time_hi_version << 64) | (clock_seq << 48) | node)
|
||||
if int is not None:
|
||||
if not 0 <= int < 1<<128:
|
||||
raise ValueError('int is out of range (need a 128-bit value)')
|
||||
if not 0 <= int <= _UINT_128_MAX:
|
||||
raise ValueError('int is out of range (need a 128-bit value)')
|
||||
if version is not None:
|
||||
if not 1 <= version <= 5:
|
||||
if not 1 <= version <= 8:
|
||||
raise ValueError('illegal version number')
|
||||
# Set the variant to RFC 4122.
|
||||
int &= ~(0xc000 << 48)
|
||||
int |= 0x8000 << 48
|
||||
# clear the variant and the version number bits
|
||||
int &= _RFC_4122_CLEARFLAGS_MASK
|
||||
# Set the variant to RFC 4122/9562.
|
||||
int |= 0x8000_0000_0000_0000 # (0x8000 << 48)
|
||||
# Set the version number.
|
||||
int &= ~(0xf000 << 64)
|
||||
int |= version << 76
|
||||
object.__setattr__(self, 'int', int)
|
||||
object.__setattr__(self, 'is_safe', is_safe)
|
||||
|
||||
@classmethod
|
||||
def _from_int(cls, value):
|
||||
"""Create a UUID from an integer *value*. Internal use only."""
|
||||
assert 0 <= value <= _UINT_128_MAX, repr(value)
|
||||
self = object.__new__(cls)
|
||||
object.__setattr__(self, 'int', value)
|
||||
object.__setattr__(self, 'is_safe', SafeUUID.unknown)
|
||||
return self
|
||||
|
||||
def __getstate__(self):
|
||||
d = {'int': self.int}
|
||||
if self.is_safe != SafeUUID.unknown:
|
||||
@@ -281,9 +329,8 @@ class UUID:
|
||||
raise TypeError('UUID objects are immutable')
|
||||
|
||||
def __str__(self):
|
||||
hex = '%032x' % self.int
|
||||
return '%s-%s-%s-%s-%s' % (
|
||||
hex[:8], hex[8:12], hex[12:16], hex[16:20], hex[20:])
|
||||
x = self.hex
|
||||
return f'{x[:8]}-{x[8:12]}-{x[12:16]}-{x[16:20]}-{x[20:]}'
|
||||
|
||||
@property
|
||||
def bytes(self):
|
||||
@@ -322,8 +369,22 @@ class UUID:
|
||||
|
||||
@property
|
||||
def time(self):
|
||||
return (((self.time_hi_version & 0x0fff) << 48) |
|
||||
(self.time_mid << 32) | self.time_low)
|
||||
if self.version == 6:
|
||||
# time_hi (32) | time_mid (16) | ver (4) | time_lo (12) | ... (64)
|
||||
time_hi = self.int >> 96
|
||||
time_lo = (self.int >> 64) & 0x0fff
|
||||
return time_hi << 28 | (self.time_mid << 12) | time_lo
|
||||
elif self.version == 7:
|
||||
# unix_ts_ms (48) | ... (80)
|
||||
return self.int >> 80
|
||||
else:
|
||||
# time_lo (32) | time_mid (16) | ver (4) | time_hi (12) | ... (64)
|
||||
#
|
||||
# For compatibility purposes, we do not warn or raise when the
|
||||
# version is not 1 (timestamp is irrelevant to other versions).
|
||||
time_hi = (self.int >> 64) & 0x0fff
|
||||
time_lo = self.int >> 96
|
||||
return time_hi << 48 | (self.time_mid << 32) | time_lo
|
||||
|
||||
@property
|
||||
def clock_seq(self):
|
||||
@@ -336,7 +397,7 @@ class UUID:
|
||||
|
||||
@property
|
||||
def hex(self):
|
||||
return '%032x' % self.int
|
||||
return self.bytes.hex()
|
||||
|
||||
@property
|
||||
def urn(self):
|
||||
@@ -355,7 +416,7 @@ class UUID:
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
# The version bits are only meaningful for RFC 4122 UUIDs.
|
||||
# The version bits are only meaningful for RFC 4122/9562 UUIDs.
|
||||
if self.variant == RFC_4122:
|
||||
return int((self.int >> 76) & 0xf)
|
||||
|
||||
@@ -374,7 +435,7 @@ def _get_command_stdout(command, *args):
|
||||
# for are actually localized, but in theory some system could do so.)
|
||||
env = dict(os.environ)
|
||||
env['LC_ALL'] = 'C'
|
||||
# Empty strings will be quoted by popen so we should just ommit it
|
||||
# Empty strings will be quoted by popen so we should just omit it
|
||||
if args != ('',):
|
||||
command = (executable, *args)
|
||||
else:
|
||||
@@ -572,7 +633,7 @@ def _netstat_getnode():
|
||||
try:
|
||||
import _uuid
|
||||
_generate_time_safe = getattr(_uuid, "generate_time_safe", None)
|
||||
_has_stable_extractable_node = getattr(_uuid, "has_stable_extractable_node", False)
|
||||
_has_stable_extractable_node = _uuid.has_stable_extractable_node
|
||||
_UuidCreate = getattr(_uuid, "UuidCreate", None)
|
||||
except ImportError:
|
||||
_uuid = None
|
||||
@@ -679,7 +740,6 @@ def uuid1(node=None, clock_seq=None):
|
||||
return UUID(bytes=uuid_time, is_safe=is_safe)
|
||||
|
||||
global _last_timestamp
|
||||
import time
|
||||
nanoseconds = time.time_ns()
|
||||
# 0x01b21dd213814000 is the number of 100-ns intervals between the
|
||||
# UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
|
||||
@@ -704,24 +764,171 @@ def uuid3(namespace, name):
|
||||
"""Generate a UUID from the MD5 hash of a namespace UUID and a name."""
|
||||
if isinstance(name, str):
|
||||
name = bytes(name, "utf-8")
|
||||
from hashlib import md5
|
||||
digest = md5(
|
||||
namespace.bytes + name,
|
||||
usedforsecurity=False
|
||||
).digest()
|
||||
return UUID(bytes=digest[:16], version=3)
|
||||
import hashlib
|
||||
h = hashlib.md5(namespace.bytes + name, usedforsecurity=False)
|
||||
int_uuid_3 = int.from_bytes(h.digest())
|
||||
int_uuid_3 &= _RFC_4122_CLEARFLAGS_MASK
|
||||
int_uuid_3 |= _RFC_4122_VERSION_3_FLAGS
|
||||
return UUID._from_int(int_uuid_3)
|
||||
|
||||
def uuid4():
|
||||
"""Generate a random UUID."""
|
||||
return UUID(bytes=os.urandom(16), version=4)
|
||||
int_uuid_4 = int.from_bytes(os.urandom(16))
|
||||
int_uuid_4 &= _RFC_4122_CLEARFLAGS_MASK
|
||||
int_uuid_4 |= _RFC_4122_VERSION_4_FLAGS
|
||||
return UUID._from_int(int_uuid_4)
|
||||
|
||||
def uuid5(namespace, name):
|
||||
"""Generate a UUID from the SHA-1 hash of a namespace UUID and a name."""
|
||||
if isinstance(name, str):
|
||||
name = bytes(name, "utf-8")
|
||||
from hashlib import sha1
|
||||
hash = sha1(namespace.bytes + name).digest()
|
||||
return UUID(bytes=hash[:16], version=5)
|
||||
import hashlib
|
||||
h = hashlib.sha1(namespace.bytes + name, usedforsecurity=False)
|
||||
int_uuid_5 = int.from_bytes(h.digest()[:16])
|
||||
int_uuid_5 &= _RFC_4122_CLEARFLAGS_MASK
|
||||
int_uuid_5 |= _RFC_4122_VERSION_5_FLAGS
|
||||
return UUID._from_int(int_uuid_5)
|
||||
|
||||
|
||||
_last_timestamp_v6 = None
|
||||
|
||||
def uuid6(node=None, clock_seq=None):
|
||||
"""Similar to :func:`uuid1` but where fields are ordered differently
|
||||
for improved DB locality.
|
||||
|
||||
More precisely, given a 60-bit timestamp value as specified for UUIDv1,
|
||||
for UUIDv6 the first 48 most significant bits are stored first, followed
|
||||
by the 4-bit version (same position), followed by the remaining 12 bits
|
||||
of the original 60-bit timestamp.
|
||||
"""
|
||||
global _last_timestamp_v6
|
||||
import time
|
||||
nanoseconds = time.time_ns()
|
||||
# 0x01b21dd213814000 is the number of 100-ns intervals between the
|
||||
# UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
|
||||
timestamp = nanoseconds // 100 + 0x01b21dd213814000
|
||||
if _last_timestamp_v6 is not None and timestamp <= _last_timestamp_v6:
|
||||
timestamp = _last_timestamp_v6 + 1
|
||||
_last_timestamp_v6 = timestamp
|
||||
if clock_seq is None:
|
||||
import random
|
||||
clock_seq = random.getrandbits(14) # instead of stable storage
|
||||
time_hi_and_mid = (timestamp >> 12) & 0xffff_ffff_ffff
|
||||
time_lo = timestamp & 0x0fff # keep 12 bits and clear version bits
|
||||
clock_s = clock_seq & 0x3fff # keep 14 bits and clear variant bits
|
||||
if node is None:
|
||||
node = getnode()
|
||||
# --- 32 + 16 --- -- 4 -- -- 12 -- -- 2 -- -- 14 --- 48
|
||||
# time_hi_and_mid | version | time_lo | variant | clock_seq | node
|
||||
int_uuid_6 = time_hi_and_mid << 80
|
||||
int_uuid_6 |= time_lo << 64
|
||||
int_uuid_6 |= clock_s << 48
|
||||
int_uuid_6 |= node & 0xffff_ffff_ffff
|
||||
# by construction, the variant and version bits are already cleared
|
||||
int_uuid_6 |= _RFC_4122_VERSION_6_FLAGS
|
||||
return UUID._from_int(int_uuid_6)
|
||||
|
||||
|
||||
_last_timestamp_v7 = None
|
||||
_last_counter_v7 = 0 # 42-bit counter
|
||||
|
||||
def _uuid7_get_counter_and_tail():
|
||||
rand = int.from_bytes(os.urandom(10))
|
||||
# 42-bit counter with MSB set to 0
|
||||
counter = (rand >> 32) & 0x1ff_ffff_ffff
|
||||
# 32-bit random data
|
||||
tail = rand & 0xffff_ffff
|
||||
return counter, tail
|
||||
|
||||
|
||||
def uuid7():
|
||||
"""Generate a UUID from a Unix timestamp in milliseconds and random bits.
|
||||
|
||||
UUIDv7 objects feature monotonicity within a millisecond.
|
||||
"""
|
||||
# --- 48 --- -- 4 -- --- 12 --- -- 2 -- --- 30 --- - 32 -
|
||||
# unix_ts_ms | version | counter_hi | variant | counter_lo | random
|
||||
#
|
||||
# 'counter = counter_hi | counter_lo' is a 42-bit counter constructed
|
||||
# with Method 1 of RFC 9562, §6.2, and its MSB is set to 0.
|
||||
#
|
||||
# 'random' is a 32-bit random value regenerated for every new UUID.
|
||||
#
|
||||
# If multiple UUIDs are generated within the same millisecond, the LSB
|
||||
# of 'counter' is incremented by 1. When overflowing, the timestamp is
|
||||
# advanced and the counter is reset to a random 42-bit integer with MSB
|
||||
# set to 0.
|
||||
|
||||
global _last_timestamp_v7
|
||||
global _last_counter_v7
|
||||
|
||||
nanoseconds = time.time_ns()
|
||||
timestamp_ms = nanoseconds // 1_000_000
|
||||
|
||||
if _last_timestamp_v7 is None or timestamp_ms > _last_timestamp_v7:
|
||||
counter, tail = _uuid7_get_counter_and_tail()
|
||||
else:
|
||||
if timestamp_ms < _last_timestamp_v7:
|
||||
timestamp_ms = _last_timestamp_v7 + 1
|
||||
# advance the 42-bit counter
|
||||
counter = _last_counter_v7 + 1
|
||||
if counter > 0x3ff_ffff_ffff:
|
||||
# advance the 48-bit timestamp
|
||||
timestamp_ms += 1
|
||||
counter, tail = _uuid7_get_counter_and_tail()
|
||||
else:
|
||||
# 32-bit random data
|
||||
tail = int.from_bytes(os.urandom(4))
|
||||
|
||||
unix_ts_ms = timestamp_ms & 0xffff_ffff_ffff
|
||||
counter_msbs = counter >> 30
|
||||
# keep 12 counter's MSBs and clear variant bits
|
||||
counter_hi = counter_msbs & 0x0fff
|
||||
# keep 30 counter's LSBs and clear version bits
|
||||
counter_lo = counter & 0x3fff_ffff
|
||||
# ensure that the tail is always a 32-bit integer (by construction,
|
||||
# it is already the case, but future interfaces may allow the user
|
||||
# to specify the random tail)
|
||||
tail &= 0xffff_ffff
|
||||
|
||||
int_uuid_7 = unix_ts_ms << 80
|
||||
int_uuid_7 |= counter_hi << 64
|
||||
int_uuid_7 |= counter_lo << 32
|
||||
int_uuid_7 |= tail
|
||||
# by construction, the variant and version bits are already cleared
|
||||
int_uuid_7 |= _RFC_4122_VERSION_7_FLAGS
|
||||
res = UUID._from_int(int_uuid_7)
|
||||
|
||||
# defer global update until all computations are done
|
||||
_last_timestamp_v7 = timestamp_ms
|
||||
_last_counter_v7 = counter
|
||||
return res
|
||||
|
||||
|
||||
def uuid8(a=None, b=None, c=None):
|
||||
"""Generate a UUID from three custom blocks.
|
||||
|
||||
* 'a' is the first 48-bit chunk of the UUID (octets 0-5);
|
||||
* 'b' is the mid 12-bit chunk (octets 6-7);
|
||||
* 'c' is the last 62-bit chunk (octets 8-15).
|
||||
|
||||
When a value is not specified, a pseudo-random value is generated.
|
||||
"""
|
||||
if a is None:
|
||||
import random
|
||||
a = random.getrandbits(48)
|
||||
if b is None:
|
||||
import random
|
||||
b = random.getrandbits(12)
|
||||
if c is None:
|
||||
import random
|
||||
c = random.getrandbits(62)
|
||||
int_uuid_8 = (a & 0xffff_ffff_ffff) << 80
|
||||
int_uuid_8 |= (b & 0xfff) << 64
|
||||
int_uuid_8 |= c & 0x3fff_ffff_ffff_ffff
|
||||
# by construction, the variant and version bits are already cleared
|
||||
int_uuid_8 |= _RFC_4122_VERSION_8_FLAGS
|
||||
return UUID._from_int(int_uuid_8)
|
||||
|
||||
|
||||
def main():
|
||||
@@ -730,7 +937,10 @@ def main():
|
||||
"uuid1": uuid1,
|
||||
"uuid3": uuid3,
|
||||
"uuid4": uuid4,
|
||||
"uuid5": uuid5
|
||||
"uuid5": uuid5,
|
||||
"uuid6": uuid6,
|
||||
"uuid7": uuid7,
|
||||
"uuid8": uuid8,
|
||||
}
|
||||
uuid_namespace_funcs = ("uuid3", "uuid5")
|
||||
namespaces = {
|
||||
@@ -742,18 +952,24 @@ def main():
|
||||
|
||||
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.")
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
description="Generate a UUID using the selected UUID function.",
|
||||
color=True,
|
||||
)
|
||||
parser.add_argument("-u", "--uuid",
|
||||
choices=uuid_funcs.keys(),
|
||||
default="uuid4",
|
||||
help="function to generate the UUID")
|
||||
parser.add_argument("-n", "--namespace",
|
||||
help="The namespace is a UUID, or '@ns' where 'ns' is a "
|
||||
"well-known predefined UUID addressed by namespace name. "
|
||||
"Such as @dns, @url, @oid, and @x500. "
|
||||
"Only required for uuid3/uuid5 functions.")
|
||||
choices=["any UUID", *namespaces.keys()],
|
||||
help="uuid3/uuid5 only: "
|
||||
"a UUID, or a well-known predefined UUID addressed "
|
||||
"by namespace name")
|
||||
parser.add_argument("-N", "--name",
|
||||
help="The name used as part of generating the uuid. "
|
||||
"Only required for uuid3/uuid5 functions.")
|
||||
help="uuid3/uuid5 only: "
|
||||
"name used as part of generating the UUID")
|
||||
parser.add_argument("-C", "--count", metavar="NUM", type=int, default=1,
|
||||
help="generate NUM fresh UUIDs")
|
||||
|
||||
args = parser.parse_args()
|
||||
uuid_func = uuid_funcs[args.uuid]
|
||||
@@ -768,9 +984,11 @@ def main():
|
||||
"Run 'python -m uuid -h' for more information."
|
||||
)
|
||||
namespace = namespaces[namespace] if namespace in namespaces else UUID(namespace)
|
||||
print(uuid_func(namespace, name))
|
||||
for _ in range(args.count):
|
||||
print(uuid_func(namespace, name))
|
||||
else:
|
||||
print(uuid_func())
|
||||
for _ in range(args.count):
|
||||
print(uuid_func())
|
||||
|
||||
|
||||
# The following standard UUIDs are for use with uuid3() or uuid5().
|
||||
@@ -780,5 +998,10 @@ NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8')
|
||||
NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8')
|
||||
NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8')
|
||||
|
||||
# RFC 9562 Sections 5.9 and 5.10 define the special Nil and Max UUID formats.
|
||||
|
||||
NIL = UUID('00000000-0000-0000-0000-000000000000')
|
||||
MAX = UUID('ffffffff-ffff-ffff-ffff-ffffffffffff')
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user