Skip to main content

Universally Unique Lexicographically Sortable Identifier

Project description

ulid
====

|Build Status| |Build Status| |codecov| |Code Climate| |Issue Count|

|PyPI Version| |PyPI Versions|

|Updates|

|Documentation Status|

|Stories in Ready|

`Universally Unique Lexicographically Sortable
Identifier <https://github.com/alizain/ulid>`__ in `Python
3 <https://www.python.org/>`__.

Status
~~~~~~

This project is actively maintained.

Installation
~~~~~~~~~~~~

To install ulid from `pip <https://pypi.python.org/pypi/pip>`__:

.. code:: bash

$ pip install ulid-py

To install ulid from source:

.. code:: bash

$ git clone git@github.com:ahawker/ulid.git
$ cd ulid && python setup.py install

Usage
~~~~~

Create a brand new ULID.

The timestamp value (48-bits) is from
`time.time() <https://docs.python.org/3/library/time.html?highlight=time.time#time.time>`__
with millisecond precision.

The randomness value (80-bits) is from
`os.urandom() <https://docs.python.org/3/library/os.html?highlight=os.urandom#os.urandom>`__.

.. code:: python

>>> import ulid
>>> ulid.new()
<ULID('01BJQE4QTHMFP0S5J153XCFSP9')>

Create a new ULID from an existing 128-bit value, such as a
`UUID <https://docs.python.org/3/library/uuid.html>`__.

Supports ULID values as ``int``, ``bytes``, ``str``, and ``UUID`` types.

.. code:: python

>>> import ulid, uuid
>>> value = uuid.uuid4()
>>> value
UUID('0983d0a2-ff15-4d83-8f37-7dd945b5aa39')
>>> ulid.from_uuid(value)
<ULID('09GF8A5ZRN9P1RYDVXV52VBAHS')>

Create a new ULID from an existing timestamp value, such as a
`datetime <https://docs.python.org/3/library/datetime.html#module-datetime>`__
object.

Supports timestamp values as ``int``, ``float``, ``str``, ``bytes``,
``bytearray``, ``memoryview``, ``datetime``, ``Timestamp``, and ``ULID``
types.

.. code:: python

>>> import datetime, ulid
>>> ulid.from_timestamp(datetime.datetime(1999, 1, 1))
<ULID('00TM9HX0008S220A3PWSFVNFEH')>

Create a new ULID from an existing randomness value.

Supports randomness values as ``int``, ``float``, ``str``, ``bytes``,
``bytearray``, ``memoryview``, ``Randomness``, and ``ULID`` types.

.. code:: python

>>> import os, ulid
>>> randomness = os.urandom(10)
>>> ulid.from_randomness(randomness)
>>> <ULID('01BJQHX2XEDK0VN0GMYWT9JN8S')>

Once you have a ULID object, there are a number of ways to interact with
it.

The ``timestamp`` method will give you a snapshot view of the first
48-bits of the ULID while the ``randomness`` method will give you a
snapshot of the last 80-bits.

.. code:: python

>>> import ulid
>>> u = ulid.new()
>>> u
<ULID('01BJQM7SC7D5VVTG3J68ABFQ3N')>
>>> u.timestamp()
<Timestamp('01BJQM7SC7')>
>>> u.randomness()
<Randomness('D5VVTG3J68ABFQ3N')>

The ``ULID``, ``Timestamp``, and ``Randomness`` classes all derive from
the same base class, a ``MemoryView``.

A ``MemoryView`` provides the ``str``, ``int``, and ``bytes`` methods
for changing any values representation.

.. code:: python

>>> import ulid
>>> u = ulid.new()
>>> u
<ULID('01BJQMF54D093DXEAWZ6JYRPAQ')>
>>> u.timestamp()
<Timestamp('01BJQMF54D')>
>>> u.timestamp().int
1497589322893
>>> u.timestamp().bytes
b'\x01\\\xafG\x94\x8d'
>>> u.timestamp().datetime
datetime.datetime(2017, 6, 16, 5, 2, 2, 893000)
>>> u.randomness().bytes
b'\x02F\xde\xb9\\\xf9\xa5\xecYW'
>>> u.bytes[6:] == u.randomness().bytes
True
>>> u.str
'01BJQMF54D093DXEAWZ6JYRPAQ'
>>> u.int
1810474399624548315999517391436142935

A ``MemoryView`` also provides rich comparison functionality.

.. code:: python

>>> import datetime, time, ulid
>>> u1 = ulid.new()
>>> time.sleep(5)
>>> u2 = ulid.new()
>>> u1 < u2
True
>>> u3 = ulid.from_timestamp(datetime.datetime(2039, 1, 1))
>>> u1 < u2 < u3
True
>>> [u.timestamp().datetime for u in sorted([u2, u3, u1])]
[datetime.datetime(2017, 6, 16, 5, 7, 14, 847000), datetime.datetime(2017, 6, 16, 5, 7, 26, 775000), datetime.datetime(2039, 1, 1, 8, 0)]

Future Items
~~~~~~~~~~~~

- Collection of benchmarks to track performance.
- Backport to Python 2.7?
- See `Github Issues <https://github.com/ahawker/ulid/issues>`__ for
more!

Goals
~~~~~

A fast implementation in pure python of the spec with binary format
support.

Contributing
~~~~~~~~~~~~

If you would like to contribute, simply fork the repository, push your
changes and send a pull request. Pull requests will be brought into the
``master`` branch via a rebase and fast-forward merge with the goal of
having a linear branch history with no merge commits.

License
~~~~~~~

`Apache 2.0 <LICENSE>`__

Why not UUID?
-------------

UUID can be suboptimal for many uses-cases because:

- It isn’t the most character efficient way of encoding 128 bits of
randomness
- UUID v1/v2 is impractical in many environments, as it requires access
to a unique, stable MAC address
- UUID v3/v5 requires a unique seed and produces randomly distributed
IDs, which can cause fragmentation in many data structures
- UUID v4 provides no other information than randomness which can cause
fragmentation in many data structures

ULID provides:

- 128-bit compatibility with UUID
- 1.21e+24 unique ULIDs per millisecond
- Lexicographically sortable!
- Canonically encoded as a 26 character string, as opposed to the 36
character UUID
- Uses Crockford’s base32 for better efficiency and readability (5 bits
per character)
- Case insensitive
- No special characters (URL safe)

Specification
-------------

Below is the current specification of ULID as implemented in this
repository.

The binary format is implemented.

::

01AN4Z07BY 79KA1307SR9X4MV3

|----------| |----------------|
Timestamp Randomness
10chars 16chars
48bits 80bits

Components
~~~~~~~~~~

**Timestamp** \* 48 bit integer \* UNIX-time in milliseconds \* Won’t
run out of space till the year 10895 AD.

**Randomness** \* 80 bits \* Cryptographically secure source of
randomness, if possible

Sorting
~~~~~~~

The left-most character must be sorted first, and the right-most
character sorted last (lexical order). The default ASCII character set
must be used. Within the same millisecond, sort order is not guaranteed

Encoding
~~~~~~~~

Crockford’s Base32 is used as shown. This alphabet excludes the letters
I, L, O, and U to avoid confusion and abuse.

::

0123456789ABCDEFGHJKMNPQRSTVWXYZ

Binary Layout and Byte Order
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The components are encoded as 16 octets. Each component is encoded with
the Most Significant Byte first (network byte order).

::

0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 32_bit_uint_time_high |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 16_bit_uint_time_low | 16_bit_uint_random |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 32_bit_uint_random |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 32_bit_uint_random |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

String Representation
~~~~~~~~~~~~~~~~~~~~~

::

ttttttttttrrrrrrrrrrrrrrrr

where
t is Timestamp
r is Randomness

Links
~~~~~

- `Original Implementation
(Javascript) <https://github.com/alizain/ulid>`__
- `ulid (python) <https://github.com/mdipierro/ulid>`__

.. |Build Status| image:: https://travis-ci.org/ahawker/ulid.svg?branch=master
:target: https://travis-ci.org/ahawker/ulid
.. |Build Status| image:: https://ci.appveyor.com/api/projects/status/fy0hufnb8h6gwk4d/branch/master?svg=true
:target: https://ci.appveyor.com/project/ahawker/ulid/branch/master
.. |codecov| image:: https://codecov.io/gh/ahawker/ulid/branch/master/graph/badge.svg
:target: https://codecov.io/gh/ahawker/ulid
.. |Code Climate| image:: https://codeclimate.com/github/ahawker/ulid/badges/gpa.svg
:target: https://codeclimate.com/github/ahawker/ulid
.. |Issue Count| image:: https://codeclimate.com/github/ahawker/ulid/badges/issue_count.svg
:target: https://codeclimate.com/github/ahawker/ulid
.. |PyPI Version| image:: https://badge.fury.io/py/ulid-py.svg
:target: https://badge.fury.io/py/ulid-py
.. |PyPI Versions| image:: https://img.shields.io/pypi/pyversions/ulid-py.svg
:target: https://pypi.python.org/pypi/ulid-py
.. |Updates| image:: https://pyup.io/repos/github/ahawker/ulid/shield.svg
:target: https://pyup.io/repos/github/ahawker/ulid/
.. |Documentation Status| image:: https://readthedocs.org/projects/ulid/badge/?version=latest
:target: http://ulid.readthedocs.io/en/latest/?badge=latest
.. |Stories in Ready| image:: https://badge.waffle.io/ahawker/ulid.svg?label=ready&title=Ready
:target: http://waffle.io/ahawker/ulid


Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ulid-py-0.0.8.tar.gz (14.6 kB view details)

Uploaded Source

Built Distribution

ulid_py-0.0.8-py2.py3-none-any.whl (15.9 kB view details)

Uploaded Python 2 Python 3

File details

Details for the file ulid-py-0.0.8.tar.gz.

File metadata

  • Download URL: ulid-py-0.0.8.tar.gz
  • Upload date:
  • Size: 14.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.6.3 requests-toolbelt/0.8.0 tqdm/4.29.1 CPython/3.4.6

File hashes

Hashes for ulid-py-0.0.8.tar.gz
Algorithm Hash digest
SHA256 a92d432ba07676a7759c10a463b52b7743e33fdb741ec9f3706ea64930d08378
MD5 855d8a3d8a209e322ac1a4c1677ac508
BLAKE2b-256 cb4e4e26eb0107ad88874276ce7b1c0634565d2f0ccea00dff46796de5c870f2

See more details on using hashes here.

File details

Details for the file ulid_py-0.0.8-py2.py3-none-any.whl.

File metadata

  • Download URL: ulid_py-0.0.8-py2.py3-none-any.whl
  • Upload date:
  • Size: 15.9 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.6.3 requests-toolbelt/0.8.0 tqdm/4.29.1 CPython/3.4.6

File hashes

Hashes for ulid_py-0.0.8-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 0050eddd0f7d029cf3f1ee94eda89ecfddcb06d17891936a2880d39ec7f0ba6d
MD5 117877fd0b5a025a0f93a229f8791141
BLAKE2b-256 996c6d0562f3847cdff8ac775eb6f900039e44860d4be82ce52ec0f476cfb31d

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page