diff options
Diffstat (limited to 'third_party/python/slugid')
-rw-r--r-- | third_party/python/slugid/PKG-INFO | 14 | ||||
-rw-r--r-- | third_party/python/slugid/README.rst | 122 | ||||
-rw-r--r-- | third_party/python/slugid/setup.cfg | 5 | ||||
-rwxr-xr-x | third_party/python/slugid/setup.py | 39 | ||||
-rw-r--r-- | third_party/python/slugid/slugid/__init__.py | 48 | ||||
-rw-r--r-- | third_party/python/slugid/slugid/slugid.py | 48 |
6 files changed, 276 insertions, 0 deletions
diff --git a/third_party/python/slugid/PKG-INFO b/third_party/python/slugid/PKG-INFO new file mode 100644 index 0000000000..ba71c90bab --- /dev/null +++ b/third_party/python/slugid/PKG-INFO @@ -0,0 +1,14 @@ +Metadata-Version: 1.1 +Name: slugid +Version: 1.0.7 +Summary: Base64 encoded uuid v4 slugs +Home-page: http://taskcluster.github.io/slugid.py +Author: Pete Moore +Author-email: pmoore@mozilla.com +License: MPL 2.0 +Description: UNKNOWN +Platform: UNKNOWN +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3.5 diff --git a/third_party/python/slugid/README.rst b/third_party/python/slugid/README.rst new file mode 100644 index 0000000000..6b902b4f51 --- /dev/null +++ b/third_party/python/slugid/README.rst @@ -0,0 +1,122 @@ +slugid.py - Compressed UUIDs for python +======================================= + +.. image:: https://tools.taskcluster.net/lib/assets/taskcluster-120.png + +|Build Status| |Coverage Status| |License| |pypi Version| |Downloads| + +A python 2.7 and python 3.5 compatible module for generating v4 UUIDs and +encoding them into 22 character URL-safe base64 slug representation (see `RFC +4648 sec. 5`_). + +Slugs are url-safe base64 encoded v4 uuids, stripped of base64 ``=`` padding. + +There are two methods for generating slugs - ``slugid.v4()`` and +``slugid.nice()``. + +- The ``slugid.v4()`` method returns a slug from a randomly generated v4 uuid. +- The ``slugid.nice()`` method returns a v4 slug which conforms to a set of + "nice" properties. At the moment the only "nice" property is that the slug + starts with ``[A-Za-f]``, which in turn implies that the first (most + significant) bit of its associated uuid is set to 0. + +The purpose of the ``slugid.nice()`` method is to support having slugids which +can be used in more contexts safely. Regular slugids can safely be used in +urls, and for example in AMQP routing keys. However, slugs beginning with ``-`` +may cause problems when used as command line parameters. + +In contrast, slugids generated by the ``slugid.nice()`` method can safely be +used as command line parameters. This comes at a cost to entropy (121 bits vs +122 bits for regular v4 slugs). + +Slug consumers should consider carefully which of these two slug generation +methods to call. Is it more important to have maximum entropy, or to have +slugids that do not need special treatment when used as command line +parameters? This is especially important if you are providing a service which +supplies slugs to unexpecting tool developers downstream, who may not realise +the risks of using your regular v4 slugs as command line parameters, especially +since this would arise only as an intermittent issue (one time in 64). + +Generated slugs take the form ``[A-Za-z0-9_-]{22}``, or more precisely: + +- ``slugid.v4()`` slugs conform to + ``[A-Za-z0-9_-]{8}[Q-T][A-Za-z0-9_-][CGKOSWaeimquy26-][A-Za-z0-9_-]{10}[AQgw]`` + +- ``slugid.nice()`` slugs conform to + ``[A-Za-f][A-Za-z0-9_-]{7}[Q-T][A-Za-z0-9_-][CGKOSWaeimquy26-][A-Za-z0-9_-]{10}[AQgw]`` + +RFC 4122 defines the setting of 6 bits of the v4 UUID which implies v4 slugs +provide 128 - 6 = 122 bits entropy. Due to the (un)setting of the first bit +of "nice" slugs, nice slugs provide therefore 121 bits entropy. + + +Usage +----- + +.. code-block:: python + + import slugid + + # Generate "nice" URL-safe base64 encoded UUID version 4 (random) + slug = slugid.nice() # a8_YezW8T7e1jLxG7evy-A + + # Alternative, if slugs will not be used as command line parameters + slug = slugid.v4() # -9OpXaCORAaFh4sJRk7PUA + + # Get python uuid.UUID object + uuid = slugid.decode(slug) + + # Compress to slug again + assert(slug == slugid.encode(uuid)) + + +RNG Characteristics +------------------- +UUID generation is performed by the built-in python `uuid library`_ which does +not document its randomness, but falls back to system uuid-generation libraries +where available, then urandom, then random. Therefore generated slugids match +these rng characteristics. + +License +------- +The ``slugid`` library is released on the MPL 2.0 license, see the ``LICENSE`` +for complete license. + +Testing +------- + +.. code-block:: bash + + pip install -r requirements.txt + tox + +Publishing +---------- +To republish this library to pypi.python.org, update the version number in +``slugid/__init__.py``, commit it, push to github, and then run: + +.. code-block:: bash + + # delete stale versions + rm -rf dist + + # build source package + python setup.py sdist + + # publish it + twine upload -s dist/* + + +.. _RFC 4648 sec. 5: http://tools.ietf.org/html/rfc4648#section-5 +.. _uuid library: https://docs.python.org/2/library/uuid.html + +.. |Build Status| image:: https://travis-ci.org/taskcluster/slugid.py.svg?branch=master + :target: http://travis-ci.org/taskcluster/slugid.py +.. |Coverage Status| image:: https://coveralls.io/repos/taskcluster/slugid.py/badge.svg?branch=master&service=github + :target: https://coveralls.io/github/taskcluster/slugid.py?branch=master +.. |License| image:: https://img.shields.io/badge/license-MPL%202.0-orange.svg + :target: https://github.com/taskcluster/slugid.py/blob/master/LICENSE +.. |pypi Version| image:: https://img.shields.io/pypi/v/slugid.svg + :target: https://pypi.python.org/pypi/slugid +.. |Downloads| image:: https://img.shields.io/pypi/dm/slugid.svg + :target: https://pypi.python.org/pypi/slugid diff --git a/third_party/python/slugid/setup.cfg b/third_party/python/slugid/setup.cfg new file mode 100644 index 0000000000..861a9f5542 --- /dev/null +++ b/third_party/python/slugid/setup.cfg @@ -0,0 +1,5 @@ +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff --git a/third_party/python/slugid/setup.py b/third_party/python/slugid/setup.py new file mode 100755 index 0000000000..c45c726bd4 --- /dev/null +++ b/third_party/python/slugid/setup.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +import re +from codecs import open + +try: + from setuptools import setup +except ImportError: + from distutils.core import setup + +packages = [ + 'slugid', +] + +version = '' +with open('slugid/__init__.py', 'r') as fd: + version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', + fd.read(), re.MULTILINE).group(1) + +if not version: + raise RuntimeError('Cannot find version information') + +setup( + name='slugid', + version=version, + description='Base64 encoded uuid v4 slugs', + author='Pete Moore', + author_email='pmoore@mozilla.com', + url='http://taskcluster.github.io/slugid.py', + packages=packages, + package_data={'': ['LICENSE', 'README.md']}, + license='MPL 2.0', + classifiers=( + 'Intended Audience :: Developers', + 'Natural Language :: English', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.5', + ), +) diff --git a/third_party/python/slugid/slugid/__init__.py b/third_party/python/slugid/slugid/__init__.py new file mode 100644 index 0000000000..796f07b65f --- /dev/null +++ b/third_party/python/slugid/slugid/__init__.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +# ************** +# * Slugid API * +# ************** +# +# @)@) +# _|_| ( ) +# _(___,`\ _,--------------._ (( /`, )) +# `==` `*-_,' O `~._ ( ( _/ | ) ) +# `, : o } `~._.~` * ', +# \ - _ O - ,' +# | ; - - " ; o / +# | O o ,-` +# \ _,-:""""""'`:-._ - . O / +# `""""""~'` `._ _,-` +# """""" + +""" +SlugID: Base 64 encoded v4 UUIDs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Usage: + + >>> import slugid + >>> s = slugid.nice() + >>> s + eWIgwMgxSfeXQ36iPbOxiQ + >>> u = slugid.decode(s) + >>> u + UUID('796220c0-c831-49f7-9743-7ea23db3b189') + >>> slugid.encode(u) + eWIgwMgxSfeXQ36iPbOxiQ + >>> slugid.v4() + -9OpXaCORAaFh4sJRk7PUA +""" +from .slugid import decode, encode, nice, v4 + +__title__ = 'slugid' +__version__ = '1.0.7' +__author__ = 'Peter Moore' +__license__ = 'MPL 2.0' +__all__ = [ + 'decode', + 'encode', + 'nice', + 'v4', +] diff --git a/third_party/python/slugid/slugid/slugid.py b/third_party/python/slugid/slugid/slugid.py new file mode 100644 index 0000000000..2a1377c84b --- /dev/null +++ b/third_party/python/slugid/slugid/slugid.py @@ -0,0 +1,48 @@ +# Licensed under the Mozilla Public Licence 2.0. +# https://www.mozilla.org/en-US/MPL/2.0 + +import sys +import uuid +import base64 + + +def encode(uuid_): + """ + Returns the given uuid.UUID object as a 22 character slug. This can be a + regular v4 slug or a "nice" slug. + """ + return base64.urlsafe_b64encode(uuid_.bytes)[:-2] # Drop '==' padding + + +def decode(slug): + """ + Returns the uuid.UUID object represented by the given v4 or "nice" slug + """ + if sys.version_info.major != 2 and isinstance(slug, bytes): + slug = slug.decode('ascii') + slug = slug + '==' # base64 padding + return uuid.UUID(bytes=base64.urlsafe_b64decode(slug)) + + +def v4(): + """ + Returns a randomly generated uuid v4 compliant slug + """ + return base64.urlsafe_b64encode(uuid.uuid4().bytes)[:-2] # Drop '==' padding + + +def nice(): + """ + Returns a randomly generated uuid v4 compliant slug which conforms to a set + of "nice" properties, at the cost of some entropy. Currently this means one + extra fixed bit (the first bit of the uuid is set to 0) which guarantees the + slug will begin with [A-Za-f]. For example such slugs don't require special + handling when used as command line parameters (whereas non-nice slugs may + start with `-` which can confuse command line tools). + + Potentially other "nice" properties may be added in future to further + restrict the range of potential uuids that may be generated. + """ + rawBytes = bytearray(uuid.uuid4().bytes) + rawBytes[0] = rawBytes[0] & 0x7f # Ensure slug starts with [A-Za-f] + return base64.urlsafe_b64encode(rawBytes)[:-2] # Drop '==' padding |