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

Commit c7b3242

Browse files
authored
Merge pull request #80 from iotaledger/feature/62-properBytesFromTrytes
Renamed 'trytes' codec to 'trytes_ascii'.
2 parents 879dbb2 + a108db5 commit c7b3242

File tree

7 files changed

+148
-61
lines changed

7 files changed

+148
-61
lines changed

iota/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
}
2929

3030

31-
# Activate TrytesCodec.
31+
# Activate codecs.
3232
from .codecs import *
3333

3434
# Make some imports accessible from the top level of the package.

iota/codecs.py

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
unicode_literals
44

55
from codecs import Codec, CodecInfo, register as lookup_function
6+
from warnings import warn
67

78
from iota.exceptions import with_context
89
from six import PY3, binary_type
910

1011
__all__ = [
11-
'TrytesCodec',
12+
'AsciiTrytesCodec',
1213
'TrytesDecodeError',
1314
]
1415

@@ -20,11 +21,26 @@ class TrytesDecodeError(ValueError):
2021
pass
2122

2223

23-
class TrytesCodec(Codec):
24+
class AsciiTrytesCodec(Codec):
2425
"""
25-
Codec for converting byte strings into trytes, and vice versa.
26+
Legacy codec for converting byte strings into trytes, and vice versa.
27+
28+
This method encodes each pair of trytes as an ASCII code point (and
29+
vice versa when decoding).
30+
31+
The end result requires more space than if the trytes were converted
32+
mathematically, but because the result is ASCII, it's easier to work
33+
with.
34+
35+
Think of this kind of like Base 64 for balanced ternary (:
36+
"""
37+
name = 'trytes_ascii'
38+
39+
compat_name = 'trytes'
40+
"""
41+
Old name for this codec.
42+
Note: Will be removed in PyOTA v2.1!
2643
"""
27-
name = 'trytes'
2844

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

174190
@lookup_function
175191
def check_trytes_codec(encoding):
176-
if encoding == TrytesCodec.name:
177-
return TrytesCodec.get_codec_info()
192+
"""
193+
Determines which codec to use for the specified encoding.
194+
195+
References:
196+
- https://docs.python.org/3/library/codecs.html#codecs.register
197+
"""
198+
if encoding == AsciiTrytesCodec.name:
199+
return AsciiTrytesCodec.get_codec_info()
200+
201+
elif encoding == AsciiTrytesCodec.compat_name:
202+
warn(
203+
'"{old_codec}" codec will be removed in PyOTA v2.1. '
204+
'Use "{new_codec}" instead.'.format(
205+
new_codec = AsciiTrytesCodec.name,
206+
old_codec = AsciiTrytesCodec.compat_name,
207+
),
208+
209+
DeprecationWarning,
210+
)
211+
return AsciiTrytesCodec.get_codec_info()
178212

179213
return None

iota/transaction/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ def tail_transaction(self):
457457
return self[0]
458458

459459
def get_messages(self, errors='drop'):
460-
# type: () -> List[Text]
460+
# type: (Text) -> List[Text]
461461
"""
462462
Attempts to decipher encoded messages from the transactions in the
463463
bundle.

iota/types.py

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
from itertools import chain
77
from math import ceil
88
from random import SystemRandom
9-
from typing import AnyStr, Generator, Iterable, Iterator, List, \
9+
from typing import Any, AnyStr, Generator, Iterable, Iterator, List, \
1010
MutableSequence, Optional, Text, Union
1111

1212
from six import PY2, binary_type, itervalues, python_2_unicode_compatible, \
1313
text_type
1414

15-
from iota import TRITS_PER_TRYTE, TrytesCodec
15+
from iota import AsciiTrytesCodec, TRITS_PER_TRYTE
1616
from iota.crypto import HASH_LENGTH
1717
from iota.crypto.kerl import Kerl
1818
from iota.exceptions import with_context
@@ -99,7 +99,7 @@ def random(cls, length):
9999
:param length:
100100
Number of trytes to generate.
101101
"""
102-
alphabet = list(itervalues(TrytesCodec.alphabet))
102+
alphabet = list(itervalues(AsciiTrytesCodec.alphabet))
103103
generator = SystemRandom()
104104

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

113113
@classmethod
114-
def from_bytes(cls, bytes_, *args, **kwargs):
115-
# type: (Union[binary_type, bytearray], ...) -> TryteString
114+
def from_bytes(cls, bytes_, codec=AsciiTrytesCodec.name, *args, **kwargs):
115+
# type: (Union[binary_type, bytearray], Text, *Any, **Any) -> TryteString
116116
"""
117117
Creates a TryteString from a sequence of bytes.
118118
119119
:param bytes_:
120120
Source bytes.
121121
122+
:param codec:
123+
Which codec to use:
124+
125+
- 'binary': Converts each byte into a sequence of trits with
126+
the same value (this is usually what you want).
127+
- 'ascii': Uses the legacy ASCII codec.
128+
122129
:param args:
123130
Additional positional arguments to pass to the initializer.
124131
125132
:param kwargs:
126133
Additional keyword arguments to pass to the initializer.
127134
"""
128-
return cls(encode(bytes_, 'trytes'), *args, **kwargs)
135+
return cls(encode(bytes_, codec), *args, **kwargs)
129136

130137
@classmethod
131138
def from_string(cls, string, *args, **kwargs):
132-
# type: (Text, ...) -> TryteString
139+
# type: (Text, *Any, **Any) -> TryteString
133140
"""
134141
Creates a TryteString from a Unicode string.
135142
136-
Note: The string will be encoded using UTF-8.
137-
138143
:param string:
139144
Source string.
140145
@@ -144,11 +149,16 @@ def from_string(cls, string, *args, **kwargs):
144149
:param kwargs:
145150
Additional keyword arguments to pass to the initializer.
146151
"""
147-
return cls.from_bytes(string.encode('utf-8'), *args, **kwargs)
152+
return cls.from_bytes(
153+
bytes_ = string.encode('utf-8'),
154+
codec = AsciiTrytesCodec.name,
155+
*args,
156+
**kwargs
157+
)
148158

149159
@classmethod
150160
def from_trytes(cls, trytes, *args, **kwargs):
151-
# type: (Iterable[Iterable[int]], ...) -> TryteString
161+
# type: (Iterable[Iterable[int]], *Any, **Any) -> TryteString
152162
"""
153163
Creates a TryteString from a sequence of trytes.
154164
@@ -174,13 +184,13 @@ def from_trytes(cls, trytes, *args, **kwargs):
174184
if converted < 0:
175185
converted += 27
176186

177-
chars.append(TrytesCodec.alphabet[converted])
187+
chars.append(AsciiTrytesCodec.alphabet[converted])
178188

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

181191
@classmethod
182192
def from_trits(cls, trits, *args, **kwargs):
183-
# type: (Iterable[int], ...) -> TryteString
193+
# type: (Iterable[int], *Any, **Any) -> TryteString
184194
"""
185195
Creates a TryteString from a sequence of trits.
186196
@@ -275,7 +285,7 @@ def __init__(self, trytes, pad=None):
275285
trytes = bytearray(trytes)
276286

277287
for i, ordinal in enumerate(trytes):
278-
if ordinal not in TrytesCodec.index:
288+
if ordinal not in AsciiTrytesCodec.index:
279289
raise with_context(
280290
exc = ValueError(
281291
'Invalid character {char!r} at position {i} '
@@ -313,9 +323,9 @@ def __bytes__(self):
313323
Note: This does not decode the trytes into bytes/characters; it
314324
only returns an ASCII representation of the trytes themselves!
315325
316-
If you want to:
317-
- Decode trytes into bytes: use :py:meth:`as_bytes`.
318-
- Decode trytes into Unicode: use :py:meth:`as_string`.
326+
If you want to...
327+
- ... decode trytes into bytes: use :py:meth:`as_bytes`.
328+
- ... decode trytes into Unicode: use :py:meth:`as_string`.
319329
"""
320330
return binary_type(self._trytes)
321331

@@ -479,8 +489,8 @@ def iter_chunks(self, chunk_size):
479489
"""
480490
return ChunkIterator(self, chunk_size)
481491

482-
def as_bytes(self, errors='strict'):
483-
# type: (Text) -> binary_type
492+
def as_bytes(self, errors='strict', codec=AsciiTrytesCodec.name):
493+
# type: (Text, Text) -> binary_type
484494
"""
485495
Converts the TryteString into a byte string.
486496
@@ -490,12 +500,19 @@ def as_bytes(self, errors='strict'):
490500
- 'replace': replace with '?'.
491501
- 'ignore': omit the tryte from the result.
492502
503+
:param codec:
504+
Which codec to use:
505+
506+
- 'binary': Converts each sequence of 5 trits into a byte with
507+
the same value (this is usually what you want).
508+
- 'ascii': Uses the legacy ASCII codec.
509+
493510
:raise:
494511
- :py:class:`iota.codecs.TrytesDecodeError` if the trytes cannot
495512
be decoded into bytes.
496513
"""
497-
# :bc: In Python 2, `decode` does not accept keyword arguments.
498-
return decode(self._trytes, 'trytes', errors)
514+
# In Python 2, :py:func:`decode` does not accept keyword arguments.
515+
return decode(self._trytes, codec, errors)
499516

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

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

529-
return decode(trytes, 'trytes', errors).decode('utf-8', errors)
546+
return decode(trytes, AsciiTrytesCodec.name, errors).decode('utf-8', errors)
530547

531548
def as_json_compatible(self):
532549
# type: () -> Text
@@ -546,7 +563,7 @@ def as_integers(self):
546563
Each integer is a value between -13 and 13.
547564
"""
548565
return [
549-
self._normalize(TrytesCodec.index[c])
566+
self._normalize(AsciiTrytesCodec.index[c])
550567
for c in self._trytes
551568
]
552569

0 commit comments

Comments
 (0)