Skip to content

Commit e2bf75c

Browse files
committed
[GR-13496] bytes.maketrans has to return bytes, not dict.
PullRequest: graalpython/377
2 parents 5248a5a + d1019a6 commit e2bf75c

File tree

5 files changed

+97
-30
lines changed

5 files changed

+97
-30
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_bytes.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,3 +629,19 @@ def test_add_mv_to_bytearray():
629629
mv = memoryview(b'world')
630630
ba += mv
631631
assert ba == b'hello world'
632+
633+
class BaseLikeBytes:
634+
635+
def test_maketrans(self):
636+
transtable = b'\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037 !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`xyzdefghijklmnopqrstuvwxyz{|}~\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377'
637+
self.assertEqual(self.type2test.maketrans(b'abc', b'xyz'), transtable)
638+
transtable = b'\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037 !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374xyz'
639+
self.assertEqual(self.type2test.maketrans(b'\375\376\377', b'xyz'), transtable)
640+
self.assertRaises(ValueError, self.type2test.maketrans, b'abc', b'xyzq')
641+
self.assertRaises(TypeError, self.type2test.maketrans, 'abc', 'def')
642+
643+
class BytesTest(BaseLikeBytes, unittest.TestCase):
644+
type2test = bytes
645+
646+
class ByteArrayTest(BaseLikeBytes, unittest.TestCase):
647+
type2test = bytearray

graalpython/com.oracle.graal.python.test/src/tests/test_string.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#
44
# Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
55
import unittest
6-
6+
import string
77
import sys
88

99

@@ -952,6 +952,24 @@ def test_translate():
952952
else:
953953
assert False, "should raise"
954954

955+
def test_translate_from_byte_table():
956+
table = bytes.maketrans(bytes(string.ascii_lowercase, 'ascii'), bytes(string.ascii_uppercase, 'ascii'))
957+
assert "ahoj".translate(table) == "AHOJ"
958+
assert "ahoj".translate(bytearray(table)) == "AHOJ"
959+
assert "ahoj".translate(memoryview(table)) == "AHOJ"
960+
961+
def test_tranlslate_from_short_table():
962+
table = b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`ABCDEFGH'
963+
assert "ahoj".translate(table) == "AHoj"
964+
965+
def test_translate_nonascii_from_byte_table():
966+
table = bytes.maketrans(bytes(string.ascii_lowercase, 'ascii'), bytes(string.ascii_uppercase, 'ascii'))
967+
assert "ačhřožj".translate(table) == "AčHřOžJ"
968+
969+
def test_translate_from_long_byte_table():
970+
table = bytes.maketrans(bytes(string.ascii_lowercase, 'ascii'), bytes(string.ascii_uppercase, 'ascii'))
971+
table *= 30
972+
assert 'ahoj453875287ščřžýáí'.translate(table) == 'AHOJ453875287A\rY~ýáí'
955973

956974
def test_splitlines():
957975
assert len(str.splitlines("\n\n")) == 2

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/AbstractBytesBuiltins.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import com.oracle.graal.python.builtins.objects.ints.PInt;
5050
import com.oracle.graal.python.builtins.objects.list.ListBuiltins.ListAppendNode;
5151
import com.oracle.graal.python.builtins.objects.list.PList;
52+
import com.oracle.graal.python.builtins.objects.type.PythonClass;
5253
import com.oracle.graal.python.nodes.argument.ReadArgumentNode;
5354
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
5455
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
@@ -775,4 +776,34 @@ protected AbstractSplitNode createRecursiveNode() {
775776
return AbstractBytesBuiltinsFactory.RSplitNodeFactory.create(new ReadArgumentNode[]{});
776777
}
777778
}
779+
780+
// static bytes.maketrans()
781+
// static bytearray.maketrans()
782+
@Builtin(name = "maketrans", fixedNumOfPositionalArgs = 3, isClassmethod = true)
783+
@GenerateNodeFactory
784+
public abstract static class MakeTransNode extends PythonBuiltinNode {
785+
786+
@Specialization
787+
public PBytes maketrans(@SuppressWarnings("unused") PythonClass cls, Object from, Object to,
788+
@Cached("create()") BytesNodes.ToBytesNode toByteNode) {
789+
byte[] fromB = toByteNode.execute(from);
790+
byte[] toB = toByteNode.execute(to);
791+
if (fromB.length != toB.length) {
792+
throw raise(PythonErrorType.ValueError, "maketrans arguments must have same length");
793+
}
794+
795+
byte[] table = new byte[256];
796+
for (int i = 0; i < 256; i++) {
797+
table[i] = (byte) i;
798+
}
799+
800+
for (int i = 0; i < fromB.length; i++) {
801+
byte value = fromB[i];
802+
table[value < 0 ? value + 256 : value] = toB[i];
803+
}
804+
805+
return factory().createBytes(table);
806+
}
807+
808+
}
778809
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesBuiltins.java

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,8 @@
5454
import com.oracle.graal.python.builtins.PythonBuiltins;
5555
import com.oracle.graal.python.builtins.objects.PNone;
5656
import com.oracle.graal.python.builtins.objects.PNotImplemented;
57-
import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes.SetItemNode;
5857
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
5958
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.NormalizeIndexNode;
60-
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.ToByteArrayNode;
61-
import com.oracle.graal.python.builtins.objects.dict.PDict;
6259
import com.oracle.graal.python.builtins.objects.iterator.PSequenceIterator;
6360
import com.oracle.graal.python.builtins.objects.list.PList;
6461
import com.oracle.graal.python.builtins.objects.memoryview.PMemoryView;
@@ -546,32 +543,6 @@ Object getitem(PBytes self, Object idx, Object value) {
546543
}
547544
}
548545

549-
// static str.maketrans()
550-
@Builtin(name = "maketrans", fixedNumOfPositionalArgs = 2)
551-
@GenerateNodeFactory
552-
public abstract static class MakeTransNode extends PythonBuiltinNode {
553-
554-
@Specialization
555-
public PDict maketrans(PBytes from, PBytes to,
556-
@Cached("create()") SetItemNode setItemNode,
557-
@Cached("create()") ToByteArrayNode toByteArrayNode) {
558-
byte[] fromB = toByteArrayNode.execute(from.getSequenceStorage());
559-
byte[] toB = toByteArrayNode.execute(to.getSequenceStorage());
560-
if (fromB.length != toB.length) {
561-
throw new RuntimeException("maketrans arguments must have same length");
562-
}
563-
564-
PDict translation = factory().createDict();
565-
for (int i = 0; i < fromB.length; i++) {
566-
int key = fromB[i];
567-
int value = toB[i];
568-
setItemNode.execute(translation, key, value);
569-
}
570-
571-
return translation;
572-
}
573-
}
574-
575546
@Builtin(name = "replace", fixedNumOfPositionalArgs = 3)
576547
@GenerateNodeFactory
577548
abstract static class ReplaceNode extends PythonTernaryBuiltinNode {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,16 @@
6464
import com.oracle.graal.python.builtins.PythonBuiltins;
6565
import com.oracle.graal.python.builtins.objects.PNone;
6666
import com.oracle.graal.python.builtins.objects.PNotImplemented;
67+
import com.oracle.graal.python.builtins.objects.bytes.BytesNodes;
68+
import com.oracle.graal.python.builtins.objects.bytes.PIBytesLike;
6769
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.SetItemNode;
6870
import com.oracle.graal.python.builtins.objects.dict.PDict;
6971
import com.oracle.graal.python.builtins.objects.ints.PInt;
7072
import com.oracle.graal.python.builtins.objects.iterator.PStringIterator;
7173
import com.oracle.graal.python.builtins.objects.list.ListBuiltins.ListAppendNode;
7274
import com.oracle.graal.python.builtins.objects.list.ListBuiltins.ListReverseNode;
7375
import com.oracle.graal.python.builtins.objects.list.PList;
76+
import com.oracle.graal.python.builtins.objects.memoryview.PMemoryView;
7477
import com.oracle.graal.python.builtins.objects.slice.PSlice;
7578
import com.oracle.graal.python.builtins.objects.slice.PSlice.SliceInfo;
7679
import com.oracle.graal.python.builtins.objects.str.StringBuiltinsFactory.SpliceNodeGen;
@@ -868,6 +871,34 @@ public String translate(String self, PDict table,
868871

869872
return new String(translatedChars);
870873
}
874+
875+
@TruffleBoundary
876+
private static String translateFromByteTable(String text, byte[] table) {
877+
char[] translatedChars = new char[text.length()];
878+
// convert only ascii or up to the lenght of table
879+
for (int i = 0; i < text.length(); i++) {
880+
char code = text.charAt(i);
881+
if (code < table.length) {
882+
translatedChars[i] = (char) (table[code] & 0xFF);
883+
} else {
884+
translatedChars[i] = code;
885+
}
886+
}
887+
return new String(translatedChars);
888+
}
889+
890+
@Specialization
891+
public String translate(String self, PIBytesLike table,
892+
@Cached("create()") BytesNodes.ToBytesNode getBytesNode) {
893+
return translateFromByteTable(self, getBytesNode.execute(table));
894+
}
895+
896+
@Specialization
897+
public String translate(String self, PMemoryView table,
898+
@Cached("create()") BytesNodes.ToBytesNode getBytesNode) {
899+
return translateFromByteTable(self, getBytesNode.execute(table));
900+
}
901+
871902
}
872903

873904
protected abstract static class SpliceNode extends PNodeWithContext {

0 commit comments

Comments
 (0)