summaryrefslogtreecommitdiffstats
path: root/yql/essentials/udfs/common/python
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2025-07-24 21:46:28 +0300
committerrobot-piglet <[email protected]>2025-07-24 21:58:13 +0300
commit475c997f5ded6c9025b31345e894afb2fd00de27 (patch)
tree1be88f3ae818b4b9c5d2d3350a44e88a6d57b250 /yql/essentials/udfs/common/python
parentaac2e8a24f54055971d7291453001b440a0be98c (diff)
Intermediate changes
commit_hash:4b0cad6620396d4b5422b264f4aa162104d45e8d
Diffstat (limited to 'yql/essentials/udfs/common/python')
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_cast.cpp14
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_cast_ut.cpp25
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_test_engine.h37
3 files changed, 72 insertions, 4 deletions
diff --git a/yql/essentials/udfs/common/python/bindings/py_cast.cpp b/yql/essentials/udfs/common/python/bindings/py_cast.cpp
index fe2a6eb5002..1a90773cd45 100644
--- a/yql/essentials/udfs/common/python/bindings/py_cast.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_cast.cpp
@@ -916,7 +916,19 @@ TPyObjectPtr ToPyArgs(
TPyObjectPtr tuple(PyTuple_New(argsCount));
for (ui32 i = 0; i < argsCount; i++) {
- auto arg = ToPyObject(ctx, inspector.GetArgType(i), args[i]);
+ const auto argType = inspector.GetArgType(i);
+ auto arg = ToPyObject(ctx, argType, args[i]);
+ // PyTuple_SET_ITEM doesn't handle the case if nullptr is
+ // given as a payload to be set (unlike PyList_Append or
+ // PyDict_SetItem do), so we have to explicitly handle
+ // the failed export from UnboxedValue to PyObject here.
+ if (!arg) {
+ ::TStringBuilder sb;
+ sb << "Failed to export ";
+ NUdf::TTypePrinter(*ctx->PyCtx->TypeInfoHelper, argType).Out(sb.Out);
+ sb << " given as args[" << i << "]: ";
+ UdfTerminate((sb << ctx->PyCtx->Pos << GetLastErrorAsString()).data());
+ }
PyTuple_SET_ITEM(tuple.Get(), i, arg.Release());
}
diff --git a/yql/essentials/udfs/common/python/bindings/py_cast_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_cast_ut.cpp
index d4ee90278c2..3c6514aea02 100644
--- a/yql/essentials/udfs/common/python/bindings/py_cast_ut.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_cast_ut.cpp
@@ -124,4 +124,29 @@ Y_UNIT_TEST_SUITE(TPyCastTest) {
Y_UNIT_TEST(BadFromPythonJson) {
TestBadUtf8Encode<NUdf::TJson>();
}
+
+ Y_UNIT_TEST(BadToPythonJson) {
+ TPythonTestEngine engine;
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ engine.UnsafeCall<void(NUdf::TJson)>(
+ [](const TType*, const NUdf::IValueBuilder& builder) {
+ // XXX: The value below is built with the
+ // following expression:
+ // $query = "a=1&t%EDb=2";
+ // $qdict = Url::QueryStringToDict($query);
+ // $qyson = Yson::From($qdict);
+ // $badJson = Yson::SerializeJson($qyson);
+ //
+ // For more info, see YQL-20231 and YQL-20220.
+ constexpr TStringBuf badJson = "\x7b\x22\x61\x22\x3a\x5b\x22\x31\x22\x5d\x2c\x22\x74\xed\x62\x22\x3a\x5b\x22\x32\x22\x5d\x7d";
+ return builder.NewString(badJson);
+ },
+ "def Test(arg):\n"
+ " pass",
+ [](const NUdf::TUnboxedValuePod&) {
+ Y_UNREACHABLE();
+ }
+ ),
+ yexception, "Failed to export Json given as args[0]");
+ }
} // Y_UNIT_TEST_SUITE(TPyCastTest)
diff --git a/yql/essentials/udfs/common/python/bindings/py_test_engine.h b/yql/essentials/udfs/common/python/bindings/py_test_engine.h
index ff0a61cbb8a..6809fc61cff 100644
--- a/yql/essentials/udfs/common/python/bindings/py_test_engine.h
+++ b/yql/essentials/udfs/common/python/bindings/py_test_engine.h
@@ -115,6 +115,32 @@ public:
ToMiniKQLWithArg<TChecker>(type, argValue, script, std::move(checker));
}
+ template <typename FunctionType,
+ typename TMiniKQLValueBuilder,
+ typename TChecker>
+ void UnsafeCall(TMiniKQLValueBuilder&& builder,
+ const TStringBuf& script,
+ TChecker&& checker)
+ {
+ TPyObjectPtr function = CompilePythonFunction(script);
+ const auto functionType = GetTypeBuilder().SimpleSignatureType<FunctionType>();
+ NUdf::TCallableTypeInspector inspector(*CastCtx_->PyCtx->TypeInfoHelper, functionType);
+ Y_ENSURE(inspector.GetArgsCount() == 1);
+ const TType* argType = static_cast<const TType*>(inspector.GetArgType(0));
+ NUdf::TUnboxedValue value = builder(argType, GetValueBuilder());
+ TPyObjectPtr pyArgs = ToPyArgs(CastCtx_, functionType, &value, inspector);
+ TPyObjectPtr resultObj = PyObject_CallObject(function.Get(), pyArgs.Get());
+
+ if (!resultObj) {
+ return checker(NUdf::TUnboxedValuePod::Invalid());
+ }
+
+ const auto returnType = inspector.GetReturnType();
+ Y_ENSURE(CastCtx_->PyCtx->TypeInfoHelper->GetTypeKind(returnType) != NUdf::ETypeKind::Stream);
+
+ checker(FromPyObject(CastCtx_, returnType, resultObj.Get()));
+ }
+
template <typename TMiniKQLValueBuilder>
TPyObjectPtr ToPython(
NUdf::TType* udfType,
@@ -189,9 +215,7 @@ public:
}
private:
- TPyObjectPtr RunPythonFunction(
- const TStringBuf& script, PyObject* args = nullptr)
- {
+ TPyObjectPtr CompilePythonFunction(const TStringBuf& script) {
TString filename(TStringBuf("embedded:test.py"));
TPyObjectPtr code(Py_CompileString(script.data(), filename.data(), Py_file_input));
if (!code) {
@@ -211,6 +235,13 @@ private:
PyErr_Print();
UNIT_FAIL("function 'Test' is not found in module");
}
+ return function;
+ }
+
+ TPyObjectPtr RunPythonFunction(
+ const TStringBuf& script, PyObject* args = nullptr)
+ {
+ TPyObjectPtr function(CompilePythonFunction(script));
return PyObject_CallObject(function.Get(), args);
}