PythonLogSymbolsComponent.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778
  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 <PythonLogSymbolsComponent.h>
  9. #include <Source/PythonCommon.h>
  10. #include <Source/PythonUtility.h>
  11. #include <Source/PythonTypeCasters.h>
  12. #include <Source/PythonProxyBus.h>
  13. #include <Source/PythonProxyObject.h>
  14. #include <pybind11/embed.h>
  15. #include <AzCore/PlatformDef.h>
  16. #include <AzCore/RTTI/AttributeReader.h>
  17. #include <AzCore/RTTI/BehaviorContext.h>
  18. #include <AzCore/IO/SystemFile.h>
  19. #include <AzCore/IO/FileIO.h>
  20. #include <AzCore/std/sort.h>
  21. #include <AzCore/std/smart_ptr/make_shared.h>
  22. #include <AzCore/Serialization/Utils.h>
  23. #include <AzFramework/CommandLine/CommandRegistrationBus.h>
  24. #include <AzFramework/StringFunc/StringFunc.h>
  25. namespace EditorPythonBindings
  26. {
  27. namespace Internal
  28. {
  29. struct FileHandle final
  30. {
  31. explicit FileHandle(AZ::IO::HandleType handle)
  32. : m_handle(handle)
  33. {}
  34. ~FileHandle()
  35. {
  36. Close();
  37. }
  38. void Close()
  39. {
  40. if (IsValid())
  41. {
  42. AZ::IO::FileIOBase::GetInstance()->Close(m_handle);
  43. }
  44. m_handle = AZ::IO::InvalidHandle;
  45. }
  46. bool IsValid() const
  47. {
  48. return m_handle != AZ::IO::InvalidHandle;
  49. }
  50. operator AZ::IO::HandleType() const { return m_handle; }
  51. AZ::IO::HandleType m_handle;
  52. };
  53. void Indent(int level, AZStd::string& buffer)
  54. {
  55. buffer.append(level * 4, ' ');
  56. }
  57. void AddCommentBlock(int level, const AZStd::string& comment, AZStd::string& buffer)
  58. {
  59. Indent(level, buffer);
  60. AzFramework::StringFunc::Append(buffer, "\"\"\"\n");
  61. Indent(level, buffer);
  62. AzFramework::StringFunc::Append(buffer, comment.c_str());
  63. Indent(level, buffer);
  64. AzFramework::StringFunc::Append(buffer, "\"\"\"\n");
  65. }
  66. }
  67. void PythonLogSymbolsComponent::Reflect(AZ::ReflectContext* context)
  68. {
  69. if (auto&& serialize = azrtti_cast<AZ::SerializeContext*>(context))
  70. {
  71. serialize->Class<PythonLogSymbolsComponent, AZ::Component>()
  72. ->Version(0);
  73. }
  74. }
  75. void PythonLogSymbolsComponent::Activate()
  76. {
  77. PythonSymbolEventBus::Handler::BusConnect();
  78. EditorPythonBindingsNotificationBus::Handler::BusConnect();
  79. AZ::Interface<AzToolsFramework::EditorPythonConsoleInterface>::Register(this);
  80. if (PythonSymbolEventBus::GetTotalNumOfEventHandlers() > 1)
  81. {
  82. OnPostInitialize();
  83. }
  84. }
  85. void PythonLogSymbolsComponent::Deactivate()
  86. {
  87. AZ::Interface<AzToolsFramework::EditorPythonConsoleInterface>::Unregister(this);
  88. PythonSymbolEventBus::Handler::BusDisconnect();
  89. EditorPythonBindingsNotificationBus::Handler::BusDisconnect();
  90. }
  91. void PythonLogSymbolsComponent::OnPostInitialize()
  92. {
  93. m_basePath.clear();
  94. if (AZ::IO::FileIOBase::GetInstance()->GetAlias("@user@"))
  95. {
  96. // clear out the previous symbols path
  97. char pythonSymbolsPath[AZ_MAX_PATH_LEN];
  98. AZ::IO::FileIOBase::GetInstance()->ResolvePath("@user@/python_symbols", pythonSymbolsPath, AZ_MAX_PATH_LEN);
  99. AZ::IO::FileIOBase::GetInstance()->CreatePath(pythonSymbolsPath);
  100. m_basePath = pythonSymbolsPath;
  101. }
  102. EditorPythonBindingsNotificationBus::Handler::BusDisconnect();
  103. PythonSymbolEventBus::ExecuteQueuedEvents();
  104. }
  105. void PythonLogSymbolsComponent::WriteMethod(AZ::IO::HandleType handle, AZStd::string_view methodName, const AZ::BehaviorMethod& behaviorMethod, const AZ::BehaviorClass* behaviorClass)
  106. {
  107. AZStd::string buffer;
  108. int indentLevel = 0;
  109. AZStd::vector<AZStd::string> pythonArgs;
  110. const bool isMemberLike = behaviorClass ? PythonProxyObjectManagement::IsMemberLike(behaviorMethod, behaviorClass->m_typeId) : false;
  111. if (isMemberLike)
  112. {
  113. indentLevel = 1;
  114. Internal::Indent(indentLevel, buffer);
  115. pythonArgs.emplace_back("self");
  116. }
  117. else
  118. {
  119. indentLevel = 0;
  120. }
  121. AzFramework::StringFunc::Append(buffer, "def ");
  122. if (isMemberLike || !behaviorClass)
  123. {
  124. AzFramework::StringFunc::Append(buffer, methodName.data());
  125. }
  126. else
  127. {
  128. AzFramework::StringFunc::Append(buffer, behaviorClass->m_name.c_str());
  129. AzFramework::StringFunc::Append(buffer, "_");
  130. AzFramework::StringFunc::Append(buffer, methodName.data());
  131. }
  132. AzFramework::StringFunc::Append(buffer, "(");
  133. AZStd::string bufferArg;
  134. for (size_t argIndex = 0; argIndex < behaviorMethod.GetNumArguments(); ++argIndex)
  135. {
  136. const AZStd::string* name = behaviorMethod.GetArgumentName(argIndex);
  137. if (!name || name->empty())
  138. {
  139. bufferArg = AZStd::string::format(" arg%zu", argIndex);
  140. }
  141. else
  142. {
  143. bufferArg = *name;
  144. }
  145. AZStd::string type = FetchPythonTypeName(*behaviorMethod.GetArgument(argIndex));
  146. if (!type.empty())
  147. {
  148. AzFramework::StringFunc::Append(bufferArg, ": ");
  149. AzFramework::StringFunc::Append(bufferArg, type.data());
  150. }
  151. pythonArgs.push_back(bufferArg);
  152. bufferArg.clear();
  153. }
  154. AZStd::string argsList;
  155. AzFramework::StringFunc::Join(buffer, pythonArgs.begin(), pythonArgs.end(), ",");
  156. AzFramework::StringFunc::Append(buffer, ") -> None:\n");
  157. Internal::Indent(indentLevel + 1, buffer);
  158. AzFramework::StringFunc::Append(buffer, "pass\n\n");
  159. AZ::IO::FileIOBase::GetInstance()->Write(handle, buffer.c_str(), buffer.size());
  160. }
  161. void PythonLogSymbolsComponent::WriteProperty(AZ::IO::HandleType handle, int level, AZStd::string_view propertyName, const AZ::BehaviorProperty& property, [[maybe_unused]] const AZ::BehaviorClass * behaviorClass)
  162. {
  163. AZStd::string buffer;
  164. // property declaration
  165. Internal::Indent(level, buffer);
  166. AzFramework::StringFunc::Append(buffer, "@property\n");
  167. Internal::Indent(level, buffer);
  168. AzFramework::StringFunc::Append(buffer, "def ");
  169. AzFramework::StringFunc::Append(buffer, propertyName.data());
  170. AzFramework::StringFunc::Append(buffer, "(self) -> ");
  171. AZStd::string_view type = FetchPythonTypeAndTraits(property.GetTypeId(), AZ::BehaviorParameter::TR_NONE);
  172. if (type.empty())
  173. {
  174. AzFramework::StringFunc::Append(buffer, "Any");
  175. }
  176. else
  177. {
  178. AzFramework::StringFunc::Append(buffer, type.data());
  179. }
  180. AzFramework::StringFunc::Append(buffer, ":\n");
  181. Internal::Indent(level + 1, buffer);
  182. AzFramework::StringFunc::Append(buffer, "pass\n\n");
  183. AZ::IO::FileIOBase::GetInstance()->Write(handle, buffer.c_str(), buffer.size());
  184. }
  185. void PythonLogSymbolsComponent::LogClass(const AZStd::string moduleName, const AZ::BehaviorClass* behaviorClass)
  186. {
  187. LogClassWithName(moduleName, behaviorClass, behaviorClass->m_name.c_str());
  188. }
  189. void PythonLogSymbolsComponent::LogClassWithName(const AZStd::string moduleName, const AZ::BehaviorClass* behaviorClass, const AZStd::string className)
  190. {
  191. auto fileHandle = OpenModuleAt(moduleName);
  192. if (fileHandle->IsValid())
  193. {
  194. // Behavior Class types with member methods and properties
  195. AZStd::string buffer;
  196. AzFramework::StringFunc::Append(buffer, "class ");
  197. AzFramework::StringFunc::Append(buffer, className.data());
  198. AzFramework::StringFunc::Append(buffer, ":\n");
  199. AZ::IO::FileIOBase::GetInstance()->Write(*fileHandle, buffer.c_str(), buffer.size());
  200. buffer.clear();
  201. if (behaviorClass->m_methods.empty() && behaviorClass->m_properties.empty())
  202. {
  203. AZStd::string body{ " # behavior class type with no methods or properties \n" };
  204. Internal::Indent(1, body);
  205. AzFramework::StringFunc::Append(body, "pass\n\n");
  206. AZ::IO::FileIOBase::GetInstance()->Write(*fileHandle, body.c_str(), body.size());
  207. }
  208. else
  209. {
  210. for (const auto& properyEntry : behaviorClass->m_properties)
  211. {
  212. AZ::BehaviorProperty* property = properyEntry.second;
  213. AZStd::string propertyName{ properyEntry.first };
  214. Scope::FetchScriptName(property->m_attributes, propertyName);
  215. WriteProperty(*fileHandle, 1, propertyName, *property, behaviorClass);
  216. }
  217. for (const auto& methodEntry : behaviorClass->m_methods)
  218. {
  219. AZ::BehaviorMethod* method = methodEntry.second;
  220. if (method && PythonProxyObjectManagement::IsMemberLike(*method, behaviorClass->m_typeId))
  221. {
  222. AZStd::string baseMethodName{ methodEntry.first };
  223. Scope::FetchScriptName(method->m_attributes, baseMethodName);
  224. WriteMethod(*fileHandle, baseMethodName, *method, behaviorClass);
  225. }
  226. }
  227. }
  228. }
  229. }
  230. void PythonLogSymbolsComponent::LogClassMethod(
  231. const AZStd::string moduleName,
  232. const AZStd::string globalMethodName,
  233. [[maybe_unused]] const AZ::BehaviorClass* behaviorClass,
  234. const AZ::BehaviorMethod* behaviorMethod)
  235. {
  236. auto fileHandle = OpenModuleAt(moduleName);
  237. if (fileHandle->IsValid())
  238. {
  239. WriteMethod(*fileHandle, globalMethodName, *behaviorMethod, nullptr);
  240. }
  241. }
  242. void PythonLogSymbolsComponent::LogBus(const AZStd::string moduleName, const AZStd::string busName, const AZ::BehaviorEBus* behaviorEBus)
  243. {
  244. if (behaviorEBus->m_events.empty())
  245. {
  246. return;
  247. }
  248. auto fileHandle = OpenModuleAt(moduleName);
  249. if (fileHandle->IsValid())
  250. {
  251. AZStd::string buffer;
  252. const auto& eventSenderEntry = behaviorEBus->m_events.begin();
  253. const AZ::BehaviorEBusEventSender& sender = eventSenderEntry->second;
  254. AzFramework::StringFunc::Append(buffer, "def ");
  255. AzFramework::StringFunc::Append(buffer, busName.data());
  256. bool isBroadcast = false;
  257. if (sender.m_event)
  258. {
  259. AZStd::string addressType = FetchPythonTypeName(behaviorEBus->m_idParam);
  260. if (addressType.empty())
  261. {
  262. AzFramework::StringFunc::Append(buffer, "(busCallType: int, busEventName: str, address: Any, args: Tuple[Any])");
  263. }
  264. else
  265. {
  266. AzFramework::StringFunc::Append(buffer, "(busCallType: int, busEventName: str, address: ");
  267. AzFramework::StringFunc::Append(buffer, AZStd::string::format(AZ_STRING_FORMAT, AZ_STRING_ARG(addressType)).c_str());
  268. AzFramework::StringFunc::Append(buffer, ", args: Tuple[Any])");
  269. }
  270. }
  271. else
  272. {
  273. AzFramework::StringFunc::Append(buffer, "(busCallType: int, busEventName: str, args: Tuple[Any])");
  274. isBroadcast = true;
  275. }
  276. AzFramework::StringFunc::Append(buffer, " -> Any:\n");
  277. AZ::IO::FileIOBase::GetInstance()->Write(*fileHandle, buffer.c_str(), buffer.size());
  278. buffer.clear();
  279. auto eventInfoBuilder = [this](const AZ::BehaviorMethod* behaviorMethod, AZStd::string& inOutStrBuffer, [[maybe_unused]] TypeMap& typeCache)
  280. {
  281. AzFramework::StringFunc::Append(inOutStrBuffer, "(");
  282. size_t numArguments = behaviorMethod->GetNumArguments();
  283. const AZ::BehaviorParameter* busIdArg = behaviorMethod->GetBusIdArgument();
  284. for (size_t i = 0; i < numArguments; ++i)
  285. {
  286. const AZ::BehaviorParameter* argParam = behaviorMethod->GetArgument(i);
  287. if (argParam == busIdArg)
  288. {
  289. // address argument is part of the bus call, skip from event argument list
  290. continue;
  291. }
  292. AZStd::string_view argType = FetchPythonTypeAndTraits(argParam->m_typeId, argParam->m_traits);
  293. AzFramework::StringFunc::Append(inOutStrBuffer, argType.data());
  294. if (i < (numArguments - 1))
  295. {
  296. AzFramework::StringFunc::Append(inOutStrBuffer, ", ");
  297. }
  298. }
  299. const AZ::BehaviorParameter* resultParam = behaviorMethod->GetResult();
  300. AZStd::string returnType = FetchPythonTypeName(*resultParam);
  301. AZStd::string returnTypeStr = AZStd::string::format(") -> " AZ_STRING_FORMAT" \n", AZ_STRING_ARG(returnType));
  302. AzFramework::StringFunc::Append(inOutStrBuffer, returnTypeStr.c_str());
  303. };
  304. // record the event names the behavior can send, their parameters and return type
  305. AZStd::string comment = behaviorEBus->m_toolTip;
  306. if (!behaviorEBus->m_events.empty())
  307. {
  308. AzFramework::StringFunc::Append(comment, "The following bus Call types, Event names and Argument types are supported by this bus:\n");
  309. AZStd::vector<AZStd::string> events;
  310. for (const auto& eventSenderEntry2 : behaviorEBus->m_events)
  311. {
  312. const AZStd::string& eventName = eventSenderEntry2.first;
  313. AZStd::string eventNameStr = AZStd::string::format("'%s', ", eventName.c_str());
  314. // prefer m_event info over m_broadcast
  315. if (!isBroadcast && eventSenderEntry2.second.m_event != nullptr)
  316. {
  317. AZStd::string eventInfo;
  318. AzFramework::StringFunc::Append(eventInfo, "bus.Event, ");
  319. AzFramework::StringFunc::Append(eventInfo, eventNameStr.c_str());
  320. eventInfoBuilder(eventSenderEntry2.second.m_event, eventInfo, m_typeCache);
  321. events.push_back(eventInfo);
  322. }
  323. else if (isBroadcast && eventSenderEntry2.second.m_broadcast != nullptr)
  324. {
  325. AZStd::string eventInfo;
  326. AzFramework::StringFunc::Append(eventInfo, "bus.Broadcast, ");
  327. AzFramework::StringFunc::Append(eventInfo, eventNameStr.c_str());
  328. eventInfoBuilder(eventSenderEntry2.second.m_broadcast, eventInfo, m_typeCache);
  329. events.push_back(eventInfo);
  330. }
  331. else
  332. {
  333. AZ_Warning("python", false, "Event %s is expected to have valid event information.", eventName.c_str());
  334. }
  335. }
  336. AZStd::sort(events.begin(), events.end());
  337. for (auto& eventInfo : events)
  338. {
  339. Internal::Indent(1, comment);
  340. AzFramework::StringFunc::Append(comment, eventInfo.c_str());
  341. }
  342. }
  343. Internal::AddCommentBlock(1, comment, buffer);
  344. Internal::Indent(1, buffer);
  345. AzFramework::StringFunc::Append(buffer, "pass\n\n");
  346. AZ::IO::FileIOBase::GetInstance()->Write(*fileHandle, buffer.c_str(), buffer.size());
  347. // can the EBus create & destroy a handler?
  348. if (behaviorEBus->m_createHandler && behaviorEBus->m_destroyHandler)
  349. {
  350. buffer.clear();
  351. AzFramework::StringFunc::Append(buffer, "def ");
  352. AzFramework::StringFunc::Append(buffer, busName.data());
  353. AzFramework::StringFunc::Append(buffer, "Handler() -> None:\n");
  354. Internal::Indent(1, buffer);
  355. AzFramework::StringFunc::Append(buffer, "pass\n\n");
  356. AZ::IO::FileIOBase::GetInstance()->Write(*fileHandle, buffer.c_str(), buffer.size());
  357. }
  358. }
  359. }
  360. void PythonLogSymbolsComponent::LogGlobalMethod(const AZStd::string moduleName, const AZStd::string methodName, const AZ::BehaviorMethod* behaviorMethod)
  361. {
  362. auto fileHandle = OpenModuleAt(moduleName);
  363. if (fileHandle->IsValid())
  364. {
  365. WriteMethod(*fileHandle, methodName, *behaviorMethod, nullptr);
  366. }
  367. auto functionMapIt = m_globalFunctionMap.find(moduleName);
  368. if (functionMapIt == m_globalFunctionMap.end())
  369. {
  370. auto moduleSetIt = m_moduleSet.find(moduleName);
  371. if (moduleSetIt != m_moduleSet.end())
  372. {
  373. m_globalFunctionMap[*moduleSetIt] = { AZStd::make_pair(behaviorMethod, methodName) };
  374. }
  375. }
  376. else
  377. {
  378. GlobalFunctionList& globalFunctionList = functionMapIt->second;
  379. globalFunctionList.emplace_back(AZStd::make_pair(behaviorMethod, methodName));
  380. }
  381. }
  382. void PythonLogSymbolsComponent::LogGlobalProperty(
  383. const AZStd::string moduleName,
  384. const AZStd::string propertyName,
  385. const AZ::BehaviorProperty* behaviorProperty)
  386. {
  387. if (!behaviorProperty->m_getter || !behaviorProperty->m_getter->GetResult())
  388. {
  389. return;
  390. }
  391. auto fileHandle = OpenModuleAt(moduleName);
  392. if (fileHandle->IsValid())
  393. {
  394. AZStd::string buffer;
  395. // add header
  396. AZ::u64 filesize = 0;
  397. AZ::IO::FileIOBase::GetInstance()->Size(fileHandle->m_handle, filesize);
  398. if (filesize == 0)
  399. {
  400. AzFramework::StringFunc::Append(buffer, "class property():\n");
  401. }
  402. Internal::Indent(1, buffer);
  403. AzFramework::StringFunc::Append(buffer, propertyName.data());
  404. AzFramework::StringFunc::Append(buffer, ": ClassVar[");
  405. const AZ::BehaviorParameter* resultParam = behaviorProperty->m_getter->GetResult();
  406. AZStd::string_view type = FetchPythonTypeAndTraits(resultParam->m_typeId, resultParam->m_traits);
  407. if (type.empty())
  408. {
  409. AzFramework::StringFunc::Append(buffer, "Any");
  410. }
  411. else
  412. {
  413. AzFramework::StringFunc::Append(buffer, type.data());
  414. }
  415. AzFramework::StringFunc::Append(buffer, "] = None");
  416. if (behaviorProperty->m_getter && !behaviorProperty->m_setter)
  417. {
  418. AzFramework::StringFunc::Append(buffer, " # read only");
  419. }
  420. AzFramework::StringFunc::Append(buffer, "\n");
  421. AZ::IO::FileIOBase::GetInstance()->Write(*fileHandle, buffer.c_str(), buffer.size());
  422. }
  423. }
  424. void PythonLogSymbolsComponent::Finalize()
  425. {
  426. auto fileHandle = OpenInitFileAt("azlmbr.bus");
  427. if (fileHandle->IsValid())
  428. {
  429. AZStd::string buffer;
  430. AzFramework::StringFunc::Append(buffer, "# Bus dispatch types:\n");
  431. AzFramework::StringFunc::Append(buffer, "from typing_extensions import Final\n");
  432. AzFramework::StringFunc::Append(buffer, "Broadcast: Final[int] = 0\n");
  433. AzFramework::StringFunc::Append(buffer, "Event: Final[int] = 1\n");
  434. AzFramework::StringFunc::Append(buffer, "QueueBroadcast: Final[int] = 2\n");
  435. AzFramework::StringFunc::Append(buffer, "QueueEvent: Final[int] = 3\n");
  436. AZ::IO::FileIOBase::GetInstance()->Write(*fileHandle, buffer.c_str(), buffer.size());
  437. }
  438. fileHandle->Close();
  439. }
  440. void PythonLogSymbolsComponent::GetModuleList(AZStd::vector<AZStd::string_view>& moduleList) const
  441. {
  442. moduleList.clear();
  443. moduleList.reserve(m_moduleSet.size());
  444. AZStd::copy(m_moduleSet.begin(), m_moduleSet.end(), AZStd::back_inserter(moduleList));
  445. }
  446. void PythonLogSymbolsComponent::GetGlobalFunctionList(GlobalFunctionCollection& globalFunctionCollection) const
  447. {
  448. globalFunctionCollection.clear();
  449. for (const auto& globalFunctionMapEntry : m_globalFunctionMap)
  450. {
  451. const AZStd::string_view moduleName{ globalFunctionMapEntry.first };
  452. const GlobalFunctionList& moduleFunctionList = globalFunctionMapEntry.second;
  453. AZStd::transform(moduleFunctionList.begin(), moduleFunctionList.end(), AZStd::back_inserter(globalFunctionCollection), [moduleName](auto& entry) -> auto
  454. {
  455. const GlobalFunctionEntry& globalFunctionEntry = entry;
  456. const AZ::BehaviorMethod* behaviorMethod = entry.first;
  457. return AzToolsFramework::EditorPythonConsoleInterface::GlobalFunction({ moduleName, globalFunctionEntry.second, behaviorMethod->m_debugDescription });
  458. });
  459. }
  460. }
  461. AZStd::string PythonLogSymbolsComponent::FetchListType(const AZ::TypeId& typeId)
  462. {
  463. AZStd::string type = "list";
  464. AZStd::vector<AZ::Uuid> typeList = AZ::Utils::GetContainedTypes(typeId);
  465. if (!typeList.empty())
  466. {
  467. // trait info not available, so defaulting to TR_NONE
  468. AZStd::string_view itemType = FetchPythonTypeAndTraits(typeList[0], AZ::BehaviorParameter::TR_NONE);
  469. if (!itemType.empty())
  470. {
  471. type = AZStd::string::format("List[" AZ_STRING_FORMAT "]", AZ_STRING_ARG(itemType));
  472. }
  473. }
  474. return type;
  475. }
  476. AZStd::string PythonLogSymbolsComponent::FetchMapType(const AZ::TypeId& typeId)
  477. {
  478. AZStd::string type = "dict";
  479. AZStd::vector<AZ::Uuid> typeList = AZ::Utils::GetContainedTypes(typeId);
  480. if (!typeList.empty())
  481. {
  482. // trait info not available, so defaulting to TR_NONE
  483. AZStd::string_view kType = FetchPythonTypeAndTraits(typeList[0], AZ::BehaviorParameter::TR_NONE);
  484. AZStd::string_view vType = FetchPythonTypeAndTraits(typeList[1], AZ::BehaviorParameter::TR_NONE);
  485. if (!kType.empty() && !vType.empty())
  486. {
  487. type = AZStd::string::format("Dict[" AZ_STRING_FORMAT ", " AZ_STRING_FORMAT "]",
  488. AZ_STRING_ARG(kType), AZ_STRING_ARG(vType));
  489. }
  490. }
  491. return type;
  492. }
  493. AZStd::string PythonLogSymbolsComponent::FetchOutcomeType(const AZ::TypeId& typeId)
  494. {
  495. AZStd::string type = "Outcome";
  496. AZStd::pair<AZ::Uuid, AZ::Uuid> outcomeTypes = AZ::Utils::GetOutcomeTypes(typeId);
  497. // trait info not available, so defaulting to TR_NONE
  498. AZStd::string_view valueT = FetchPythonTypeAndTraits(outcomeTypes.first, AZ::BehaviorParameter::TR_NONE);
  499. AZStd::string_view errorT = FetchPythonTypeAndTraits(outcomeTypes.second, AZ::BehaviorParameter::TR_NONE);
  500. if (!valueT.empty() && !errorT.empty())
  501. {
  502. type = AZStd::string::format("Outcome[" AZ_STRING_FORMAT ", " AZ_STRING_FORMAT "]",
  503. AZ_STRING_ARG(valueT), AZ_STRING_ARG(errorT));
  504. }
  505. return type;
  506. }
  507. AZStd::string PythonLogSymbolsComponent::TypeNameFallback(const AZ::TypeId& typeId)
  508. {
  509. // fall back to class data m_name
  510. AZ::SerializeContext* serializeContext = nullptr;
  511. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  512. if (serializeContext)
  513. {
  514. auto classData = serializeContext->FindClassData(typeId);
  515. if (classData)
  516. {
  517. return classData->m_name;
  518. }
  519. }
  520. return "";
  521. }
  522. AZStd::string_view PythonLogSymbolsComponent::FetchPythonTypeAndTraits(const AZ::TypeId& typeId, AZ::u32 traits)
  523. {
  524. if (m_typeCache.find(typeId) == m_typeCache.end())
  525. {
  526. AZStd::string type;
  527. if (AZ::AzTypeInfo<AZStd::string_view>::Uuid() == typeId ||
  528. AZ::AzTypeInfo<AZStd::string>::Uuid() == typeId)
  529. {
  530. type = "str";
  531. }
  532. else if (AZ::AzTypeInfo<char>::Uuid() == typeId &&
  533. traits & AZ::BehaviorParameter::TR_POINTER &&
  534. traits & AZ::BehaviorParameter::TR_CONST)
  535. {
  536. type = "str";
  537. }
  538. else if (AZ::AzTypeInfo<float>::Uuid() == typeId ||
  539. AZ::AzTypeInfo<double>::Uuid() == typeId)
  540. {
  541. type = "float";
  542. }
  543. else if (AZ::AzTypeInfo<bool>::Uuid() == typeId)
  544. {
  545. type = "bool";
  546. }
  547. else if (AZ::AzTypeInfo<AZ::s8>::Uuid() == typeId ||
  548. AZ::AzTypeInfo<AZ::u8>::Uuid() == typeId ||
  549. AZ::AzTypeInfo<AZ::s16>::Uuid() == typeId ||
  550. AZ::AzTypeInfo<AZ::u16>::Uuid() == typeId ||
  551. AZ::AzTypeInfo<AZ::s32>::Uuid() == typeId ||
  552. AZ::AzTypeInfo<AZ::u32>::Uuid() == typeId ||
  553. AZ::AzTypeInfo<AZ::s64>::Uuid() == typeId ||
  554. AZ::AzTypeInfo<AZ::u64>::Uuid() == typeId)
  555. {
  556. type = "int";
  557. }
  558. else if (AZ::AzTypeInfo<AZStd::vector<AZ::u8>>::Uuid() == typeId)
  559. {
  560. type = "bytes";
  561. }
  562. else if (AZ::AzTypeInfo<AZStd::any>::Uuid() == typeId)
  563. {
  564. type = "object";
  565. }
  566. else if (AZ::AzTypeInfo<void>::Uuid() == typeId)
  567. {
  568. type = "None";
  569. }
  570. else if (AZ::Utils::IsVectorContainerType(typeId))
  571. {
  572. type = FetchListType(typeId);
  573. }
  574. else if (AZ::Utils::IsMapContainerType(typeId))
  575. {
  576. type = FetchMapType(typeId);
  577. }
  578. else if (AZ::Utils::IsOutcomeType(typeId))
  579. {
  580. type = FetchOutcomeType(typeId);
  581. }
  582. else
  583. {
  584. type = TypeNameFallback(typeId);
  585. }
  586. m_typeCache[typeId] = type;
  587. }
  588. return m_typeCache[typeId];
  589. }
  590. AZStd::string PythonLogSymbolsComponent::FetchPythonTypeName(const AZ::BehaviorParameter& param)
  591. {
  592. AZStd::string pythonType = FetchPythonTypeAndTraits(param.m_typeId, param.m_traits);
  593. if (pythonType.empty())
  594. {
  595. if (AZ::StringFunc::Equal(param.m_name, "void"))
  596. {
  597. return "None";
  598. }
  599. return param.m_name;
  600. }
  601. return pythonType;
  602. }
  603. PythonLogSymbolsComponent::FileHandlePtr PythonLogSymbolsComponent::OpenInitFileAt(AZStd::string_view moduleName)
  604. {
  605. if (m_basePath.empty())
  606. {
  607. return AZStd::make_shared<Internal::FileHandle>(AZ::IO::InvalidHandle);
  608. }
  609. // creates the __init__.py file in this path
  610. AZStd::string modulePath(moduleName);
  611. AzFramework::StringFunc::Replace(modulePath, ".", AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
  612. AZStd::string initFile;
  613. AzFramework::StringFunc::Path::Join(m_basePath.c_str(), modulePath.c_str(), initFile);
  614. AzFramework::StringFunc::Append(initFile, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
  615. AzFramework::StringFunc::Append(initFile, "__init__.pyi");
  616. AZ::IO::OpenMode openMode = AZ::IO::OpenMode::ModeText | AZ::IO::OpenMode::ModeWrite;
  617. AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
  618. AZ::IO::Result result = AZ::IO::FileIOBase::GetInstance()->Open(initFile.c_str(), openMode, fileHandle);
  619. if (result)
  620. {
  621. return AZStd::make_shared<Internal::FileHandle>(fileHandle);
  622. }
  623. return AZStd::make_shared<Internal::FileHandle>(AZ::IO::InvalidHandle);
  624. }
  625. PythonLogSymbolsComponent::FileHandlePtr PythonLogSymbolsComponent::OpenModuleAt(AZStd::string_view moduleName)
  626. {
  627. if (m_basePath.empty())
  628. {
  629. return AZStd::make_shared<Internal::FileHandle>(AZ::IO::InvalidHandle);
  630. }
  631. bool resetFile = false;
  632. if (m_moduleSet.find(moduleName) == m_moduleSet.end())
  633. {
  634. m_moduleSet.insert(moduleName);
  635. resetFile = true;
  636. }
  637. AZStd::vector<AZStd::string> moduleParts;
  638. AzFramework::StringFunc::Tokenize(moduleName.data(), moduleParts, '.');
  639. // prepare target PYI file
  640. AZStd::string targetModule = moduleParts.back();
  641. moduleParts.pop_back();
  642. AzFramework::StringFunc::Append(targetModule, ".pyi");
  643. // create an __init__.py file as the base module path
  644. AZStd::string initModule;
  645. AzFramework::StringFunc::Join(initModule, moduleParts.begin(), moduleParts.end(), '.');
  646. OpenInitFileAt(initModule);
  647. AZStd::string modulePath;
  648. AzFramework::StringFunc::Append(modulePath, m_basePath.c_str());
  649. AzFramework::StringFunc::Append(modulePath, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
  650. AzFramework::StringFunc::Join(modulePath, moduleParts.begin(), moduleParts.end(), AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
  651. // prepare the path
  652. AZ::IO::FileIOBase::GetInstance()->CreatePath(modulePath.c_str());
  653. // assemble the file path
  654. AzFramework::StringFunc::Append(modulePath, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
  655. AzFramework::StringFunc::Append(modulePath, targetModule.c_str());
  656. AzFramework::StringFunc::AssetDatabasePath::Normalize(modulePath);
  657. AZ::IO::OpenMode openMode = AZ::IO::OpenMode::ModeText;
  658. if (AZ::IO::SystemFile::Exists(modulePath.c_str()))
  659. {
  660. openMode |= (resetFile) ? AZ::IO::OpenMode::ModeWrite : AZ::IO::OpenMode::ModeAppend;
  661. }
  662. else
  663. {
  664. openMode |= AZ::IO::OpenMode::ModeWrite;
  665. }
  666. AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
  667. AZ::IO::Result result = AZ::IO::FileIOBase::GetInstance()->Open(modulePath.c_str(), openMode, fileHandle);
  668. if (result)
  669. {
  670. return AZStd::make_shared<Internal::FileHandle>(fileHandle);
  671. }
  672. return AZStd::make_shared<Internal::FileHandle>(AZ::IO::InvalidHandle);
  673. }
  674. }