From aa8690a4c6764d4053ca48399465316bc05108a8 Mon Sep 17 00:00:00 2001 From: Dan Callahan Date: Thu, 4 Mar 2021 13:43:50 +0000 Subject: [PATCH 1/7] 2to3 Signed-off-by: Dan Callahan --- test_unpaddedbase64.py | 26 +++++++++++++------------- unpaddedbase64.py | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/test_unpaddedbase64.py b/test_unpaddedbase64.py index a3c8463..5e0bb9c 100644 --- a/test_unpaddedbase64.py +++ b/test_unpaddedbase64.py @@ -19,23 +19,23 @@ class TestUnpaddedBase64(unittest.TestCase): def test_encode(self): - self.assertEqual(encode_base64(b''), u'') - self.assertEqual(encode_base64(b'\x00'), u'AA') - self.assertEqual(encode_base64(b'\x00\x00'), u'AAA') - self.assertEqual(encode_base64(b'\x00\x00\x00'), u'AAAA') + self.assertEqual(encode_base64(b''), '') + self.assertEqual(encode_base64(b'\x00'), 'AA') + self.assertEqual(encode_base64(b'\x00\x00'), 'AAA') + self.assertEqual(encode_base64(b'\x00\x00\x00'), 'AAAA') def test_decode(self): - self.assertEqual(decode_base64(u''), b'') - self.assertEqual(decode_base64(u'AA'), b'\x00') - self.assertEqual(decode_base64(u'AAA'), b'\x00\x00') - self.assertEqual(decode_base64(u'AAAA'), b'\x00\x00\x00') + self.assertEqual(decode_base64(''), b'') + self.assertEqual(decode_base64('AA'), b'\x00') + self.assertEqual(decode_base64('AAA'), b'\x00\x00') + self.assertEqual(decode_base64('AAAA'), b'\x00\x00\x00') with self.assertRaises(Exception): - decode_base64(u'A') + decode_base64('A') def test_encode_urlunsafe_chars(self): - self.assertEqual(encode_base64(b'\xff\xe6\x9a'), u'/+aa') - self.assertEqual(encode_base64(b'\xff\xe6\x9a', True), u'_-aa') + self.assertEqual(encode_base64(b'\xff\xe6\x9a'), '/+aa') + self.assertEqual(encode_base64(b'\xff\xe6\x9a', True), '_-aa') def test_decode_urlunsafe_chars(self): - self.assertEqual(decode_base64(u'/+aa'), b'\xff\xe6\x9a') - self.assertEqual(decode_base64(u'_-aa'), b'\xff\xe6\x9a') + self.assertEqual(decode_base64('/+aa'), b'\xff\xe6\x9a') + self.assertEqual(decode_base64('_-aa'), b'\xff\xe6\x9a') diff --git a/unpaddedbase64.py b/unpaddedbase64.py index 245697c..03a7213 100644 --- a/unpaddedbase64.py +++ b/unpaddedbase64.py @@ -23,7 +23,7 @@ def encode_base64(input_bytes, urlsafe=False): encode = base64.urlsafe_b64encode if urlsafe else base64.b64encode output_bytes = encode(input_bytes) output_string = output_bytes.decode("ascii") - return output_string.rstrip(u"=") + return output_string.rstrip("=") def decode_base64(input_string): @@ -34,7 +34,7 @@ def decode_base64(input_string): input_len = len(input_bytes) padding = b"=" * (3 - ((input_len + 3) % 4)) decode = base64.b64decode - if u'-' in input_string or u'_' in input_string: + if '-' in input_string or '_' in input_string: decode = base64.urlsafe_b64decode output_bytes = decode(input_bytes + padding) return output_bytes From f39b7b4d88b18f968a5796837b50e1daf0a839d0 Mon Sep 17 00:00:00 2001 From: Dan Callahan Date: Thu, 4 Mar 2021 13:51:57 +0000 Subject: [PATCH 2/7] black Signed-off-by: Dan Callahan --- test_unpaddedbase64.py | 27 +++++++++++++-------------- unpaddedbase64.py | 2 +- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/test_unpaddedbase64.py b/test_unpaddedbase64.py index 5e0bb9c..abe0944 100644 --- a/test_unpaddedbase64.py +++ b/test_unpaddedbase64.py @@ -17,25 +17,24 @@ class TestUnpaddedBase64(unittest.TestCase): - def test_encode(self): - self.assertEqual(encode_base64(b''), '') - self.assertEqual(encode_base64(b'\x00'), 'AA') - self.assertEqual(encode_base64(b'\x00\x00'), 'AAA') - self.assertEqual(encode_base64(b'\x00\x00\x00'), 'AAAA') + self.assertEqual(encode_base64(b""), "") + self.assertEqual(encode_base64(b"\x00"), "AA") + self.assertEqual(encode_base64(b"\x00\x00"), "AAA") + self.assertEqual(encode_base64(b"\x00\x00\x00"), "AAAA") def test_decode(self): - self.assertEqual(decode_base64(''), b'') - self.assertEqual(decode_base64('AA'), b'\x00') - self.assertEqual(decode_base64('AAA'), b'\x00\x00') - self.assertEqual(decode_base64('AAAA'), b'\x00\x00\x00') + self.assertEqual(decode_base64(""), b"") + self.assertEqual(decode_base64("AA"), b"\x00") + self.assertEqual(decode_base64("AAA"), b"\x00\x00") + self.assertEqual(decode_base64("AAAA"), b"\x00\x00\x00") with self.assertRaises(Exception): - decode_base64('A') + decode_base64("A") def test_encode_urlunsafe_chars(self): - self.assertEqual(encode_base64(b'\xff\xe6\x9a'), '/+aa') - self.assertEqual(encode_base64(b'\xff\xe6\x9a', True), '_-aa') + self.assertEqual(encode_base64(b"\xff\xe6\x9a"), "/+aa") + self.assertEqual(encode_base64(b"\xff\xe6\x9a", True), "_-aa") def test_decode_urlunsafe_chars(self): - self.assertEqual(decode_base64('/+aa'), b'\xff\xe6\x9a') - self.assertEqual(decode_base64('_-aa'), b'\xff\xe6\x9a') + self.assertEqual(decode_base64("/+aa"), b"\xff\xe6\x9a") + self.assertEqual(decode_base64("_-aa"), b"\xff\xe6\x9a") diff --git a/unpaddedbase64.py b/unpaddedbase64.py index 03a7213..4658a18 100644 --- a/unpaddedbase64.py +++ b/unpaddedbase64.py @@ -34,7 +34,7 @@ def decode_base64(input_string): input_len = len(input_bytes) padding = b"=" * (3 - ((input_len + 3) % 4)) decode = base64.b64decode - if '-' in input_string or '_' in input_string: + if "-" in input_string or "_" in input_string: decode = base64.urlsafe_b64decode output_bytes = decode(input_bytes + padding) return output_bytes From e89dd9844f348d8c75cc937c1484189809d7267b Mon Sep 17 00:00:00 2001 From: Dan Callahan Date: Thu, 4 Mar 2021 14:01:56 +0000 Subject: [PATCH 3/7] Avoid need to infer urlsafe in decode_base64 Signed-off-by: Dan Callahan --- unpaddedbase64.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/unpaddedbase64.py b/unpaddedbase64.py index 4658a18..20a3b52 100644 --- a/unpaddedbase64.py +++ b/unpaddedbase64.py @@ -18,7 +18,7 @@ def encode_base64(input_bytes, urlsafe=False): - """Encode bytes as a base64 string without any padding.""" + """Encode bytes as an unpadded base64 string.""" encode = base64.urlsafe_b64encode if urlsafe else base64.b64encode output_bytes = encode(input_bytes) @@ -27,14 +27,12 @@ def encode_base64(input_bytes, urlsafe=False): def decode_base64(input_string): - """Decode a base64 string to bytes inferring padding from the length of the - string.""" + """Decode an unpadded standard or urlsafe base64 string to bytes.""" input_bytes = input_string.encode("ascii") input_len = len(input_bytes) padding = b"=" * (3 - ((input_len + 3) % 4)) - decode = base64.b64decode - if "-" in input_string or "_" in input_string: - decode = base64.urlsafe_b64decode - output_bytes = decode(input_bytes + padding) + + # Passing altchars here allows decoding both standard and urlsafe base64 + output_bytes = base64.b64decode(input_bytes + padding, altchars=b"-_") return output_bytes From e33dc0899999a07129b62b3d2fba8a1aa6853a14 Mon Sep 17 00:00:00 2001 From: Dan Callahan Date: Thu, 4 Mar 2021 14:23:19 +0000 Subject: [PATCH 4/7] mypy Signed-off-by: Dan Callahan --- test_unpaddedbase64.py | 8 ++++---- unpaddedbase64.py | 10 +++++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/test_unpaddedbase64.py b/test_unpaddedbase64.py index abe0944..52e5d38 100644 --- a/test_unpaddedbase64.py +++ b/test_unpaddedbase64.py @@ -17,13 +17,13 @@ class TestUnpaddedBase64(unittest.TestCase): - def test_encode(self): + def test_encode(self) -> None: self.assertEqual(encode_base64(b""), "") self.assertEqual(encode_base64(b"\x00"), "AA") self.assertEqual(encode_base64(b"\x00\x00"), "AAA") self.assertEqual(encode_base64(b"\x00\x00\x00"), "AAAA") - def test_decode(self): + def test_decode(self) -> None: self.assertEqual(decode_base64(""), b"") self.assertEqual(decode_base64("AA"), b"\x00") self.assertEqual(decode_base64("AAA"), b"\x00\x00") @@ -31,10 +31,10 @@ def test_decode(self): with self.assertRaises(Exception): decode_base64("A") - def test_encode_urlunsafe_chars(self): + def test_encode_urlunsafe_chars(self) -> None: self.assertEqual(encode_base64(b"\xff\xe6\x9a"), "/+aa") self.assertEqual(encode_base64(b"\xff\xe6\x9a", True), "_-aa") - def test_decode_urlunsafe_chars(self): + def test_decode_urlunsafe_chars(self) -> None: self.assertEqual(decode_base64("/+aa"), b"\xff\xe6\x9a") self.assertEqual(decode_base64("_-aa"), b"\xff\xe6\x9a") diff --git a/unpaddedbase64.py b/unpaddedbase64.py index 20a3b52..9bca0be 100644 --- a/unpaddedbase64.py +++ b/unpaddedbase64.py @@ -17,16 +17,20 @@ __version__ = "1.1.0" -def encode_base64(input_bytes, urlsafe=False): +def encode_base64(input_bytes: bytes, urlsafe: bool = False) -> str: """Encode bytes as an unpadded base64 string.""" - encode = base64.urlsafe_b64encode if urlsafe else base64.b64encode + if urlsafe: + encode = base64.urlsafe_b64encode + else: + encode = base64.b64encode + output_bytes = encode(input_bytes) output_string = output_bytes.decode("ascii") return output_string.rstrip("=") -def decode_base64(input_string): +def decode_base64(input_string: str) -> bytes: """Decode an unpadded standard or urlsafe base64 string to bytes.""" input_bytes = input_string.encode("ascii") From d1afbbdf660e88dd0ab909e8b68b2f302dcc5f57 Mon Sep 17 00:00:00 2001 From: Dan Callahan Date: Thu, 4 Mar 2021 16:07:21 +0000 Subject: [PATCH 5/7] Update copyright dates Signed-off-by: Dan Callahan --- test_unpaddedbase64.py | 1 + unpaddedbase64.py | 1 + 2 files changed, 2 insertions(+) diff --git a/test_unpaddedbase64.py b/test_unpaddedbase64.py index 52e5d38..90d21a0 100644 --- a/test_unpaddedbase64.py +++ b/test_unpaddedbase64.py @@ -1,4 +1,5 @@ # Copyright 2015 OpenMarket Ltd +# Copyright 2021 The Matrix.org Foundation C.I.C. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/unpaddedbase64.py b/unpaddedbase64.py index 9bca0be..987931c 100644 --- a/unpaddedbase64.py +++ b/unpaddedbase64.py @@ -1,4 +1,5 @@ # Copyright 2014, 2015 OpenMarket Ltd +# Copyright 2021 The Matrix.org Foundation C.I.C. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From 1a46398cd8d558cfecb373ae4b96912e685cdab8 Mon Sep 17 00:00:00 2001 From: Dan Callahan Date: Thu, 4 Mar 2021 16:06:26 +0000 Subject: [PATCH 6/7] Switch to declarative setup.cfg Signed-off-by: Dan Callahan --- MANIFEST.in | 4 ---- README.rst | 16 ++++------------ pyproject.toml | 3 +++ setup.cfg | 16 ++++++++++++++++ setup.py | 49 ----------------------------------------------- unpaddedbase64.py | 2 -- 6 files changed, 23 insertions(+), 67 deletions(-) delete mode 100644 MANIFEST.in create mode 100644 pyproject.toml create mode 100644 setup.cfg delete mode 100755 setup.py diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 6429bd5..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -include *.in -include *.py -include LICENSE -include tox.ini diff --git a/README.rst b/README.rst index 5796b7b..9c211d5 100644 --- a/README.rst +++ b/README.rst @@ -1,18 +1,10 @@ Unpadded Base64 =============== -.. image:: https://img.shields.io/pypi/v/unpaddedbase64.svg - :target: https://pypi.python.org/pypi/unpaddedbase64/ - :alt: Latest Version - -.. image:: https://img.shields.io/travis/matrix-org/python-unpaddedbase64.svg - :target: https://travis-ci.org/matrix-org/python-unpaddedbase64 - Encode and decode Base64 without "=" padding. `RFC 4648`_ specifies that Base64 should be padded to a multiple of 4 bytes -using "=" characters. However this conveys no benefit so many protocols choose -to use Base64 without the "=" padding. +using "=" characters. However many protocols choose to omit the "=" padding. .. _`RFC 4648`: https://tools.ietf.org/html/rfc4648 @@ -21,7 +13,7 @@ Installing .. code:: bash - pip install unpaddedbase64 + python3 -m pip install unpaddedbase64 Using ----- @@ -29,5 +21,5 @@ Using .. code:: python import unpaddedbase64 - assert (unpaddedbase64.encode_base64(b'\x00')) == u'AA' - assert (unpaddedbase64.decode_base64(u'AA')) == b'\x00' + assert (unpaddedbase64.encode_base64(b'\x00')) == 'AA' + assert (unpaddedbase64.decode_base64('AA')) == b'\x00' diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9f92222 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=43", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..9b2b471 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,16 @@ +[metadata] +name = unpaddedbase64 +version = 1.1.0 +description = Unpadded Base64 +long_description = file: README.rst +keywords = base64 +url = https://github.com/matrix-org/python-unpaddedbase64 +project_urls = + Issue Tracker = https://github.com/matrix-org/python-unpaddedbase64/issues +classifiers = + License :: OSI Approved :: Apache Software License + Programming Language :: Python :: 3 + +[options] +py_modules = unpaddedbase64 +python_requires = >=3.6 diff --git a/setup.py b/setup.py deleted file mode 100755 index 6d7ee74..0000000 --- a/setup.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2015 OpenMarket Ltd -# -# 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 setuptools import setup -from codecs import open -import os - -here = os.path.abspath(os.path.dirname(__file__)) - - -def read_file(path_segments): - """Read a UTF-8 file from the package. Takes a list of strings to join to - make the path""" - file_path = os.path.join(here, *path_segments) - with open(file_path, encoding="utf-8") as f: - return f.read() - - -def exec_file(path_segments, name): - """Extract a constant from a python file by looking for a line defining - the constant and executing it.""" - result = {} - code = read_file(path_segments) - lines = [line for line in code.split('\n') if line.startswith(name)] - exec("\n".join(lines), result) - return result[name] - - -setup( - name="unpaddedbase64", - version=exec_file(("unpaddedbase64.py",), "__version__"), - py_modules=["unpaddedbase64"], - description="Unpadded Base64", - long_description=read_file(("README.rst",)), - keywords="base64", -) diff --git a/unpaddedbase64.py b/unpaddedbase64.py index 987931c..9f45bee 100644 --- a/unpaddedbase64.py +++ b/unpaddedbase64.py @@ -15,8 +15,6 @@ import base64 -__version__ = "1.1.0" - def encode_base64(input_bytes: bytes, urlsafe: bool = False) -> str: """Encode bytes as an unpadded base64 string.""" From 7eb9e4fae2377008b65de1b308720953dc01cef4 Mon Sep 17 00:00:00 2001 From: Dan Callahan Date: Thu, 4 Mar 2021 22:59:57 +0000 Subject: [PATCH 7/7] Replace Travis with GitHub Actions Signed-off-by: Dan Callahan --- .github/workflows/continuous-integration.yml | 48 ++++++++++++++++++++ .travis.yml | 15 ------ setup.cfg | 9 +++- tox.ini | 27 ----------- 4 files changed, 56 insertions(+), 43 deletions(-) create mode 100644 .github/workflows/continuous-integration.yml delete mode 100644 .travis.yml delete mode 100644 tox.ini diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml new file mode 100644 index 0000000..799a7ed --- /dev/null +++ b/.github/workflows/continuous-integration.yml @@ -0,0 +1,48 @@ +name: Continuous Integration + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install flake8 black mypy + - name: Lint with flake8 + run: | + flake8 *.py + - name: Lint with black + run: | + black --check --diff *.py + - name: Lint with mypy + run: | + mypy --strict *.py + + test: + needs: lint + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.x', '3.6', 'pypy-3.6'] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Test with unittest + run: | + python -m unittest --verbose diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b10c59a..0000000 --- a/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: python -env: -- TOXENV=packaging -- TOXENV=pep8 -- TOXENV=py3pep8 -- TOXENV=py27 -- TOXENV=py33 -- TOXENV=py34 -- TOXENV=pypy - -install: -- pip install tox - -script: -- tox diff --git a/setup.cfg b/setup.cfg index 9b2b471..07e0c8c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = unpaddedbase64 -version = 1.1.0 +version = 2.0.0rc1 description = Unpadded Base64 long_description = file: README.rst keywords = base64 @@ -14,3 +14,10 @@ classifiers = [options] py_modules = unpaddedbase64 python_requires = >=3.6 + +[mypy] +python_version = 3.6 + +[flake8] +max-line-length = 88 +extend-ignore = E203, W503 diff --git a/tox.ini b/tox.ini deleted file mode 100644 index c368ee7..0000000 --- a/tox.ini +++ /dev/null @@ -1,27 +0,0 @@ -[tox] -envlist = packaging, pep8, py3pep8, py27, py33, py34, pypy - -[testenv] -deps = - coverage - pytest -commands = - coverage run --source unpaddedbase64 -m pytest - coverage report -m --fail-under 100 - -[testenv:packaging] -deps = - check-manifest -commands = check-manifest - -[testenv:pep8] -basepython = python2.7 -deps = - flake8 -commands = flake8 . - -[testenv:py3pep8] -basepython = python3.4 -deps = - flake8 -commands = flake8 .