summaryrefslogtreecommitdiffstats
path: root/third_party/python/slugid
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/python/slugid')
-rw-r--r--third_party/python/slugid/PKG-INFO14
-rw-r--r--third_party/python/slugid/README.rst122
-rw-r--r--third_party/python/slugid/setup.cfg5
-rwxr-xr-xthird_party/python/slugid/setup.py39
-rw-r--r--third_party/python/slugid/slugid/__init__.py48
-rw-r--r--third_party/python/slugid/slugid/slugid.py48
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