From 57675390de22405e6b706085aa8091039182395d Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Thu, 29 Mar 2018 20:09:10 +0900 Subject: [PATCH 01/36] Add Coveralls badge --- README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 3f5744d..3f2bc24 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ base62 ====== -|Build Status| |PyPI| +|Build Status| |Coveralls| |PyPI| A Python module for ``base62`` encoding. Ported from PHP code that I wrote in mid-2000, which can be found @@ -11,6 +11,8 @@ in mid-2000, which can be found :target: https://travis-ci.org/suminb/base62 .. |PyPI| image:: https://img.shields.io/pypi/v/pybase62.svg :target: https://pypi.python.org/pypi/pybase62 +.. |Coveralls| image:: https://coveralls.io/repos/github/suminb/base62/badge.svg?branch=master + :target: https://coveralls.io/github/suminb/base62?branch=develop Rationale From f1ed369878c374471ba53ea7faa85366f500f4f7 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Wed, 6 Jun 2018 00:42:15 +0900 Subject: [PATCH 02/36] Add an assertion statement --- tests/test_basic.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_basic.py b/tests/test_basic.py index c852524..3dcbd6a 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -29,6 +29,11 @@ def test_basic(): assert base62.encode(34441886726) == 'base62' assert base62.decode('base62') == 34441886726 + # NOTE: For backward compatibility. When I first wrote this module in PHP, + # I used to use the `0z` prefix to denote a base62 encoded string (similar + # to `0x` for hexadecimal strings). + assert base62.decode('0zbase62') == 34441886726 + @pytest.mark.parametrize('b, i', bytes_int_pairs) def test_bytes_to_int(b, i): From 9a542ed2892af784fb85ae8b29273912bca106fb Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Wed, 6 Jun 2018 00:43:46 +0900 Subject: [PATCH 03/36] Test for invalid alphabets --- tests/test_basic.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_basic.py b/tests/test_basic.py index 3dcbd6a..bac57f7 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -59,3 +59,8 @@ def test_roundtrip(input_bytes): output_bytes = base62.decodebytes(base62_encoded) assert isinstance(output_bytes, bytes) assert input_bytes == output_bytes + + +def test_invalid_alphabet(): + with pytest.raises(ValueError): + base62.decode('+') From f29e40141d1ebb4a7518c48d1f7e6eda80fae7ba Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Wed, 6 Jun 2018 00:45:37 +0900 Subject: [PATCH 04/36] Test case for invalid byte string --- tests/test_basic.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_basic.py b/tests/test_basic.py index bac57f7..96098d2 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -64,3 +64,8 @@ def test_roundtrip(input_bytes): def test_invalid_alphabet(): with pytest.raises(ValueError): base62.decode('+') + + +def test_invalid_string(): + with pytest.raises(TypeError): + base62.encodebytes({}) From 3780da893fcb94bf9879b726693887db7f8f14fe Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Wed, 6 Jun 2018 00:54:53 +0900 Subject: [PATCH 05/36] Fix pytest version to 3.6.1 --- tests/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/requirements.txt b/tests/requirements.txt index c7c53a1..dac19b6 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,3 +1,3 @@ -pytest +pytest==3.6.1 pytest-cov coveralls From f09cadf8a7ccab9b81a2b576bd85e6942fe85cbb Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Wed, 6 Jun 2018 00:57:21 +0900 Subject: [PATCH 06/36] Drop support for Python 2.6 Travis CI shows a warning message: DEPRECATION: Python 2.6 is no longer supported by the Python core team, please upgrade your Python. A future version of pip will drop support for Python 2.6 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a8205ae..dfed060 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ sudo: false language: python python: - - "2.6" - "2.7" - "3.3" - "3.4" From 15950a0b25814053f45dbcbab58f1845427cd4dc Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Mon, 30 Jul 2018 13:23:40 +0900 Subject: [PATCH 07/36] Update version to 0.3.3 --- base62.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base62.py b/base62.py index 138039a..45825b8 100644 --- a/base62.py +++ b/base62.py @@ -9,7 +9,7 @@ __title__ = 'base62' __author__ = 'Sumin Byeon' __email__ = 'suminb@gmail.com' -__version__ = '0.3.2' +__version__ = '0.3.3' CHARSET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' BASE = 62 From 1bb3a247d38a35c1d4824866acd9cb1c8f7a9b6c Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Mon, 30 Jul 2018 13:35:52 +0900 Subject: [PATCH 08/36] Update README --- README.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.rst b/README.rst index 3f2bc24..7a7c01a 100644 --- a/README.rst +++ b/README.rst @@ -100,3 +100,13 @@ If ``pytest`` is not installed, you may want to run the following commands: :: pip install -r tests/requirements.txt + + +Deployment +========== + +Deploy a source package (to `pypi `_) as follows: + +:: + + python setup.py sdist upload From 53ef1c5be7ec8d4e870a728f0c64ddcf1ab232b4 Mon Sep 17 00:00:00 2001 From: Joel Noyce Barnham Date: Tue, 26 Jun 2018 13:43:52 +0100 Subject: [PATCH 09/36] Use an inverted charset to integrate with external code --- base62.py | 2 +- tests/test_basic.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/base62.py b/base62.py index 45825b8..96aa7c9 100644 --- a/base62.py +++ b/base62.py @@ -11,7 +11,7 @@ __email__ = 'suminb@gmail.com' __version__ = '0.3.3' -CHARSET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' +CHARSET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' BASE = 62 diff --git a/tests/test_basic.py b/tests/test_basic.py index 96098d2..6d47035 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -26,13 +26,13 @@ def test_basic(): assert base62.decode('0000') == 0 assert base62.decode('000001') == 1 - assert base62.encode(34441886726) == 'base62' - assert base62.decode('base62') == 34441886726 + assert base62.encode(10231951886) == 'base62' + assert base62.decode('base62') == 10231951886 # NOTE: For backward compatibility. When I first wrote this module in PHP, # I used to use the `0z` prefix to denote a base62 encoded string (similar # to `0x` for hexadecimal strings). - assert base62.decode('0zbase62') == 34441886726 + assert base62.decode('0zbase62') == 10231951886 @pytest.mark.parametrize('b, i', bytes_int_pairs) From 44331f761d17b1a80584873a7d2adaf7dc34e529 Mon Sep 17 00:00:00 2001 From: Joel Noyce Barnham Date: Tue, 26 Jun 2018 15:44:20 +0100 Subject: [PATCH 10/36] Support both types of charset --- base62.py | 31 ++++++++++++++++++------------- tests/test_basic.py | 29 +++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/base62.py b/base62.py index 96aa7c9..92ff0c2 100644 --- a/base62.py +++ b/base62.py @@ -9,10 +9,15 @@ __title__ = 'base62' __author__ = 'Sumin Byeon' __email__ = 'suminb@gmail.com' -__version__ = '0.3.3' +__version__ = '0.4.0' -CHARSET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' BASE = 62 +CHARSET_DEFAULT = ( + '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' +) +CHARSET_INVERTED = ( + '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' +) def bytes_to_int(s, byteorder='big', signed=False): @@ -36,7 +41,7 @@ def bytes_to_int(s, byteorder='big', signed=False): return sum(ds) -def encode(n, minlen=1): +def encode(n, minlen=1, charset=CHARSET_DEFAULT): """Encodes a given integer ``n``.""" chs = [] @@ -44,7 +49,7 @@ def encode(n, minlen=1): r = n % BASE n //= BASE - chs.append(CHARSET[r]) + chs.append(charset[r]) if len(chs) > 0: chs.reverse() @@ -52,21 +57,21 @@ def encode(n, minlen=1): chs.append('0') s = ''.join(chs) - s = CHARSET[0] * max(minlen - len(s), 0) + s + s = charset[0] * max(minlen - len(s), 0) + s return s -def encodebytes(s): +def encodebytes(s, charset=CHARSET_DEFAULT): """Encodes a bytestring into a base62 string. :param s: A byte array """ _check_bytes_type(s) - return encode(bytes_to_int(s)) + return encode(bytes_to_int(s), charset=charset) -def decode(b): +def decode(b, charset=CHARSET_DEFAULT): """Decodes a base62 encoded value ``b``.""" if b.startswith('0z'): @@ -74,20 +79,20 @@ def decode(b): l, i, v = len(b), 0, 0 for x in b: - v += _value(x) * (BASE ** (l - (i + 1))) + v += _value(x, charset=charset) * (BASE ** (l - (i + 1))) i += 1 return v -def decodebytes(s): +def decodebytes(s, charset=CHARSET_DEFAULT): """Decodes a string of base62 data into a bytes object. :param s: A string to be decoded in base62 :rtype: bytes """ - decoded = decode(s) + decoded = decode(s, charset=charset) buf = bytearray() while decoded > 0: buf.append(decoded & 0xff) @@ -97,11 +102,11 @@ def decodebytes(s): return bytes(buf) -def _value(ch): +def _value(ch, charset): """Decodes an individual digit of a base62 encoded string.""" try: - return CHARSET.index(ch) + return charset.index(ch) except ValueError: raise ValueError('base62: Invalid character (%s)' % ch) diff --git a/tests/test_basic.py b/tests/test_basic.py index 6d47035..6bd422d 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -14,7 +14,8 @@ def test_const(): - assert len(base62.CHARSET) == base62.BASE == 62 + assert len(base62.CHARSET_DEFAULT) == base62.BASE == 62 + assert len(base62.CHARSET_INVERTED) == base62.BASE == 62 def test_basic(): @@ -26,13 +27,33 @@ def test_basic(): assert base62.decode('0000') == 0 assert base62.decode('000001') == 1 - assert base62.encode(10231951886) == 'base62' - assert base62.decode('base62') == 10231951886 + assert base62.encode(34441886726) == 'base62' + assert base62.decode('base62') == 34441886726 # NOTE: For backward compatibility. When I first wrote this module in PHP, # I used to use the `0z` prefix to denote a base62 encoded string (similar # to `0x` for hexadecimal strings). - assert base62.decode('0zbase62') == 10231951886 + assert base62.decode('0zbase62') == 34441886726 + + +def test_basic_inverted(): + kwargs = {'charset': base62.CHARSET_INVERTED} + + assert base62.encode(0, **kwargs) == '0' + assert base62.encode(0, minlen=0, **kwargs) == '0' + assert base62.encode(0, minlen=1, **kwargs) == '0' + assert base62.encode(0, minlen=5, **kwargs) == '00000' + assert base62.decode('0', **kwargs) == 0 + assert base62.decode('0000', **kwargs) == 0 + assert base62.decode('000001', **kwargs) == 1 + + assert base62.encode(10231951886, **kwargs) == 'base62' + assert base62.decode('base62', **kwargs) == 10231951886 + + # NOTE: For backward compatibility. When I first wrote this module in PHP, + # I used to use the `0z` prefix to denote a base62 encoded string (similar + # to `0x` for hexadecimal strings). + assert base62.decode('0zbase62', **kwargs) == 10231951886 @pytest.mark.parametrize('b, i', bytes_int_pairs) From 082f510fd3f58bc9741510311c0ac527fb7273f2 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Thu, 27 Jun 2019 22:18:30 +0900 Subject: [PATCH 11/36] Setting up SonarCloud This closes #14 --- .travis.yml | 5 +++++ sonar-project.properties | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 sonar-project.properties diff --git a/.travis.yml b/.travis.yml index dfed060..18cd2ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,10 @@ dist: trusty sudo: false language: python +addons: + sonarcloud: + organization: "suminb-github" + python: - "2.7" - "3.3" @@ -18,3 +22,4 @@ script: after_success: - coveralls + - sonar-scanner diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..39686ef --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,4 @@ +sonar.projectKey=base62 +sonar.sources=. +sonar.host.url=https://sonarcloud.io +sonar.login=travisci From dcaaf28c3bb87d4960ebcc575940d88c0186ebb5 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Thu, 25 Jul 2019 15:56:21 +0900 Subject: [PATCH 12/36] Advertise supported Python versions --- setup.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/setup.py b/setup.py index ac7a803..2949d1d 100644 --- a/setup.py +++ b/setup.py @@ -12,13 +12,21 @@ def readme(): return '(Could not read from README.rst)' -setup(name='pybase62', - py_modules=['base62'], - version=base62.__version__, - description='Python module for base62 encoding', - long_description=readme(), - author='Sumin Byeon', - author_email='suminb@gmail.com', - url='http://github.com/suminb/base62', - packages=[], - ) +setup( + name='pybase62', + py_modules=['base62'], + version=base62.__version__, + description='Python module for base62 encoding', + long_description=readme(), + author='Sumin Byeon', + author_email='suminb@gmail.com', + url='http://github.com/suminb/base62', + packages=[], + classifiers=[ + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + ], +) From 385ff3b087a6c1a94c6aad6fcd8c9155f1713263 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Fri, 26 Jul 2019 11:22:34 +0900 Subject: [PATCH 13/36] Update version (0.4.0 -> 0.4.1) --- base62.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base62.py b/base62.py index 92ff0c2..6b2c647 100644 --- a/base62.py +++ b/base62.py @@ -9,7 +9,7 @@ __title__ = 'base62' __author__ = 'Sumin Byeon' __email__ = 'suminb@gmail.com' -__version__ = '0.4.0' +__version__ = '0.4.1' BASE = 62 CHARSET_DEFAULT = ( From 31e921797ede9af2c6fb7e4927f41b52648c370b Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Wed, 11 Sep 2019 12:54:21 +0900 Subject: [PATCH 14/36] Use plural forms for RESTful URLs --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 7a7c01a..c70e0d0 100644 --- a/README.rst +++ b/README.rst @@ -22,13 +22,13 @@ When writing a web application, often times we would like to keep the URLs short :: - http://localhost/post/V1Biicwt + http://localhost/posts/V1Biicwt This certainly gives a more concise look than the following. :: - http://localhost/post/109237591284123 + http://localhost/posts/109237591284123 This was the original motivation to write this module, but there shall be much more broader potential use cases of this module. The main advantage of From db64549abbf4fb642d0008a522283c7a54eacec1 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Wed, 11 Sep 2019 12:56:42 +0900 Subject: [PATCH 15/36] Support latest/future Python versions --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 18cd2ab..4ba8775 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,8 @@ python: - "3.4" - "3.5" - "3.6" + - "3.7" + - "3.8-dev" # - "pypy" # disable pypy builds until supported by trusty containers install: From f46622d53a4f985ca61e9db6e4448e37169ad7a7 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Wed, 11 Sep 2019 15:03:52 +0900 Subject: [PATCH 16/36] Remove `dist` key So that it does not point a particular Linux distro --- .travis.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4ba8775..94c1a8b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,5 @@ -dist: trusty sudo: false language: python - -addons: - sonarcloud: - organization: "suminb-github" - python: - "2.7" - "3.3" @@ -16,6 +10,10 @@ python: - "3.8-dev" # - "pypy" # disable pypy builds until supported by trusty containers +addons: + sonarcloud: + organization: "suminb-github" + install: - pip install --requirement tests/requirements.txt From a9b9e7ef3a8b9099b7f9b50100633ad795797897 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Sat, 28 Sep 2019 02:56:21 +0900 Subject: [PATCH 17/36] Remove older versions of Python --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 94c1a8b..de554ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,13 +2,12 @@ sudo: false language: python python: - "2.7" - - "3.3" - - "3.4" - "3.5" - "3.6" - "3.7" - "3.8-dev" # - "pypy" # disable pypy builds until supported by trusty containers + # Python development lifecycle: https://devguide.python.org/devcycle/ addons: sonarcloud: From cfae1e24a538845507795d33f587f382774464ce Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Thu, 10 Oct 2019 01:34:18 +0900 Subject: [PATCH 18/36] Make sure the return type of encodebytes() is string --- base62.py | 2 ++ tests/test_basic.py | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/base62.py b/base62.py index 6b2c647..f024ca9 100644 --- a/base62.py +++ b/base62.py @@ -65,6 +65,8 @@ def encodebytes(s, charset=CHARSET_DEFAULT): """Encodes a bytestring into a base62 string. :param s: A byte array + :type s: bytes + :rtype: str """ _check_bytes_type(s) diff --git a/tests/test_basic.py b/tests/test_basic.py index 6bd422d..daa1995 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -66,7 +66,13 @@ def test_encodebytes(b, i): assert base62.encodebytes(b) == base62.encode(i) -@pytest.mark.parametrize('s', ['0', '1', 'a', 'z', 'ykzvd7ga']) +def test_encodebytes_rtype(): + """Make sure the return type of encodebytes() is string.""" + encoded = base62.encodebytes(b'1234') + assert isinstance(encoded, str) + + +@pytest.mark.parametrize('s', ['0', '1', 'a', 'z', 'ykzvd7ga', '0z1234']) def test_decodebytes(s): assert base62.bytes_to_int(base62.decodebytes(s)) == base62.decode(s) From 4f72c4903ef8adcb77d0b0a05cfcdd6e03c22856 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Thu, 10 Oct 2019 01:39:18 +0900 Subject: [PATCH 19/36] Make sure the return type of decodebytes() is bytes --- base62.py | 1 + tests/test_basic.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/base62.py b/base62.py index f024ca9..841fdc5 100644 --- a/base62.py +++ b/base62.py @@ -91,6 +91,7 @@ def decodebytes(s, charset=CHARSET_DEFAULT): """Decodes a string of base62 data into a bytes object. :param s: A string to be decoded in base62 + :type s: str :rtype: bytes """ diff --git a/tests/test_basic.py b/tests/test_basic.py index daa1995..4f6926e 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -77,6 +77,12 @@ def test_decodebytes(s): assert base62.bytes_to_int(base62.decodebytes(s)) == base62.decode(s) +def test_decodebytes_rtype(): + """Make sure the return type of decodebytes() is bytes.""" + decoded = base62.decodebytes('1234') + assert isinstance(decoded, bytes) + + @pytest.mark.parametrize('input_bytes', [ b'', b'0', b'bytes to encode', b'\x01\x00\x80']) def test_roundtrip(input_bytes): From e3fbcac044d885446d39efcea002e46a59862c44 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Thu, 10 Oct 2019 01:49:44 +0900 Subject: [PATCH 20/36] Revise type checking code --- base62.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/base62.py b/base62.py index 841fdc5..7a52ad8 100644 --- a/base62.py +++ b/base62.py @@ -69,7 +69,7 @@ def encodebytes(s, charset=CHARSET_DEFAULT): :rtype: str """ - _check_bytes_type(s) + _check_type(s, bytes) return encode(bytes_to_int(s), charset=charset) @@ -114,9 +114,10 @@ def _value(ch, charset): raise ValueError('base62: Invalid character (%s)' % ch) -def _check_bytes_type(s): +def _check_type(value, expected_type): """Checks if the input is in an appropriate type.""" - if not isinstance(s, bytes): - msg = 'expected bytes-like object, not %s' % s.__class__.__name__ - raise TypeError(msg) + if not isinstance(value, expected_type): + msg = 'Expected {} object, not {}'.format( + expected_type, value.__class__.__name__) + raise TypeError(msg) \ No newline at end of file From 808c9701420e2e20d6241b28e08dd7ac04bc5410 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Thu, 10 Oct 2019 01:55:20 +0900 Subject: [PATCH 21/36] Type checking for encodebytes(), decodebytes() --- base62.py | 8 ++++++++ tests/test_basic.py | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/base62.py b/base62.py index 7a52ad8..e9027d7 100644 --- a/base62.py +++ b/base62.py @@ -19,6 +19,13 @@ '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ) +try: + # NOTE: This is for Python 2. Shall be removed as soon as Python 2 is + # deprecated. + string_types = (str, unicode) +except NameError: + string_types = (str,) + def bytes_to_int(s, byteorder='big', signed=False): """Converts a byte array to an integer value. @@ -95,6 +102,7 @@ def decodebytes(s, charset=CHARSET_DEFAULT): :rtype: bytes """ + _check_type(s, string_types) decoded = decode(s, charset=charset) buf = bytearray() while decoded > 0: diff --git a/tests/test_basic.py b/tests/test_basic.py index 4f6926e..7740560 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -66,6 +66,11 @@ def test_encodebytes(b, i): assert base62.encodebytes(b) == base62.encode(i) +def test_encodebytes_type(): + with pytest.raises(TypeError): + base62.encodebytes('1234') + + def test_encodebytes_rtype(): """Make sure the return type of encodebytes() is string.""" encoded = base62.encodebytes(b'1234') @@ -77,6 +82,11 @@ def test_decodebytes(s): assert base62.bytes_to_int(base62.decodebytes(s)) == base62.decode(s) +def test_decodebytes_type(): + with pytest.raises(TypeError): + base62.decodebytes(b'1234') + + def test_decodebytes_rtype(): """Make sure the return type of decodebytes() is bytes.""" decoded = base62.decodebytes('1234') From f122193ad70750de7397f656be57e6e10bf6e9d0 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Thu, 10 Oct 2019 02:14:55 +0900 Subject: [PATCH 22/36] Maintain backward compatibility with Python 2.7 --- base62.py | 4 +++- tests/test_basic.py | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/base62.py b/base62.py index e9027d7..e412df6 100644 --- a/base62.py +++ b/base62.py @@ -23,8 +23,10 @@ # NOTE: This is for Python 2. Shall be removed as soon as Python 2 is # deprecated. string_types = (str, unicode) + bytes_types = (bytes, bytearray,) except NameError: string_types = (str,) + bytes_types = (bytes,) def bytes_to_int(s, byteorder='big', signed=False): @@ -76,7 +78,7 @@ def encodebytes(s, charset=CHARSET_DEFAULT): :rtype: str """ - _check_type(s, bytes) + _check_type(s, bytes_types) return encode(bytes_to_int(s), charset=charset) diff --git a/tests/test_basic.py b/tests/test_basic.py index 7740560..8f3c4c7 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -1,3 +1,5 @@ +import sys + import pytest import base62 @@ -66,6 +68,9 @@ def test_encodebytes(b, i): assert base62.encodebytes(b) == base62.encode(i) +@pytest.mark.skipif( + sys.version_info < (3, 0), + reason='Python 2.x does not have clear distinction between str and bytes types') def test_encodebytes_type(): with pytest.raises(TypeError): base62.encodebytes('1234') @@ -82,6 +87,9 @@ def test_decodebytes(s): assert base62.bytes_to_int(base62.decodebytes(s)) == base62.decode(s) +@pytest.mark.skipif( + sys.version_info < (3, 0), + reason='Python 2.x does not have clear distinction between str and bytes types') def test_decodebytes_type(): with pytest.raises(TypeError): base62.decodebytes(b'1234') From 9e7e8963fdfae40737e0c14ea24feb8655f2fcb6 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Thu, 10 Oct 2019 02:20:30 +0900 Subject: [PATCH 23/36] Use a meaningful variable name (s -> encoded) --- base62.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/base62.py b/base62.py index e412df6..f958e1f 100644 --- a/base62.py +++ b/base62.py @@ -82,30 +82,34 @@ def encodebytes(s, charset=CHARSET_DEFAULT): return encode(bytes_to_int(s), charset=charset) -def decode(b, charset=CHARSET_DEFAULT): - """Decodes a base62 encoded value ``b``.""" +def decode(encoded, charset=CHARSET_DEFAULT): + """Decodes a base62 encoded value ``encoded``. - if b.startswith('0z'): - b = b[2:] + :type encoded: str + :rtype: int + """ + _check_type(encoded, string_types) + + if encoded.startswith('0z'): + encoded = encoded[2:] - l, i, v = len(b), 0, 0 - for x in b: + l, i, v = len(encoded), 0, 0 + for x in encoded: v += _value(x, charset=charset) * (BASE ** (l - (i + 1))) i += 1 return v -def decodebytes(s, charset=CHARSET_DEFAULT): +def decodebytes(encoded, charset=CHARSET_DEFAULT): """Decodes a string of base62 data into a bytes object. - :param s: A string to be decoded in base62 - :type s: str + :param encoded: A string to be decoded in base62 + :type encoded: str :rtype: bytes """ - _check_type(s, string_types) - decoded = decode(s, charset=charset) + decoded = decode(encoded, charset=charset) buf = bytearray() while decoded > 0: buf.append(decoded & 0xff) From 6e281667d15e5a7a37f2e49519f53294b9056a3c Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Thu, 10 Oct 2019 02:23:09 +0900 Subject: [PATCH 24/36] Use a meaningful variable name (s -> barray) --- base62.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/base62.py b/base62.py index f958e1f..de7026f 100644 --- a/base62.py +++ b/base62.py @@ -29,7 +29,7 @@ bytes_types = (bytes,) -def bytes_to_int(s, byteorder='big', signed=False): +def bytes_to_int(barray, byteorder='big', signed=False): """Converts a byte array to an integer value. Python 3 comes with a built-in function to do this, but we would like to @@ -37,15 +37,15 @@ def bytes_to_int(s, byteorder='big', signed=False): """ try: - return int.from_bytes(s, byteorder, signed=signed) + return int.from_bytes(barray, byteorder, signed=signed) except AttributeError: # For Python 2.x if byteorder != 'big' or signed: raise NotImplementedError() # NOTE: This won't work if a generator is given - n = len(s) - ds = (x << (8 * (n - 1 - i)) for i, x in enumerate(bytearray(s))) + n = len(barray) + ds = (x << (8 * (n - 1 - i)) for i, x in enumerate(bytearray(barray))) return sum(ds) @@ -70,16 +70,16 @@ def encode(n, minlen=1, charset=CHARSET_DEFAULT): return s -def encodebytes(s, charset=CHARSET_DEFAULT): +def encodebytes(barray, charset=CHARSET_DEFAULT): """Encodes a bytestring into a base62 string. - :param s: A byte array - :type s: bytes + :param barray: A byte array + :type barray: bytes :rtype: str """ - _check_type(s, bytes_types) - return encode(bytes_to_int(s), charset=charset) + _check_type(barray, bytes_types) + return encode(bytes_to_int(barray), charset=charset) def decode(encoded, charset=CHARSET_DEFAULT): From a07c4d27ac3c758a55a8dbe4a9c448a8226d9adc Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Thu, 10 Oct 2019 02:26:41 +0900 Subject: [PATCH 25/36] Update version info (0.4.1 -> 0.4.2) --- base62.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base62.py b/base62.py index de7026f..f868ffd 100644 --- a/base62.py +++ b/base62.py @@ -9,7 +9,7 @@ __title__ = 'base62' __author__ = 'Sumin Byeon' __email__ = 'suminb@gmail.com' -__version__ = '0.4.1' +__version__ = '0.4.2' BASE = 62 CHARSET_DEFAULT = ( From 58b2bbaadbbfdf078ddf67c83d352e5342f2ce05 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Thu, 10 Oct 2019 02:27:48 +0900 Subject: [PATCH 26/36] Minor revisions on README --- README.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index c70e0d0..5b748d8 100644 --- a/README.rst +++ b/README.rst @@ -18,7 +18,8 @@ in mid-2000, which can be found Rationale --------- -When writing a web application, often times we would like to keep the URLs short. +When writing a web application, often times we would like to keep the URLs +short. :: @@ -95,7 +96,7 @@ You may run some test cases to ensure all functionalities are operational. py.test -v -If ``pytest`` is not installed, you may want to run the following commands: +If ``pytest`` is not installed, you may want to run the following command: :: From a05f3461511a9200e357c3506fa5883fa7ae8b96 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Thu, 10 Oct 2019 02:48:30 +0900 Subject: [PATCH 27/36] Documentation on function commutativity --- README.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.rst b/README.rst index 5b748d8..7f9b1f2 100644 --- a/README.rst +++ b/README.rst @@ -87,6 +87,20 @@ From version ``0.2.0``, ``base62`` supports ``bytes`` array encoding as well. >>> base62.decodebytes('1') b'\x01' +Some may be inclined to assume that they both take ``bytes`` types as input +due to their namings. However, ``encodebytes()`` takes ``bytes`` types +whereas ``decodebytes()`` takes ``str`` types as an input. They are intended +to be commutative, so that a *roundtrip* between both functions yields the +original value. + +Formally speaking, we say function :math:`f` and :math:`g` commute if +:math:`f \circ g = g \circ f` where :math:`f(g(x)) = (f \circ g)(x)`. + +Therefore, we may expect the following relationships: + +* ``value == encodebytes(decodebytes(value))`` +* ``value == decodebytes(encodebytes(value))`` + Tests ===== From 7751cce4d63881683dbf108ad381e78da0d2cc42 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Thu, 10 Oct 2019 03:24:22 +0900 Subject: [PATCH 28/36] Temporarily remove `math` directives --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 7f9b1f2..e867026 100644 --- a/README.rst +++ b/README.rst @@ -93,8 +93,8 @@ whereas ``decodebytes()`` takes ``str`` types as an input. They are intended to be commutative, so that a *roundtrip* between both functions yields the original value. -Formally speaking, we say function :math:`f` and :math:`g` commute if -:math:`f \circ g = g \circ f` where :math:`f(g(x)) = (f \circ g)(x)`. +Formally speaking, we say function *f* and *g* commute if *f∘g* = *g∘f* where +*f(g(x))* = *(f∘g)(x)*. Therefore, we may expect the following relationships: From 5d24f8a69729c7503bd87550023cbbcef869ccb6 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Thu, 10 Oct 2019 03:41:15 +0900 Subject: [PATCH 29/36] Update supported languages information --- setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 2949d1d..95e5df4 100644 --- a/setup.py +++ b/setup.py @@ -24,9 +24,8 @@ def readme(): packages=[], classifiers=[ 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', ], ) From 52205a4b382e5fe930da259027ff3a7486b60e76 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Fri, 11 Oct 2019 13:32:31 +0900 Subject: [PATCH 30/36] Support for PyPy --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index de554ed..a00205d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,7 @@ python: - "3.6" - "3.7" - "3.8-dev" - # - "pypy" # disable pypy builds until supported by trusty containers - # Python development lifecycle: https://devguide.python.org/devcycle/ + - "pypy" addons: sonarcloud: From 95620451e0c5518d7a0d2b7fae50399e4dda01f0 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Wed, 30 Oct 2019 01:04:07 +0900 Subject: [PATCH 31/36] Reformat code with Black --- base62.py | 42 +++++++++++------------ setup.py | 24 +++++++------- tests/test_basic.py | 81 +++++++++++++++++++++++---------------------- 3 files changed, 75 insertions(+), 72 deletions(-) diff --git a/base62.py b/base62.py index f868ffd..a3cd679 100644 --- a/base62.py +++ b/base62.py @@ -6,30 +6,29 @@ Originated from http://blog.suminb.com/archives/558 """ -__title__ = 'base62' -__author__ = 'Sumin Byeon' -__email__ = 'suminb@gmail.com' -__version__ = '0.4.2' +__title__ = "base62" +__author__ = "Sumin Byeon" +__email__ = "suminb@gmail.com" +__version__ = "0.4.2" BASE = 62 -CHARSET_DEFAULT = ( - '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' -) -CHARSET_INVERTED = ( - '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' -) +CHARSET_DEFAULT = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +CHARSET_INVERTED = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" try: # NOTE: This is for Python 2. Shall be removed as soon as Python 2 is # deprecated. string_types = (str, unicode) - bytes_types = (bytes, bytearray,) + bytes_types = ( + bytes, + bytearray, + ) except NameError: string_types = (str,) bytes_types = (bytes,) -def bytes_to_int(barray, byteorder='big', signed=False): +def bytes_to_int(barray, byteorder="big", signed=False): """Converts a byte array to an integer value. Python 3 comes with a built-in function to do this, but we would like to @@ -40,7 +39,7 @@ def bytes_to_int(barray, byteorder='big', signed=False): return int.from_bytes(barray, byteorder, signed=signed) except AttributeError: # For Python 2.x - if byteorder != 'big' or signed: + if byteorder != "big" or signed: raise NotImplementedError() # NOTE: This won't work if a generator is given @@ -63,9 +62,9 @@ def encode(n, minlen=1, charset=CHARSET_DEFAULT): if len(chs) > 0: chs.reverse() else: - chs.append('0') + chs.append("0") - s = ''.join(chs) + s = "".join(chs) s = charset[0] * max(minlen - len(s), 0) + s return s @@ -90,7 +89,7 @@ def decode(encoded, charset=CHARSET_DEFAULT): """ _check_type(encoded, string_types) - if encoded.startswith('0z'): + if encoded.startswith("0z"): encoded = encoded[2:] l, i, v = len(encoded), 0, 0 @@ -112,7 +111,7 @@ def decodebytes(encoded, charset=CHARSET_DEFAULT): decoded = decode(encoded, charset=charset) buf = bytearray() while decoded > 0: - buf.append(decoded & 0xff) + buf.append(decoded & 0xFF) decoded //= 256 buf.reverse() @@ -125,13 +124,14 @@ def _value(ch, charset): try: return charset.index(ch) except ValueError: - raise ValueError('base62: Invalid character (%s)' % ch) + raise ValueError("base62: Invalid character (%s)" % ch) def _check_type(value, expected_type): """Checks if the input is in an appropriate type.""" if not isinstance(value, expected_type): - msg = 'Expected {} object, not {}'.format( - expected_type, value.__class__.__name__) - raise TypeError(msg) \ No newline at end of file + msg = "Expected {} object, not {}".format( + expected_type, value.__class__.__name__ + ) + raise TypeError(msg) diff --git a/setup.py b/setup.py index 95e5df4..05184c7 100644 --- a/setup.py +++ b/setup.py @@ -6,26 +6,26 @@ def readme(): try: - with open('README.rst') as f: + with open("README.rst") as f: return f.read() except: - return '(Could not read from README.rst)' + return "(Could not read from README.rst)" setup( - name='pybase62', - py_modules=['base62'], + name="pybase62", + py_modules=["base62"], version=base62.__version__, - description='Python module for base62 encoding', + description="Python module for base62 encoding", long_description=readme(), - author='Sumin Byeon', - author_email='suminb@gmail.com', - url='http://github.com/suminb/base62', + author="Sumin Byeon", + author_email="suminb@gmail.com", + url="http://github.com/suminb/base62", packages=[], classifiers=[ - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", ], ) diff --git a/tests/test_basic.py b/tests/test_basic.py index 8f3c4c7..ba302fb 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -6,12 +6,12 @@ bytes_int_pairs = [ - (b'\x00', 0), - (b'\x01', 1), - (b'\x01\x01', 0x0101), - (b'\xff\xff', 0xffff), - (b'\x01\x01\x01', 0x010101), - (b'\x01\x02\x03\x04\x05\x06\x07\x08', 0x0102030405060708), + (b"\x00", 0), + (b"\x01", 1), + (b"\x01\x01", 0x0101), + (b"\xff\xff", 0xFFFF), + (b"\x01\x01\x01", 0x010101), + (b"\x01\x02\x03\x04\x05\x06\x07\x08", 0x0102030405060708), ] @@ -21,88 +21,91 @@ def test_const(): def test_basic(): - assert base62.encode(0) == '0' - assert base62.encode(0, minlen=0) == '0' - assert base62.encode(0, minlen=1) == '0' - assert base62.encode(0, minlen=5) == '00000' - assert base62.decode('0') == 0 - assert base62.decode('0000') == 0 - assert base62.decode('000001') == 1 + assert base62.encode(0) == "0" + assert base62.encode(0, minlen=0) == "0" + assert base62.encode(0, minlen=1) == "0" + assert base62.encode(0, minlen=5) == "00000" + assert base62.decode("0") == 0 + assert base62.decode("0000") == 0 + assert base62.decode("000001") == 1 - assert base62.encode(34441886726) == 'base62' - assert base62.decode('base62') == 34441886726 + assert base62.encode(34441886726) == "base62" + assert base62.decode("base62") == 34441886726 # NOTE: For backward compatibility. When I first wrote this module in PHP, # I used to use the `0z` prefix to denote a base62 encoded string (similar # to `0x` for hexadecimal strings). - assert base62.decode('0zbase62') == 34441886726 + assert base62.decode("0zbase62") == 34441886726 def test_basic_inverted(): - kwargs = {'charset': base62.CHARSET_INVERTED} + kwargs = {"charset": base62.CHARSET_INVERTED} - assert base62.encode(0, **kwargs) == '0' - assert base62.encode(0, minlen=0, **kwargs) == '0' - assert base62.encode(0, minlen=1, **kwargs) == '0' - assert base62.encode(0, minlen=5, **kwargs) == '00000' - assert base62.decode('0', **kwargs) == 0 - assert base62.decode('0000', **kwargs) == 0 - assert base62.decode('000001', **kwargs) == 1 + assert base62.encode(0, **kwargs) == "0" + assert base62.encode(0, minlen=0, **kwargs) == "0" + assert base62.encode(0, minlen=1, **kwargs) == "0" + assert base62.encode(0, minlen=5, **kwargs) == "00000" + assert base62.decode("0", **kwargs) == 0 + assert base62.decode("0000", **kwargs) == 0 + assert base62.decode("000001", **kwargs) == 1 - assert base62.encode(10231951886, **kwargs) == 'base62' - assert base62.decode('base62', **kwargs) == 10231951886 + assert base62.encode(10231951886, **kwargs) == "base62" + assert base62.decode("base62", **kwargs) == 10231951886 # NOTE: For backward compatibility. When I first wrote this module in PHP, # I used to use the `0z` prefix to denote a base62 encoded string (similar # to `0x` for hexadecimal strings). - assert base62.decode('0zbase62', **kwargs) == 10231951886 + assert base62.decode("0zbase62", **kwargs) == 10231951886 -@pytest.mark.parametrize('b, i', bytes_int_pairs) +@pytest.mark.parametrize("b, i", bytes_int_pairs) def test_bytes_to_int(b, i): assert base62.bytes_to_int(b) == i -@pytest.mark.parametrize('b, i', bytes_int_pairs) +@pytest.mark.parametrize("b, i", bytes_int_pairs) def test_encodebytes(b, i): assert base62.encodebytes(b) == base62.encode(i) @pytest.mark.skipif( sys.version_info < (3, 0), - reason='Python 2.x does not have clear distinction between str and bytes types') + reason="Python 2.x does not have clear distinction between str and bytes types", +) def test_encodebytes_type(): with pytest.raises(TypeError): - base62.encodebytes('1234') + base62.encodebytes("1234") def test_encodebytes_rtype(): """Make sure the return type of encodebytes() is string.""" - encoded = base62.encodebytes(b'1234') + encoded = base62.encodebytes(b"1234") assert isinstance(encoded, str) -@pytest.mark.parametrize('s', ['0', '1', 'a', 'z', 'ykzvd7ga', '0z1234']) +@pytest.mark.parametrize("s", ["0", "1", "a", "z", "ykzvd7ga", "0z1234"]) def test_decodebytes(s): assert base62.bytes_to_int(base62.decodebytes(s)) == base62.decode(s) @pytest.mark.skipif( sys.version_info < (3, 0), - reason='Python 2.x does not have clear distinction between str and bytes types') + reason="Python 2.x does not have clear distinction between str and bytes types", +) def test_decodebytes_type(): with pytest.raises(TypeError): - base62.decodebytes(b'1234') + base62.decodebytes(b"1234") def test_decodebytes_rtype(): """Make sure the return type of decodebytes() is bytes.""" - decoded = base62.decodebytes('1234') + decoded = base62.decodebytes("1234") assert isinstance(decoded, bytes) -@pytest.mark.parametrize('input_bytes', [ - b'', b'0', b'bytes to encode', b'\x01\x00\x80']) +@pytest.mark.parametrize( + "input_bytes", [b"", b"0", b"bytes to encode", b"\x01\x00\x80"] +) def test_roundtrip(input_bytes): """Ensures type consistency. Suggested by @dhimmel""" base62_encoded = base62.encodebytes(input_bytes) @@ -114,7 +117,7 @@ def test_roundtrip(input_bytes): def test_invalid_alphabet(): with pytest.raises(ValueError): - base62.decode('+') + base62.decode("+") def test_invalid_string(): From 5e09e26393feadd7750bbf0bb34dd7692f936ad0 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Wed, 30 Oct 2019 01:04:51 +0900 Subject: [PATCH 32/36] Run Black command during build stage --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index a00205d..6847ca3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,8 +14,10 @@ addons: install: - pip install --requirement tests/requirements.txt + - pip install black script: + - black --check . - py.test tests --cov base62 --durations=10 after_success: From 3f180b7a60c1fd701e352fe311c518226675fe65 Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Wed, 30 Oct 2019 01:35:13 +0900 Subject: [PATCH 33/36] Run Black for Python 3.6 and above only --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6847ca3..616d055 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,10 +14,13 @@ addons: install: - pip install --requirement tests/requirements.txt - - pip install black + - pip install "black; python_version >= '3.6'" script: - - black --check . + - | + if [ -x "$(command -v black)" ]; then + black --check . + fi - py.test tests --cov base62 --durations=10 after_success: From 6b78441b44f68f15616cc3475d182467a33f48ee Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Wed, 30 Oct 2019 01:40:24 +0900 Subject: [PATCH 34/36] Update version info (0.4.2 -> 0.4.3) --- base62.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base62.py b/base62.py index a3cd679..f4f5cdd 100644 --- a/base62.py +++ b/base62.py @@ -9,7 +9,7 @@ __title__ = "base62" __author__ = "Sumin Byeon" __email__ = "suminb@gmail.com" -__version__ = "0.4.2" +__version__ = "0.4.3" BASE = 62 CHARSET_DEFAULT = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" From c0b853c52925085f072ac99d61cc91cda22cce1f Mon Sep 17 00:00:00 2001 From: Sumin Byeon Date: Wed, 30 Oct 2019 01:42:00 +0900 Subject: [PATCH 35/36] Update URL --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index e867026..eac602f 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ base62 A Python module for ``base62`` encoding. Ported from PHP code that I wrote in mid-2000, which can be found -`here `__. +`here `__. .. |Build Status| image:: https://travis-ci.org/suminb/base62.svg?branch=master :target: https://travis-ci.org/suminb/base62 From 8ff106ac5a4dc2dff051d7f4ee00e576c0940f34 Mon Sep 17 00:00:00 2001 From: Jimmy Jia Date: Fri, 24 Jul 2020 13:53:00 -0400 Subject: [PATCH 36/36] feat: Support encoding with arbitrary charsets --- base62.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/base62.py b/base62.py index f4f5cdd..3249d24 100644 --- a/base62.py +++ b/base62.py @@ -11,7 +11,6 @@ __email__ = "suminb@gmail.com" __version__ = "0.4.3" -BASE = 62 CHARSET_DEFAULT = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" CHARSET_INVERTED = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -51,11 +50,12 @@ def bytes_to_int(barray, byteorder="big", signed=False): def encode(n, minlen=1, charset=CHARSET_DEFAULT): """Encodes a given integer ``n``.""" + base = len(charset) chs = [] while n > 0: - r = n % BASE - n //= BASE + r = n % base + n //= base chs.append(charset[r]) @@ -88,6 +88,7 @@ def decode(encoded, charset=CHARSET_DEFAULT): :rtype: int """ _check_type(encoded, string_types) + base = len(charset) if encoded.startswith("0z"): encoded = encoded[2:]