codekingpro/portable-devtools
114k
1r"""UUID objects (universally unique identifiers) according to RFC 4122/9562.2 3This module provides immutable UUID objects (class UUID) and functions for4generating UUIDs corresponding to a specific UUID version as specified in5RFC 4122/9562, e.g., uuid1() for UUID version 1, uuid3() for UUID version 3,6and so on.7 8Note that UUID version 2 is deliberately omitted as it is outside the scope9of the RFC.10 11If all you want is a unique ID, you should probably call uuid1() or uuid4().12Note that uuid1() may compromise privacy since it creates a UUID containing13the computer's network address. uuid4() creates a random UUID.14 15Typical usage:16 17 >>> import uuid18 19 # make a UUID based on the host ID and current time20 >>> uuid.uuid1() # doctest: +SKIP21 UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')22 23 # make a UUID using an MD5 hash of a namespace UUID and a name24 >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')25 UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')26 27 # make a random UUID28 >>> uuid.uuid4() # doctest: +SKIP29 UUID('16fd2706-8baf-433b-82eb-8c7fada847da')30 31 # make a UUID using a SHA-1 hash of a namespace UUID and a name32 >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')33 UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')34 35 # make a UUID from a string of hex digits (braces and hyphens ignored)36 >>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}')37 38 # convert a UUID to a string of hex digits in standard form39 >>> str(x)40 '00010203-0405-0607-0809-0a0b0c0d0e0f'41 42 # get the raw 16 bytes of the UUID43 >>> x.bytes44 b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f'45 46 # make a UUID from a 16-byte string47 >>> uuid.UUID(bytes=x.bytes)48 UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')49 50 # get the Nil UUID51 >>> uuid.NIL52 UUID('00000000-0000-0000-0000-000000000000')53 54 # get the Max UUID55 >>> uuid.MAX56 UUID('ffffffff-ffff-ffff-ffff-ffffffffffff')57"""58 59import os60import sys61import time62 63from enum import Enum, _simple_enum64 65 66__author__ = 'Ka-Ping Yee <ping@zesty.ca>'67 68# The recognized platforms - known behaviors69if sys.platform in {'win32', 'darwin', 'emscripten', 'wasi'}:70 _AIX = _LINUX = False71elif sys.platform == 'linux':72 _LINUX = True73 _AIX = False74else:75 import platform76 _platform_system = platform.system()77 _AIX = _platform_system == 'AIX'78 _LINUX = _platform_system in ('Linux', 'Android')79 80_MAC_DELIM = b':'81_MAC_OMITS_LEADING_ZEROES = False82if _AIX:83 _MAC_DELIM = b'.'84 _MAC_OMITS_LEADING_ZEROES = True85 86RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [87 'reserved for NCS compatibility', 'specified in RFC 4122',88 'reserved for Microsoft compatibility', 'reserved for future definition']89 90int_ = int # The built-in int type91bytes_ = bytes # The built-in bytes type92 93 94@_simple_enum(Enum)95class SafeUUID:96 safe = 097 unsafe = -198 unknown = None99 100 101_UINT_128_MAX = (1 << 128) - 1102# 128-bit mask to clear the variant and version bits of a UUID integral value103_RFC_4122_CLEARFLAGS_MASK = ~((0xf000 << 64) | (0xc000 << 48))104# RFC 4122 variant bits and version bits to activate on a UUID integral value.105_RFC_4122_VERSION_1_FLAGS = ((1 << 76) | (0x8000 << 48))106_RFC_4122_VERSION_3_FLAGS = ((3 << 76) | (0x8000 << 48))107_RFC_4122_VERSION_4_FLAGS = ((4 << 76) | (0x8000 << 48))108_RFC_4122_VERSION_5_FLAGS = ((5 << 76) | (0x8000 << 48))109_RFC_4122_VERSION_6_FLAGS = ((6 << 76) | (0x8000 << 48))110_RFC_4122_VERSION_7_FLAGS = ((7 << 76) | (0x8000 << 48))111_RFC_4122_VERSION_8_FLAGS = ((8 << 76) | (0x8000 << 48))112 113 114class UUID:115 """Instances of the UUID class represent UUIDs as specified in RFC 4122.116 UUID objects are immutable, hashable, and usable as dictionary keys.117 Converting a UUID to a string with str() yields something in the form118 '12345678-1234-1234-1234-123456789abc'. The UUID constructor accepts119 five possible forms: a similar string of hexadecimal digits, or a tuple120 of six integer fields (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and121 48-bit values respectively) as an argument named 'fields', or a string122 of 16 bytes (with all the integer fields in big-endian order) as an123 argument named 'bytes', or a string of 16 bytes (with the first three124 fields in little-endian order) as an argument named 'bytes_le', or a125 single 128-bit integer as an argument named 'int'.126 127 UUIDs have these read-only attributes:128 129 bytes the UUID as a 16-byte string (containing the six130 integer fields in big-endian byte order)131 132 bytes_le the UUID as a 16-byte string (with time_low, time_mid,133 and time_hi_version in little-endian byte order)134 135 fields a tuple of the six integer fields of the UUID,136 which are also available as six individual attributes137 and two derived attributes. Those attributes are not138 always relevant to all UUID versions:139 140 The 'time_*' attributes are only relevant to version 1.141 142 The 'clock_seq*' and 'node' attributes are only relevant143 to versions 1 and 6.144 145 The 'time' attribute is only relevant to versions 1, 6146 and 7.147 148 time_low the first 32 bits of the UUID149 time_mid the next 16 bits of the UUID150 time_hi_version the next 16 bits of the UUID151 clock_seq_hi_variant the next 8 bits of the UUID152 clock_seq_low the next 8 bits of the UUID153 node the last 48 bits of the UUID154 155 time the 60-bit timestamp for UUIDv1/v6,156 or the 48-bit timestamp for UUIDv7157 clock_seq the 14-bit sequence number158 159 hex the UUID as a 32-character hexadecimal string160 161 int the UUID as a 128-bit integer162 163 urn the UUID as a URN as specified in RFC 4122/9562164 165 variant the UUID variant (one of the constants RESERVED_NCS,166 RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE)167 168 version the UUID version number (1 through 8, meaningful only169 when the variant is RFC_4122)170 171 is_safe An enum indicating whether the UUID has been generated in172 a way that is safe for multiprocessing applications, via173 uuid_generate_time_safe(3).174 """175 176 __slots__ = ('int', 'is_safe', '__weakref__')177 178 def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None,179 int=None, version=None,180 *, is_safe=SafeUUID.unknown):181 r"""Create a UUID from either a string of 32 hexadecimal digits,182 a string of 16 bytes as the 'bytes' argument, a string of 16 bytes183 in little-endian order as the 'bytes_le' argument, a tuple of six184 integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version,185 8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as186 the 'fields' argument, or a single 128-bit integer as the 'int'187 argument. When a string of hex digits is given, curly braces,188 hyphens, and a URN prefix are all optional. For example, these189 expressions all yield the same UUID:190 191 UUID('{12345678-1234-5678-1234-567812345678}')192 UUID('12345678123456781234567812345678')193 UUID('urn:uuid:12345678-1234-5678-1234-567812345678')194 UUID(bytes='\x12\x34\x56\x78'*4)195 UUID(bytes_le='\x78\x56\x34\x12\x34\x12\x78\x56' +196 '\x12\x34\x56\x78\x12\x34\x56\x78')197 UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))198 UUID(int=0x12345678123456781234567812345678)199 200 Exactly one of 'hex', 'bytes', 'bytes_le', 'fields', or 'int' must201 be given. The 'version' argument is optional; if given, the resulting202 UUID will have its variant and version set according to RFC 4122,203 overriding the given 'hex', 'bytes', 'bytes_le', 'fields', or 'int'.204 205 is_safe is an enum exposed as an attribute on the instance. It206 indicates whether the UUID has been generated in a way that is safe207 for multiprocessing applications, via uuid_generate_time_safe(3).208 """209 210 if [hex, bytes, bytes_le, fields, int].count(None) != 4:211 raise TypeError('one of the hex, bytes, bytes_le, fields, '212 'or int arguments must be given')213 if int is not None:214 pass215 elif hex is not None:216 hex = hex.replace('urn:', '').replace('uuid:', '')217 hex = hex.strip('{}').replace('-', '')218 if len(hex) != 32:219 raise ValueError('badly formed hexadecimal UUID string')220 int = int_(hex, 16)221 elif bytes_le is not None:222 if len(bytes_le) != 16:223 raise ValueError('bytes_le is not a 16-char string')224 assert isinstance(bytes_le, bytes_), repr(bytes_le)225 bytes = (bytes_le[4-1::-1] + bytes_le[6-1:4-1:-1] +226 bytes_le[8-1:6-1:-1] + bytes_le[8:])227 int = int_.from_bytes(bytes) # big endian228 elif bytes is not None:229 if len(bytes) != 16:230 raise ValueError('bytes is not a 16-char string')231 assert isinstance(bytes, bytes_), repr(bytes)232 int = int_.from_bytes(bytes) # big endian233 elif fields is not None:234 if len(fields) != 6:235 raise ValueError('fields is not a 6-tuple')236 (time_low, time_mid, time_hi_version,237 clock_seq_hi_variant, clock_seq_low, node) = fields238 if not 0 <= time_low < (1 << 32):239 raise ValueError('field 1 out of range (need a 32-bit value)')240 if not 0 <= time_mid < (1 << 16):241 raise ValueError('field 2 out of range (need a 16-bit value)')242 if not 0 <= time_hi_version < (1 << 16):243 raise ValueError('field 3 out of range (need a 16-bit value)')244 if not 0 <= clock_seq_hi_variant < (1 << 8):245 raise ValueError('field 4 out of range (need an 8-bit value)')246 if not 0 <= clock_seq_low < (1 << 8):247 raise ValueError('field 5 out of range (need an 8-bit value)')248 if not 0 <= node < (1 << 48):249 raise ValueError('field 6 out of range (need a 48-bit value)')250 clock_seq = (clock_seq_hi_variant << 8) | clock_seq_low251 int = ((time_low << 96) | (time_mid << 80) |252 (time_hi_version << 64) | (clock_seq << 48) | node)253 if not 0 <= int <= _UINT_128_MAX:254 raise ValueError('int is out of range (need a 128-bit value)')255 if version is not None:256 if not 1 <= version <= 8:257 raise ValueError('illegal version number')258 # clear the variant and the version number bits259 int &= _RFC_4122_CLEARFLAGS_MASK260 # Set the variant to RFC 4122/9562.261 int |= 0x8000_0000_0000_0000 # (0x8000 << 48)262 # Set the version number.263 int |= version << 76264 object.__setattr__(self, 'int', int)265 object.__setattr__(self, 'is_safe', is_safe)266 267 @classmethod268 def _from_int(cls, value):269 """Create a UUID from an integer *value*. Internal use only."""270 assert 0 <= value <= _UINT_128_MAX, repr(value)271 self = object.__new__(cls)272 object.__setattr__(self, 'int', value)273 object.__setattr__(self, 'is_safe', SafeUUID.unknown)274 return self275 276 def __getstate__(self):277 d = {'int': self.int}278 if self.is_safe != SafeUUID.unknown:279 # is_safe is a SafeUUID instance. Return just its value, so that280 # it can be un-pickled in older Python versions without SafeUUID.281 d['is_safe'] = self.is_safe.value282 return d283 284 def __setstate__(self, state):285 object.__setattr__(self, 'int', state['int'])286 # is_safe was added in 3.7; it is also omitted when it is "unknown"287 object.__setattr__(self, 'is_safe',288 SafeUUID(state['is_safe'])289 if 'is_safe' in state else SafeUUID.unknown)290 291 def __eq__(self, other):292 if isinstance(other, UUID):293 return self.int == other.int294 return NotImplemented295 296 # Q. What's the value of being able to sort UUIDs?297 # A. Use them as keys in a B-Tree or similar mapping.298 299 def __lt__(self, other):300 if isinstance(other, UUID):301 return self.int < other.int302 return NotImplemented303 304 def __gt__(self, other):305 if isinstance(other, UUID):306 return self.int > other.int307 return NotImplemented308 309 def __le__(self, other):310 if isinstance(other, UUID):311 return self.int <= other.int312 return NotImplemented313 314 def __ge__(self, other):315 if isinstance(other, UUID):316 return self.int >= other.int317 return NotImplemented318 319 def __hash__(self):320 return hash(self.int)321 322 def __int__(self):323 return self.int324 325 def __repr__(self):326 return '%s(%r)' % (self.__class__.__name__, str(self))327 328 def __setattr__(self, name, value):329 raise TypeError('UUID objects are immutable')330 331 def __str__(self):332 x = self.hex333 return f'{x[:8]}-{x[8:12]}-{x[12:16]}-{x[16:20]}-{x[20:]}'334 335 @property336 def bytes(self):337 return self.int.to_bytes(16) # big endian338 339 @property340 def bytes_le(self):341 bytes = self.bytes342 return (bytes[4-1::-1] + bytes[6-1:4-1:-1] + bytes[8-1:6-1:-1] +343 bytes[8:])344 345 @property346 def fields(self):347 return (self.time_low, self.time_mid, self.time_hi_version,348 self.clock_seq_hi_variant, self.clock_seq_low, self.node)349 350 @property351 def time_low(self):352 return self.int >> 96353 354 @property355 def time_mid(self):356 return (self.int >> 80) & 0xffff357 358 @property359 def time_hi_version(self):360 return (self.int >> 64) & 0xffff361 362 @property363 def clock_seq_hi_variant(self):364 return (self.int >> 56) & 0xff365 366 @property367 def clock_seq_low(self):368 return (self.int >> 48) & 0xff369 370 @property371 def time(self):372 if self.version == 6:373 # time_hi (32) | time_mid (16) | ver (4) | time_lo (12) | ... (64)374 time_hi = self.int >> 96375 time_lo = (self.int >> 64) & 0x0fff376 return time_hi << 28 | (self.time_mid << 12) | time_lo377 elif self.version == 7:378 # unix_ts_ms (48) | ... (80)379 return self.int >> 80380 else:381 # time_lo (32) | time_mid (16) | ver (4) | time_hi (12) | ... (64)382 #383 # For compatibility purposes, we do not warn or raise when the384 # version is not 1 (timestamp is irrelevant to other versions).385 time_hi = (self.int >> 64) & 0x0fff386 time_lo = self.int >> 96387 return time_hi << 48 | (self.time_mid << 32) | time_lo388 389 @property390 def clock_seq(self):391 return (((self.clock_seq_hi_variant & 0x3f) << 8) |392 self.clock_seq_low)393 394 @property395 def node(self):396 return self.int & 0xffffffffffff397 398 @property399 def hex(self):400 return self.bytes.hex()401 402 @property403 def urn(self):404 return 'urn:uuid:' + str(self)405 406 @property407 def variant(self):408 if not self.int & (0x8000 << 48):409 return RESERVED_NCS410 elif not self.int & (0x4000 << 48):411 return RFC_4122412 elif not self.int & (0x2000 << 48):413 return RESERVED_MICROSOFT414 else:415 return RESERVED_FUTURE416 417 @property418 def version(self):419 # The version bits are only meaningful for RFC 4122/9562 UUIDs.420 if self.variant == RFC_4122:421 return int((self.int >> 76) & 0xf)422 423 424def _get_command_stdout(command, *args):425 import io, os, shutil, subprocess426 427 try:428 path_dirs = os.environ.get('PATH', os.defpath).split(os.pathsep)429 path_dirs.extend(['/sbin', '/usr/sbin'])430 executable = shutil.which(command, path=os.pathsep.join(path_dirs))431 if executable is None:432 return None433 # LC_ALL=C to ensure English output, stderr=DEVNULL to prevent output434 # on stderr (Note: we don't have an example where the words we search435 # for are actually localized, but in theory some system could do so.)436 env = dict(os.environ)437 env['LC_ALL'] = 'C'438 # Empty strings will be quoted by popen so we should just omit it439 if args != ('',):440 command = (executable, *args)441 else:442 command = (executable,)443 proc = subprocess.Popen(command,444 stdout=subprocess.PIPE,445 stderr=subprocess.DEVNULL,446 env=env)447 if not proc:448 return None449 stdout, stderr = proc.communicate()450 return io.BytesIO(stdout)451 except (OSError, subprocess.SubprocessError):452 return None453 454 455# For MAC (a.k.a. IEEE 802, or EUI-48) addresses, the second least significant456# bit of the first octet signifies whether the MAC address is universally (0)457# or locally (1) administered. Network cards from hardware manufacturers will458# always be universally administered to guarantee global uniqueness of the MAC459# address, but any particular machine may have other interfaces which are460# locally administered. An example of the latter is the bridge interface to461# the Touch Bar on MacBook Pros.462#463# This bit works out to be the 42nd bit counting from 1 being the least464# significant, or 1<<41. We'll prefer universally administered MAC addresses465# over locally administered ones since the former are globally unique, but466# we'll return the first of the latter found if that's all the machine has.467#468# See https://en.wikipedia.org/wiki/MAC_address#Universal_vs._local_(U/L_bit)469 470def _is_universal(mac):471 return not (mac & (1 << 41))472 473 474def _find_mac_near_keyword(command, args, keywords, get_word_index):475 """Searches a command's output for a MAC address near a keyword.476 477 Each line of words in the output is case-insensitively searched for478 any of the given keywords. Upon a match, get_word_index is invoked479 to pick a word from the line, given the index of the match. For480 example, lambda i: 0 would get the first word on the line, while481 lambda i: i - 1 would get the word preceding the keyword.482 """483 stdout = _get_command_stdout(command, args)484 if stdout is None:485 return None486 487 first_local_mac = None488 for line in stdout:489 words = line.lower().rstrip().split()490 for i in range(len(words)):491 if words[i] in keywords:492 try:493 word = words[get_word_index(i)]494 mac = int(word.replace(_MAC_DELIM, b''), 16)495 except (ValueError, IndexError):496 # Virtual interfaces, such as those provided by497 # VPNs, do not have a colon-delimited MAC address498 # as expected, but a 16-byte HWAddr separated by499 # dashes. These should be ignored in favor of a500 # real MAC address501 pass502 else:503 if _is_universal(mac):504 return mac505 first_local_mac = first_local_mac or mac506 return first_local_mac or None507 508 509def _parse_mac(word):510 # Accept 'HH:HH:HH:HH:HH:HH' MAC address (ex: '52:54:00:9d:0e:67'),511 # but reject IPv6 address (ex: 'fe80::5054:ff:fe9' or '123:2:3:4:5:6:7:8').512 #513 # Virtual interfaces, such as those provided by VPNs, do not have a514 # colon-delimited MAC address as expected, but a 16-byte HWAddr separated515 # by dashes. These should be ignored in favor of a real MAC address516 parts = word.split(_MAC_DELIM)517 if len(parts) != 6:518 return519 if _MAC_OMITS_LEADING_ZEROES:520 # (Only) on AIX the macaddr value given is not prefixed by 0, e.g.521 # en0 1500 link#2 fa.bc.de.f7.62.4 110854824 0 160133733 0 0522 # not523 # en0 1500 link#2 fa.bc.de.f7.62.04 110854824 0 160133733 0 0524 if not all(1 <= len(part) <= 2 for part in parts):525 return526 hexstr = b''.join(part.rjust(2, b'0') for part in parts)527 else:528 if not all(len(part) == 2 for part in parts):529 return530 hexstr = b''.join(parts)531 try:532 return int(hexstr, 16)533 except ValueError:534 return535 536 537def _find_mac_under_heading(command, args, heading):538 """Looks for a MAC address under a heading in a command's output.539 540 The first line of words in the output is searched for the given541 heading. Words at the same word index as the heading in subsequent542 lines are then examined to see if they look like MAC addresses.543 """544 stdout = _get_command_stdout(command, args)545 if stdout is None:546 return None547 548 keywords = stdout.readline().rstrip().split()549 try:550 column_index = keywords.index(heading)551 except ValueError:552 return None553 554 first_local_mac = None555 for line in stdout:556 words = line.rstrip().split()557 try:558 word = words[column_index]559 except IndexError:560 continue561 562 mac = _parse_mac(word)563 if mac is None:564 continue565 if _is_universal(mac):566 return mac567 if first_local_mac is None:568 first_local_mac = mac569 570 return first_local_mac571 572 573# The following functions call external programs to 'get' a macaddr value to574# be used as basis for an uuid575def _ifconfig_getnode():576 """Get the hardware address on Unix by running ifconfig."""577 # This works on Linux ('' or '-a'), Tru64 ('-av'), but not all Unixes.578 keywords = (b'hwaddr', b'ether', b'address:', b'lladdr')579 for args in ('', '-a', '-av'):580 mac = _find_mac_near_keyword('ifconfig', args, keywords, lambda i: i+1)581 if mac:582 return mac583 return None584 585def _ip_getnode():586 """Get the hardware address on Unix by running ip."""587 # This works on Linux with iproute2.588 mac = _find_mac_near_keyword('ip', 'link', [b'link/ether'], lambda i: i+1)589 if mac:590 return mac591 return None592 593def _arp_getnode():594 """Get the hardware address on Unix by running arp."""595 import os, socket596 if not hasattr(socket, "gethostbyname"):597 return None598 try:599 ip_addr = socket.gethostbyname(socket.gethostname())600 except OSError:601 return None602 603 # Try getting the MAC addr from arp based on our IP address (Solaris).604 mac = _find_mac_near_keyword('arp', '-an', [os.fsencode(ip_addr)], lambda i: -1)605 if mac:606 return mac607 608 # This works on OpenBSD609 mac = _find_mac_near_keyword('arp', '-an', [os.fsencode(ip_addr)], lambda i: i+1)610 if mac:611 return mac612 613 # This works on Linux, FreeBSD and NetBSD614 mac = _find_mac_near_keyword('arp', '-an', [os.fsencode('(%s)' % ip_addr)],615 lambda i: i+2)616 # Return None instead of 0.617 if mac:618 return mac619 return None620 621def _lanscan_getnode():622 """Get the hardware address on Unix by running lanscan."""623 # This might work on HP-UX.624 return _find_mac_near_keyword('lanscan', '-ai', [b'lan0'], lambda i: 0)625 626def _netstat_getnode():627 """Get the hardware address on Unix by running netstat."""628 # This works on AIX and might work on Tru64 UNIX.629 return _find_mac_under_heading('netstat', '-ian', b'Address')630 631 632# Import optional C extension at toplevel, to help disabling it when testing633try:634 import _uuid635 _generate_time_safe = getattr(_uuid, "generate_time_safe", None)636 _has_stable_extractable_node = _uuid.has_stable_extractable_node637 _UuidCreate = getattr(_uuid, "UuidCreate", None)638except ImportError:639 _uuid = None640 _generate_time_safe = None641 _has_stable_extractable_node = False642 _UuidCreate = None643 644 645def _unix_getnode():646 """Get the hardware address on Unix using the _uuid extension module."""647 if _generate_time_safe and _has_stable_extractable_node:648 uuid_time, _ = _generate_time_safe()649 return UUID(bytes=uuid_time).node650 651def _windll_getnode():652 """Get the hardware address on Windows using the _uuid extension module."""653 if _UuidCreate and _has_stable_extractable_node:654 uuid_bytes = _UuidCreate()655 return UUID(bytes_le=uuid_bytes).node656 657def _random_getnode():658 """Get a random node ID."""659 # RFC 9562, §6.10-3 says that660 #661 # Implementations MAY elect to obtain a 48-bit cryptographic-quality662 # random number as per Section 6.9 to use as the Node ID. [...] [and]663 # implementations MUST set the least significant bit of the first octet664 # of the Node ID to 1. This bit is the unicast or multicast bit, which665 # will never be set in IEEE 802 addresses obtained from network cards.666 #667 # The "multicast bit" of a MAC address is defined to be "the least668 # significant bit of the first octet". This works out to be the 41st bit669 # counting from 1 being the least significant bit, or 1<<40.670 #671 # See https://en.wikipedia.org/w/index.php?title=MAC_address&oldid=1128764812#Universal_vs._local_(U/L_bit)672 return int.from_bytes(os.urandom(6)) | (1 << 40)673 674 675# _OS_GETTERS, when known, are targeted for a specific OS or platform.676# The order is by 'common practice' on the specified platform.677# Note: 'posix' and 'windows' _OS_GETTERS are prefixed by a dll/dlload() method678# which, when successful, means none of these "external" methods are called.679# _GETTERS is (also) used by test_uuid.py to SkipUnless(), e.g.,680# @unittest.skipUnless(_uuid._ifconfig_getnode in _uuid._GETTERS, ...)681if _LINUX:682 _OS_GETTERS = [_ip_getnode, _ifconfig_getnode]683elif sys.platform == 'darwin':684 _OS_GETTERS = [_ifconfig_getnode, _arp_getnode, _netstat_getnode]685elif sys.platform == 'win32':686 # bpo-40201: _windll_getnode will always succeed, so these are not needed687 _OS_GETTERS = []688elif _AIX:689 _OS_GETTERS = [_netstat_getnode]690else:691 _OS_GETTERS = [_ifconfig_getnode, _ip_getnode, _arp_getnode,692 _netstat_getnode, _lanscan_getnode]693if os.name == 'posix':694 _GETTERS = [_unix_getnode] + _OS_GETTERS695elif os.name == 'nt':696 _GETTERS = [_windll_getnode] + _OS_GETTERS697else:698 _GETTERS = _OS_GETTERS699 700_node = None701 702def getnode():703 """Get the hardware address as a 48-bit positive integer.704 705 The first time this runs, it may launch a separate program, which could706 be quite slow. If all attempts to obtain the hardware address fail, we707 choose a random 48-bit number with its eighth bit set to 1 as recommended708 in RFC 4122.709 """710 global _node711 if _node is not None:712 return _node713 714 for getter in _GETTERS + [_random_getnode]:715 try:716 _node = getter()717 except:718 continue719 if (_node is not None) and (0 <= _node < (1 << 48)):720 return _node721 assert False, '_random_getnode() returned invalid value: {}'.format(_node)722 723 724_last_timestamp = None725 726def uuid1(node=None, clock_seq=None):727 """Generate a UUID from a host ID, sequence number, and the current time.728 If 'node' is not given, getnode() is used to obtain the hardware729 address. If 'clock_seq' is given, it is used as the sequence number;730 otherwise a random 14-bit sequence number is chosen."""731 732 # When the system provides a version-1 UUID generator, use it (but don't733 # use UuidCreate here because its UUIDs don't conform to RFC 4122).734 if _generate_time_safe is not None and node is clock_seq is None:735 uuid_time, safely_generated = _generate_time_safe()736 try:737 is_safe = SafeUUID(safely_generated)738 except ValueError:739 is_safe = SafeUUID.unknown740 return UUID(bytes=uuid_time, is_safe=is_safe)741 742 global _last_timestamp743 nanoseconds = time.time_ns()744 # 0x01b21dd213814000 is the number of 100-ns intervals between the745 # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.746 timestamp = nanoseconds // 100 + 0x01b21dd213814000747 if _last_timestamp is not None and timestamp <= _last_timestamp:748 timestamp = _last_timestamp + 1749 _last_timestamp = timestamp750 if clock_seq is None:751 import random752 clock_seq = random.getrandbits(14) # instead of stable storage753 time_low = timestamp & 0xffffffff754 time_mid = (timestamp >> 32) & 0xffff755 time_hi_version = (timestamp >> 48) & 0x0fff756 clock_seq_low = clock_seq & 0xff757 clock_seq_hi_variant = (clock_seq >> 8) & 0x3f758 if node is None:759 node = getnode()760 return UUID(fields=(time_low, time_mid, time_hi_version,761 clock_seq_hi_variant, clock_seq_low, node), version=1)762 763def uuid3(namespace, name):764 """Generate a UUID from the MD5 hash of a namespace UUID and a name."""765 if isinstance(name, str):766 name = bytes(name, "utf-8")767 import hashlib768 h = hashlib.md5(namespace.bytes + name, usedforsecurity=False)769 int_uuid_3 = int.from_bytes(h.digest())770 int_uuid_3 &= _RFC_4122_CLEARFLAGS_MASK771 int_uuid_3 |= _RFC_4122_VERSION_3_FLAGS772 return UUID._from_int(int_uuid_3)773 774def uuid4():775 """Generate a random UUID."""776 int_uuid_4 = int.from_bytes(os.urandom(16))777 int_uuid_4 &= _RFC_4122_CLEARFLAGS_MASK778 int_uuid_4 |= _RFC_4122_VERSION_4_FLAGS779 return UUID._from_int(int_uuid_4)780 781def uuid5(namespace, name):782 """Generate a UUID from the SHA-1 hash of a namespace UUID and a name."""783 if isinstance(name, str):784 name = bytes(name, "utf-8")785 import hashlib786 h = hashlib.sha1(namespace.bytes + name, usedforsecurity=False)787 int_uuid_5 = int.from_bytes(h.digest()[:16])788 int_uuid_5 &= _RFC_4122_CLEARFLAGS_MASK789 int_uuid_5 |= _RFC_4122_VERSION_5_FLAGS790 return UUID._from_int(int_uuid_5)791 792 793_last_timestamp_v6 = None794 795def uuid6(node=None, clock_seq=None):796 """Similar to :func:`uuid1` but where fields are ordered differently797 for improved DB locality.798 799 More precisely, given a 60-bit timestamp value as specified for UUIDv1,800 for UUIDv6 the first 48 most significant bits are stored first, followed801 by the 4-bit version (same position), followed by the remaining 12 bits802 of the original 60-bit timestamp.803 """804 global _last_timestamp_v6805 import time806 nanoseconds = time.time_ns()807 # 0x01b21dd213814000 is the number of 100-ns intervals between the808 # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.809 timestamp = nanoseconds // 100 + 0x01b21dd213814000810 if _last_timestamp_v6 is not None and timestamp <= _last_timestamp_v6:811 timestamp = _last_timestamp_v6 + 1812 _last_timestamp_v6 = timestamp813 if clock_seq is None:814 import random815 clock_seq = random.getrandbits(14) # instead of stable storage816 time_hi_and_mid = (timestamp >> 12) & 0xffff_ffff_ffff817 time_lo = timestamp & 0x0fff # keep 12 bits and clear version bits818 clock_s = clock_seq & 0x3fff # keep 14 bits and clear variant bits819 if node is None:820 node = getnode()821 # --- 32 + 16 --- -- 4 -- -- 12 -- -- 2 -- -- 14 --- 48822 # time_hi_and_mid | version | time_lo | variant | clock_seq | node823 int_uuid_6 = time_hi_and_mid << 80824 int_uuid_6 |= time_lo << 64825 int_uuid_6 |= clock_s << 48826 int_uuid_6 |= node & 0xffff_ffff_ffff827 # by construction, the variant and version bits are already cleared828 int_uuid_6 |= _RFC_4122_VERSION_6_FLAGS829 return UUID._from_int(int_uuid_6)830 831 832_last_timestamp_v7 = None833_last_counter_v7 = 0 # 42-bit counter834 835def _uuid7_get_counter_and_tail():836 rand = int.from_bytes(os.urandom(10))837 # 42-bit counter with MSB set to 0838 counter = (rand >> 32) & 0x1ff_ffff_ffff839 # 32-bit random data840 tail = rand & 0xffff_ffff841 return counter, tail842 843 844def uuid7():845 """Generate a UUID from a Unix timestamp in milliseconds and random bits.846 847 UUIDv7 objects feature monotonicity within a millisecond.848 """849 # --- 48 --- -- 4 -- --- 12 --- -- 2 -- --- 30 --- - 32 -850 # unix_ts_ms | version | counter_hi | variant | counter_lo | random851 #852 # 'counter = counter_hi | counter_lo' is a 42-bit counter constructed853 # with Method 1 of RFC 9562, §6.2, and its MSB is set to 0.854 #855 # 'random' is a 32-bit random value regenerated for every new UUID.856 #857 # If multiple UUIDs are generated within the same millisecond, the LSB858 # of 'counter' is incremented by 1. When overflowing, the timestamp is859 # advanced and the counter is reset to a random 42-bit integer with MSB860 # set to 0.861 862 global _last_timestamp_v7863 global _last_counter_v7864 865 nanoseconds = time.time_ns()866 timestamp_ms = nanoseconds // 1_000_000867 868 if _last_timestamp_v7 is None or timestamp_ms > _last_timestamp_v7:869 counter, tail = _uuid7_get_counter_and_tail()870 else:871 if timestamp_ms < _last_timestamp_v7:872 timestamp_ms = _last_timestamp_v7 + 1873 # advance the 42-bit counter874 counter = _last_counter_v7 + 1875 if counter > 0x3ff_ffff_ffff:876 # advance the 48-bit timestamp877 timestamp_ms += 1878 counter, tail = _uuid7_get_counter_and_tail()879 else:880 # 32-bit random data881 tail = int.from_bytes(os.urandom(4))882 883 unix_ts_ms = timestamp_ms & 0xffff_ffff_ffff884 counter_msbs = counter >> 30885 # keep 12 counter's MSBs and clear variant bits886 counter_hi = counter_msbs & 0x0fff887 # keep 30 counter's LSBs and clear version bits888 counter_lo = counter & 0x3fff_ffff889 # ensure that the tail is always a 32-bit integer (by construction,890 # it is already the case, but future interfaces may allow the user891 # to specify the random tail)892 tail &= 0xffff_ffff893 894 int_uuid_7 = unix_ts_ms << 80895 int_uuid_7 |= counter_hi << 64896 int_uuid_7 |= counter_lo << 32897 int_uuid_7 |= tail898 # by construction, the variant and version bits are already cleared899 int_uuid_7 |= _RFC_4122_VERSION_7_FLAGS900 res = UUID._from_int(int_uuid_7)901 902 # defer global update until all computations are done903 _last_timestamp_v7 = timestamp_ms904 _last_counter_v7 = counter905 return res906 907 908def uuid8(a=None, b=None, c=None):909 """Generate a UUID from three custom blocks.910 911 * 'a' is the first 48-bit chunk of the UUID (octets 0-5);912 * 'b' is the mid 12-bit chunk (octets 6-7);913 * 'c' is the last 62-bit chunk (octets 8-15).914 915 When a value is not specified, a pseudo-random value is generated.916 """917 if a is None:918 import random919 a = random.getrandbits(48)920 if b is None:921 import random922 b = random.getrandbits(12)923 if c is None:924 import random925 c = random.getrandbits(62)926 int_uuid_8 = (a & 0xffff_ffff_ffff) << 80927 int_uuid_8 |= (b & 0xfff) << 64928 int_uuid_8 |= c & 0x3fff_ffff_ffff_ffff929 # by construction, the variant and version bits are already cleared930 int_uuid_8 |= _RFC_4122_VERSION_8_FLAGS931 return UUID._from_int(int_uuid_8)932 933 934def main():935 """Run the uuid command line interface."""936 uuid_funcs = {937 "uuid1": uuid1,938 "uuid3": uuid3,939 "uuid4": uuid4,940 "uuid5": uuid5,941 "uuid6": uuid6,942 "uuid7": uuid7,943 "uuid8": uuid8,944 }945 uuid_namespace_funcs = ("uuid3", "uuid5")946 namespaces = {947 "@dns": NAMESPACE_DNS,948 "@url": NAMESPACE_URL,949 "@oid": NAMESPACE_OID,950 "@x500": NAMESPACE_X500951 }952 953 import argparse954 parser = argparse.ArgumentParser(955 formatter_class=argparse.ArgumentDefaultsHelpFormatter,956 description="Generate a UUID using the selected UUID function.",957 color=True,958 )959 parser.add_argument("-u", "--uuid",960 choices=uuid_funcs.keys(),961 default="uuid4",962 help="function to generate the UUID")963 parser.add_argument("-n", "--namespace",964 choices=["any UUID", *namespaces.keys()],965 help="uuid3/uuid5 only: "966 "a UUID, or a well-known predefined UUID addressed "967 "by namespace name")968 parser.add_argument("-N", "--name",969 help="uuid3/uuid5 only: "970 "name used as part of generating the UUID")971 parser.add_argument("-C", "--count", metavar="NUM", type=int, default=1,972 help="generate NUM fresh UUIDs")973 974 args = parser.parse_args()975 uuid_func = uuid_funcs[args.uuid]976 namespace = args.namespace977 name = args.name978 979 if args.uuid in uuid_namespace_funcs:980 if not namespace or not name:981 parser.error(982 "Incorrect number of arguments. "983 f"{args.uuid} requires a namespace and a name. "984 "Run 'python -m uuid -h' for more information."985 )986 namespace = namespaces[namespace] if namespace in namespaces else UUID(namespace)987 for _ in range(args.count):988 print(uuid_func(namespace, name))989 else:990 for _ in range(args.count):991 print(uuid_func())992 993 994# The following standard UUIDs are for use with uuid3() or uuid5().995 996NAMESPACE_DNS = UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')997NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8')998NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8')999NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8')1000 1001# RFC 9562 Sections 5.9 and 5.10 define the special Nil and Max UUID formats.1002 1003NIL = UUID('00000000-0000-0000-0000-000000000000')1004MAX = UUID('ffffffff-ffff-ffff-ffff-ffffffffffff')1005 1006if __name__ == "__main__":1007 main()1008 