diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_io.py b/graalpython/com.oracle.graal.python.test/src/tests/test_io.py index 2bad5600b3..be3a9f2edc 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_io.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_io.py @@ -353,6 +353,12 @@ def test_stringio_overwrite(self): s.write('ab') self.assertEqual('heabo', s.getvalue()) + def test_stringio_validates_newline_before_initial_value(self): + self.assertRaises(ValueError, _io.StringIO, b'', 'x') + self.assertRaises(ValueError, _io.StringIO, 1, 'x') + self.assertRaises(TypeError, _io.StringIO, b'') + self.assertEqual('', _io.StringIO(None).getvalue()) + def test_cr_not_ignored(self): d = _io.IncrementalNewlineDecoder(None, translate=False) d.decode("h\rello") diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/StringIOBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/StringIOBuiltins.java index 84b2bc626f..2b265b27a8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/StringIOBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/StringIOBuiltins.java @@ -61,6 +61,7 @@ import static com.oracle.graal.python.builtins.modules.io.IONodes.T_READLINE; import static com.oracle.graal.python.builtins.modules.io.TextIOWrapperNodes.validateNewline; import static com.oracle.graal.python.nodes.ErrorMessages.CAN_T_DO_NONZERO_CUR_RELATIVE_SEEKS; +import static com.oracle.graal.python.nodes.ErrorMessages.INITIAL_VALUE_MUST_BE_STR_OR_NONE_NOT_P; import static com.oracle.graal.python.nodes.ErrorMessages.INVALID_WHENCE_D_SHOULD_BE_0_1_OR_2; import static com.oracle.graal.python.nodes.ErrorMessages.IO_CLOSED; import static com.oracle.graal.python.nodes.ErrorMessages.IO_UNINIT; @@ -116,11 +117,13 @@ import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryClinicBuiltinNode; +import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonTernaryClinicBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectExactProfile; import com.oracle.graal.python.nodes.object.GetOrCreateDictNode; +import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; @@ -280,17 +283,11 @@ static PStringIO doNew(Object cls, @SuppressWarnings("unused") Object arg, @Slot(value = SlotKind.tp_init, isComplex = true) @SlotSignature(name = "StringIO", minNumOfPositionalArgs = 1, parameterNames = {"$self", "initial_value", "newline"}) - @ArgumentClinic(name = "initial_value", conversion = ArgumentClinic.ClinicConversion.TString, defaultValue = "T_EMPTY_STRING", useDefaultForNone = true) @GenerateNodeFactory - public abstract static class InitNode extends PythonTernaryClinicBuiltinNode { - - @Override - protected ArgumentClinicProvider getArgumentClinic() { - return StringIOBuiltinsClinicProviders.InitNodeClinicProviderGen.INSTANCE; - } + public abstract static class InitNode extends PythonTernaryBuiltinNode { @Specialization - static PNone init(VirtualFrame frame, PStringIO self, TruffleString initialValue, Object newlineArg, + static PNone init(VirtualFrame frame, PStringIO self, Object initialValueArg, Object newlineArg, @Bind Node inliningTarget, @Cached PRaiseNode lazyRaiseNode, @Cached IncrementalNewlineDecoderBuiltins.DecodeNode decodeNode, @@ -303,6 +300,7 @@ static PNone init(VirtualFrame frame, PStringIO self, TruffleString initialValue @Cached TruffleStringBuilder.ToStringNode toStringNode, @Cached TruffleStringBuilder.AppendCodePointNode appendCodePointNode, @Cached IONodes.ToTruffleStringNode toTruffleStringNode, + @Cached CastToTruffleStringNode castToStringNode, @Bind PythonLanguage language, @Cached PRaiseNode raiseNode) { TruffleString newline; @@ -318,6 +316,16 @@ static PNone init(VirtualFrame frame, PStringIO self, TruffleString initialValue if (newline != null) { validateNewline(newline, inliningTarget, lazyRaiseNode, codePointLengthNode, codePointAtIndexNode); } + TruffleString initialValue; + if (PGuards.isPNone(initialValueArg)) { + initialValue = T_EMPTY_STRING; + } else { + try { + initialValue = castToStringNode.execute(inliningTarget, initialValueArg); + } catch (CannotCastException e) { + throw raiseNode.raise(inliningTarget, TypeError, INITIAL_VALUE_MUST_BE_STR_OR_NONE_NOT_P, initialValueArg); + } + } self.setOK(false); self.clearAll(); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java index 410f9a9f28..1abe2a5749 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java @@ -999,6 +999,7 @@ public abstract class ErrorMessages { public static final TruffleString INPUT_FMT_NOT_SUPPORTED_BY_DECODER = tsLiteral("Input format not supported by decoder"); public static final TruffleString INVALID_UNSUPPORTED_OPTIONS = tsLiteral("Invalid or unsupported options"); public static final TruffleString INDENT_MUST_BE_STR_OR_NONE_NOT_P = tsLiteral("indent must be str or None, not %p"); + public static final TruffleString INITIAL_VALUE_MUST_BE_STR_OR_NONE_NOT_P = tsLiteral("initial_value must be str or None, not %p"); public static final TruffleString CORRUPT_INPUT_DATA = tsLiteral("Corrupt input data"); public static final TruffleString INSUFFICIENT_BUFFER_SPACE = tsLiteral("Insufficient buffer space"); public static final TruffleString INTERNAL_ERROR = tsLiteral("Internal error");