Skip to content
This repository has been archived by the owner on Jan 13, 2023. It is now read-only.

Commit

Permalink
[#62] Renamed trytes codec to trytes_ascii.
Browse files Browse the repository at this point in the history
The previous name will still work, but it is deprecated and will be removed in PyOTA v2.1.
  • Loading branch information
todofixthis committed Oct 15, 2017
1 parent 879dbb2 commit a108db5
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 61 deletions.
2 changes: 1 addition & 1 deletion iota/__init__.py
Expand Up @@ -28,7 +28,7 @@
}


# Activate TrytesCodec.
# Activate codecs.
from .codecs import *

# Make some imports accessible from the top level of the package.
Expand Down
46 changes: 40 additions & 6 deletions iota/codecs.py
Expand Up @@ -3,12 +3,13 @@
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

__all__ = [
'TrytesCodec',
'AsciiTrytesCodec',
'TrytesDecodeError',
]

Expand All @@ -20,11 +21,26 @@ class TrytesDecodeError(ValueError):
pass


class TrytesCodec(Codec):
class AsciiTrytesCodec(Codec):
"""
Codec for converting byte strings into trytes, and vice versa.
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!
"""
name = 'trytes'

# :bc: Without the bytearray cast, Python 2 will populate the dict
# with characters instead of integers.
Expand Down Expand Up @@ -173,7 +189,25 @@ def decode(self, input, errors='strict'):

@lookup_function
def check_trytes_codec(encoding):
if encoding == TrytesCodec.name:
return TrytesCodec.get_codec_info()
"""
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
2 changes: 1 addition & 1 deletion iota/transaction/base.py
Expand Up @@ -457,7 +457,7 @@ def tail_transaction(self):
return self[0]

def get_messages(self, errors='drop'):
# type: () -> List[Text]
# type: (Text) -> List[Text]
"""
Attempts to decipher encoded messages from the transactions in the
bundle.
Expand Down
65 changes: 41 additions & 24 deletions iota/types.py
Expand Up @@ -6,13 +6,13 @@
from itertools import chain
from math import ceil
from random import SystemRandom
from typing import AnyStr, Generator, Iterable, Iterator, List, \
from typing import Any, AnyStr, Generator, Iterable, Iterator, List, \
MutableSequence, Optional, Text, Union

from six import PY2, binary_type, itervalues, python_2_unicode_compatible, \
text_type

from iota import TRITS_PER_TRYTE, TrytesCodec
from iota import AsciiTrytesCodec, TRITS_PER_TRYTE
from iota.crypto import HASH_LENGTH
from iota.crypto.kerl import Kerl
from iota.exceptions import with_context
Expand Down Expand Up @@ -99,7 +99,7 @@ def random(cls, length):
:param length:
Number of trytes to generate.
"""
alphabet = list(itervalues(TrytesCodec.alphabet))
alphabet = list(itervalues(AsciiTrytesCodec.alphabet))
generator = SystemRandom()

# :py:meth:`SystemRandom.choices` wasn't added until Python 3.6;
Expand All @@ -111,30 +111,35 @@ def random(cls, length):
)

@classmethod
def from_bytes(cls, bytes_, *args, **kwargs):
# type: (Union[binary_type, bytearray], ...) -> TryteString
def from_bytes(cls, bytes_, codec=AsciiTrytesCodec.name, *args, **kwargs):
# type: (Union[binary_type, bytearray], Text, *Any, **Any) -> TryteString
"""
Creates a TryteString from a sequence of bytes.
:param bytes_:
Source bytes.
:param codec:
Which codec to use:
- 'binary': Converts each byte into a sequence of trits with
the same value (this is usually what you want).
- 'ascii': Uses the legacy ASCII codec.
:param args:
Additional positional arguments to pass to the initializer.
:param kwargs:
Additional keyword arguments to pass to the initializer.
"""
return cls(encode(bytes_, 'trytes'), *args, **kwargs)
return cls(encode(bytes_, codec), *args, **kwargs)

@classmethod
def from_string(cls, string, *args, **kwargs):
# type: (Text, ...) -> TryteString
# type: (Text, *Any, **Any) -> TryteString
"""
Creates a TryteString from a Unicode string.
Note: The string will be encoded using UTF-8.
:param string:
Source string.
Expand All @@ -144,11 +149,16 @@ def from_string(cls, string, *args, **kwargs):
:param kwargs:
Additional keyword arguments to pass to the initializer.
"""
return cls.from_bytes(string.encode('utf-8'), *args, **kwargs)
return cls.from_bytes(
bytes_ = string.encode('utf-8'),
codec = AsciiTrytesCodec.name,
*args,
**kwargs
)

@classmethod
def from_trytes(cls, trytes, *args, **kwargs):
# type: (Iterable[Iterable[int]], ...) -> TryteString
# type: (Iterable[Iterable[int]], *Any, **Any) -> TryteString
"""
Creates a TryteString from a sequence of trytes.
Expand All @@ -174,13 +184,13 @@ def from_trytes(cls, trytes, *args, **kwargs):
if converted < 0:
converted += 27

chars.append(TrytesCodec.alphabet[converted])
chars.append(AsciiTrytesCodec.alphabet[converted])

return cls(chars, *args, **kwargs)

@classmethod
def from_trits(cls, trits, *args, **kwargs):
# type: (Iterable[int], ...) -> TryteString
# type: (Iterable[int], *Any, **Any) -> TryteString
"""
Creates a TryteString from a sequence of trits.
Expand Down Expand Up @@ -275,7 +285,7 @@ def __init__(self, trytes, pad=None):
trytes = bytearray(trytes)

for i, ordinal in enumerate(trytes):
if ordinal not in TrytesCodec.index:
if ordinal not in AsciiTrytesCodec.index:
raise with_context(
exc = ValueError(
'Invalid character {char!r} at position {i} '
Expand Down Expand Up @@ -313,9 +323,9 @@ def __bytes__(self):
Note: This does not decode the trytes into bytes/characters; it
only returns an ASCII representation of the trytes themselves!
If you want to:
- Decode trytes into bytes: use :py:meth:`as_bytes`.
- Decode trytes into Unicode: use :py:meth:`as_string`.
If you want to...
- ... decode trytes into bytes: use :py:meth:`as_bytes`.
- ... decode trytes into Unicode: use :py:meth:`as_string`.
"""
return binary_type(self._trytes)

Expand Down Expand Up @@ -479,8 +489,8 @@ def iter_chunks(self, chunk_size):
"""
return ChunkIterator(self, chunk_size)

def as_bytes(self, errors='strict'):
# type: (Text) -> binary_type
def as_bytes(self, errors='strict', codec=AsciiTrytesCodec.name):
# type: (Text, Text) -> binary_type
"""
Converts the TryteString into a byte string.
Expand All @@ -490,12 +500,19 @@ def as_bytes(self, errors='strict'):
- 'replace': replace with '?'.
- 'ignore': omit the tryte from the result.
:param codec:
Which codec to use:
- 'binary': Converts each sequence of 5 trits into a byte with
the same value (this is usually what you want).
- 'ascii': Uses the legacy ASCII codec.
:raise:
- :py:class:`iota.codecs.TrytesDecodeError` if the trytes cannot
be decoded into bytes.
"""
# :bc: In Python 2, `decode` does not accept keyword arguments.
return decode(self._trytes, 'trytes', errors)
# In Python 2, :py:func:`decode` does not accept keyword arguments.
return decode(self._trytes, codec, errors)

def as_string(self, errors='strict', strip_padding=True):
# type: (Text, bool) -> Text
Expand Down Expand Up @@ -523,10 +540,10 @@ def as_string(self, errors='strict', strip_padding=True):
if strip_padding and (trytes[-1] == ord(b'9')):
trytes = trytes.rstrip(b'9')

# Put one back to preserve even length.
# Put one back to preserve even length for ASCII codec.
trytes += b'9' * (len(trytes) % 2)

return decode(trytes, 'trytes', errors).decode('utf-8', errors)
return decode(trytes, AsciiTrytesCodec.name, errors).decode('utf-8', errors)

def as_json_compatible(self):
# type: () -> Text
Expand All @@ -546,7 +563,7 @@ def as_integers(self):
Each integer is a value between -13 and 13.
"""
return [
self._normalize(TrytesCodec.index[c])
self._normalize(AsciiTrytesCodec.index[c])
for c in self._trytes
]

Expand Down

0 comments on commit a108db5

Please sign in to comment.