Skip to content

Commit a817e80

Browse files
committed
printf style formatting: __getitem__ is the only condition to consider an object as a mapping
1 parent 7453e06 commit a817e80

File tree

4 files changed

+48
-3
lines changed

4 files changed

+48
-3
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ def __bytes__(self):
5353
return b"bytes"
5454

5555

56+
# This is all one needs to implement to satisfy PyCheck_Mapping
57+
class MyPseudoMapping:
58+
def __getitem__(self, item):
59+
return item
60+
61+
5662
def test_formatting():
5763
# tests some corner-cases that the standard tests do not cover
5864
assert format(-12e8, "0=30,.4f") == '-0,000,000,001,200,000,000.0000'
@@ -74,6 +80,10 @@ def test_formatting():
7480
assert type(bytearray("hello %d", "ascii") % 42) == bytearray
7581
assert type(b"hello %d" % 42) == bytes
7682

83+
# No error about too many arguments,
84+
# because the object is considered as a mapping...
85+
assert " " % MyPseudoMapping() == " "
86+
7787

7888
def test_complex_formatting():
7989
assert format(3+2j, ">20,.4f") == " 3.0000+2.0000j"

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/formatting/BytesFormatProcessor.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import java.nio.charset.StandardCharsets;
5050
import java.util.Arrays;
5151

52+
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
5253
import com.oracle.graal.python.builtins.objects.PNone;
5354
import com.oracle.graal.python.builtins.objects.bytes.BytesUtils;
5455
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
@@ -115,6 +116,13 @@ protected double asFloat(Object arg) {
115116
}
116117
}
117118

119+
@Override
120+
protected boolean useAsMapping(Object args1, PythonObjectLibrary lib, Object lazyClass) {
121+
return !isString(args1, lazyClass) && isMapping(args1) && //
122+
!isSubtype(lazyClass, PythonBuiltinClassType.PBytes) && //
123+
!isSubtype(lazyClass, PythonBuiltinClassType.PByteArray);
124+
}
125+
118126
@Override
119127
protected Formatter handleSingleCharacterFormat(Spec spec) {
120128
// %c for bytes supports length one bytes buffer object (no __bytes__ coercion) or an

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/formatting/FormatProcessor.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
package com.oracle.graal.python.runtime.formatting;
88

9+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GETITEM__;
910
import static com.oracle.graal.python.nodes.SpecialMethodNames.__INDEX__;
1011
import static com.oracle.graal.python.nodes.SpecialMethodNames.__INT__;
1112
import static com.oracle.graal.python.runtime.exception.PythonErrorType.MemoryError;
@@ -17,6 +18,7 @@
1718
import java.math.MathContext;
1819

1920
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
21+
import com.oracle.graal.python.builtins.objects.PNone;
2022
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
2123
import com.oracle.graal.python.builtins.objects.floats.PFloat;
2224
import com.oracle.graal.python.builtins.objects.function.PKeyword;
@@ -219,6 +221,24 @@ protected InternalFormat.Formatter formatInteger(Object intObj, InternalFormat.S
219221
return fi;
220222
}
221223

224+
/**
225+
* Should this argument be treated as a mapping or as a single argument. This logic differs
226+
* between string and bytes formatting.
227+
*/
228+
protected abstract boolean useAsMapping(Object args1, PythonObjectLibrary lib, Object lazyClass);
229+
230+
protected boolean isString(Object args1, Object lazyClass) {
231+
return PGuards.isString(args1) || isSubtype(lazyClass, PythonBuiltinClassType.PString);
232+
}
233+
234+
protected boolean isMapping(Object args1) {
235+
return lookupAttribute(args1, __GETITEM__) != PNone.NO_VALUE;
236+
}
237+
238+
protected static boolean isSubtype(Object lazyClass, PythonBuiltinClassType clazz) {
239+
return IsSubtypeNodeGen.getUncached().execute(lazyClass, clazz);
240+
}
241+
222242
/**
223243
* Main service of this class: format one or more arguments with the format string supplied at
224244
* construction.
@@ -238,15 +258,16 @@ private T formatImpl(Object args1) {
238258

239259
// We need to do a full subtype-check because native objects may inherit from tuple but have
240260
// Java type 'PythonNativeObject' (e.g. 'namedtuple' alias 'structseq').
241-
boolean tupleArgs = PGuards.isPTuple(args1) || IsSubtypeNodeGen.getUncached().execute(PythonObjectLibrary.getUncached().getLazyPythonClass(args1), PythonBuiltinClassType.PTuple);
242-
assert tupleArgs || !PGuards.isPTuple(args1);
261+
PythonObjectLibrary args1Lib = PythonObjectLibrary.getFactory().getUncached(args1);
262+
final Object args1LazyClass = args1Lib.getLazyPythonClass(args1);
263+
boolean tupleArgs = PGuards.isPTuple(args1) || isSubtype(args1LazyClass, PythonBuiltinClassType.PTuple);
243264
if (tupleArgs) {
244265
// We will simply work through the tuple elements
245266
argIndex = 0;
246267
} else {
247268
// Not a tuple, but possibly still some kind of container: use
248269
// special argIndex values.
249-
if (!PGuards.isString(args1) && PythonObjectLibrary.getUncached().isMapping(args1)) {
270+
if (useAsMapping(args1, args1Lib, args1LazyClass)) {
250271
mapping = args1;
251272
argIndex = -3;
252273
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/formatting/StringFormatProcessor.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import com.oracle.graal.python.builtins.objects.PNone;
1717
import com.oracle.graal.python.builtins.objects.bytes.BytesUtils;
18+
import com.oracle.graal.python.builtins.objects.object.PythonObjectLibrary;
1819
import com.oracle.graal.python.builtins.objects.str.PString;
1920
import com.oracle.graal.python.builtins.objects.tuple.TupleBuiltins;
2021
import com.oracle.graal.python.nodes.ErrorMessages;
@@ -61,6 +62,11 @@ Object parseMappingKey(int start, int end) {
6162
return formatText.substring(start, end);
6263
}
6364

65+
@Override
66+
protected boolean useAsMapping(Object args1, PythonObjectLibrary lib, Object lazyClass) {
67+
return !isString(args1, lazyClass) && isMapping(args1);
68+
}
69+
6470
@Override
6571
protected InternalFormat.Formatter handleSingleCharacterFormat(Spec spec) {
6672
InternalFormat.Formatter f;

0 commit comments

Comments
 (0)