py_struct_ut.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. #include "ut3/py_test_engine.h"
  2. #include <library/cpp/testing/unittest/registar.h>
  3. using namespace NPython;
  4. Y_UNIT_TEST_SUITE(TPyStructTest) {
  5. Y_UNIT_TEST(FromPyObject) {
  6. TPythonTestEngine engine;
  7. ui32 ageIdx = 0, nameIdx = 0;
  8. auto personType = engine.GetTypeBuilder().Struct()->
  9. AddField<int>("age", &ageIdx)
  10. .AddField<char*>("name", &nameIdx)
  11. .Build();
  12. engine.ToMiniKQL(personType,
  13. "class Person:\n"
  14. " def __init__(self, age, name):\n"
  15. " self.age = age\n"
  16. " self.name = name\n"
  17. "\n"
  18. "def Test():\n"
  19. " return Person(99, 'Jamel')\n",
  20. [ageIdx, nameIdx](const NUdf::TUnboxedValuePod& value) {
  21. UNIT_ASSERT(value);
  22. UNIT_ASSERT(value.IsBoxed());
  23. auto name = value.GetElement(nameIdx);
  24. UNIT_ASSERT_STRINGS_EQUAL(name.AsStringRef(), "Jamel");
  25. auto age = value.GetElement(ageIdx);
  26. UNIT_ASSERT_EQUAL(age.Get<ui32>(), 99);
  27. });
  28. }
  29. Y_UNIT_TEST(FromPyObjectMissingOptionalField) {
  30. TPythonTestEngine engine;
  31. ui32 ageIdx = 0, nameIdx = 0;
  32. auto optionalStringType = engine.GetTypeBuilder().Optional()->Item<char*>().Build();
  33. auto personType = engine.GetTypeBuilder().Struct()->
  34. AddField<int>("age", &ageIdx)
  35. .AddField("name", optionalStringType, &nameIdx)
  36. .Build();
  37. engine.ToMiniKQL(personType,
  38. "class Person:\n"
  39. " def __init__(self, age):\n"
  40. " self.age = age\n"
  41. "\n"
  42. "def Test():\n"
  43. " return Person(99)\n",
  44. [ageIdx, nameIdx](const NUdf::TUnboxedValuePod& value) {
  45. UNIT_ASSERT(value);
  46. UNIT_ASSERT(value.IsBoxed());
  47. auto name = value.GetElement(nameIdx);
  48. UNIT_ASSERT(!name);
  49. auto age = value.GetElement(ageIdx);
  50. UNIT_ASSERT_EQUAL(age.Get<ui32>(), 99);
  51. });
  52. }
  53. Y_UNIT_TEST(FromPyDict) {
  54. TPythonTestEngine engine;
  55. ui32 ageIdx = 0, nameIdx = 0;
  56. auto personType = engine.GetTypeBuilder().Struct()->
  57. AddField<int>("age", &ageIdx)
  58. .AddField<char*>("name", &nameIdx)
  59. .Build();
  60. engine.ToMiniKQL(personType,
  61. "def Test():\n"
  62. " return { 'name': 'Jamel', 'age': 99 }\n",
  63. [ageIdx, nameIdx](const NUdf::TUnboxedValuePod& value) {
  64. UNIT_ASSERT(value);
  65. UNIT_ASSERT(value.IsBoxed());
  66. auto name = value.GetElement(nameIdx);
  67. UNIT_ASSERT_STRINGS_EQUAL(name.AsStringRef(), "Jamel");
  68. auto age = value.GetElement(ageIdx);
  69. UNIT_ASSERT_EQUAL(age.Get<ui32>(), 99);
  70. });
  71. }
  72. Y_UNIT_TEST(FromPyDictMissingOptionalField) {
  73. TPythonTestEngine engine;
  74. ui32 ageIdx = 0, nameIdx = 0;
  75. auto optionalStringType = engine.GetTypeBuilder().Optional()->Item<char*>().Build();
  76. auto personType = engine.GetTypeBuilder().Struct()->
  77. AddField<int>("age", &ageIdx)
  78. .AddField("name", optionalStringType, &nameIdx)
  79. .Build();
  80. engine.ToMiniKQL(personType,
  81. "def Test():\n"
  82. " return { 'age': 99 }\n",
  83. [ageIdx, nameIdx](const NUdf::TUnboxedValuePod& value) {
  84. UNIT_ASSERT(value);
  85. UNIT_ASSERT(value.IsBoxed());
  86. auto name = value.GetElement(nameIdx);
  87. UNIT_ASSERT(!name);
  88. auto age = value.GetElement(ageIdx);
  89. UNIT_ASSERT_EQUAL(age.Get<ui32>(), 99);
  90. });
  91. }
  92. Y_UNIT_TEST(FromPyDictBytesKeyWithNullCharacter) {
  93. TPythonTestEngine engine;
  94. ui32 ageIdx = 0;
  95. auto personType = engine.GetTypeBuilder().Struct()->
  96. AddField<int>("a\0ge", &ageIdx)
  97. .Build();
  98. engine.ToMiniKQL(personType,
  99. "def Test():\n"
  100. " return { b'a\\0ge': 99 }\n",
  101. [ageIdx](const NUdf::TUnboxedValuePod& value) {
  102. UNIT_ASSERT(value);
  103. UNIT_ASSERT(value.IsBoxed());
  104. auto age = value.GetElement(ageIdx);
  105. UNIT_ASSERT_EQUAL(age.Get<ui32>(), 99);
  106. });
  107. }
  108. Y_UNIT_TEST(FromPyNamedTuple) {
  109. TPythonTestEngine engine;
  110. ui32 ageIdx = 0, nameIdx = 0;
  111. auto personType = engine.GetTypeBuilder().Struct()->
  112. AddField<int>("age", &ageIdx)
  113. .AddField<char*>("name", &nameIdx)
  114. .Build();
  115. engine.ToMiniKQL(personType,
  116. "from collections import namedtuple\n"
  117. "def Test():\n"
  118. " Person = namedtuple('Person', 'name age')\n"
  119. " return Person(age=13, name='Tony')\n",
  120. [ageIdx, nameIdx](const NUdf::TUnboxedValuePod& value) {
  121. UNIT_ASSERT(value);
  122. UNIT_ASSERT(value.IsBoxed());
  123. auto name = value.GetElement(nameIdx);
  124. UNIT_ASSERT_STRINGS_EQUAL(name.AsStringRef(), "Tony");
  125. auto age = value.GetElement(ageIdx);
  126. UNIT_ASSERT_EQUAL(age.Get<ui32>(), 13);
  127. });
  128. }
  129. Y_UNIT_TEST(FromPyNamedTupleNoneOptionalField) {
  130. TPythonTestEngine engine;
  131. ui32 ageIdx = 0, nameIdx = 0;
  132. auto optionalStringType = engine.GetTypeBuilder().Optional()->Item<char*>().Build();
  133. auto personType = engine.GetTypeBuilder().Struct()->
  134. AddField<int>("age", &ageIdx)
  135. .AddField("name", optionalStringType, &nameIdx)
  136. .Build();
  137. engine.ToMiniKQL(personType,
  138. "from collections import namedtuple\n"
  139. "def Test():\n"
  140. " Pers = namedtuple('Person', 'name age')\n"
  141. " return Pers(name=None, age=15)\n",
  142. [ageIdx, nameIdx](const NUdf::TUnboxedValuePod& value) {
  143. UNIT_ASSERT(value);
  144. UNIT_ASSERT(value.IsBoxed());
  145. auto name = value.GetElement(nameIdx);
  146. UNIT_ASSERT(!name);
  147. auto age = value.GetElement(ageIdx);
  148. UNIT_ASSERT_EQUAL(age.Get<ui32>(), 15);
  149. });
  150. }
  151. Y_UNIT_TEST(FromPyEmptyStruct) {
  152. TPythonTestEngine engine;
  153. auto emptyStruct = engine.GetTypeBuilder().Struct()->Build();
  154. engine.ToMiniKQL(emptyStruct,
  155. "class Empty: pass\n"
  156. "\n"
  157. "def Test():\n"
  158. " return Empty()\n",
  159. [](const NUdf::TUnboxedValuePod&) {});
  160. }
  161. Y_UNIT_TEST(ToPyObject) {
  162. TPythonTestEngine engine;
  163. ui32 ageIdx = 0, nameIdx = 0, addressIdx = 0, cityIdx = 0, streetIdx = 0, buildingIdx = 0;
  164. auto addressType = engine.GetTypeBuilder().Struct()->
  165. AddField<NUdf::TUtf8>("city", &cityIdx)
  166. .AddField<NUdf::TUtf8>("street", &streetIdx)
  167. .AddField<ui16>("building", &buildingIdx)
  168. .Build();
  169. auto personType = engine.GetTypeBuilder().Struct()->
  170. AddField<ui16>("age", &ageIdx)
  171. .AddField<NUdf::TUtf8>("name", &nameIdx)
  172. .AddField("address", addressType, &addressIdx)
  173. .Build();
  174. engine.ToPython(personType,
  175. [=](const TType* type, const NUdf::IValueBuilder& vb) {
  176. NUdf::TUnboxedValue* items = nullptr;
  177. auto new_struct = vb.NewArray(static_cast<const TStructType*>(type)->GetMembersCount(), items);
  178. items[ageIdx] = NUdf::TUnboxedValuePod(ui16(97));
  179. items[nameIdx] = vb.NewString("Jamel");
  180. NUdf::TUnboxedValue* items2 = nullptr;
  181. items[addressIdx] = vb.NewArray(static_cast<const TStructType*>(static_cast<const TStructType*>(type)->GetMemberType(addressIdx))->GetMembersCount(), items2);
  182. items2[cityIdx] = vb.NewString("Moscow");;
  183. items2[streetIdx] = vb.NewString("L'va Tolstogo");
  184. items2[buildingIdx] = NUdf::TUnboxedValuePod(ui16(16));
  185. return new_struct;
  186. },
  187. "def Test(value):\n"
  188. " assert isinstance(value, object)\n"
  189. " assert value.name == 'Jamel'\n"
  190. " assert value.age == 97\n"
  191. " assert value.address.city == 'Moscow'\n"
  192. " assert value.address.building == 16\n"
  193. );
  194. }
  195. Y_UNIT_TEST(ToPyObjectKeywordsAsFields) {
  196. TPythonTestEngine engine;
  197. ui32 passIdx = 0, whileIdx = 0, ifIdx = 0, notIdx = 0;
  198. auto structType = engine.GetTypeBuilder().Struct()->
  199. AddField<NUdf::TUtf8>("pass", &passIdx)
  200. .AddField<NUdf::TUtf8>("while", &whileIdx)
  201. .AddField<NUdf::TUtf8>("if", &ifIdx)
  202. .AddField<NUdf::TUtf8>("not", &notIdx)
  203. .Build();
  204. engine.ToPython(structType,
  205. [=](const TType* type, const NUdf::IValueBuilder& vb) {
  206. NUdf::TUnboxedValue* items = nullptr;
  207. auto new_struct = vb.NewArray(static_cast<const TStructType*>(type)->GetMembersCount(), items);
  208. items[ifIdx] = vb.NewString("You");
  209. items[whileIdx] = vb.NewString("Shall");
  210. items[notIdx] = vb.NewString("Not");
  211. items[passIdx] = vb.NewString("Pass");
  212. return new_struct;
  213. },
  214. "def Test(value):\n"
  215. " assert getattr(value, 'if') == 'You'\n"
  216. " assert getattr(value, 'while') == 'Shall'\n"
  217. " assert getattr(value, 'not') == 'Not'\n"
  218. " assert getattr(value, 'pass') == 'Pass'\n"
  219. );
  220. }
  221. #if PY_MAJOR_VERSION >= 3 // TODO: Fix for python 2
  222. Y_UNIT_TEST(ToPyObjectTryModify) {
  223. TPythonTestEngine engine;
  224. ui32 field1Idx = 0, field2Idx = 0;
  225. auto structType = engine.GetTypeBuilder().Struct()->
  226. AddField<NUdf::TUtf8>("field1", &field1Idx)
  227. .AddField<NUdf::TUtf8>("field2", &field2Idx)
  228. .Build();
  229. engine.ToPython(structType,
  230. [=](const TType* type, const NUdf::IValueBuilder& vb) {
  231. NUdf::TUnboxedValue* items = nullptr;
  232. auto new_struct = vb.NewArray(static_cast<const TStructType*>(type)->GetMembersCount(), items);
  233. items[field1Idx] = NUdf::TUnboxedValuePod::Zero();
  234. items[field2Idx] = NUdf::TUnboxedValuePod::Embedded("empty");
  235. return new_struct;
  236. },
  237. "def Test(value):\n"
  238. " try:\n"
  239. " setattr(value, 'field1', 17)\n"
  240. " except AttributeError:\n"
  241. " pass\n"
  242. " else:\n"
  243. " assert False\n"
  244. " try:\n"
  245. " value.field2 = 18\n"
  246. " except AttributeError:\n"
  247. " pass\n"
  248. " else:\n"
  249. " assert False\n"
  250. );
  251. }
  252. #endif
  253. Y_UNIT_TEST(ToPyObjectEmptyStruct) {
  254. TPythonTestEngine engine;
  255. auto personType = engine.GetTypeBuilder().Struct()->Build();
  256. engine.ToPython(personType,
  257. [](const TType*, const NUdf::IValueBuilder& vb) {
  258. return vb.NewEmptyList();
  259. },
  260. "def Test(value):\n"
  261. " assert isinstance(value, object)\n"
  262. #if PY_MAJOR_VERSION >= 3
  263. " assert len(value) == 0\n"
  264. #endif
  265. );
  266. }
  267. }