Add utilities for base64

Writing a code using the base64 module (of the Python standard
library) working on Python 2.7 and 3.4 requires many checks on the
input and/or output type:

* base64.b64encode() only accepts byte string: text must be
  explicitly encoded to ASCII
* base64.b64decode() returns bytes: output must be decoded from UTF-8
  when text is expected

This change adds two pairs of encode/decode functions:

* encode_as_bytes(), decode_as_bytes(): always return the result as
  a byte string
* encode_as_text(), decode_as_text(): always return the result as a
  text string

Encode functions accept text: text is encoded to UTF-8 by default,
but the encoding is configurable.

Decode functions accept text: text is decoded from ASCII.

decode_as_text() decodes the result from UTF-8 by default, but again
the encoding is configurable.


The new submodule is called "base64" to be able to replace:

    import base64

with:

    from oslo_serialization import base64

If the base64 module of the stdlib is needed, it can be imported as a
different name. Example:

    import base64 as std_base64


The encoding example:

    if isinstance(text, six.text_type):
       text = text.encode('utf-8')
    text_b64 = base64.b64encode(text)
    text_b64 = text_b64.decode('ascii')

can be replaced with:

    text_b64 = base64.encode_as_text(text)


The decoding example:

    if isinstance(encoded, six.text_type):
       encoded = encoded.decode('ascii')
    text = base64.b64decode(encoded)
    text = text.decode('utf-8')

can be replaced with:

    text = base64.decode_as_text(text)

Change-Id: Icf8df9c947bc0c5f4838508b756ed8f53efd9fc4
This commit is contained in:
Victor Stinner 2015-10-01 16:04:59 +02:00
parent dbcab5f327
commit e49e812900
4 changed files with 142 additions and 1 deletions

View File

@ -11,7 +11,7 @@
:alt: Downloads :alt: Downloads
The oslo.serialization library provides support for representing objects The oslo.serialization library provides support for representing objects
in transmittable and storable formats, such as JSON and MessagePack. in transmittable and storable formats, such as Base64, JSON and MessagePack.
* Free software: Apache license * Free software: Apache license
* Documentation: http://docs.openstack.org/developer/oslo.serialization * Documentation: http://docs.openstack.org/developer/oslo.serialization

View File

@ -1,3 +1,9 @@
base64
======
.. automodule:: oslo_serialization.base64
:members:
jsonutils jsonutils
========= =========

View File

@ -0,0 +1,79 @@
# Copyright 2015 Red Hat
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import base64
import six
def encode_as_bytes(s, encoding='utf-8'):
"""Encode a string using Base64.
If *s* is a text string, first encode it to *encoding* (UTF-8 by default).
:param s: bytes or text string to be encoded
:param encoding: encoding used to encode *s* if it's a text string
:returns: Base64 encoded byte string (bytes)
Use encode_as_text() to get the Base64 encoded string as text.
"""
if isinstance(s, six.text_type):
s = s.encode(encoding)
return base64.b64encode(s)
def encode_as_text(s, encoding='utf-8'):
"""Encode a string using Base64.
If *s* is a text string, first encode it to *encoding* (UTF-8 by default).
:param s: bytes or text string to be encoded
:param encoding: encoding used to encode *s* if it's a text string
:returns: Base64 encoded text string (Unicode)
Use encode_as_bytes() to get the Base64 encoded string as bytes.
"""
encoded = encode_as_bytes(s, encoding=encoding)
return encoded.decode('ascii')
def decode_as_bytes(encoded):
"""Decode a Base64 encoded string.
:param encoded: bytes or text Base64 encoded string to be decoded
:returns: decoded bytes string (bytes)
Use decode_as_text() to get the decoded string as text.
"""
if isinstance(encoded, bytes):
encoded = encoded.decode('ascii')
return base64.b64decode(encoded)
def decode_as_text(encoded, encoding='utf-8'):
"""Decode a Base64 encoded string.
Decode the Base64 string and then decode the result from *encoding*
(UTF-8 by default).
:param encoded: bytes or text Base64 encoded string to be decoded
:returns: decoded text string (bytes)
Use decode_as_bytes() to get the decoded string as bytes.
"""
decoded = decode_as_bytes(encoded)
return decoded.decode(encoding)

View File

@ -0,0 +1,56 @@
# Copyright 2015 Red Hat
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_serialization import base64
from oslotest import base as test_base
class Base64Tests(test_base.BaseTestCase):
def test_encode_as_bytes(self):
self.assertEqual(base64.encode_as_bytes(b'text'),
b'dGV4dA==')
self.assertEqual(base64.encode_as_bytes(u'text'),
b'dGV4dA==')
self.assertEqual(base64.encode_as_bytes(u'e:\xe9'),
b'ZTrDqQ==')
self.assertEqual(base64.encode_as_bytes(u'e:\xe9', encoding='latin1'),
b'ZTrp')
def test_encode_as_text(self):
self.assertEqual(base64.encode_as_text(b'text'),
u'dGV4dA==')
self.assertEqual(base64.encode_as_text(u'text'),
u'dGV4dA==')
self.assertEqual(base64.encode_as_text(u'e:\xe9'),
u'ZTrDqQ==')
self.assertEqual(base64.encode_as_text(u'e:\xe9', encoding='latin1'),
u'ZTrp')
def test_decode_as_bytes(self):
self.assertEqual(base64.decode_as_bytes(b'dGV4dA=='),
b'text')
self.assertEqual(base64.decode_as_bytes(u'dGV4dA=='),
b'text')
def test_decode_as_text(self):
self.assertEqual(base64.decode_as_text(b'dGV4dA=='),
u'text')
self.assertEqual(base64.decode_as_text(u'dGV4dA=='),
u'text')
self.assertEqual(base64.decode_as_text(u'ZTrDqQ=='),
u'e:\xe9')
self.assertEqual(base64.decode_as_text(u'ZTrp', encoding='latin1'),
u'e:\xe9')