This repository has been archived by the owner on Jan 13, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 125
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#145] Reformatted codecs module for PEP-8.
- Loading branch information
1 parent
a74ec33
commit 1dcad0a
Showing
1 changed file
with
185 additions
and
178 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,213 +1,220 @@ | ||
# coding=utf-8 | ||
from __future__ import absolute_import, division, print_function, \ | ||
unicode_literals | ||
unicode_literals | ||
|
||
from codecs import Codec, CodecInfo, register as lookup_function | ||
from warnings import warn | ||
|
||
from iota.exceptions import with_context | ||
from six import PY3, binary_type | ||
|
||
from iota.exceptions import with_context | ||
|
||
__all__ = [ | ||
'AsciiTrytesCodec', | ||
'TrytesDecodeError', | ||
'AsciiTrytesCodec', | ||
'TrytesDecodeError', | ||
] | ||
|
||
|
||
class TrytesDecodeError(ValueError): | ||
""" | ||
Indicates that a tryte string could not be decoded to bytes. | ||
""" | ||
pass | ||
""" | ||
Indicates that a tryte string could not be decoded to bytes. | ||
""" | ||
pass | ||
|
||
|
||
class AsciiTrytesCodec(Codec): | ||
""" | ||
Legacy codec for converting byte strings into trytes, and vice versa. | ||
This method encodes each pair of trytes as an ASCII code point (and | ||
vice versa when decoding). | ||
The end result requires more space than if the trytes were converted | ||
mathematically, but because the result is ASCII, it's easier to work | ||
with. | ||
Think of this kind of like Base 64 for balanced ternary (: | ||
""" | ||
name = 'trytes_ascii' | ||
|
||
compat_name = 'trytes' | ||
""" | ||
Old name for this codec. | ||
Note: Will be removed in PyOTA v2.1! | ||
""" | ||
|
||
# :bc: Without the bytearray cast, Python 2 will populate the dict | ||
# with characters instead of integers. | ||
# noinspection SpellCheckingInspection | ||
alphabet = dict(enumerate(bytearray(b'9ABCDEFGHIJKLMNOPQRSTUVWXYZ'))) | ||
""" | ||
Used to encode bytes into trytes. | ||
""" | ||
|
||
index = dict(zip(alphabet.values(), alphabet.keys())) | ||
""" | ||
Used to decode trytes into bytes. | ||
""" | ||
|
||
@classmethod | ||
def get_codec_info(cls): | ||
""" | ||
Returns information used by the codecs library to configure the | ||
codec for use. | ||
""" | ||
codec = cls() | ||
Legacy codec for converting byte strings into trytes, and vice | ||
versa. | ||
codec_info = { | ||
'encode': codec.encode, | ||
'decode': codec.decode, | ||
} | ||
This method encodes each pair of trytes as an ASCII code point (and | ||
vice versa when decoding). | ||
# In Python 2, all codecs are made equal. | ||
# In Python 3, some codecs are more equal than others. | ||
if PY3: | ||
codec_info['_is_text_encoding'] = False | ||
The end result requires more space than if the trytes were converted | ||
mathematically, but because the result is ASCII, it's easier to work | ||
with. | ||
return CodecInfo(**codec_info) | ||
Think of this kind of like Base 64 for balanced ternary (: | ||
""" | ||
name = 'trytes_ascii' | ||
|
||
# noinspection PyShadowingBuiltins | ||
def encode(self, input, errors='strict'): | ||
compat_name = 'trytes' | ||
""" | ||
Encodes a byte string into trytes. | ||
Old name for this codec. | ||
Note: Will be removed in PyOTA v2.1! | ||
""" | ||
if isinstance(input, memoryview): | ||
input = input.tobytes() | ||
|
||
if not isinstance(input, (binary_type, bytearray)): | ||
raise with_context( | ||
exc = TypeError("Can't encode {type}; byte string expected.".format( | ||
type = type(input).__name__, | ||
)), | ||
|
||
context = { | ||
'input': input, | ||
}, | ||
) | ||
# :bc: Without the bytearray cast, Python 2 will populate the dict | ||
# with characters instead of integers. | ||
# noinspection SpellCheckingInspection | ||
alphabet = dict(enumerate(bytearray(b'9ABCDEFGHIJKLMNOPQRSTUVWXYZ'))) | ||
""" | ||
Used to encode bytes into trytes. | ||
""" | ||
|
||
# :bc: In Python 2, iterating over a byte string yields characters | ||
# instead of integers. | ||
if not isinstance(input, bytearray): | ||
input = bytearray(input) | ||
index = dict(zip(alphabet.values(), alphabet.keys())) | ||
""" | ||
Used to decode trytes into bytes. | ||
""" | ||
|
||
trytes = bytearray() | ||
@classmethod | ||
def get_codec_info(cls): | ||
""" | ||
Returns information used by the codecs library to configure the | ||
codec for use. | ||
""" | ||
codec = cls() | ||
|
||
codec_info = { | ||
'encode': codec.encode, | ||
'decode': codec.decode, | ||
} | ||
|
||
# In Python 2, all codecs are made equal. | ||
# In Python 3, some codecs are more equal than others. | ||
if PY3: | ||
codec_info['_is_text_encoding'] = False | ||
|
||
return CodecInfo(**codec_info) | ||
|
||
# noinspection PyShadowingBuiltins | ||
def encode(self, input, errors='strict'): | ||
""" | ||
Encodes a byte string into trytes. | ||
""" | ||
if isinstance(input, memoryview): | ||
input = input.tobytes() | ||
|
||
if not isinstance(input, (binary_type, bytearray)): | ||
raise with_context( | ||
exc=TypeError( | ||
"Can't encode {type}; byte string expected.".format( | ||
type=type(input).__name__, | ||
)), | ||
|
||
context={ | ||
'input': input, | ||
}, | ||
) | ||
|
||
# :bc: In Python 2, iterating over a byte string yields | ||
# characters instead of integers. | ||
if not isinstance(input, bytearray): | ||
input = bytearray(input) | ||
|
||
trytes = bytearray() | ||
|
||
for c in input: | ||
second, first = divmod(c, len(self.alphabet)) | ||
|
||
trytes.append(self.alphabet[first]) | ||
trytes.append(self.alphabet[second]) | ||
|
||
return binary_type(trytes), len(input) | ||
|
||
# noinspection PyShadowingBuiltins | ||
def decode(self, input, errors='strict'): | ||
""" | ||
Decodes a tryte string into bytes. | ||
""" | ||
if isinstance(input, memoryview): | ||
input = input.tobytes() | ||
|
||
if not isinstance(input, (binary_type, bytearray)): | ||
raise with_context( | ||
exc=TypeError( | ||
"Can't decode {type}; byte string expected.".format( | ||
type=type(input).__name__, | ||
)), | ||
|
||
context={ | ||
'input': input, | ||
}, | ||
) | ||
|
||
# :bc: In Python 2, iterating over a byte string yields | ||
# characters instead of integers. | ||
if not isinstance(input, bytearray): | ||
input = bytearray(input) | ||
|
||
bytes_ = bytearray() | ||
|
||
for i in range(0, len(input), 2): | ||
try: | ||
first, second = input[i:i + 2] | ||
except ValueError: | ||
if errors == 'strict': | ||
raise with_context( | ||
exc=TrytesDecodeError( | ||
"'{name}' codec can't decode value; " | ||
"tryte sequence has odd length.".format( | ||
name=self.name, | ||
), | ||
), | ||
|
||
context={ | ||
'input': input, | ||
}, | ||
) | ||
elif errors == 'replace': | ||
bytes_ += b'?' | ||
|
||
continue | ||
|
||
try: | ||
bytes_.append( | ||
self.index[first] | ||
+ (self.index[second] * len(self.index)) | ||
) | ||
except ValueError: | ||
# This combination of trytes yields a value > 255 when | ||
# decoded. | ||
# Naturally, we can't represent this using ASCII. | ||
if errors == 'strict': | ||
raise with_context( | ||
exc=TrytesDecodeError( | ||
"'{name}' codec can't decode trytes {pair} " | ||
"at position {i}-{j}: " | ||
"ordinal not in range(255)".format( | ||
name=self.name, | ||
pair=chr(first) + chr(second), | ||
i=i, | ||
j=i + 1, | ||
), | ||
), | ||
|
||
context={ | ||
'input': input, | ||
} | ||
) | ||
elif errors == 'replace': | ||
bytes_ += b'?' | ||
|
||
return binary_type(bytes_), len(input) | ||
|
||
for c in input: | ||
second, first = divmod(c, len(self.alphabet)) | ||
|
||
trytes.append(self.alphabet[first]) | ||
trytes.append(self.alphabet[second]) | ||
@lookup_function | ||
def check_trytes_codec(encoding): | ||
""" | ||
Determines which codec to use for the specified encoding. | ||
return binary_type(trytes), len(input) | ||
References: | ||
# noinspection PyShadowingBuiltins | ||
def decode(self, input, errors='strict'): | ||
""" | ||
Decodes a tryte string into bytes. | ||
- https://docs.python.org/3/library/codecs.html#codecs.register | ||
""" | ||
if isinstance(input, memoryview): | ||
input = input.tobytes() | ||
|
||
if not isinstance(input, (binary_type, bytearray)): | ||
raise with_context( | ||
exc = TypeError("Can't decode {type}; byte string expected.".format( | ||
type = type(input).__name__, | ||
)), | ||
|
||
context = { | ||
'input': input, | ||
}, | ||
) | ||
|
||
# :bc: In Python 2, iterating over a byte string yields characters | ||
# instead of integers. | ||
if not isinstance(input, bytearray): | ||
input = bytearray(input) | ||
|
||
bytes_ = bytearray() | ||
|
||
for i in range(0, len(input), 2): | ||
try: | ||
first, second = input[i:i+2] | ||
except ValueError: | ||
if errors == 'strict': | ||
raise with_context( | ||
exc = TrytesDecodeError( | ||
"'{name}' codec can't decode value; " | ||
"tryte sequence has odd length.".format( | ||
name = self.name, | ||
), | ||
if encoding == AsciiTrytesCodec.name: | ||
return AsciiTrytesCodec.get_codec_info() | ||
|
||
elif encoding == AsciiTrytesCodec.compat_name: | ||
warn( | ||
'"{old_codec}" codec will be removed in PyOTA v2.1. ' | ||
'Use "{new_codec}" instead.'.format( | ||
new_codec=AsciiTrytesCodec.name, | ||
old_codec=AsciiTrytesCodec.compat_name, | ||
), | ||
|
||
context = { | ||
'input': input, | ||
}, | ||
) | ||
elif errors == 'replace': | ||
bytes_ += b'?' | ||
|
||
continue | ||
|
||
try: | ||
bytes_.append( | ||
self.index[first] | ||
+ (self.index[second] * len(self.index)) | ||
DeprecationWarning, | ||
) | ||
except ValueError: | ||
# This combination of trytes yields a value > 255 when | ||
# decoded. Naturally, we can't represent this using ASCII. | ||
if errors == 'strict': | ||
raise with_context( | ||
exc = TrytesDecodeError( | ||
"'{name}' codec can't decode trytes {pair} at position {i}-{j}: " | ||
"ordinal not in range(255)".format( | ||
name = self.name, | ||
pair = chr(first) + chr(second), | ||
i = i, | ||
j = i+1, | ||
), | ||
), | ||
|
||
context = { | ||
'input': input, | ||
} | ||
) | ||
elif errors == 'replace': | ||
bytes_ += b'?' | ||
return AsciiTrytesCodec.get_codec_info() | ||
|
||
return binary_type(bytes_), len(input) | ||
|
||
|
||
@lookup_function | ||
def check_trytes_codec(encoding): | ||
""" | ||
Determines which codec to use for the specified encoding. | ||
References: | ||
- https://docs.python.org/3/library/codecs.html#codecs.register | ||
""" | ||
if encoding == AsciiTrytesCodec.name: | ||
return AsciiTrytesCodec.get_codec_info() | ||
|
||
elif encoding == AsciiTrytesCodec.compat_name: | ||
warn( | ||
'"{old_codec}" codec will be removed in PyOTA v2.1. ' | ||
'Use "{new_codec}" instead.'.format( | ||
new_codec = AsciiTrytesCodec.name, | ||
old_codec = AsciiTrytesCodec.compat_name, | ||
), | ||
|
||
DeprecationWarning, | ||
) | ||
return AsciiTrytesCodec.get_codec_info() | ||
|
||
return None | ||
return None |