uuid - Identificatori Universalmente Univoci

Scopo Implementare gli Identificatori Universalmente Univoci (Universally Unique Identifiers) come descritti in RFC 4122
Versione Python 2.5 e superiore

RFC 4122 definisce un sistema per creare Identificatori Universalmente Univoci per delle risorse in modo che non sia necessario mantenerne traccia tramite la gestione di un archivio centralizzato. I valori UUID sono lunghi 128 bit e "possono garantire l'univocità attraverso lo spazio ed il tempo". Sono utili per identificare documenti, host, applicazioni client, ed altre situazioni nelle quali sia necessario un valore univoco. RFC è in particolare orientato verso la creazione di uno spazio dei nomi chiamato "Uniform Resource Name".

Le specifiche coprono tre algoritmi principali:

  • Uso degli indirizzi IEEE 802 MAC come fonte di univocità
  • Uso dei numeri pseudo-casuali
  • Uso di stringhe ben note in combinazione con hashing crittografico

In tutti i casi il valore di origine viene combinato con l'ora di sistema ed una sequenza di valori temporali (per mantenere univocità nel caso che l'orologia sia messo indietro).

UUID 1 - Indirizzi IEEE 802 MAC

Nella versione UUID 1 valori sono calcolati usndo l'indirizzo MAC dell'host. Il modulo uuid usa getnode() per recuperare un valore MAC in un dato sistema:

import uuid

print hex(uuid.getnode())
$ python uuid_getnode.py
0x1aa09d114fL

Se un sistema ha più di una scheda di rete, e di conseguenza più di un MAC, potrebbe essere restituito uno qualsiasi dei valori.

Per generare un UUID per un dato host, identificato dal suo indirizzo MAC, si usa la funzione uuid1(). Si può passare un identificatore di nodo, oppure lasciare il campo vuoto per usare il valore restituito da getnode().

import uuid

u = uuid.uuid1()

print u
print type(u)
print 'bytes   :', repr(u.bytes)
print 'hex     :', u.hex
print 'int     :', u.int
print 'urn     :', u.urn
print 'variant :', u.variant
print 'version :', u.version
print 'fields  :', u.fields
print '\ttime_low            : ', u.time_low
print '\ttime_mid            : ', u.time_mid
print '\ttime_hi_version     : ', u.time_hi_version
print '\tclock_seq_hi_variant: ', u.clock_seq_hi_variant
print '\tclock_seq_low       : ', u.clock_seq_low
print '\tnode                : ', u.node
print '\ttime                : ', u.time
print '\tclock_seq           : ', u.clock_seq

I componenti dell'oggetto UUID restituito possono essere indirizzati tramite degli attributi di istanza a sola lettura. Alcuni attributi, come hex, int ed urn sono diverse rappresentazioni del valore UUID.

$ python uuid_uuid1.py
e5aada06-31d0-11e0-882c-001aa09d114f

bytes   : '\xe5\xaa\xda\x061\xd0\x11\xe0\x88,\x00\x1a\xa0\x9d\x11O'
hex     : e5aada0631d011e0882c001aa09d114f
int     : 305280323556775533215399297575468994895
urn     : urn:uuid:e5aada06-31d0-11e0-882c-001aa09d114f
variant : specified in RFC 4122
version : 1
fields  : (3853179398L, 12752L, 4576L, 136L, 44L, 114363797839L)
	time_low            :  3853179398
	time_mid            :  12752
	time_hi_version     :  4576
	clock_seq_hi_variant:  136
	clock_seq_low       :  44
	node                :  114363797839
	time                :  135162762097252870
	clock_seq           :  2092

A causa della componente tempo, ogni volta che viene chiamato uuid1() viene restituito un nuovo valore.

import uuid

for i in xrange(3):
    print uuid.uuid1()

Si noti che nell'output cambia solo la componente temporale (all'inizio della stringa).

$ python uuid_uuid_repeat.py

573190de-31d1-11e0-a532-001aa09d114f
5731d0b2-31d1-11e0-a532-001aa09d114f
57320500-31d1-11e0-a532-001aa09d114f

Visto ogni computer ha il proprio indirizzo MAC, quando si eseguono gli script di esempio i valori saranno completamente diversi, visto che cambierà anche l'identificatore di nodo alla fine dell'UUID.

import uuid

node1 = uuid.getnode()
print hex(node1), uuid.uuid1(node1)

node2 =  0x1e5274040e
print hex(node2), uuid.uuid1(node2)
$ python uuid_uuid_othermac.py

0x1aa09d114fL d4cd9538-31d1-11e0-971e-001aa09d114f
0x1e5274040eL d4cd98a8-31d1-11e0-a0cb-001e5274040e

UUID 3 e 5 - Valori Basati sui Nomi

E' anche utile, in certi contesti, creare valori UUID da nomi invece che da valori casuali o basati sul tempo. Le versioni 3 e 5 delle specifiche UUID usano valori hash crittografici (MD5 oppure SHA-1) per combinare valori di origine specifici dello spazio dei nomi con "nomi" (nomi di host DNS, URL, identificatori di oggetti, ecc.). Ci sono diversi spazi dei nomi ben noti, identificati da valori UUID predefiniti, per lavorare con DNS, URL, ISO OID ed X.500 Distinguished Names. Si possono anche definire spazi dei nomi specifici per una propria applicazione generando e salvando i valori UUID.

Per crare un UUID da un nome DNS, si passa uuid.NAMESPACE_DNS come parametro per lo spazio dei nomi ad uuid3() oppure uuid5():

import uuid

hostnames = ['www.doughellmann.com', 'blog.doughellmann.com']

for name in hostnames:
    print name
    print '\tMD5   :', uuid.uuid3(uuid.NAMESPACE_DNS, name)
    print '\tSHA-1 :', uuid.uuid5(uuid.NAMESPACE_DNS, name)
$ python uuid_uuid3_uuid5.py

www.doughellmann.com
	MD5   : bcd02e22-68f0-3046-a512-327cca9def8f
	SHA-1 : e3329b12-30b7-57c4-8117-c2cd34a87ce9
blog.doughellmann.com
	MD5   : 9bdabfce-dfd6-37ab-8a3f-7f7293bcf111
	SHA-1 : fa829736-7ef8-5239-9906-b4775a5abacb

Il valore UUID per un dato nome in uno spazio dei nomi è sempre lo stesso, non importa quando o dove esso viene calcolato. I valori per lo stesso nome in spazi dei nomi diversi sono diversi.

import uuid

for i in xrange(3):
    print uuid.uuid3(uuid.NAMESPACE_DNS, 'www.doughellmann.com')
$ python uuid_uuid3_repeat.py

bcd02e22-68f0-3046-a512-327cca9def8f
bcd02e22-68f0-3046-a512-327cca9def8f
bcd02e22-68f0-3046-a512-327cca9def8f

UUID 4 - Valori Casuali

Talvolta i valori UUID basati sull'host e sullo spazio dei nomi sono sono "diversi abbastanza". Ad esempio, in casi dove si voglia usare UUID come chiave di ricerca, è desiderabile una sequenza di valori più casuale con più differenziazioni per evitare collisioni in una tabella di hash. Il disporre di valori con meno cifre comuni rende inoltre più semplice trovarli nei file di registro. Per aggiungere una differenziazione maggiore nel proprio UUID, si usa uuid4() per generare i valori usando dati in input casuali.

import uuid

for i in xrange(3):
    print uuid.uuid4()
$ python uuid_uuid4.py

02fdb5cc-8e8c-41ec-b016-6c76cee34823
7ce11b55-b95c-407f-9685-ccf7b4027ee0
ef7eed78-fb59-4d06-b28d-8f4e5be1a984

Lavorare con Oggetti UUID

Oltre a generare nuovi valori UUID, si possono anche elaborare stringhe in diversi formati per creare oggetti UUID. Questo rende facile il controntarli, ordinarli ecc.

import uuid

def show(msg, l):
    print msg
    for v in l:
        print '\t', v
    print

input_values = [
    'urn:uuid:f2f84497-b3bf-493a-bba9-7c68e6def80b',
    '{417a5ebb-01f7-4ed5-aeac-3d56cd5037b0}',
    '2115773a-5bf1-11dd-ab48-001ec200d9e0',
    ]

show('input_values', input_values)

uuids = [ uuid.UUID(s) for s in input_values ]
show('convertiti in uuid', uuids)

uuids.sort()
show('ordinati', uuids)
$ python uuid_uuid_objects.py

input_values
	urn:uuid:f2f84497-b3bf-493a-bba9-7c68e6def80b
	{417a5ebb-01f7-4ed5-aeac-3d56cd5037b0}
	2115773a-5bf1-11dd-ab48-001ec200d9e0

convertiti in uuid
	f2f84497-b3bf-493a-bba9-7c68e6def80b
	417a5ebb-01f7-4ed5-aeac-3d56cd5037b0
	2115773a-5bf1-11dd-ab48-001ec200d9e0

ordinati
	2115773a-5bf1-11dd-ab48-001ec200d9e0
	417a5ebb-01f7-4ed5-aeac-3d56cd5037b0
	f2f84497-b3bf-493a-bba9-7c68e6def80b

Vedere anche:

uuid
La documentazione della libreria standard per questo modulo
RFC 4122
Uno spazio dei nomi URN per Universally Unique IDentifiers (UUID)