Skip to content

Commit 511a046

Browse files
committed
Implement __format__ for strings, improve __format__ for floats
1 parent bc2cdda commit 511a046

File tree

5 files changed

+66
-9
lines changed

5 files changed

+66
-9
lines changed

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,14 @@ def test_formatting_errors(self):
144144
self.assertRaises(OverflowError, lambda: b"%c" % 260)
145145

146146
self.assertRaises(ValueError, lambda: format(3+2j, "f=30,.4f"))
147-
self.assertRaises(ValueError, lambda: format(3+2j, "0=30,.4f"))
147+
self.assertRaises(ValueError, lambda: format(3+2j, "0=30,.4f"))
148+
149+
150+
class MyFloat(float):
151+
def __str__(self):
152+
return "__str__ overridden for float"
153+
154+
155+
def test_overridden_str():
156+
assert "{}".format(MyFloat(2)) == "__str__ overridden for float"
157+
assert "{0:10}".format(MyFloat(2)) == " 2.0"

graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_fstring.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*graalpython.lib-python.3.test.test_fstring.TestCase.test_compile_time_concat_errors
88
*graalpython.lib-python.3.test.test_fstring.TestCase.test_unterminated_string
99
*graalpython.lib-python.3.test.test_fstring.TestCase.test_mismatched_parens
10+
*graalpython.lib-python.3.test.test_fstring.TestCase.test_compile_time_concat
1011
*graalpython.lib-python.3.test.test_fstring.TestCase.test_comments
1112
*graalpython.lib-python.3.test.test_fstring.TestCase.test_del
1213
*graalpython.lib-python.3.test.test_fstring.TestCase.test_dict

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/FloatBuiltins.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@
6060
import static com.oracle.graal.python.nodes.SpecialMethodNames.__TRUEDIV__;
6161
import static com.oracle.graal.python.nodes.SpecialMethodNames.__TRUNC__;
6262
import static com.oracle.graal.python.runtime.formatting.FormattingUtils.prepareSpecForFloat;
63-
import static com.oracle.graal.python.runtime.formatting.FormattingUtils.shouldBeAsStr;
6463

6564
import java.math.BigDecimal;
6665
import java.math.BigInteger;
@@ -85,6 +84,7 @@
8584
import com.oracle.graal.python.nodes.ErrorMessages;
8685
import com.oracle.graal.python.nodes.SpecialMethodNames;
8786
import com.oracle.graal.python.nodes.call.special.LookupAndCallTernaryNode;
87+
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
8888
import com.oracle.graal.python.nodes.call.special.LookupAndCallVarargsNode;
8989
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
9090
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
@@ -97,6 +97,7 @@
9797
import com.oracle.graal.python.runtime.PythonContext;
9898
import com.oracle.graal.python.runtime.exception.PythonErrorType;
9999
import com.oracle.graal.python.runtime.formatting.FloatFormatter;
100+
import com.oracle.graal.python.runtime.formatting.FormattingUtils;
100101
import com.oracle.graal.python.runtime.formatting.InternalFormat;
101102
import com.oracle.truffle.api.CompilerDirectives;
102103
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -166,16 +167,18 @@ abstract static class ReprNode extends StrNode {
166167
@Builtin(name = __FORMAT__, minNumOfPositionalArgs = 2)
167168
@GenerateNodeFactory
168169
@TypeSystemReference(PythonArithmeticTypes.class)
170+
@ImportStatic(FormattingUtils.class)
169171
abstract static class FormatNode extends PythonBinaryBuiltinNode {
170172

171-
@Specialization
173+
@Specialization(guards = "shouldBeAsStr(formatString)")
174+
Object emptyFormat(VirtualFrame frame, Object self, @SuppressWarnings("unused") String formatString,
175+
@Cached("create(__STR__)") LookupAndCallUnaryNode strCall) {
176+
return strCall.executeObject(frame, self);
177+
}
178+
179+
@Specialization(guards = "!shouldBeAsStr(formatString)")
172180
@TruffleBoundary
173-
String format(double self, String formatString,
174-
@Cached("create()") StrNode strNode,
175-
@Cached("createBinaryProfile()") ConditionProfile strProfile) {
176-
if (strProfile.profile(shouldBeAsStr(formatString))) {
177-
return strNode.str(self);
178-
}
181+
String format(double self, String formatString) {
179182
InternalFormat.Spec spec = InternalFormat.fromText(getCore(), formatString, __FORMAT__);
180183
FloatFormatter formatter = new FloatFormatter(getCore(), prepareSpecForFloat(spec, getCore(), "float"));
181184
formatter.format(self);

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import static com.oracle.graal.python.nodes.SpecialMethodNames.__ADD__;
2929
import static com.oracle.graal.python.nodes.SpecialMethodNames.__CONTAINS__;
3030
import static com.oracle.graal.python.nodes.SpecialMethodNames.__EQ__;
31+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__FORMAT__;
3132
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GETITEM__;
3233
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GE__;
3334
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GT__;
@@ -95,6 +96,7 @@
9596
import com.oracle.graal.python.nodes.SpecialMethodNames;
9697
import com.oracle.graal.python.nodes.builtins.ListNodes.AppendNode;
9798
import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
99+
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
98100
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
99101
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
100102
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
@@ -112,9 +114,13 @@
112114
import com.oracle.graal.python.nodes.util.CastToJavaStringNodeGen;
113115
import com.oracle.graal.python.runtime.ExecutionContext.IndirectCallContext;
114116
import com.oracle.graal.python.runtime.PythonContext;
117+
import com.oracle.graal.python.runtime.PythonCore;
115118
import com.oracle.graal.python.runtime.PythonOptions;
116119
import com.oracle.graal.python.runtime.exception.PException;
120+
import com.oracle.graal.python.runtime.formatting.InternalFormat;
121+
import com.oracle.graal.python.runtime.formatting.InternalFormat.Spec;
117122
import com.oracle.graal.python.runtime.formatting.StringFormatProcessor;
123+
import com.oracle.graal.python.runtime.formatting.TextFormatter;
118124
import com.oracle.truffle.api.CompilerAsserts;
119125
import com.oracle.truffle.api.CompilerDirectives;
120126
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -153,6 +159,38 @@ static String doGeneric(Object self,
153159
}
154160
}
155161

162+
@Builtin(name = __FORMAT__, minNumOfPositionalArgs = 2)
163+
@GenerateNodeFactory
164+
@TypeSystemReference(PythonArithmeticTypes.class)
165+
abstract static class FormatNode extends PythonBinaryBuiltinNode {
166+
167+
@Specialization(guards = "formatString.isEmpty()")
168+
Object emptyFormat(VirtualFrame frame, Object self, @SuppressWarnings("unused") String formatString,
169+
@Cached("create(__STR__)") LookupAndCallUnaryNode strCall) {
170+
return strCall.executeObject(frame, self);
171+
}
172+
173+
@Specialization(guards = "!formatString.isEmpty()")
174+
Object format(Object self, String formatString,
175+
@Cached CastToJavaStringCheckedNode castToJavaStringNode) {
176+
String str = castToJavaStringNode.cast(self, INVALID_RECEIVER, __STR__, self);
177+
return formatString(getCore(), formatString, str);
178+
}
179+
180+
@Fallback
181+
Object other(@SuppressWarnings("unused") Object self, Object formatString) {
182+
throw raise(PythonBuiltinClassType.TypeError, ErrorMessages.ARG_D_MUST_BE_S_NOT_P, "format()", 2, "str", formatString);
183+
}
184+
185+
@TruffleBoundary
186+
private static Object formatString(PythonCore core, String formatString, String str) {
187+
Spec spec = InternalFormat.fromText(core, formatString, __FORMAT__);
188+
TextFormatter formatter = new TextFormatter(core, spec.withDefaults(Spec.STRING));
189+
formatter.format(str);
190+
return formatter.pad().getResult();
191+
}
192+
}
193+
156194
@Builtin(name = __REPR__, minNumOfPositionalArgs = 1)
157195
@GenerateNodeFactory
158196
public abstract static class ReprNode extends PythonUnaryBuiltinNode {

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

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

99
import com.oracle.graal.python.runtime.PythonCore;
10+
import com.oracle.graal.python.runtime.formatting.FormattingBuffer.StringFormattingBuffer;
1011
import com.oracle.graal.python.runtime.formatting.InternalFormat.Spec;
1112

1213
/**
@@ -28,6 +29,10 @@ public TextFormatter(PythonCore core, FormattingBuffer result, Spec spec) {
2829
super(core, result, spec);
2930
}
3031

32+
public TextFormatter(PythonCore core, Spec spec) {
33+
super(core, new StringFormattingBuffer(32), spec);
34+
}
35+
3136
/*
3237
* Re-implement the text appends so they return the right type.
3338
*/

0 commit comments

Comments
 (0)