PythonUtility.cpp 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "AzCore/Script/ScriptContextAttributes.h"
  9. #include <EditorPythonBindings/PythonUtility.h>
  10. #include <Source/PythonMarshalComponent.h>
  11. #include <Source/PythonProxyObject.h>
  12. #include <Source/PythonTypeCasters.h>
  13. #include <AzCore/RTTI/BehaviorContext.h>
  14. #include <AzCore/RTTI/TypeInfo.h>
  15. #include <AzCore/Serialization/SerializeContext.h>
  16. #include <AzCore/Serialization/Utils.h>
  17. #include <AzCore/std/sort.h>
  18. #include <AzFramework/StringFunc/StringFunc.h>
  19. #include <EditorPythonBindings/CustomTypeBindingBus.h>
  20. #include <pybind11/embed.h>
  21. namespace EditorPythonBindings
  22. {
  23. namespace Module
  24. {
  25. pybind11::module DeterminePackageModule(PackageMapType& modulePackageMap, AZStd::string_view moduleName, pybind11::module parentModule, pybind11::module fallbackModule, [[maybe_unused]] bool alertUsingFallback)
  26. {
  27. if (moduleName.empty() || !moduleName[0])
  28. {
  29. AZ_Warning("python", !alertUsingFallback, "Could not determine missing or empty module; using fallback module");
  30. return fallbackModule;
  31. }
  32. else if (parentModule.is_none())
  33. {
  34. AZ_Warning("python", !alertUsingFallback, "Could not determine using None parent module; using fallback module");
  35. return fallbackModule;
  36. }
  37. AZStd::string parentModuleName(PyModule_GetName(parentModule.ptr()));
  38. modulePackageMap[parentModuleName] = parentModule;
  39. pybind11::module currentModule = parentModule;
  40. AZStd::string fullModuleName(parentModuleName);
  41. fullModuleName.append(".");
  42. fullModuleName.append(moduleName);
  43. AZStd::vector<AZStd::string> moduleParts;
  44. AzFramework::StringFunc::Tokenize(fullModuleName.c_str(), moduleParts, ".", false, false);
  45. for (int modulePartsIndex = 0; modulePartsIndex < moduleParts.size(); ++modulePartsIndex)
  46. {
  47. AZStd::string currentModulePath;
  48. AzFramework::StringFunc::Join(currentModulePath, moduleParts.begin(), moduleParts.begin() + modulePartsIndex + 1, ".");
  49. auto itPackageEntry = modulePackageMap.find(currentModulePath.c_str());
  50. if (itPackageEntry != modulePackageMap.end())
  51. {
  52. currentModule = itPackageEntry->second;
  53. }
  54. else
  55. {
  56. PyObject* newModule = PyImport_AddModule(currentModulePath.c_str());
  57. if (!newModule)
  58. {
  59. AZ_Warning("python", false, "Could not add module named %s; using fallback module", currentModulePath.c_str());
  60. return fallbackModule;
  61. }
  62. else
  63. {
  64. auto newSubModule = pybind11::reinterpret_borrow<pybind11::module>(newModule);
  65. modulePackageMap[currentModulePath] = newSubModule;
  66. const char* subModuleName = moduleParts[modulePartsIndex].c_str();
  67. currentModule.attr(subModuleName) = newSubModule;
  68. currentModule = newSubModule;
  69. }
  70. }
  71. }
  72. return currentModule;
  73. }
  74. }
  75. namespace Internal
  76. {
  77. void LogSerializeTypeInfo(const AZ::TypeId& typeId)
  78. {
  79. AZStd::string info = AZStd::string::format("Serialize class info for typeId %s (", typeId.ToString<AZStd::string>().c_str());
  80. AZ::SerializeContext* serializeContext{ nullptr };
  81. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  82. if (serializeContext)
  83. {
  84. auto&& classInfo = serializeContext->FindClassData(typeId);
  85. if (classInfo)
  86. {
  87. info = AZStd::string::format("name:%s version:%d isContainer:%s",
  88. classInfo->m_name, classInfo->m_version, classInfo->m_container ? "true" : "false");
  89. }
  90. auto&& genericClassInfo = serializeContext->FindGenericClassInfo(typeId);
  91. if (genericClassInfo)
  92. {
  93. info += " generic:true";
  94. info += AZStd::string::format(" specialized typeId: %s",
  95. genericClassInfo->GetSpecializedTypeId().ToString<AZStd::string>().c_str());
  96. info += AZStd::string::format(" generic typeId: %s",
  97. genericClassInfo->GetGenericTypeId().ToString<AZStd::string>().c_str());
  98. size_t numTemplatedArguments = genericClassInfo->GetNumTemplatedArguments();
  99. info += AZStd::string::format(" template arguments %zu", genericClassInfo->GetNumTemplatedArguments());
  100. for (size_t index = 0; index < numTemplatedArguments; ++index)
  101. {
  102. info += AZStd::string::format(" [%zu] template type: %s",
  103. index,
  104. genericClassInfo->GetTemplatedTypeId(index).ToString<AZStd::string>().c_str());
  105. }
  106. }
  107. }
  108. info += ")";
  109. AZ_Warning("python", false, "Serialize generic class info %s", info.c_str());
  110. }
  111. AZStd::optional<AZ::TypeId> IsEnumClass(const AZ::BehaviorParameter& behaviorParameter)
  112. {
  113. if (behaviorParameter.m_azRtti)
  114. {
  115. // If the underlying type of the supplied type is different, then T is an enum
  116. AZ::TypeId underlyingTypeId = AZ::Internal::GetUnderlyingTypeId(*behaviorParameter.m_azRtti);
  117. if (underlyingTypeId != behaviorParameter.m_typeId)
  118. {
  119. return AZStd::make_optional(underlyingTypeId);
  120. }
  121. }
  122. return AZStd::nullopt;
  123. }
  124. template <typename T>
  125. bool ConvertPythonFromEnumClass(const AZ::TypeId& underlyingTypeId, AZ::BehaviorArgument& behaviorValue, AZ::s64& outboundPythonValue)
  126. {
  127. if (underlyingTypeId == AZ::AzTypeInfo<T>::Uuid())
  128. {
  129. outboundPythonValue = aznumeric_cast<AZ::s64>(*behaviorValue.GetAsUnsafe<T>());
  130. return true;
  131. }
  132. return false;
  133. }
  134. AZStd::optional<pybind11::object> ConvertFromEnumClass(AZ::BehaviorArgument& behaviorValue)
  135. {
  136. if (!behaviorValue.m_azRtti)
  137. {
  138. return AZStd::nullopt;
  139. }
  140. AZ::TypeId underlyingTypeId = AZ::Internal::GetUnderlyingTypeId(*behaviorValue.m_azRtti);
  141. if (underlyingTypeId != behaviorValue.m_typeId)
  142. {
  143. AZ::s64 outboundPythonValue = 0;
  144. bool converted =
  145. ConvertPythonFromEnumClass<long>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  146. ConvertPythonFromEnumClass<unsigned long>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  147. ConvertPythonFromEnumClass<AZ::u8>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  148. ConvertPythonFromEnumClass<AZ::u16>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  149. ConvertPythonFromEnumClass<AZ::u32>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  150. ConvertPythonFromEnumClass<AZ::u64>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  151. ConvertPythonFromEnumClass<AZ::s8>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  152. ConvertPythonFromEnumClass<AZ::s16>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  153. ConvertPythonFromEnumClass<AZ::s32>(underlyingTypeId, behaviorValue, outboundPythonValue) ||
  154. ConvertPythonFromEnumClass<AZ::s64>(underlyingTypeId, behaviorValue, outboundPythonValue);
  155. AZ_Error("python", converted, "Enumeration backed by a non-numeric integer type.");
  156. return converted ? AZStd::make_optional(pybind11::cast<AZ::s64>(AZStd::move(outboundPythonValue))) : AZStd::nullopt;
  157. }
  158. return AZStd::nullopt;
  159. }
  160. template <typename T>
  161. bool ConvertBehaviorParameterEnum(pybind11::object obj, const AZ::TypeId& underlyingTypeId, AZ::BehaviorArgument& parameter)
  162. {
  163. if (underlyingTypeId == AZ::AzTypeInfo<T>::Uuid())
  164. {
  165. void* value = parameter.m_tempData.allocate(sizeof(T), AZStd::alignment_of<T>::value, 0);
  166. *reinterpret_cast<T*>(value) = pybind11::cast<T>(obj);
  167. if (parameter.m_traits & AZ::BehaviorParameter::TR_POINTER)
  168. {
  169. *reinterpret_cast<void**>(parameter.m_value) = reinterpret_cast<T*>(&value);
  170. }
  171. else
  172. {
  173. parameter.m_value = value;
  174. }
  175. return true;
  176. }
  177. return false;
  178. }
  179. bool ConvertEnumClassFromPython(
  180. pybind11::object obj, const AZ::BehaviorParameter& behaviorArgument, AZ::BehaviorArgument& parameter)
  181. {
  182. if (behaviorArgument.m_azRtti)
  183. {
  184. // If the underlying type of the supplied type is different, then T is an enum
  185. const AZ::TypeId underlyingTypeId = AZ::Internal::GetUnderlyingTypeId(*behaviorArgument.m_azRtti);
  186. if (underlyingTypeId != behaviorArgument.m_typeId)
  187. {
  188. parameter.m_name = behaviorArgument.m_name;
  189. parameter.m_azRtti = behaviorArgument.m_azRtti;
  190. parameter.m_traits = behaviorArgument.m_traits;
  191. parameter.m_typeId = behaviorArgument.m_typeId;
  192. bool handled =
  193. ConvertBehaviorParameterEnum<long>(obj, underlyingTypeId, parameter) ||
  194. ConvertBehaviorParameterEnum<unsigned long>(obj, underlyingTypeId, parameter) ||
  195. ConvertBehaviorParameterEnum<AZ::u8>(obj, underlyingTypeId, parameter) ||
  196. ConvertBehaviorParameterEnum<AZ::u16>(obj, underlyingTypeId, parameter) ||
  197. ConvertBehaviorParameterEnum<AZ::u32>(obj, underlyingTypeId, parameter) ||
  198. ConvertBehaviorParameterEnum<AZ::u64>(obj, underlyingTypeId, parameter) ||
  199. ConvertBehaviorParameterEnum<AZ::s8>(obj, underlyingTypeId, parameter) ||
  200. ConvertBehaviorParameterEnum<AZ::s16>(obj, underlyingTypeId, parameter) ||
  201. ConvertBehaviorParameterEnum<AZ::s32>(obj, underlyingTypeId, parameter) ||
  202. ConvertBehaviorParameterEnum<AZ::s64>(obj, underlyingTypeId, parameter);
  203. AZ_Error("python", handled, "Enumeration backed by a non-numeric integer type.");
  204. return handled;
  205. }
  206. }
  207. return false;
  208. }
  209. // type checks
  210. bool IsPrimitiveType(const AZ::TypeId& typeId)
  211. {
  212. return (
  213. typeId == AZ::AzTypeInfo<bool>::Uuid() ||
  214. typeId == AZ::AzTypeInfo<char>::Uuid() ||
  215. typeId == AZ::AzTypeInfo<float>::Uuid() ||
  216. typeId == AZ::AzTypeInfo<double>::Uuid() ||
  217. typeId == AZ::AzTypeInfo<long>::Uuid() ||
  218. typeId == AZ::AzTypeInfo<unsigned long>::Uuid() ||
  219. typeId == AZ::AzTypeInfo<AZ::s8>::Uuid() ||
  220. typeId == AZ::AzTypeInfo<AZ::u8>::Uuid() ||
  221. typeId == AZ::AzTypeInfo<AZ::s16>::Uuid() ||
  222. typeId == AZ::AzTypeInfo<AZ::u16>::Uuid() ||
  223. typeId == AZ::AzTypeInfo<AZ::s32>::Uuid() ||
  224. typeId == AZ::AzTypeInfo<AZ::u32>::Uuid() ||
  225. typeId == AZ::AzTypeInfo<AZ::s64>::Uuid() ||
  226. typeId == AZ::AzTypeInfo<AZ::u64>::Uuid());
  227. }
  228. bool IsPointerType(const AZ::u32 traits)
  229. {
  230. return (((traits & AZ::BehaviorParameter::TR_POINTER) == AZ::BehaviorParameter::TR_POINTER) ||
  231. ((traits & AZ::BehaviorParameter::TR_REFERENCE) == AZ::BehaviorParameter::TR_REFERENCE));
  232. }
  233. // allocation patterns
  234. void StoreVariableCustomTypeDeleter(
  235. CustomTypeBindingNotifications::ValueHandle handle,
  236. AZ::TypeId typeId,
  237. Convert::StackVariableAllocator& stackVariableAllocator)
  238. {
  239. auto deallocateValue = [typeId = std::move(typeId), handle]() mutable
  240. {
  241. CustomTypeBindingNotificationBus::Event(
  242. typeId,
  243. &CustomTypeBindingNotificationBus::Events::CleanUpValue,
  244. handle);
  245. };
  246. stackVariableAllocator.StoreVariableDeleter(deallocateValue);
  247. }
  248. bool AllocateBehaviorObjectByClass(const AZ::BehaviorClass* behaviorClass, AZ::BehaviorObject& behaviorObject)
  249. {
  250. if (behaviorClass)
  251. {
  252. if (behaviorClass->m_defaultConstructor)
  253. {
  254. AZ::BehaviorObject newBehaviorObject = behaviorClass->Create();
  255. behaviorObject.m_typeId = newBehaviorObject.m_typeId;
  256. behaviorObject.m_address = newBehaviorObject.m_address;
  257. return true;
  258. }
  259. else
  260. {
  261. AZ_Warning("python", behaviorClass->m_defaultConstructor, "Missing default constructor for AZ::BehaviorClass for typeId:%s", behaviorClass->m_name.c_str());
  262. }
  263. }
  264. return false;
  265. }
  266. bool AllocateBehaviorValueParameter(const AZ::BehaviorMethod* behaviorMethod, AZ::BehaviorArgument& result, Convert::StackVariableAllocator& stackVariableAllocator)
  267. {
  268. if (const AZ::BehaviorParameter* resultType = behaviorMethod->GetResult())
  269. {
  270. result.Set(*resultType);
  271. if (auto underlyingTypeId = Internal::IsEnumClass(result); underlyingTypeId)
  272. {
  273. result.m_typeId = underlyingTypeId.value();
  274. }
  275. if (resultType->m_traits & AZ::BehaviorParameter::TR_POINTER)
  276. {
  277. result.m_value = result.m_tempData.allocate(sizeof(intptr_t), alignof(intptr_t));
  278. return true;
  279. }
  280. if (resultType->m_traits & AZ::BehaviorParameter::TR_REFERENCE)
  281. {
  282. return true;
  283. }
  284. if (IsPrimitiveType(resultType->m_typeId))
  285. {
  286. result.m_value = result.m_tempData.allocate(sizeof(intptr_t), alignof(intptr_t));
  287. return true;
  288. }
  289. AZ::BehaviorContext* behaviorContext(nullptr);
  290. AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
  291. if (!behaviorContext)
  292. {
  293. AZ_Assert(false, "A behavior context is required!");
  294. return false;
  295. }
  296. const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(behaviorContext, resultType->m_typeId);
  297. if (behaviorClass)
  298. {
  299. AZ::BehaviorObject behaviorObject;
  300. if (AllocateBehaviorObjectByClass(behaviorClass, behaviorObject))
  301. {
  302. result.m_value = behaviorObject.m_address;
  303. result.m_typeId = resultType->m_typeId;
  304. return true;
  305. }
  306. }
  307. else
  308. {
  309. CustomTypeBindingNotifications::AllocationHandle allocationHandleResult;
  310. CustomTypeBindingNotificationBus::EventResult(
  311. allocationHandleResult,
  312. result.m_typeId,
  313. &CustomTypeBindingNotificationBus::Events::AllocateDefault);
  314. if (allocationHandleResult)
  315. {
  316. CustomTypeBindingNotifications::ValueHandle handle = allocationHandleResult.value().first;
  317. const AZ::BehaviorObject& behaviorObject = allocationHandleResult.value().second;
  318. StoreVariableCustomTypeDeleter(handle, behaviorObject.m_typeId, stackVariableAllocator);
  319. result.m_value = behaviorObject.m_address;
  320. result.m_typeId = behaviorObject.m_typeId;
  321. return true;
  322. }
  323. // So far no allocation scheme has been found for this typeId, but the SerializeContext might have more information
  324. // so this code tries to pull out more type information about the typeId so that the user can get more human readable
  325. // information than a UUID
  326. LogSerializeTypeInfo(resultType->m_typeId);
  327. AZ_Error("python", behaviorClass, "A behavior class for method %s is missing for type '%s' (%s)!",
  328. behaviorMethod->m_name.c_str(),
  329. resultType->m_name,
  330. resultType->m_typeId.ToString<AZStd::string>().c_str());
  331. }
  332. }
  333. return false;
  334. }
  335. void DeallocateBehaviorValueParameter(AZ::BehaviorArgument& valueParameter)
  336. {
  337. if (IsPointerType(valueParameter.m_traits) || IsPrimitiveType(valueParameter.m_typeId))
  338. {
  339. // no constructor was used
  340. return;
  341. }
  342. AZ::BehaviorContext* behaviorContext(nullptr);
  343. AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
  344. if (!behaviorContext)
  345. {
  346. AZ_Assert(false, "A behavior context is required!");
  347. }
  348. const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(behaviorContext, valueParameter.m_typeId);
  349. if (behaviorClass)
  350. {
  351. AZ::BehaviorObject behaviorObject;
  352. behaviorObject.m_address = valueParameter.m_value;
  353. behaviorObject.m_typeId = valueParameter.m_typeId;
  354. behaviorClass->Destroy(behaviorObject);
  355. }
  356. }
  357. }
  358. namespace Convert
  359. {
  360. // StackVariableAllocator
  361. StackVariableAllocator::~StackVariableAllocator()
  362. {
  363. for (auto& cleanUp : m_cleanUpItems)
  364. {
  365. cleanUp();
  366. }
  367. }
  368. void StackVariableAllocator::StoreVariableDeleter(VariableDeleter&& deleter)
  369. {
  370. m_cleanUpItems.emplace_back(deleter);
  371. }
  372. // from Python to BehaviorArgument
  373. bool PythonProxyObjectToBehaviorValueParameter(const AZ::BehaviorParameter& behaviorArgument, pybind11::object pyObj, AZ::BehaviorArgument& parameter)
  374. {
  375. auto behaviorObject = pybind11::cast<EditorPythonBindings::PythonProxyObject*>(pyObj)->GetBehaviorObject();
  376. if (behaviorObject)
  377. {
  378. const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(behaviorObject.value()->m_typeId);
  379. if (!behaviorClass)
  380. {
  381. AZ_Warning("python", false, "Missing BehaviorClass for typeId %s", behaviorObject.value()->m_typeId.ToString<AZStd::string>().c_str());
  382. return false;
  383. }
  384. if (behaviorClass->m_azRtti)
  385. {
  386. // is exact type or can be down casted?
  387. if (!behaviorClass->m_azRtti->IsTypeOf(behaviorArgument.m_typeId))
  388. {
  389. return false;
  390. }
  391. }
  392. else if (behaviorObject.value()->m_typeId != behaviorArgument.m_typeId)
  393. {
  394. // type mismatch detected
  395. return false;
  396. }
  397. if ((behaviorArgument.m_traits & AZ::BehaviorParameter::TR_POINTER) == AZ::BehaviorParameter::TR_POINTER)
  398. {
  399. parameter.m_value = &behaviorObject.value()->m_address;
  400. }
  401. else
  402. {
  403. parameter.m_value = behaviorObject.value()->m_address;
  404. }
  405. parameter.m_typeId = behaviorClass->m_typeId;
  406. parameter.m_azRtti = behaviorClass->m_azRtti;
  407. parameter.m_traits = behaviorArgument.m_traits;
  408. parameter.m_name = behaviorArgument.m_name;
  409. return true;
  410. }
  411. return false;
  412. }
  413. bool CustomPythonToBehavior(
  414. const AZ::BehaviorParameter& behaviorArgument,
  415. pybind11::object pyObj,
  416. AZ::BehaviorArgument& outBehavior,
  417. StackVariableAllocator& stackVariableAllocator)
  418. {
  419. AZStd::optional<CustomTypeBindingNotifications::ValueHandle> handle;
  420. CustomTypeBindingNotificationBus::EventResult(
  421. handle,
  422. behaviorArgument.m_typeId,
  423. &CustomTypeBindingNotificationBus::Events::PythonToBehavior,
  424. pyObj.ptr(),
  425. static_cast<AZ::BehaviorParameter::Traits>(behaviorArgument.m_traits),
  426. outBehavior);
  427. if (handle)
  428. {
  429. Internal::StoreVariableCustomTypeDeleter(handle.value(), behaviorArgument.m_typeId, stackVariableAllocator);
  430. outBehavior.m_typeId = behaviorArgument.m_typeId;
  431. outBehavior.m_traits = behaviorArgument.m_traits;
  432. outBehavior.m_name = behaviorArgument.m_name;
  433. outBehavior.m_azRtti = behaviorArgument.m_azRtti;
  434. return true;
  435. }
  436. return false;
  437. }
  438. bool PythonToBehaviorValueParameter(const AZ::BehaviorParameter& behaviorArgument, pybind11::object pyObj, AZ::BehaviorArgument& parameter, Convert::StackVariableAllocator& stackVariableAllocator)
  439. {
  440. AZStd::optional<PythonMarshalTypeRequests::BehaviorValueResult> result;
  441. PythonMarshalTypeRequests::BehaviorTraits traits = static_cast<PythonMarshalTypeRequests::BehaviorTraits>(behaviorArgument.m_traits);
  442. PythonMarshalTypeRequestBus::EventResult(result, behaviorArgument.m_typeId, &PythonMarshalTypeRequestBus::Events::PythonToBehaviorValueParameter, traits, pyObj, parameter);
  443. if (result && result.value().first)
  444. {
  445. auto deleter = AZStd::move(result.value().second);
  446. if (deleter)
  447. {
  448. stackVariableAllocator.StoreVariableDeleter(AZStd::move(deleter));
  449. }
  450. parameter.m_typeId = behaviorArgument.m_typeId;
  451. parameter.m_traits = behaviorArgument.m_traits;
  452. parameter.m_name = behaviorArgument.m_name;
  453. parameter.m_azRtti = behaviorArgument.m_azRtti;
  454. return true;
  455. }
  456. else if (auto underlyingTypeId = Internal::IsEnumClass(behaviorArgument); underlyingTypeId)
  457. {
  458. AZ::BehaviorParameter tempArg;
  459. tempArg.m_azRtti = behaviorArgument.m_azRtti;
  460. tempArg.m_traits = behaviorArgument.m_traits;
  461. tempArg.m_name = behaviorArgument.m_name;
  462. tempArg.m_typeId = underlyingTypeId.value();
  463. if (PythonToBehaviorValueParameter(tempArg, pyObj, parameter, stackVariableAllocator))
  464. {
  465. parameter.m_typeId = behaviorArgument.m_typeId;
  466. return true;
  467. }
  468. }
  469. else if (pybind11::isinstance<EditorPythonBindings::PythonProxyObject>(pyObj))
  470. {
  471. return PythonProxyObjectToBehaviorValueParameter(behaviorArgument, pyObj, parameter);
  472. }
  473. else if (CustomPythonToBehavior(behaviorArgument, pyObj, parameter, stackVariableAllocator))
  474. {
  475. return true;
  476. }
  477. return false;
  478. }
  479. // from BehaviorArgument to Python
  480. AZStd::optional<pybind11::object> CustomBehaviorToPython(AZ::BehaviorArgument& behaviorValue, Convert::StackVariableAllocator& stackVariableAllocator)
  481. {
  482. AZStd::optional<CustomTypeBindingNotifications::ValueHandle> handle;
  483. PyObject* outPyObj = nullptr;
  484. CustomTypeBindingNotificationBus::EventResult(
  485. handle,
  486. behaviorValue.m_typeId,
  487. &CustomTypeBindingNotificationBus::Events::BehaviorToPython, behaviorValue, outPyObj);
  488. if (outPyObj != nullptr && handle)
  489. {
  490. Internal::StoreVariableCustomTypeDeleter(handle.value(), behaviorValue.m_typeId, stackVariableAllocator);
  491. return { pybind11::reinterpret_borrow<pybind11::object>(outPyObj) };
  492. }
  493. return AZStd::nullopt;
  494. }
  495. pybind11::object BehaviorValueParameterToPython(AZ::BehaviorArgument& behaviorValue, Convert::StackVariableAllocator& stackVariableAllocator)
  496. {
  497. auto pyValue = Internal::ConvertFromEnumClass(behaviorValue);
  498. if (pyValue.has_value())
  499. {
  500. return pyValue.value();
  501. }
  502. AZStd::optional<PythonMarshalTypeRequests::PythonValueResult> result;
  503. PythonMarshalTypeRequestBus::EventResult(
  504. result, behaviorValue.m_typeId, &PythonMarshalTypeRequestBus::Events::BehaviorValueParameterToPython, behaviorValue);
  505. if (result.has_value())
  506. {
  507. auto deleter = AZStd::move(result.value().second);
  508. if (deleter)
  509. {
  510. stackVariableAllocator.StoreVariableDeleter(AZStd::move(deleter));
  511. }
  512. return result.value().first;
  513. }
  514. else if (auto customResult = CustomBehaviorToPython(behaviorValue, stackVariableAllocator); customResult)
  515. {
  516. return customResult.value();
  517. }
  518. else if (behaviorValue.m_typeId != AZ::Uuid::CreateNull() && behaviorValue.GetValueAddress())
  519. {
  520. return PythonProxyObjectManagement::CreatePythonProxyObject(behaviorValue.m_typeId, behaviorValue.GetValueAddress());
  521. }
  522. AZ_Warning("python", false, "Cannot convert type %s",
  523. behaviorValue.m_name ? behaviorValue.m_name : behaviorValue.m_typeId.ToString<AZStd::string>().c_str());
  524. return pybind11::cast<pybind11::none>(Py_None);
  525. }
  526. AZStd::string GetPythonTypeName(pybind11::object pyObj)
  527. {
  528. if (pybind11::isinstance<PythonProxyObject>(pyObj))
  529. {
  530. return pybind11::cast<PythonProxyObject*>(pyObj)->GetWrappedTypeName();
  531. }
  532. return pybind11::cast<AZStd::string>(pybind11::str(pyObj.get_type()));
  533. }
  534. }
  535. namespace Call
  536. {
  537. constexpr size_t MaxBehaviorMethodArguments = 32;
  538. using BehaviorMethodArgumentArray = AZStd::array<AZ::BehaviorArgument, MaxBehaviorMethodArguments>;
  539. pybind11::object InvokeBehaviorMethodWithResult(AZ::BehaviorMethod* behaviorMethod, pybind11::args pythonInputArgs, AZ::BehaviorObject self, AZ::BehaviorArgument& result)
  540. {
  541. if (behaviorMethod->GetNumArguments() > MaxBehaviorMethodArguments || pythonInputArgs.size() > MaxBehaviorMethodArguments)
  542. {
  543. AZ_Error("python", false, "Too many arguments for class method; set:%zu max:%zu", behaviorMethod->GetMinNumberOfArguments(), MaxBehaviorMethodArguments);
  544. return pybind11::cast<pybind11::none>(Py_None);
  545. }
  546. Convert::StackVariableAllocator stackVariableAllocator;
  547. BehaviorMethodArgumentArray parameters;
  548. int parameterCount = 0;
  549. if (self.IsValid())
  550. {
  551. // record the "this" pointer's metadata like its RTTI so that it can be
  552. // down casted to a parent class type if needed to invoke a parent method
  553. AZ::BehaviorArgument theThisPointer;
  554. if (const AZ::BehaviorParameter* thisInfo = behaviorMethod->GetArgument(0))
  555. {
  556. // avoiding the "Special handling for the generic object holder." since it assumes
  557. // the BehaviorObject.m_value is a pointer; the reference version is already dereferenced
  558. if ((thisInfo->m_traits & AZ::BehaviorParameter::TR_POINTER) == AZ::BehaviorParameter::TR_POINTER)
  559. {
  560. theThisPointer.m_value = &self.m_address;
  561. }
  562. else
  563. {
  564. theThisPointer.m_value = self.m_address;
  565. }
  566. theThisPointer.Set(*thisInfo);
  567. parameters[0].Set(theThisPointer);
  568. ++parameterCount;
  569. }
  570. else
  571. {
  572. AZ_Warning("python", false, "Missing self info index 0 in class method %s", behaviorMethod->m_name.c_str());
  573. return pybind11::cast<pybind11::none>(Py_None);
  574. }
  575. }
  576. // prepare the parameters for the BehaviorMethod
  577. for (auto pythonArg : pythonInputArgs)
  578. {
  579. if (parameterCount < behaviorMethod->GetNumArguments())
  580. {
  581. auto currentPythonArg = pybind11::cast<pybind11::object>(pythonArg);
  582. const AZ::BehaviorParameter* behaviorArgument = behaviorMethod->GetArgument(parameterCount);
  583. if (!behaviorArgument)
  584. {
  585. AZ_Warning("python", false, "Missing argument at index %d in class method %s", parameterCount, behaviorMethod->m_name.c_str());
  586. return pybind11::cast<pybind11::none>(Py_None);
  587. }
  588. if (!Convert::PythonToBehaviorValueParameter(*behaviorArgument, currentPythonArg, parameters[parameterCount], stackVariableAllocator))
  589. {
  590. AZ_Warning("python", false, "BehaviorMethod %s: Parameter at [%d] index expects (%s:%s) for method but got type (%s)",
  591. behaviorMethod->m_name.c_str(),
  592. parameterCount,
  593. behaviorArgument->m_name, behaviorArgument->m_typeId.ToString<AZStd::string>().c_str(),
  594. Convert::GetPythonTypeName(currentPythonArg).c_str());
  595. return pybind11::cast<pybind11::none>(Py_None);
  596. }
  597. ++parameterCount;
  598. }
  599. }
  600. // did the Python script send the right amount of arguments?
  601. const auto totalPythonArgs = pythonInputArgs.size() + (self.IsValid() ? 1 : 0); // +1 for the 'this' coming in from a marshaled Python/BehaviorObject
  602. if (totalPythonArgs < behaviorMethod->GetMinNumberOfArguments())
  603. {
  604. AZ_Warning("python", false, "Method %s requires at least %zu parameters got %zu", behaviorMethod->m_name.c_str(), behaviorMethod->GetMinNumberOfArguments(), totalPythonArgs);
  605. return pybind11::cast<pybind11::none>(Py_None);
  606. }
  607. else if (totalPythonArgs > behaviorMethod->GetNumArguments())
  608. {
  609. AZ_Warning("python", false, "Method %s requires %zu parameters but it got more (%zu) - excess parameters will not be used.", behaviorMethod->m_name.c_str(), behaviorMethod->GetMinNumberOfArguments(), totalPythonArgs);
  610. }
  611. if (behaviorMethod->HasResult())
  612. {
  613. if (Internal::AllocateBehaviorValueParameter(behaviorMethod, result, stackVariableAllocator))
  614. {
  615. if (behaviorMethod->Call(parameters.begin(), static_cast<unsigned int>(totalPythonArgs), &result))
  616. {
  617. result.m_azRtti = behaviorMethod->GetResult()->m_azRtti;
  618. result.m_typeId = behaviorMethod->GetResult()->m_typeId;
  619. result.m_traits = behaviorMethod->GetResult()->m_traits;
  620. return Convert::BehaviorValueParameterToPython(result, stackVariableAllocator);
  621. }
  622. else
  623. {
  624. AZ_Warning("python", false, "Failed to call class method %s", behaviorMethod->m_name.c_str());
  625. }
  626. }
  627. else
  628. {
  629. AZ_Warning("python", false, "Failed to allocate return value for method %s", behaviorMethod->m_name.c_str());
  630. }
  631. }
  632. else if (!behaviorMethod->Call(parameters.begin(), static_cast<unsigned int>(totalPythonArgs)))
  633. {
  634. AZ_Warning("python", false, "Failed to invoke class method %s", behaviorMethod->m_name.c_str());
  635. }
  636. return pybind11::cast<pybind11::none>(Py_None);
  637. }
  638. pybind11::object InvokeBehaviorMethod(AZ::BehaviorMethod* behaviorMethod, pybind11::args pythonInputArgs, AZ::BehaviorObject self)
  639. {
  640. AZ::BehaviorArgument result;
  641. result.m_value = nullptr;
  642. pybind11::object pythonOutput = InvokeBehaviorMethodWithResult(behaviorMethod, pythonInputArgs, self, result);
  643. if (result.m_value)
  644. {
  645. Internal::DeallocateBehaviorValueParameter(result);
  646. }
  647. return pythonOutput;
  648. }
  649. pybind11::object StaticMethod(AZ::BehaviorMethod* behaviorMethod, pybind11::args pythonInputArgs)
  650. {
  651. return InvokeBehaviorMethod(behaviorMethod, pythonInputArgs, {});
  652. }
  653. pybind11::object ClassMethod(AZ::BehaviorMethod* behaviorMethod, AZ::BehaviorObject self, pybind11::args pythonInputArgs)
  654. {
  655. if (behaviorMethod->GetNumArguments() == 0)
  656. {
  657. AZ_Error("python", false, "A member level function should require at least one argument");
  658. }
  659. else if (!self.IsValid())
  660. {
  661. AZ_Error("python", false, "Method %s requires at valid self object to invoke", behaviorMethod->m_name.c_str());
  662. }
  663. else
  664. {
  665. return InvokeBehaviorMethod(behaviorMethod, pythonInputArgs, self);
  666. }
  667. return pybind11::cast<pybind11::none>(Py_None);
  668. }
  669. } // namespace Call
  670. namespace Text
  671. {
  672. namespace Internal
  673. {
  674. AZStd::string ReadStringAttribute(const AZ::AttributeArray& attributes, const AZ::Crc32& attribute)
  675. {
  676. AZStd::string attributeValue = "";
  677. if (auto attributeItem = azrtti_cast<AZ::AttributeData<AZStd::string>*>(AZ::FindAttribute(attribute, attributes)))
  678. {
  679. attributeValue = attributeItem->Get(nullptr);
  680. return attributeValue;
  681. }
  682. if (auto attributeItem = azrtti_cast<AZ::AttributeData<const char*>*>(AZ::FindAttribute(attribute, attributes)))
  683. {
  684. attributeValue = attributeItem->Get(nullptr);
  685. return attributeValue;
  686. }
  687. return {};
  688. }
  689. AZStd::string TypeNameFallback(const AZ::TypeId& typeId)
  690. {
  691. // fall back to class data m_name
  692. AZ::SerializeContext* serializeContext = nullptr;
  693. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  694. if (serializeContext)
  695. {
  696. const auto classData = serializeContext->FindClassData(typeId);
  697. if (classData)
  698. {
  699. return classData->m_name;
  700. }
  701. }
  702. return "";
  703. }
  704. void Indent(const int level, AZStd::string& buffer)
  705. {
  706. buffer.append(level * 4, ' ');
  707. }
  708. void AddCommentBlock(int level, const AZStd::string& comment, AZStd::string& buffer)
  709. {
  710. Indent(level, buffer);
  711. AzFramework::StringFunc::Append(buffer, "\"\"\"\n");
  712. Indent(level, buffer);
  713. AzFramework::StringFunc::Append(buffer, comment.c_str());
  714. Indent(level, buffer);
  715. AzFramework::StringFunc::Append(buffer, "\"\"\"\n");
  716. }
  717. } // namespace Internal
  718. AZStd::string PythonBehaviorDescription::FetchListType(const AZ::TypeId& typeId)
  719. {
  720. AZStd::string type = "list";
  721. AZStd::vector<AZ::Uuid> typeList = AZ::Utils::GetContainedTypes(typeId);
  722. if (!typeList.empty())
  723. {
  724. // trait info not available, so defaulting to TR_NONE
  725. AZStd::string_view itemType = FetchPythonTypeAndTraits(typeList[0], AZ::BehaviorParameter::TR_NONE);
  726. if (!itemType.empty())
  727. {
  728. type = AZStd::string::format("List[" AZ_STRING_FORMAT "]", AZ_STRING_ARG(itemType));
  729. }
  730. }
  731. return type;
  732. }
  733. AZStd::string PythonBehaviorDescription::FetchMapType(const AZ::TypeId& typeId)
  734. {
  735. AZStd::string type = "dict";
  736. AZStd::vector<AZ::Uuid> typeList = AZ::Utils::GetContainedTypes(typeId);
  737. if (!typeList.empty())
  738. {
  739. // trait info not available, so defaulting to TR_NONE
  740. AZStd::string_view kType = FetchPythonTypeAndTraits(typeList[0], AZ::BehaviorParameter::TR_NONE);
  741. AZStd::string_view vType = FetchPythonTypeAndTraits(typeList[1], AZ::BehaviorParameter::TR_NONE);
  742. if (!kType.empty() && !vType.empty())
  743. {
  744. type = AZStd::string::format(
  745. "Dict[" AZ_STRING_FORMAT ", " AZ_STRING_FORMAT "]", AZ_STRING_ARG(kType), AZ_STRING_ARG(vType));
  746. }
  747. }
  748. return type;
  749. }
  750. AZStd::string_view PythonBehaviorDescription::FetchPythonTypeAndTraits(const AZ::TypeId& typeId, AZ::u32 traits)
  751. {
  752. if (m_typeCache.find(typeId) == m_typeCache.end())
  753. {
  754. AZStd::string type;
  755. if (AZ::AzTypeInfo<AZStd::string_view>::Uuid() == typeId || AZ::AzTypeInfo<AZStd::string>::Uuid() == typeId)
  756. {
  757. type = "str";
  758. }
  759. else if (
  760. AZ::AzTypeInfo<char>::Uuid() == typeId && traits & AZ::BehaviorParameter::TR_POINTER &&
  761. traits & AZ::BehaviorParameter::TR_CONST)
  762. {
  763. type = "str";
  764. }
  765. else if (AZ::AzTypeInfo<float>::Uuid() == typeId || AZ::AzTypeInfo<double>::Uuid() == typeId)
  766. {
  767. type = "float";
  768. }
  769. else if (AZ::AzTypeInfo<bool>::Uuid() == typeId)
  770. {
  771. type = "bool";
  772. }
  773. else if (
  774. AZ::AzTypeInfo<long>::Uuid() == typeId || AZ::AzTypeInfo<unsigned long>::Uuid() == typeId ||
  775. AZ::AzTypeInfo<AZ::s8>::Uuid() == typeId || AZ::AzTypeInfo<AZ::u8>::Uuid() == typeId ||
  776. AZ::AzTypeInfo<AZ::s16>::Uuid() == typeId || AZ::AzTypeInfo<AZ::u16>::Uuid() == typeId ||
  777. AZ::AzTypeInfo<AZ::s32>::Uuid() == typeId || AZ::AzTypeInfo<AZ::u32>::Uuid() == typeId ||
  778. AZ::AzTypeInfo<AZ::s64>::Uuid() == typeId || AZ::AzTypeInfo<AZ::u64>::Uuid() == typeId)
  779. {
  780. type = "int";
  781. }
  782. else if (AZ::AzTypeInfo<AZStd::vector<AZ::u8>>::Uuid() == typeId)
  783. {
  784. type = "bytes";
  785. }
  786. else if (AZ::AzTypeInfo<AZStd::any>::Uuid() == typeId)
  787. {
  788. type = "object";
  789. }
  790. else if (AZ::AzTypeInfo<void>::Uuid() == typeId)
  791. {
  792. type = "None";
  793. }
  794. else if (AZ::Utils::IsVectorContainerType(typeId))
  795. {
  796. type = FetchListType(typeId);
  797. }
  798. else if (AZ::Utils::IsMapContainerType(typeId))
  799. {
  800. type = FetchMapType(typeId);
  801. }
  802. else if (AZ::Utils::IsOutcomeType(typeId))
  803. {
  804. type = FetchOutcomeType(typeId);
  805. }
  806. else
  807. {
  808. type = Internal::TypeNameFallback(typeId);
  809. }
  810. m_typeCache[typeId] = type;
  811. }
  812. return m_typeCache[typeId];
  813. }
  814. AZStd::string PythonBehaviorDescription::FetchPythonTypeName(const AZ::BehaviorParameter& param)
  815. {
  816. AZStd::string pythonType = FetchPythonTypeAndTraits(param.m_typeId, param.m_traits);
  817. if (pythonType.empty())
  818. {
  819. if (AZ::StringFunc::Equal(param.m_name, "void"))
  820. {
  821. return "None";
  822. }
  823. return param.m_name;
  824. }
  825. return pythonType;
  826. }
  827. AZStd::string PythonBehaviorDescription::FetchOutcomeType(const AZ::TypeId& typeId)
  828. {
  829. AZStd::string type = "Outcome";
  830. AZStd::pair<AZ::Uuid, AZ::Uuid> outcomeTypes = AZ::Utils::GetOutcomeTypes(typeId);
  831. // trait info not available, so defaulting to TR_NONE
  832. AZStd::string_view valueT = FetchPythonTypeAndTraits(outcomeTypes.first, AZ::BehaviorParameter::TR_NONE);
  833. AZStd::string_view errorT = FetchPythonTypeAndTraits(outcomeTypes.second, AZ::BehaviorParameter::TR_NONE);
  834. if (!valueT.empty() && !errorT.empty())
  835. {
  836. type = AZStd::string::format(
  837. "Outcome[" AZ_STRING_FORMAT ", " AZ_STRING_FORMAT "]", AZ_STRING_ARG(valueT), AZ_STRING_ARG(errorT));
  838. }
  839. return type;
  840. }
  841. //! Creates a string containing bus events and documentation.
  842. AZStd::string PythonBehaviorDescription::BusDefinition(const AZStd::string_view& busName, const AZ::BehaviorEBus* behaviorEBus)
  843. {
  844. AZStd::string buffer;
  845. if (!behaviorEBus || behaviorEBus->m_events.empty())
  846. {
  847. return buffer;
  848. }
  849. const auto& eventSenderEntry = behaviorEBus->m_events.begin();
  850. const AZ::BehaviorEBusEventSender& sender = eventSenderEntry->second;
  851. AzFramework::StringFunc::Append(buffer, "def ");
  852. AzFramework::StringFunc::Append(buffer, busName.data());
  853. bool isBroadcast = false;
  854. if (sender.m_event)
  855. {
  856. AZStd::string addressType = FetchPythonTypeName(behaviorEBus->m_idParam);
  857. if (addressType.empty())
  858. {
  859. AzFramework::StringFunc::Append(buffer, "(busCallType: int, busEventName: str, address: Any, args: Tuple[Any])");
  860. }
  861. else
  862. {
  863. AzFramework::StringFunc::Append(buffer, "(busCallType: int, busEventName: str, address: ");
  864. AzFramework::StringFunc::Append(buffer, AZStd::string::format(AZ_STRING_FORMAT, AZ_STRING_ARG(addressType)).c_str());
  865. AzFramework::StringFunc::Append(buffer, ", args: Tuple[Any])");
  866. }
  867. }
  868. else
  869. {
  870. AzFramework::StringFunc::Append(buffer, "(busCallType: int, busEventName: str, args: Tuple[Any])");
  871. isBroadcast = true;
  872. }
  873. AzFramework::StringFunc::Append(buffer, " -> Any:\n");
  874. auto eventInfoBuilder =
  875. [this](const AZ::BehaviorMethod* behaviorMethod, AZStd::string& inOutStrBuffer, [[maybe_unused]] TypeMap& typeCache)
  876. {
  877. AzFramework::StringFunc::Append(inOutStrBuffer, "(");
  878. size_t numArguments = behaviorMethod->GetNumArguments();
  879. const AZ::BehaviorParameter* busIdArg = behaviorMethod->GetBusIdArgument();
  880. for (size_t i = 0; i < numArguments; ++i)
  881. {
  882. const AZ::BehaviorParameter* argParam = behaviorMethod->GetArgument(i);
  883. if (argParam == busIdArg)
  884. {
  885. // address argument is part of the bus call, skip from event argument list
  886. continue;
  887. }
  888. AZStd::string_view argType = FetchPythonTypeAndTraits(argParam->m_typeId, argParam->m_traits);
  889. AzFramework::StringFunc::Append(inOutStrBuffer, argType.data());
  890. if (i < (numArguments - 1))
  891. {
  892. AzFramework::StringFunc::Append(inOutStrBuffer, ", ");
  893. }
  894. }
  895. const AZ::BehaviorParameter* resultParam = behaviorMethod->GetResult();
  896. AZStd::string returnType = FetchPythonTypeName(*resultParam);
  897. AZStd::string returnTypeStr = AZStd::string::format(") -> " AZ_STRING_FORMAT " \n", AZ_STRING_ARG(returnType));
  898. AzFramework::StringFunc::Append(inOutStrBuffer, returnTypeStr.c_str());
  899. };
  900. // record the event names the behavior can send, their parameters and return type
  901. AZStd::string comment = behaviorEBus->m_toolTip;
  902. if (comment.empty())
  903. {
  904. comment = Internal::ReadStringAttribute(behaviorEBus->m_attributes, AZ::Script::Attributes::ToolTip);
  905. }
  906. if (!behaviorEBus->m_events.empty())
  907. {
  908. AzFramework::StringFunc::Append(
  909. comment, "The following bus Call types, Event names and Argument types are supported by this bus:\n");
  910. AZStd::vector<AZStd::string> events;
  911. for (const auto& eventSenderEntry2 : behaviorEBus->m_events)
  912. {
  913. const AZStd::string& eventName = eventSenderEntry2.first;
  914. AZStd::string eventNameStr = AZStd::string::format("'%s', ", eventName.c_str());
  915. // prefer m_event info over m_broadcast
  916. if (!isBroadcast && eventSenderEntry2.second.m_event != nullptr)
  917. {
  918. AZStd::string eventInfo;
  919. AzFramework::StringFunc::Append(eventInfo, "bus.Event, ");
  920. AzFramework::StringFunc::Append(eventInfo, eventNameStr.c_str());
  921. eventInfoBuilder(eventSenderEntry2.second.m_event, eventInfo, m_typeCache);
  922. events.push_back(eventInfo);
  923. }
  924. else if (isBroadcast && eventSenderEntry2.second.m_broadcast != nullptr)
  925. {
  926. AZStd::string eventInfo;
  927. AzFramework::StringFunc::Append(eventInfo, "bus.Broadcast, ");
  928. AzFramework::StringFunc::Append(eventInfo, eventNameStr.c_str());
  929. eventInfoBuilder(eventSenderEntry2.second.m_broadcast, eventInfo, m_typeCache);
  930. events.push_back(eventInfo);
  931. }
  932. else
  933. {
  934. AZ_Warning("python", false, "Event %s is expected to have valid event information.", eventName.c_str());
  935. }
  936. }
  937. AZStd::sort(events.begin(), events.end());
  938. for (auto& eventInfo : events)
  939. {
  940. Internal::Indent(1, comment);
  941. AzFramework::StringFunc::Append(comment, eventInfo.c_str());
  942. }
  943. }
  944. Internal::AddCommentBlock(1, comment, buffer);
  945. Internal::Indent(1, buffer);
  946. AzFramework::StringFunc::Append(buffer, "pass\n\n");
  947. // can the EBus create & destroy a handler?
  948. if (behaviorEBus->m_createHandler && behaviorEBus->m_destroyHandler)
  949. {
  950. AzFramework::StringFunc::Append(buffer, "def ");
  951. AzFramework::StringFunc::Append(buffer, busName.data());
  952. AzFramework::StringFunc::Append(buffer, "Handler() -> None:\n");
  953. Internal::Indent(1, buffer);
  954. AzFramework::StringFunc::Append(buffer, "pass\n\n");
  955. }
  956. return buffer;
  957. }
  958. //! Creates a string with class or global method definition and documentation.
  959. AZStd::string PythonBehaviorDescription::MethodDefinition(
  960. const AZStd::string_view& methodName,
  961. const AZ::BehaviorMethod& behaviorMethod,
  962. const AZ::BehaviorClass* behaviorClass,
  963. bool defineTooltip,
  964. bool defineDebugDescription)
  965. {
  966. AZStd::string buffer;
  967. AZStd::vector<AZStd::string> pythonArgs;
  968. const bool isMemberLike =
  969. behaviorClass ? PythonProxyObjectManagement::IsMemberLike(behaviorMethod, behaviorClass->m_typeId) : false;
  970. int indentLevel = 0;
  971. if (isMemberLike)
  972. {
  973. indentLevel = 1;
  974. Internal::Indent(indentLevel, buffer);
  975. pythonArgs.emplace_back("self");
  976. }
  977. AzFramework::StringFunc::Append(buffer, "def ");
  978. if (isMemberLike || !behaviorClass)
  979. {
  980. AzFramework::StringFunc::Append(buffer, methodName.data());
  981. }
  982. else
  983. {
  984. AzFramework::StringFunc::Append(buffer, behaviorClass->m_name.c_str());
  985. AzFramework::StringFunc::Append(buffer, "_");
  986. AzFramework::StringFunc::Append(buffer, methodName.data());
  987. }
  988. AzFramework::StringFunc::Append(buffer, "(");
  989. AZStd::string bufferArg;
  990. for (size_t argIndex = 0; argIndex < behaviorMethod.GetNumArguments(); ++argIndex)
  991. {
  992. const AZStd::string* name = behaviorMethod.GetArgumentName(argIndex);
  993. if (!name || name->empty())
  994. {
  995. bufferArg = AZStd::string::format(" arg%zu", argIndex);
  996. }
  997. else
  998. {
  999. bufferArg = *name;
  1000. }
  1001. AZStd::string type = FetchPythonTypeName(*behaviorMethod.GetArgument(argIndex));
  1002. if (!type.empty())
  1003. {
  1004. AzFramework::StringFunc::Append(bufferArg, ": ");
  1005. AzFramework::StringFunc::Append(bufferArg, type.data());
  1006. }
  1007. pythonArgs.push_back(bufferArg);
  1008. bufferArg.clear();
  1009. }
  1010. AZStd::string argsList;
  1011. AzFramework::StringFunc::Join(buffer, pythonArgs.begin(), pythonArgs.end(), ",");
  1012. AzFramework::StringFunc::Append(buffer, ") -> None:\n");
  1013. AZStd::string methodTooltipAndDebugDescription = "";
  1014. if (defineDebugDescription && behaviorMethod.m_debugDescription != nullptr)
  1015. {
  1016. AZStd::string debugDescription(behaviorMethod.m_debugDescription);
  1017. if (!debugDescription.empty())
  1018. {
  1019. methodTooltipAndDebugDescription += debugDescription;
  1020. methodTooltipAndDebugDescription += "\n";
  1021. }
  1022. }
  1023. if (defineTooltip)
  1024. {
  1025. AZStd::string methodTooltip = Internal::ReadStringAttribute(behaviorMethod.m_attributes, AZ::Script::Attributes::ToolTip);
  1026. if (!methodTooltip.empty())
  1027. {
  1028. methodTooltipAndDebugDescription += methodTooltip;
  1029. methodTooltipAndDebugDescription += "\n";
  1030. }
  1031. }
  1032. if (!methodTooltipAndDebugDescription.empty())
  1033. {
  1034. Internal::AddCommentBlock(indentLevel + 1, methodTooltipAndDebugDescription, buffer);
  1035. }
  1036. Internal::Indent(indentLevel + 1, buffer);
  1037. AzFramework::StringFunc::Append(buffer, "pass\n\n");
  1038. return buffer;
  1039. }
  1040. AZStd::string PythonBehaviorDescription::ClassDefinition(
  1041. const AZ::BehaviorClass* behaviorClass,
  1042. const AZStd::string_view& className,
  1043. bool defineProperties,
  1044. bool defineMethods,
  1045. bool defineTooltip)
  1046. {
  1047. AZStd::string buffer;
  1048. AzFramework::StringFunc::Append(buffer, "class ");
  1049. AzFramework::StringFunc::Append(buffer, className.data());
  1050. AzFramework::StringFunc::Append(buffer, ":\n");
  1051. if (behaviorClass->m_methods.empty() && behaviorClass->m_properties.empty())
  1052. {
  1053. AZStd::string body;
  1054. if (defineProperties && defineMethods)
  1055. {
  1056. body = " # behavior class type with no methods or properties \n";
  1057. }
  1058. else if (defineProperties)
  1059. {
  1060. body = " # behavior class type with no properties \n";
  1061. }
  1062. else if (defineMethods)
  1063. {
  1064. body = " # behavior class type with no methods \n";
  1065. }
  1066. else
  1067. {
  1068. body = "";
  1069. }
  1070. if (defineTooltip)
  1071. {
  1072. AZStd::string classTooltip =
  1073. Internal::ReadStringAttribute(behaviorClass->m_attributes, AZ::Script::Attributes::ToolTip);
  1074. if (!classTooltip.empty())
  1075. {
  1076. Internal::AddCommentBlock(1, classTooltip, body);
  1077. }
  1078. }
  1079. Internal::Indent(1, body);
  1080. AzFramework::StringFunc::Append(body, "pass\n\n");
  1081. AzFramework::StringFunc::Append(buffer, body.c_str());
  1082. }
  1083. else
  1084. {
  1085. if (defineProperties)
  1086. {
  1087. for (const auto& propertyEntry : behaviorClass->m_properties)
  1088. {
  1089. AZ::BehaviorProperty* property = propertyEntry.second;
  1090. AZStd::string propertyName{ propertyEntry.first };
  1091. Scope::FetchScriptName(property->m_attributes, propertyName);
  1092. AZStd::string propertyDef = PropertyDefinition(propertyName, 1, *property, behaviorClass);
  1093. AzFramework::StringFunc::Append(buffer, propertyDef.c_str());
  1094. }
  1095. }
  1096. if (defineMethods)
  1097. {
  1098. for (const auto& methodEntry : behaviorClass->m_methods)
  1099. {
  1100. AZ::BehaviorMethod* method = methodEntry.second;
  1101. if (method && PythonProxyObjectManagement::IsMemberLike(*method, behaviorClass->m_typeId))
  1102. {
  1103. AZStd::string baseMethodName{ methodEntry.first };
  1104. Scope::FetchScriptName(method->m_attributes, baseMethodName);
  1105. AZStd::string methodDef = MethodDefinition(baseMethodName, *method, behaviorClass, defineTooltip);
  1106. AzFramework::StringFunc::Append(buffer, methodDef.c_str());
  1107. }
  1108. }
  1109. }
  1110. }
  1111. return buffer;
  1112. }
  1113. AZStd::string PythonBehaviorDescription::PropertyDefinition(
  1114. AZStd::string_view propertyName, int level, const AZ::BehaviorProperty& property, [[maybe_unused]] const AZ::BehaviorClass* behaviorClass)
  1115. {
  1116. AZStd::string buffer;
  1117. Internal::Indent(level, buffer);
  1118. AzFramework::StringFunc::Append(buffer, "@property\n");
  1119. Internal::Indent(level, buffer);
  1120. AzFramework::StringFunc::Append(buffer, "def ");
  1121. AzFramework::StringFunc::Append(buffer, propertyName.data());
  1122. AzFramework::StringFunc::Append(buffer, "(self) -> ");
  1123. AZStd::string_view type = FetchPythonTypeAndTraits(property.GetTypeId(), AZ::BehaviorParameter::TR_NONE);
  1124. if (type.empty())
  1125. {
  1126. AzFramework::StringFunc::Append(buffer, "Any");
  1127. }
  1128. else
  1129. {
  1130. AzFramework::StringFunc::Append(buffer, type.data());
  1131. }
  1132. AzFramework::StringFunc::Append(buffer, ":\n");
  1133. Internal::Indent(level + 1, buffer);
  1134. AzFramework::StringFunc::Append(buffer, "pass\n\n");
  1135. return buffer;
  1136. }
  1137. AZStd::string PythonBehaviorDescription::GlobalPropertyDefinition(
  1138. [[maybe_unused]] const AZStd::string_view& moduleName,
  1139. const AZStd::string_view& propertyName,
  1140. const AZ::BehaviorProperty& behaviorProperty,
  1141. bool needsHeader)
  1142. {
  1143. AZStd::string buffer;
  1144. // add header
  1145. if (needsHeader)
  1146. {
  1147. AzFramework::StringFunc::Append(buffer, "class property():\n");
  1148. }
  1149. Internal::Indent(1, buffer);
  1150. AzFramework::StringFunc::Append(buffer, propertyName.data());
  1151. AzFramework::StringFunc::Append(buffer, ": ClassVar[");
  1152. const AZ::BehaviorParameter* resultParam = behaviorProperty.m_getter->GetResult();
  1153. AZStd::string_view type = FetchPythonTypeAndTraits(resultParam->m_typeId, resultParam->m_traits);
  1154. if (type.empty())
  1155. {
  1156. AzFramework::StringFunc::Append(buffer, "Any");
  1157. }
  1158. else
  1159. {
  1160. AzFramework::StringFunc::Append(buffer, type.data());
  1161. }
  1162. AzFramework::StringFunc::Append(buffer, "] = None");
  1163. if (behaviorProperty.m_getter && !behaviorProperty.m_setter)
  1164. {
  1165. AzFramework::StringFunc::Append(buffer, " # read only");
  1166. }
  1167. AzFramework::StringFunc::Append(buffer, "\n");
  1168. return buffer;
  1169. }
  1170. } // namespace Text
  1171. } // namespace EditorPythonBindings