Skip to content

Commit 69220dd

Browse files
committed
major changes, bump version to 0.2.7
1 parent f3b39bf commit 69220dd

File tree

7 files changed

+153
-156
lines changed

7 files changed

+153
-156
lines changed

scripts/generate_uuid.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import uuid0
44

55
parser = argparse.ArgumentParser(description='Generate a UUID0')
6-
parser.add_argument('--timestamp', '-t', type=float,
6+
parser.add_argument('--timestamp', '-t', type=float,
77
help='UNIX timestamp')
88
parser.add_argument('--number', '-n', type=int, default=200,
99
help='Number of UUIDs to generate')

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ def readme():
1919
return f.read()
2020

2121
setup(name='uuid0',
22-
version='0.2.0',
22+
version='0.2.7',
2323
description='A library to make better timestamped UUIDs for databases and web apps',
2424
long_description=readme(),
2525
url='https://github.com/oaclaf/uuid0',
2626
author='oaclaf',
2727
author_email='oaclafm@gmail.com',
2828
license='MIT',
2929
classifiers=CLASSIFIERS,
30-
packages=['uuid0', 'uuid0.django'],
30+
packages=['uuid0'],
3131
install_requires=['pybase62'],
3232
zip_safe=False)

uuid0/__init__.py

Lines changed: 1 addition & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -44,112 +44,4 @@
4444
UUID('0b7dd8d0-7e40-8360-9322-4a361d7b573f')
4545
"""
4646

47-
import os
48-
import uuid
49-
import time
50-
import struct
51-
from datetime import datetime
52-
53-
import base62 as b62
54-
55-
56-
class UUID(uuid.UUID):
57-
"""
58-
A class that offers a simple, but effective, way to deal with time-based UUIDs.
59-
Additional encoding methods are included to make UUIDs easier to use with web apps.
60-
61-
The first 6 bytes of the UUID are an unsigned integer containing the number of
62-
1/10000s periods since the UNIX epoch, the remaining bits are random (aside from
63-
the UUID version).
64-
This approach offers semi-sequential UUIDs (easy on your indexes), which include
65-
the creation time but don't include any machine-specific information.
66-
"""
67-
68-
def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None, int=None, version=None, base62=None):
69-
r"""Create a UUID0 from either a string of 32 hexadecimal digits,
70-
a string of 16 bytes as the 'bytes' argument, a string of 16 bytes
71-
in little-endian order as the 'bytes_le' argument, a tuple of six
72-
integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version,
73-
8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as
74-
the 'fields' argument, a single 128-bit integer as the 'int'
75-
argument or a base62 string as the 'base62' argument. When a string
76-
of hex digits is given, curly braces, hyphens, and a URN prefix are
77-
all optional. For example, these expressions all yield the same UUID:
78-
79-
UUID('{12345678-1234-5678-1234-567812345678}')
80-
UUID('12345678123456781234567812345678')
81-
UUID('urn:uuid:12345678-1234-5678-1234-567812345678')
82-
UUID(bytes='\x12\x34\x56\x78'*4)
83-
UUID(bytes_le='\x78\x56\x34\x12\x34\x12\x78\x56' +
84-
'\x12\x34\x56\x78\x12\x34\x56\x78')
85-
UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
86-
UUID(int=0x12345678123456781234567812345678)
87-
UUID(base62='YLmNWW2NwaipfRR50HIPA')
88-
89-
Exactly one of 'hex', 'bytes', 'bytes_le', 'fields', 'int' or
90-
'base62' must be given. The 'version' argument is optional; if
91-
given, the resulting UUID will have its variant and version set
92-
according to RFC 4122, overriding the given 'hex', 'bytes',
93-
'bytes_le', 'fields', 'int' or 'base62'.
94-
"""
95-
if base62:
96-
return super().__init__(int=b62.decode(base62), version=version)
97-
return super().__init__(hex, bytes, bytes_le, fields, int, version)
98-
99-
@property
100-
def base62(self) -> str:
101-
"""Get the UUID as a base62 encoded string"""
102-
return b62.encode(self.int)
103-
104-
@property
105-
def ts(self) -> int:
106-
"""Get the timestamp as the number of 100 microsecond periods since the UNIX epoch"""
107-
return get_ts(self)
108-
109-
@property
110-
def unix_ts(self) -> float:
111-
"""Get the UNIX timestamp"""
112-
return get_unix_ts(self)
113-
114-
@property
115-
def datetime(self) -> datetime:
116-
"""Get the timestamp as UTC datetime"""
117-
return get_datetime(self)
118-
119-
@property
120-
def datetime_local(self):
121-
"""Get the timestamp as local datetime"""
122-
return get_datetime_local(self)
123-
124-
125-
def generate(ts: float=None) -> UUID:
126-
"""Generate a new UUID. If the UNIX timestamp 'ts' is not given, the current time is used"""
127-
if not ts:
128-
ts = time.time()
129-
tsi = int(ts * 10000)
130-
t_bytes = struct.pack('>Q', tsi)[2:] # pack as an 8-byte uint and drop the first 2 bytes
131-
r_bytes = os.urandom(10)
132-
133-
return UUID(bytes=t_bytes + r_bytes)
134-
135-
136-
def get_ts(uid: uuid.UUID) -> int:
137-
"""Returns the timestamp as the number of 100 microsecond periods since the UNIX epoch"""
138-
time_part = uid.bytes[0:6]
139-
ts = struct.unpack('>Q', b'\x00\x00' + time_part)
140-
return ts[0]
141-
142-
143-
def get_unix_ts(uid: uuid.UUID) -> float:
144-
"""Returns the timestamp as UNIX time"""
145-
return get_ts(uid) / 10000.0
146-
147-
148-
def get_datetime(uid: uuid.UUID) -> datetime:
149-
"""Return the timestamp as UTC datetime"""
150-
return datetime.utcfromtimestamp(get_unix_ts(uid))
151-
152-
153-
def get_datetime_local(uid: uuid.UUID) -> datetime:
154-
"""Return the timestamp as local datetime"""
155-
return datetime.fromtimestamp(get_unix_ts(uid))
47+
from .core import *

uuid0/core.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import os
2+
import uuid
3+
import time
4+
import struct
5+
from datetime import datetime
6+
7+
import base62 as b62
8+
9+
10+
class UUID(uuid.UUID):
11+
"""
12+
A class that offers a simple, but effective, way to deal with time-based UUIDs.
13+
Additional encoding methods are included to make UUIDs easier to use with web apps.
14+
15+
The first 6 bytes of the UUID are an unsigned integer containing the number of
16+
1/10000s periods since the UNIX epoch, the remaining bits are random (aside from
17+
the UUID version).
18+
This approach offers semi-sequential UUIDs (easy on your indexes), which include
19+
the creation time but don't include any machine-specific information.
20+
"""
21+
22+
def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None, int=None, version=None, base62=None):
23+
r"""Create a UUID0 from either a string of 32 hexadecimal digits,
24+
a string of 16 bytes as the 'bytes' argument, a string of 16 bytes
25+
in little-endian order as the 'bytes_le' argument, a tuple of six
26+
integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version,
27+
8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as
28+
the 'fields' argument, a single 128-bit integer as the 'int'
29+
argument or a base62 string as the 'base62' argument. When a string
30+
of hex digits is given, curly braces, hyphens, and a URN prefix are
31+
all optional. For example, these expressions all yield the same UUID:
32+
33+
UUID('{12345678-1234-5678-1234-567812345678}')
34+
UUID('12345678123456781234567812345678')
35+
UUID('urn:uuid:12345678-1234-5678-1234-567812345678')
36+
UUID(bytes='\x12\x34\x56\x78'*4)
37+
UUID(bytes_le='\x78\x56\x34\x12\x34\x12\x78\x56' +
38+
'\x12\x34\x56\x78\x12\x34\x56\x78')
39+
UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
40+
UUID(int=0x12345678123456781234567812345678)
41+
UUID(base62='YLmNWW2NwaipfRR50HIPA')
42+
43+
Exactly one of 'hex', 'bytes', 'bytes_le', 'fields', 'int' or
44+
'base62' must be given. The 'version' argument is optional; if
45+
given, the resulting UUID will have its variant and version set
46+
according to RFC 4122, overriding the given 'hex', 'bytes',
47+
'bytes_le', 'fields', 'int' or 'base62'.
48+
"""
49+
if base62:
50+
return super().__init__(int=b62.decode(base62), version=version)
51+
return super().__init__(hex, bytes, bytes_le, fields, int, version)
52+
53+
@property
54+
def base62(self) -> str:
55+
"""Get the UUID as a base62 encoded string"""
56+
return b62.encode(self.int)
57+
58+
@property
59+
def ts(self) -> int:
60+
"""Get the timestamp as the number of 100 microsecond periods since the UNIX epoch"""
61+
return get_ts(self)
62+
63+
@property
64+
def unix_ts(self) -> float:
65+
"""Get the UNIX timestamp"""
66+
return get_unix_ts(self)
67+
68+
@property
69+
def datetime(self) -> datetime:
70+
"""Get the timestamp as UTC datetime"""
71+
return get_datetime(self)
72+
73+
@property
74+
def datetime_local(self):
75+
"""Get the timestamp as local datetime"""
76+
return get_datetime_local(self)
77+
78+
79+
def generate(ts: float=None) -> UUID:
80+
"""Generate a new UUID. If the UNIX timestamp 'ts' is not given, the current time is used"""
81+
if not ts:
82+
ts = time.time()
83+
tsi = int(ts * 10000)
84+
t_bytes = struct.pack('>Q', tsi)[2:] # pack as an 8-byte uint and drop the first 2 bytes
85+
r_bytes = os.urandom(10)
86+
87+
return UUID(bytes=t_bytes + r_bytes)
88+
89+
90+
def get_ts(uid: uuid.UUID) -> int:
91+
"""Returns the timestamp as the number of 100 microsecond periods since the UNIX epoch"""
92+
time_part = uid.bytes[0:6]
93+
ts = struct.unpack('>Q', b'\x00\x00' + time_part)
94+
return ts[0]
95+
96+
97+
def get_unix_ts(uid: uuid.UUID) -> float:
98+
"""Returns the timestamp as UNIX time"""
99+
return get_ts(uid) / 10000.0
100+
101+
102+
def get_datetime(uid: uuid.UUID) -> datetime:
103+
"""Return the timestamp as UTC datetime"""
104+
return datetime.utcfromtimestamp(get_unix_ts(uid))
105+
106+
107+
def get_datetime_local(uid: uuid.UUID) -> datetime:
108+
"""Return the timestamp as local datetime"""
109+
return datetime.fromtimestamp(get_unix_ts(uid))
110+
Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,60 @@
11
import uuid
2-
3-
import django.db.models as models
4-
import django.db.models.fields as fields
2+
from django.forms import fields
3+
from django.db import models
4+
import django.core.exceptions as exceptions
55
from django.utils.translation import ugettext_lazy as _
66

7-
import uuid0
8-
from uuid0.django import forms
7+
from .core import UUID, generate
8+
99

10+
class UUID0FormField(fields.CharField):
11+
default_error_messages = {
12+
'invalid': _('Enter a valid UUID.'),
13+
}
14+
15+
def prepare_value(self, value):
16+
if isinstance(value, UUID):
17+
return value.base62
18+
return value
1019

11-
class UUID0Field(fields.UUIDField):
20+
def to_python(self, value):
21+
value = super().to_python(value)
22+
if value in self.empty_values:
23+
return None
24+
if not isinstance(value, UUID):
25+
if isinstance(value, uuid.UUID):
26+
value = UUID(int=value.int)
27+
else:
28+
try:
29+
value = UUID(base62=value)
30+
except ValueError:
31+
raise ValidationError(self.error_messages['invalid'], code='invalid')
32+
return value
33+
34+
35+
class UUID0Field(models.UUIDField):
1236
def from_db_value(self, value, expression, connection, context):
1337
if value is None:
1438
return value
15-
return uuid0.UUID(int=value.int)
39+
return UUID(int=value.int)
1640

1741
def get_db_prep_value(self, value, connection, prepared=False):
1842
if value is None:
1943
return None
20-
if not isinstance(value, uuid0.UUID):
44+
if not isinstance(value, UUID):
2145
value = self.to_python(value)
2246

2347
if connection.features.has_native_uuid_field:
2448
return value
2549
return value.hex
2650

2751
def to_python(self, value):
28-
if value is not None and not isinstance(value, uuid0.UUID):
52+
if value is not None and not isinstance(value, UUID):
2953
if isinstance(value, uuid.UUID):
30-
return uuid0.UUID(int=value.int)
54+
return UUID(int=value.int)
3155
else:
3256
try:
33-
return uuid0.UUID(value)
57+
return UUID(value)
3458
except (AttributeError, ValueError):
3559
raise exceptions.ValidationError(
3660
self.error_messages['invalid'],
@@ -41,14 +65,14 @@ def to_python(self, value):
4165

4266
def formfield(self, **kwargs):
4367
defaults = {
44-
'form_class': forms.UUID0Field,
68+
'form_class': UUID0FormField,
4569
}
4670
defaults.update(kwargs)
4771
return super().formfield(**kwargs)
4872

4973

5074
class UUID0Model(models.Model):
51-
uuid = UUID0Field(_('UUID'), unique=True, editable=False)
75+
uuid = UUID0Field(_('UUID'), default=generate, unique=True, editable=False)
5276

5377
@property
5478
def uuid_base62(self):
@@ -64,8 +88,9 @@ def __str__(self):
6488
class Meta:
6589
abstract = True
6690

91+
6792
class UUID0PKModel(UUID0Model):
68-
uuid = UUID0Field(_('UUID'), primary_key=True, editable=False)
93+
uuid = UUID0Field(_('UUID'), default=generate, primary_key=True, editable=False)
6994

7095
class Meta:
7196
abstract = True

uuid0/django/__init__.py

Lines changed: 0 additions & 1 deletion
This file was deleted.

uuid0/django/forms.py

Lines changed: 0 additions & 29 deletions
This file was deleted.

0 commit comments

Comments
 (0)