aboutsummaryrefslogtreecommitdiffstats
path: root/yql/essentials/udfs/common/python/bindings/py_struct_ut.cpp
diff options
context:
space:
mode:
authorimunkin <imunkin@yandex-team.com>2024-11-08 10:00:23 +0300
committerimunkin <imunkin@yandex-team.com>2024-11-08 10:12:13 +0300
commita784a2f943d6e15caa6241e2e96d80aac6dbf375 (patch)
tree05f1e5366c916b988a8afb75bdab8ddeee0f6e6d /yql/essentials/udfs/common/python/bindings/py_struct_ut.cpp
parentd70137a7b530ccaa52834274913bbb5a3d1ca06e (diff)
downloadydb-a784a2f943d6e15caa6241e2e96d80aac6dbf375.tar.gz
Move yql/udfs/common/ to /yql/essentials YQL-19206
Except the following directories: * clickhouse/client * datetime * knn * roaring commit_hash:c7da95636144d28db109d6b17ddc762e9bacb59f
Diffstat (limited to 'yql/essentials/udfs/common/python/bindings/py_struct_ut.cpp')
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_struct_ut.cpp307
1 files changed, 307 insertions, 0 deletions
diff --git a/yql/essentials/udfs/common/python/bindings/py_struct_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_struct_ut.cpp
new file mode 100644
index 0000000000..a97507f549
--- /dev/null
+++ b/yql/essentials/udfs/common/python/bindings/py_struct_ut.cpp
@@ -0,0 +1,307 @@
+#include "ut3/py_test_engine.h"
+
+#include <library/cpp/testing/unittest/registar.h>
+
+
+using namespace NPython;
+
+Y_UNIT_TEST_SUITE(TPyStructTest) {
+ Y_UNIT_TEST(FromPyObject) {
+ TPythonTestEngine engine;
+
+ ui32 ageIdx = 0, nameIdx = 0;
+ auto personType = engine.GetTypeBuilder().Struct()->
+ AddField<int>("age", &ageIdx)
+ .AddField<char*>("name", &nameIdx)
+ .Build();
+
+ engine.ToMiniKQL(personType,
+ "class Person:\n"
+ " def __init__(self, age, name):\n"
+ " self.age = age\n"
+ " self.name = name\n"
+ "\n"
+ "def Test():\n"
+ " return Person(99, 'Jamel')\n",
+ [ageIdx, nameIdx](const NUdf::TUnboxedValuePod& value) {
+ UNIT_ASSERT(value);
+ UNIT_ASSERT(value.IsBoxed());
+ auto name = value.GetElement(nameIdx);
+ UNIT_ASSERT_STRINGS_EQUAL(name.AsStringRef(), "Jamel");
+ auto age = value.GetElement(ageIdx);
+ UNIT_ASSERT_EQUAL(age.Get<ui32>(), 99);
+ });
+ }
+
+ Y_UNIT_TEST(FromPyObjectMissingOptionalField) {
+ TPythonTestEngine engine;
+
+ ui32 ageIdx = 0, nameIdx = 0;
+ auto optionalStringType = engine.GetTypeBuilder().Optional()->Item<char*>().Build();
+ auto personType = engine.GetTypeBuilder().Struct()->
+ AddField<int>("age", &ageIdx)
+ .AddField("name", optionalStringType, &nameIdx)
+ .Build();
+
+ engine.ToMiniKQL(personType,
+ "class Person:\n"
+ " def __init__(self, age):\n"
+ " self.age = age\n"
+ "\n"
+ "def Test():\n"
+ " return Person(99)\n",
+ [ageIdx, nameIdx](const NUdf::TUnboxedValuePod& value) {
+ UNIT_ASSERT(value);
+ UNIT_ASSERT(value.IsBoxed());
+ auto name = value.GetElement(nameIdx);
+ UNIT_ASSERT(!name);
+ auto age = value.GetElement(ageIdx);
+ UNIT_ASSERT_EQUAL(age.Get<ui32>(), 99);
+ });
+ }
+
+ Y_UNIT_TEST(FromPyDict) {
+ TPythonTestEngine engine;
+
+ ui32 ageIdx = 0, nameIdx = 0;
+ auto personType = engine.GetTypeBuilder().Struct()->
+ AddField<int>("age", &ageIdx)
+ .AddField<char*>("name", &nameIdx)
+ .Build();
+
+ engine.ToMiniKQL(personType,
+ "def Test():\n"
+ " return { 'name': 'Jamel', 'age': 99 }\n",
+ [ageIdx, nameIdx](const NUdf::TUnboxedValuePod& value) {
+ UNIT_ASSERT(value);
+ UNIT_ASSERT(value.IsBoxed());
+ auto name = value.GetElement(nameIdx);
+ UNIT_ASSERT_STRINGS_EQUAL(name.AsStringRef(), "Jamel");
+ auto age = value.GetElement(ageIdx);
+ UNIT_ASSERT_EQUAL(age.Get<ui32>(), 99);
+ });
+ }
+
+ Y_UNIT_TEST(FromPyDictMissingOptionalField) {
+ TPythonTestEngine engine;
+
+ ui32 ageIdx = 0, nameIdx = 0;
+ auto optionalStringType = engine.GetTypeBuilder().Optional()->Item<char*>().Build();
+ auto personType = engine.GetTypeBuilder().Struct()->
+ AddField<int>("age", &ageIdx)
+ .AddField("name", optionalStringType, &nameIdx)
+ .Build();
+
+ engine.ToMiniKQL(personType,
+ "def Test():\n"
+ " return { 'age': 99 }\n",
+ [ageIdx, nameIdx](const NUdf::TUnboxedValuePod& value) {
+ UNIT_ASSERT(value);
+ UNIT_ASSERT(value.IsBoxed());
+ auto name = value.GetElement(nameIdx);
+ UNIT_ASSERT(!name);
+ auto age = value.GetElement(ageIdx);
+ UNIT_ASSERT_EQUAL(age.Get<ui32>(), 99);
+ });
+ }
+
+ Y_UNIT_TEST(FromPyDictBytesKeyWithNullCharacter) {
+ TPythonTestEngine engine;
+
+ ui32 ageIdx = 0;
+ auto personType = engine.GetTypeBuilder().Struct()->
+ AddField<int>("a\0ge", &ageIdx)
+ .Build();
+
+ engine.ToMiniKQL(personType,
+ "def Test():\n"
+ " return { b'a\\0ge': 99 }\n",
+ [ageIdx](const NUdf::TUnboxedValuePod& value) {
+ UNIT_ASSERT(value);
+ UNIT_ASSERT(value.IsBoxed());
+ auto age = value.GetElement(ageIdx);
+ UNIT_ASSERT_EQUAL(age.Get<ui32>(), 99);
+ });
+ }
+
+ Y_UNIT_TEST(FromPyNamedTuple) {
+ TPythonTestEngine engine;
+
+ ui32 ageIdx = 0, nameIdx = 0;
+ auto personType = engine.GetTypeBuilder().Struct()->
+ AddField<int>("age", &ageIdx)
+ .AddField<char*>("name", &nameIdx)
+ .Build();
+
+ engine.ToMiniKQL(personType,
+ "from collections import namedtuple\n"
+ "def Test():\n"
+ " Person = namedtuple('Person', 'name age')\n"
+ " return Person(age=13, name='Tony')\n",
+ [ageIdx, nameIdx](const NUdf::TUnboxedValuePod& value) {
+ UNIT_ASSERT(value);
+ UNIT_ASSERT(value.IsBoxed());
+ auto name = value.GetElement(nameIdx);
+ UNIT_ASSERT_STRINGS_EQUAL(name.AsStringRef(), "Tony");
+ auto age = value.GetElement(ageIdx);
+ UNIT_ASSERT_EQUAL(age.Get<ui32>(), 13);
+ });
+ }
+
+ Y_UNIT_TEST(FromPyNamedTupleNoneOptionalField) {
+ TPythonTestEngine engine;
+
+ ui32 ageIdx = 0, nameIdx = 0;
+ auto optionalStringType = engine.GetTypeBuilder().Optional()->Item<char*>().Build();
+ auto personType = engine.GetTypeBuilder().Struct()->
+ AddField<int>("age", &ageIdx)
+ .AddField("name", optionalStringType, &nameIdx)
+ .Build();
+
+ engine.ToMiniKQL(personType,
+ "from collections import namedtuple\n"
+ "def Test():\n"
+ " Pers = namedtuple('Person', 'name age')\n"
+ " return Pers(name=None, age=15)\n",
+ [ageIdx, nameIdx](const NUdf::TUnboxedValuePod& value) {
+ UNIT_ASSERT(value);
+ UNIT_ASSERT(value.IsBoxed());
+ auto name = value.GetElement(nameIdx);
+ UNIT_ASSERT(!name);
+ auto age = value.GetElement(ageIdx);
+ UNIT_ASSERT_EQUAL(age.Get<ui32>(), 15);
+ });
+ }
+
+ Y_UNIT_TEST(FromPyEmptyStruct) {
+ TPythonTestEngine engine;
+ auto emptyStruct = engine.GetTypeBuilder().Struct()->Build();
+
+ engine.ToMiniKQL(emptyStruct,
+ "class Empty: pass\n"
+ "\n"
+ "def Test():\n"
+ " return Empty()\n",
+ [](const NUdf::TUnboxedValuePod&) {});
+ }
+
+ Y_UNIT_TEST(ToPyObject) {
+ TPythonTestEngine engine;
+
+ ui32 ageIdx = 0, nameIdx = 0, addressIdx = 0, cityIdx = 0, streetIdx = 0, buildingIdx = 0;
+ auto addressType = engine.GetTypeBuilder().Struct()->
+ AddField<NUdf::TUtf8>("city", &cityIdx)
+ .AddField<NUdf::TUtf8>("street", &streetIdx)
+ .AddField<ui16>("building", &buildingIdx)
+ .Build();
+
+ auto personType = engine.GetTypeBuilder().Struct()->
+ AddField<ui16>("age", &ageIdx)
+ .AddField<NUdf::TUtf8>("name", &nameIdx)
+ .AddField("address", addressType, &addressIdx)
+ .Build();
+
+
+ engine.ToPython(personType,
+ [=](const TType* type, const NUdf::IValueBuilder& vb) {
+ NUdf::TUnboxedValue* items = nullptr;
+ auto new_struct = vb.NewArray(static_cast<const TStructType*>(type)->GetMembersCount(), items);
+ items[ageIdx] = NUdf::TUnboxedValuePod(ui16(97));
+ items[nameIdx] = vb.NewString("Jamel");
+ NUdf::TUnboxedValue* items2 = nullptr;
+ items[addressIdx] = vb.NewArray(static_cast<const TStructType*>(static_cast<const TStructType*>(type)->GetMemberType(addressIdx))->GetMembersCount(), items2);
+ items2[cityIdx] = vb.NewString("Moscow");;
+ items2[streetIdx] = vb.NewString("L'va Tolstogo");
+ items2[buildingIdx] = NUdf::TUnboxedValuePod(ui16(16));
+ return new_struct;
+ },
+ "def Test(value):\n"
+ " assert isinstance(value, object)\n"
+ " assert value.name == 'Jamel'\n"
+ " assert value.age == 97\n"
+ " assert value.address.city == 'Moscow'\n"
+ " assert value.address.building == 16\n"
+ );
+ }
+
+ Y_UNIT_TEST(ToPyObjectKeywordsAsFields) {
+ TPythonTestEngine engine;
+
+ ui32 passIdx = 0, whileIdx = 0, ifIdx = 0, notIdx = 0;
+ auto structType = engine.GetTypeBuilder().Struct()->
+ AddField<NUdf::TUtf8>("pass", &passIdx)
+ .AddField<NUdf::TUtf8>("while", &whileIdx)
+ .AddField<NUdf::TUtf8>("if", &ifIdx)
+ .AddField<NUdf::TUtf8>("not", &notIdx)
+ .Build();
+
+ engine.ToPython(structType,
+ [=](const TType* type, const NUdf::IValueBuilder& vb) {
+ NUdf::TUnboxedValue* items = nullptr;
+ auto new_struct = vb.NewArray(static_cast<const TStructType*>(type)->GetMembersCount(), items);
+ items[ifIdx] = vb.NewString("You");
+ items[whileIdx] = vb.NewString("Shall");
+ items[notIdx] = vb.NewString("Not");
+ items[passIdx] = vb.NewString("Pass");
+ return new_struct;
+ },
+ "def Test(value):\n"
+ " assert getattr(value, 'if') == 'You'\n"
+ " assert getattr(value, 'while') == 'Shall'\n"
+ " assert getattr(value, 'not') == 'Not'\n"
+ " assert getattr(value, 'pass') == 'Pass'\n"
+ );
+ }
+
+#if PY_MAJOR_VERSION >= 3 // TODO: Fix for python 2
+ Y_UNIT_TEST(ToPyObjectTryModify) {
+ TPythonTestEngine engine;
+
+ ui32 field1Idx = 0, field2Idx = 0;
+ auto structType = engine.GetTypeBuilder().Struct()->
+ AddField<NUdf::TUtf8>("field1", &field1Idx)
+ .AddField<NUdf::TUtf8>("field2", &field2Idx)
+ .Build();
+
+ engine.ToPython(structType,
+ [=](const TType* type, const NUdf::IValueBuilder& vb) {
+ NUdf::TUnboxedValue* items = nullptr;
+ auto new_struct = vb.NewArray(static_cast<const TStructType*>(type)->GetMembersCount(), items);
+ items[field1Idx] = NUdf::TUnboxedValuePod::Zero();
+ items[field2Idx] = NUdf::TUnboxedValuePod::Embedded("empty");
+ return new_struct;
+ },
+ "def Test(value):\n"
+ " try:\n"
+ " setattr(value, 'field1', 17)\n"
+ " except AttributeError:\n"
+ " pass\n"
+ " else:\n"
+ " assert False\n"
+ " try:\n"
+ " value.field2 = 18\n"
+ " except AttributeError:\n"
+ " pass\n"
+ " else:\n"
+ " assert False\n"
+ );
+ }
+#endif
+
+ Y_UNIT_TEST(ToPyObjectEmptyStruct) {
+ TPythonTestEngine engine;
+
+ auto personType = engine.GetTypeBuilder().Struct()->Build();
+
+ engine.ToPython(personType,
+ [](const TType*, const NUdf::IValueBuilder& vb) {
+ return vb.NewEmptyList();
+ },
+ "def Test(value):\n"
+ " assert isinstance(value, object)\n"
+#if PY_MAJOR_VERSION >= 3
+ " assert len(value) == 0\n"
+#endif
+ );
+ }
+}