diff --git a/koji/util.py b/koji/util.py index f0d9cc3f..4b2a3a7f 100644 --- a/koji/util.py +++ b/koji/util.py @@ -20,6 +20,7 @@ from __future__ import absolute_import from __future__ import division +import base64 import calendar import datetime import hashlib @@ -142,6 +143,42 @@ def printList(l): return ret +def base64encode(s, as_bytes=False): + """Helper function to encode string or bytes as base64 + + This function returns a string unless as_bytes is True + """ + if six.PY2: + return base64.b64encode(s) + + if isinstance(s, str): + s = s.encode('utf8') + data = base64.b64encode(s) + if as_bytes: + return data + else: + # ascii is always good enough for base64 encoded data + return data.decode('ascii') + + +# We don't need a decode wrapper, but we define this for naming consistency +base64decode = base64.b64decode + + +def decode_bytes(data, fallback='iso8859-15'): + """Decode a bytes-like object that is expected to be a valid string + + First utf8 is tried, then the fallback (defaults to iso8859-15). + The fallback behavior can be disabled by setting the option to None. + """ + try: + return data.decode('utf8') + except UnicodeDecodeError: + if fallback: + return data.decode(fallback) + raise + + def multi_fnmatch(s, patterns): """Returns true if s matches any pattern in the list diff --git a/tests/test_lib/test_base64.py b/tests/test_lib/test_base64.py new file mode 100644 index 00000000..8c8c42ee --- /dev/null +++ b/tests/test_lib/test_base64.py @@ -0,0 +1,28 @@ +# coding=utf-8 +from __future__ import absolute_import +import mock +import six +try: + import unittest2 as unittest +except ImportError: + import unittest + +from koji.util import base64encode, base64decode + + +class Base64EncodeTestCase(unittest.TestCase): + + DATA = [ + # list of pairs [string, encoded_string] + [b'Hello World', 'SGVsbG8gV29ybGQ='], + [b'BZh91AY&SY\x14\x99\\\xcf\x05y\r\x7f\xff\xff', + 'QlpoOTFBWSZTWRSZXM8FeQ1///8='] + ] + + def test_base64encode(self): + for s, expected in self.DATA: + result = base64encode(s) + self.assertEqual(result, expected) + if six.PY3: + result = base64encode(s, as_bytes=True) + self.assertEqual(result, expected.encode('ascii'))