import os
import sys
import time
from enum import Enum, _simple_enum
__author__ = 'Ka-Ping Yee <ping@zesty.ca>'
if sys.platform in {'win32', 'darwin', 'emscripten', 'wasi'}:
_AIX = _LINUX = False
elif sys.platform == 'linux':
_LINUX = True
_AIX = False
else:
import platform
_platform_system = platform.system()
_AIX = _platform_system == 'AIX'
_LINUX = _platform_system in ('Linux', 'Android')
_MAC_DELIM = b':'
_MAC_OMITS_LEADING_ZEROES = False
if _AIX:
_MAC_DELIM = b'.'
_MAC_OMITS_LEADING_ZEROES = True
RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [
'reserved for NCS compatibility', 'specified in RFC 4122',
'reserved for Microsoft compatibility', 'reserved for future definition']
int_ = int bytes_ = bytes
@_simple_enum(Enum)
class SafeUUID:
safe = 0
unsafe = -1
unknown = None
_UINT_128_MAX = (1 << 128) - 1
_RFC_4122_CLEARFLAGS_MASK = ~((0xf000 << 64) | (0xc000 << 48))
_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:
__slots__ = ('int', 'is_safe', '__weakref__')
def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None,
int=None, version=None,
*, is_safe=SafeUUID.unknown):
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 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)
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:])
int = int_.from_bytes(bytes) 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) 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):
raise ValueError('field 1 out of range (need a 32-bit value)')
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):
raise ValueError('field 3 out of range (need a 16-bit value)')
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):
raise ValueError('field 5 out of range (need an 8-bit value)')
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 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 <= 8:
raise ValueError('illegal version number')
int &= _RFC_4122_CLEARFLAGS_MASK
int |= 0x8000_0000_0000_0000 int |= version << 76
object.__setattr__(self, 'int', int)
object.__setattr__(self, 'is_safe', is_safe)
@classmethod
def _from_int(cls, value):
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:
d['is_safe'] = self.is_safe.value
return d
def __setstate__(self, state):
object.__setattr__(self, 'int', state['int'])
object.__setattr__(self, 'is_safe',
SafeUUID(state['is_safe'])
if 'is_safe' in state else SafeUUID.unknown)
def __eq__(self, other):
if isinstance(other, UUID):
return self.int == other.int
return NotImplemented
def __lt__(self, other):
if isinstance(other, UUID):
return self.int < other.int
return NotImplemented
def __gt__(self, other):
if isinstance(other, UUID):
return self.int > other.int
return NotImplemented
def __le__(self, other):
if isinstance(other, UUID):
return self.int <= other.int
return NotImplemented
def __ge__(self, other):
if isinstance(other, UUID):
return self.int >= other.int
return NotImplemented
def __hash__(self):
return hash(self.int)
def __int__(self):
return self.int
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, str(self))
def __setattr__(self, name, value):
raise TypeError('UUID objects are immutable')
def __str__(self):
x = self.hex
return f'{x[:8]}-{x[8:12]}-{x[12:16]}-{x[16:20]}-{x[20:]}'
@property
def bytes(self):
return self.int.to_bytes(16)
@property
def bytes_le(self):
bytes = self.bytes
return (bytes[4-1::-1] + bytes[6-1:4-1:-1] + bytes[8-1:6-1:-1] +
bytes[8:])
@property
def fields(self):
return (self.time_low, self.time_mid, self.time_hi_version,
self.clock_seq_hi_variant, self.clock_seq_low, self.node)
@property
def time_low(self):
return self.int >> 96
@property
def time_mid(self):
return (self.int >> 80) & 0xffff
@property
def time_hi_version(self):
return (self.int >> 64) & 0xffff
@property
def clock_seq_hi_variant(self):
return (self.int >> 56) & 0xff
@property
def clock_seq_low(self):
return (self.int >> 48) & 0xff
@property
def time(self):
if self.version == 6:
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:
return self.int >> 80
else:
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):
return (((self.clock_seq_hi_variant & 0x3f) << 8) |
self.clock_seq_low)
@property
def node(self):
return self.int & 0xffffffffffff
@property
def hex(self):
return self.bytes.hex()
@property
def urn(self):
return 'urn:uuid:' + str(self)
@property
def variant(self):
if not self.int & (0x8000 << 48):
return RESERVED_NCS
elif not self.int & (0x4000 << 48):
return RFC_4122
elif not self.int & (0x2000 << 48):
return RESERVED_MICROSOFT
else:
return RESERVED_FUTURE
@property
def version(self):
if self.variant == RFC_4122:
return int((self.int >> 76) & 0xf)
def _get_command_stdout(command, *args):
import io, os, shutil, subprocess
try:
path_dirs = os.environ.get('PATH', os.defpath).split(os.pathsep)
path_dirs.extend(['/sbin', '/usr/sbin'])
executable = shutil.which(command, path=os.pathsep.join(path_dirs))
if executable is None:
return None
env = dict(os.environ)
env['LC_ALL'] = 'C'
if args != ('',):
command = (executable, *args)
else:
command = (executable,)
proc = subprocess.Popen(command,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
env=env)
if not proc:
return None
stdout, stderr = proc.communicate()
return io.BytesIO(stdout)
except (OSError, subprocess.SubprocessError):
return None
def _is_universal(mac):
return not (mac & (1 << 41))
def _find_mac_near_keyword(command, args, keywords, get_word_index):
stdout = _get_command_stdout(command, args)
if stdout is None:
return None
first_local_mac = None
for line in stdout:
words = line.lower().rstrip().split()
for i in range(len(words)):
if words[i] in keywords:
try:
word = words[get_word_index(i)]
mac = int(word.replace(_MAC_DELIM, b''), 16)
except (ValueError, IndexError):
pass
else:
if _is_universal(mac):
return mac
first_local_mac = first_local_mac or mac
return first_local_mac or None
def _parse_mac(word):
parts = word.split(_MAC_DELIM)
if len(parts) != 6:
return
if _MAC_OMITS_LEADING_ZEROES:
if not all(1 <= len(part) <= 2 for part in parts):
return
hexstr = b''.join(part.rjust(2, b'0') for part in parts)
else:
if not all(len(part) == 2 for part in parts):
return
hexstr = b''.join(parts)
try:
return int(hexstr, 16)
except ValueError:
return
def _find_mac_under_heading(command, args, heading):
stdout = _get_command_stdout(command, args)
if stdout is None:
return None
keywords = stdout.readline().rstrip().split()
try:
column_index = keywords.index(heading)
except ValueError:
return None
first_local_mac = None
for line in stdout:
words = line.rstrip().split()
try:
word = words[column_index]
except IndexError:
continue
mac = _parse_mac(word)
if mac is None:
continue
if _is_universal(mac):
return mac
if first_local_mac is None:
first_local_mac = mac
return first_local_mac
def _ifconfig_getnode():
keywords = (b'hwaddr', b'ether', b'address:', b'lladdr')
for args in ('', '-a', '-av'):
mac = _find_mac_near_keyword('ifconfig', args, keywords, lambda i: i+1)
if mac:
return mac
return None
def _ip_getnode():
mac = _find_mac_near_keyword('ip', 'link', [b'link/ether'], lambda i: i+1)
if mac:
return mac
return None
def _arp_getnode():
import os, socket
if not hasattr(socket, "gethostbyname"):
return None
try:
ip_addr = socket.gethostbyname(socket.gethostname())
except OSError:
return None
mac = _find_mac_near_keyword('arp', '-an', [os.fsencode(ip_addr)], lambda i: -1)
if mac:
return mac
mac = _find_mac_near_keyword('arp', '-an', [os.fsencode(ip_addr)], lambda i: i+1)
if mac:
return mac
mac = _find_mac_near_keyword('arp', '-an', [os.fsencode('(%s)' % ip_addr)],
lambda i: i+2)
if mac:
return mac
return None
def _lanscan_getnode():
return _find_mac_near_keyword('lanscan', '-ai', [b'lan0'], lambda i: 0)
def _netstat_getnode():
return _find_mac_under_heading('netstat', '-ian', b'Address')
try:
import _uuid
_generate_time_safe = getattr(_uuid, "generate_time_safe", None)
_has_stable_extractable_node = _uuid.has_stable_extractable_node
_UuidCreate = getattr(_uuid, "UuidCreate", None)
except ImportError:
_uuid = None
_generate_time_safe = None
_has_stable_extractable_node = False
_UuidCreate = None
def _unix_getnode():
if _generate_time_safe and _has_stable_extractable_node:
uuid_time, _ = _generate_time_safe()
return UUID(bytes=uuid_time).node
def _windll_getnode():
if _UuidCreate and _has_stable_extractable_node:
uuid_bytes = _UuidCreate()
return UUID(bytes_le=uuid_bytes).node
def _random_getnode():
return int.from_bytes(os.urandom(6)) | (1 << 40)
if _LINUX:
_OS_GETTERS = [_ip_getnode, _ifconfig_getnode]
elif sys.platform == 'darwin':
_OS_GETTERS = [_ifconfig_getnode, _arp_getnode, _netstat_getnode]
elif sys.platform == 'win32':
_OS_GETTERS = []
elif _AIX:
_OS_GETTERS = [_netstat_getnode]
else:
_OS_GETTERS = [_ifconfig_getnode, _ip_getnode, _arp_getnode,
_netstat_getnode, _lanscan_getnode]
if os.name == 'posix':
_GETTERS = [_unix_getnode] + _OS_GETTERS
elif os.name == 'nt':
_GETTERS = [_windll_getnode] + _OS_GETTERS
else:
_GETTERS = _OS_GETTERS
_node = None
def getnode():
global _node
if _node is not None:
return _node
for getter in _GETTERS + [_random_getnode]:
try:
_node = getter()
except:
continue
if (_node is not None) and (0 <= _node < (1 << 48)):
return _node
assert False, '_random_getnode() returned invalid value: {}'.format(_node)
_last_timestamp = None
def uuid1(node=None, clock_seq=None):
if _generate_time_safe is not None and node is clock_seq is None:
uuid_time, safely_generated = _generate_time_safe()
try:
is_safe = SafeUUID(safely_generated)
except ValueError:
is_safe = SafeUUID.unknown
return UUID(bytes=uuid_time, is_safe=is_safe)
global _last_timestamp
nanoseconds = time.time_ns()
timestamp = nanoseconds // 100 + 0x01b21dd213814000
if _last_timestamp is not None and timestamp <= _last_timestamp:
timestamp = _last_timestamp + 1
_last_timestamp = timestamp
if clock_seq is None:
import random
clock_seq = random.getrandbits(14) time_low = timestamp & 0xffffffff
time_mid = (timestamp >> 32) & 0xffff
time_hi_version = (timestamp >> 48) & 0x0fff
clock_seq_low = clock_seq & 0xff
clock_seq_hi_variant = (clock_seq >> 8) & 0x3f
if node is None:
node = getnode()
return UUID(fields=(time_low, time_mid, time_hi_version,
clock_seq_hi_variant, clock_seq_low, node), version=1)
def uuid3(namespace, name):
if isinstance(name, str):
name = bytes(name, "utf-8")
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():
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):
if isinstance(name, str):
name = bytes(name, "utf-8")
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):
global _last_timestamp_v6
import time
nanoseconds = time.time_ns()
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) time_hi_and_mid = (timestamp >> 12) & 0xffff_ffff_ffff
time_lo = timestamp & 0x0fff clock_s = clock_seq & 0x3fff if node is None:
node = getnode()
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
int_uuid_6 |= _RFC_4122_VERSION_6_FLAGS
return UUID._from_int(int_uuid_6)
_last_timestamp_v7 = None
_last_counter_v7 = 0
def _uuid7_get_counter_and_tail():
rand = int.from_bytes(os.urandom(10))
counter = (rand >> 32) & 0x1ff_ffff_ffff
tail = rand & 0xffff_ffff
return counter, tail
def uuid7():
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
counter = _last_counter_v7 + 1
if counter > 0x3ff_ffff_ffff:
timestamp_ms += 1
counter, tail = _uuid7_get_counter_and_tail()
else:
tail = int.from_bytes(os.urandom(4))
unix_ts_ms = timestamp_ms & 0xffff_ffff_ffff
counter_msbs = counter >> 30
counter_hi = counter_msbs & 0x0fff
counter_lo = counter & 0x3fff_ffff
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
int_uuid_7 |= _RFC_4122_VERSION_7_FLAGS
res = UUID._from_int(int_uuid_7)
_last_timestamp_v7 = timestamp_ms
_last_counter_v7 = counter
return res
def uuid8(a=None, b=None, c=None):
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
int_uuid_8 |= _RFC_4122_VERSION_8_FLAGS
return UUID._from_int(int_uuid_8)
def main():
uuid_funcs = {
"uuid1": uuid1,
"uuid3": uuid3,
"uuid4": uuid4,
"uuid5": uuid5,
"uuid6": uuid6,
"uuid7": uuid7,
"uuid8": uuid8,
}
uuid_namespace_funcs = ("uuid3", "uuid5")
namespaces = {
"@dns": NAMESPACE_DNS,
"@url": NAMESPACE_URL,
"@oid": NAMESPACE_OID,
"@x500": NAMESPACE_X500
}
import argparse
parser = argparse.ArgumentParser(
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",
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="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]
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)
for _ in range(args.count):
print(uuid_func(namespace, name))
else:
for _ in range(args.count):
print(uuid_func())
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')
NIL = UUID('00000000-0000-0000-0000-000000000000')
MAX = UUID('ffffffff-ffff-ffff-ffff-ffffffffffff')
if __name__ == "__main__":
main()