diff --git a/Gems/EditorPythonBindings/Code/Source/PythonCommon.h b/Gems/EditorPythonBindings/Code/Include/EditorPythonBindings/PythonCommon.h similarity index 100% rename from Gems/EditorPythonBindings/Code/Source/PythonCommon.h rename to Gems/EditorPythonBindings/Code/Include/EditorPythonBindings/PythonCommon.h diff --git a/Gems/EditorPythonBindings/Code/Source/PythonUtility.h b/Gems/EditorPythonBindings/Code/Include/EditorPythonBindings/PythonUtility.h similarity index 59% rename from Gems/EditorPythonBindings/Code/Source/PythonUtility.h rename to Gems/EditorPythonBindings/Code/Include/EditorPythonBindings/PythonUtility.h index ee5f92e3a605..40188997cb2e 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonUtility.h +++ b/Gems/EditorPythonBindings/Code/Include/EditorPythonBindings/PythonUtility.h @@ -7,19 +7,19 @@ */ #pragma once -#include -#include -#include #include -#include +#include #include +#include +#include +#include namespace AZ { struct BehaviorParameter; struct BehaviorArgument; class BehaviorMethod; -} +} // namespace AZ namespace EditorPythonBindings { @@ -37,7 +37,7 @@ namespace EditorPythonBindings } return (scopeType == AZ::Script::Attributes::ScopeFlags::Automation || scopeType == AZ::Script::Attributes::ScopeFlags::Common); } - + inline void FetchScriptName(const AZ::AttributeArray& attributes, AZStd::string& baseName) { AZ::Attribute* scriptNameAttribute = AZ::FindAttribute(AZ::Script::Attributes::Alias, attributes); @@ -47,20 +47,25 @@ namespace EditorPythonBindings scopeAttributeReader.Read(baseName); } } - } + } // namespace Scope namespace Module { using PackageMapType = AZStd::unordered_map; //! Finds or creates a sub-module to add a base parent module; create all the sub-modules as well - //! @param modulePackageMap keeps track of the known modules + //! @param modulePackageMap keeps track of the known modules //! @param moduleName can be a dot separated string such as "mygen.mypackage.mymodule" //! @param parentModule the module to add new sub-modules //! @param fallbackModule the module to add new sub-modules //! @param alertUsingFallback issue a warning if using the fallback module //! @return the new submodule - pybind11::module DeterminePackageModule(PackageMapType& modulePackageMap, AZStd::string_view moduleName, pybind11::module parentModule, pybind11::module fallbackModule, bool alertUsingFallback); + pybind11::module DeterminePackageModule( + PackageMapType& modulePackageMap, + AZStd::string_view moduleName, + pybind11::module parentModule, + pybind11::module fallbackModule, + bool alertUsingFallback); inline AZStd::optional GetName(const AZ::AttributeArray& attributes) { @@ -77,15 +82,14 @@ namespace EditorPythonBindings } return {}; } - } + } // namespace Module namespace Convert { // allocation pattern for BehaviorValueParameters being stored in the stack and needs to be cleaned at the end of a block using VariableDeleter = AZStd::function; - struct StackVariableAllocator final - : public AZStd::static_buffer_allocator<256, 16> + struct StackVariableAllocator final : public AZStd::static_buffer_allocator<256, 16> { public: ~StackVariableAllocator(); @@ -99,26 +103,33 @@ namespace EditorPythonBindings //! @param behaviorValue is a parameter that came from a result or some prepared behavior value //! @param stackVariableAllocator manages the allocated parameter while in scope //! @return a valid Python object or None if no conversion was possible - pybind11::object BehaviorValueParameterToPython(AZ::BehaviorArgument& behaviorValue, Convert::StackVariableAllocator& stackVariableAllocator); + pybind11::object BehaviorValueParameterToPython( + AZ::BehaviorArgument& behaviorValue, Convert::StackVariableAllocator& stackVariableAllocator); //! Converts Python object to a behavior value parameter using an existing behaviorArgument from a Behavior Method //! @param behaviorArgument the stored argument slot from a Behavior Method to match with the pyObj to covert in the parameter //! @param parameter is the output of the conversion from Python to a Behavior value //! @param stackVariableAllocator manages the allocated parameter while in scope //! @return true if the conversion happened - bool PythonToBehaviorValueParameter(const AZ::BehaviorParameter& behaviorArgument, pybind11::object pyObj, AZ::BehaviorArgument& parameter, Convert::StackVariableAllocator& stackVariableAllocator); - - //! Converts Python object to a PythonProxyObject, if possible - //! @param behaviorArgument A stored PythonProxyObject in Python, returns FALSE if the Python object does not point to a PythonProxyObject + bool PythonToBehaviorValueParameter( + const AZ::BehaviorParameter& behaviorArgument, + pybind11::object pyObj, + AZ::BehaviorArgument& parameter, + Convert::StackVariableAllocator& stackVariableAllocator); + + //! Converts Python object to a PythonProxyObject, if possible + //! @param behaviorArgument A stored PythonProxyObject in Python, returns FALSE if the Python object does not point to a + //! PythonProxyObject //! @param parameter is the output of the conversion from Python to a Behavior value //! @return true if the conversion happened - bool PythonProxyObjectToBehaviorValueParameter(const AZ::BehaviorParameter& behaviorArgument, pybind11::object pyObj, AZ::BehaviorArgument& parameter); + bool PythonProxyObjectToBehaviorValueParameter( + const AZ::BehaviorParameter& behaviorArgument, pybind11::object pyObj, AZ::BehaviorArgument& parameter); //! Gets a readable type name for the Python object; this will unwrap a PythonProxyObject to find its underlying type name //! @param pyObj any valid Python object value //! @return text form of the Python object value type AZStd::string GetPythonTypeName(pybind11::object pyObj); - } + } // namespace Convert namespace Call { @@ -127,5 +138,57 @@ namespace EditorPythonBindings //! Calls a BehaviorMethod with a tuple of arguments for member class level functions pybind11::object ClassMethod(AZ::BehaviorMethod* behaviorMethod, AZ::BehaviorObject self, pybind11::args args); - } -} + } // namespace Call + + namespace Text + { + class PythonBehaviorDescription + { + public: + //! Get Python type for behavior typeId. + AZStd::string_view FetchPythonTypeAndTraits(const AZ::TypeId& typeId, AZ::u32 traits); + AZStd::string FetchPythonTypeName(const AZ::BehaviorParameter& param); + AZStd::string FetchOutcomeType(const AZ::TypeId& typeId); + + //! Creates a string containing bus events and documentation. + AZStd::string BusDefinition(const AZStd::string busName, const AZ::BehaviorEBus* behaviorEBus); + + //! Creates a string with class or global method definition and documentation. + //! @param defineTooltip if true, the tooltip will be included in the definition + AZStd::string MethodDefinition( + const AZStd::string methodName, + const AZ::BehaviorMethod& behaviorMethod, + const AZ::BehaviorClass* behaviorClass = nullptr, + bool defineTooltip = false, + bool defineDebugDescription = false); + + //! Creates a string with class definition and documentation. + //! @param defineProperties if true, the properties will be included in the definition + //! @param defineMethods if true, the methods will be included in the definition + //! @param defineTooltip if true, the tooltip will be included in the definition + AZStd::string ClassDefinition( + const AZ::BehaviorClass* behaviorClass, + const AZStd::string className, + bool defineProperties = true, + bool defineMethods = true, + bool defineTooltip = false); + + //! Creates a property definition + AZStd::string PropertyDefinition( + AZStd::string_view propertyName, int level, const AZ::BehaviorProperty& property, const AZ::BehaviorClass* behaviorClass); + + AZStd::string GlobalPropertyDefinition( + const AZStd::string moduleName, + const AZStd::string propertyName, + const AZ::BehaviorProperty& behaviorProperty, + bool needsHeader = true); + + private: + AZStd::string FetchListType(const AZ::TypeId& typeId); + AZStd::string FetchMapType(const AZ::TypeId& typeId); + + using TypeMap = AZStd::unordered_map; + TypeMap m_typeCache; + }; + } // namespace Text +} // namespace EditorPythonBindings diff --git a/Gems/EditorPythonBindings/Code/Source/ActionManager/PythonActionManagerHandler.cpp b/Gems/EditorPythonBindings/Code/Source/ActionManager/PythonActionManagerHandler.cpp index 15ced6bf8fa3..ee256d908f6c 100644 --- a/Gems/EditorPythonBindings/Code/Source/ActionManager/PythonActionManagerHandler.cpp +++ b/Gems/EditorPythonBindings/Code/Source/ActionManager/PythonActionManagerHandler.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include namespace EditorPythonBindings diff --git a/Gems/EditorPythonBindings/Code/Source/ActionManager/PythonEditorAction.h b/Gems/EditorPythonBindings/Code/Source/ActionManager/PythonEditorAction.h index 7ae603cc15fd..740603f2d664 100644 --- a/Gems/EditorPythonBindings/Code/Source/ActionManager/PythonEditorAction.h +++ b/Gems/EditorPythonBindings/Code/Source/ActionManager/PythonEditorAction.h @@ -11,7 +11,7 @@ #include #include -#include +#include #include namespace EditorPythonBindings diff --git a/Gems/EditorPythonBindings/Code/Source/PythonLogSymbolsComponent.cpp b/Gems/EditorPythonBindings/Code/Source/PythonLogSymbolsComponent.cpp index dc1f7d11ccf9..944e556a0e63 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonLogSymbolsComponent.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonLogSymbolsComponent.cpp @@ -8,21 +8,21 @@ #include -#include -#include -#include +#include +#include #include #include +#include #include +#include +#include #include #include #include -#include -#include -#include -#include #include +#include +#include #include #include @@ -35,7 +35,8 @@ namespace EditorPythonBindings { explicit FileHandle(AZ::IO::HandleType handle) : m_handle(handle) - {} + { + } ~FileHandle() { @@ -56,33 +57,20 @@ namespace EditorPythonBindings return m_handle != AZ::IO::InvalidHandle; } - operator AZ::IO::HandleType() const { return m_handle; } + operator AZ::IO::HandleType() const + { + return m_handle; + } AZ::IO::HandleType m_handle; }; - - void Indent(int level, AZStd::string& buffer) - { - buffer.append(level * 4, ' '); - } - - void AddCommentBlock(int level, const AZStd::string& comment, AZStd::string& buffer) - { - Indent(level, buffer); - AzFramework::StringFunc::Append(buffer, "\"\"\"\n"); - Indent(level, buffer); - AzFramework::StringFunc::Append(buffer, comment.c_str()); - Indent(level, buffer); - AzFramework::StringFunc::Append(buffer, "\"\"\"\n"); - } - } + } // namespace Internal void PythonLogSymbolsComponent::Reflect(AZ::ReflectContext* context) { if (auto&& serialize = azrtti_cast(context)) { - serialize->Class() - ->Version(0); + serialize->Class()->Version(0); } } @@ -120,96 +108,34 @@ namespace EditorPythonBindings PythonSymbolEventBus::ExecuteQueuedEvents(); } - void PythonLogSymbolsComponent::WriteMethod(AZ::IO::HandleType handle, AZStd::string_view methodName, const AZ::BehaviorMethod& behaviorMethod, const AZ::BehaviorClass* behaviorClass) + AZStd::string_view PythonLogSymbolsComponent::FetchPythonTypeAndTraits(const AZ::TypeId& typeId, AZ::u32 traits) { - AZStd::string buffer; - int indentLevel = 0; - AZStd::vector pythonArgs; - const bool isMemberLike = behaviorClass ? PythonProxyObjectManagement::IsMemberLike(behaviorMethod, behaviorClass->m_typeId) : false; - - if (isMemberLike) - { - indentLevel = 1; - Internal::Indent(indentLevel, buffer); - pythonArgs.emplace_back("self"); - } - else - { - indentLevel = 0; - } - - AzFramework::StringFunc::Append(buffer, "def "); - if (isMemberLike || !behaviorClass) - { - AzFramework::StringFunc::Append(buffer, methodName.data()); - } - else - { - AzFramework::StringFunc::Append(buffer, behaviorClass->m_name.c_str()); - AzFramework::StringFunc::Append(buffer, "_"); - AzFramework::StringFunc::Append(buffer, methodName.data()); - } - AzFramework::StringFunc::Append(buffer, "("); - - AZStd::string bufferArg; - for (size_t argIndex = 0; argIndex < behaviorMethod.GetNumArguments(); ++argIndex) - { - const AZStd::string* name = behaviorMethod.GetArgumentName(argIndex); - if (!name || name->empty()) - { - bufferArg = AZStd::string::format(" arg%zu", argIndex); - } - else - { - bufferArg = *name; - } - - AZStd::string type = FetchPythonTypeName(*behaviorMethod.GetArgument(argIndex)); - if (!type.empty()) - { - AzFramework::StringFunc::Append(bufferArg, ": "); - AzFramework::StringFunc::Append(bufferArg, type.data()); - } - - pythonArgs.push_back(bufferArg); - bufferArg.clear(); - } + return m_pythonBehaviorDescription.FetchPythonTypeAndTraits(typeId, traits); + } - AZStd::string argsList; - AzFramework::StringFunc::Join(buffer, pythonArgs.begin(), pythonArgs.end(), ","); - AzFramework::StringFunc::Append(buffer, ") -> None:\n"); - Internal::Indent(indentLevel + 1, buffer); - AzFramework::StringFunc::Append(buffer, "pass\n\n"); + AZStd::string PythonLogSymbolsComponent::FetchPythonTypeName(const AZ::BehaviorParameter& param) + { + return m_pythonBehaviorDescription.FetchPythonTypeName(param); + } + void PythonLogSymbolsComponent::WriteMethod( + AZ::IO::HandleType handle, + AZStd::string_view methodName, + const AZ::BehaviorMethod& behaviorMethod, + const AZ::BehaviorClass* behaviorClass) + { + AZStd::string buffer = m_pythonBehaviorDescription.MethodDefinition(methodName, behaviorMethod, behaviorClass); AZ::IO::FileIOBase::GetInstance()->Write(handle, buffer.c_str(), buffer.size()); } - void PythonLogSymbolsComponent::WriteProperty(AZ::IO::HandleType handle, int level, AZStd::string_view propertyName, const AZ::BehaviorProperty& property, [[maybe_unused]] const AZ::BehaviorClass * behaviorClass) + void PythonLogSymbolsComponent::WriteProperty( + AZ::IO::HandleType handle, + int level, + AZStd::string_view propertyName, + const AZ::BehaviorProperty& property, + const AZ::BehaviorClass* behaviorClass) { - AZStd::string buffer; - - // property declaration - Internal::Indent(level, buffer); - AzFramework::StringFunc::Append(buffer, "@property\n"); - - Internal::Indent(level, buffer); - AzFramework::StringFunc::Append(buffer, "def "); - AzFramework::StringFunc::Append(buffer, propertyName.data()); - AzFramework::StringFunc::Append(buffer, "(self) -> "); - - AZStd::string_view type = FetchPythonTypeAndTraits(property.GetTypeId(), AZ::BehaviorParameter::TR_NONE); - if (type.empty()) - { - AzFramework::StringFunc::Append(buffer, "Any"); - } - else - { - AzFramework::StringFunc::Append(buffer, type.data()); - } - AzFramework::StringFunc::Append(buffer, ":\n"); - Internal::Indent(level + 1, buffer); - AzFramework::StringFunc::Append(buffer, "pass\n\n"); - + AZStd::string buffer = m_pythonBehaviorDescription.PropertyDefinition(propertyName, level, property, behaviorClass); AZ::IO::FileIOBase::GetInstance()->Write(handle, buffer.c_str(), buffer.size()); } @@ -218,47 +144,14 @@ namespace EditorPythonBindings LogClassWithName(moduleName, behaviorClass, behaviorClass->m_name.c_str()); } - void PythonLogSymbolsComponent::LogClassWithName(const AZStd::string moduleName, const AZ::BehaviorClass* behaviorClass, const AZStd::string className) + void PythonLogSymbolsComponent::LogClassWithName( + const AZStd::string moduleName, const AZ::BehaviorClass* behaviorClass, const AZStd::string className) { auto fileHandle = OpenModuleAt(moduleName); if (fileHandle->IsValid()) { - // Behavior Class types with member methods and properties - AZStd::string buffer; - AzFramework::StringFunc::Append(buffer, "class "); - AzFramework::StringFunc::Append(buffer, className.data()); - AzFramework::StringFunc::Append(buffer, ":\n"); + AZStd::string buffer = m_pythonBehaviorDescription.ClassDefinition(behaviorClass, className); AZ::IO::FileIOBase::GetInstance()->Write(*fileHandle, buffer.c_str(), buffer.size()); - buffer.clear(); - - if (behaviorClass->m_methods.empty() && behaviorClass->m_properties.empty()) - { - AZStd::string body{ " # behavior class type with no methods or properties \n" }; - Internal::Indent(1, body); - AzFramework::StringFunc::Append(body, "pass\n\n"); - AZ::IO::FileIOBase::GetInstance()->Write(*fileHandle, body.c_str(), body.size()); - } - else - { - for (const auto& properyEntry : behaviorClass->m_properties) - { - AZ::BehaviorProperty* property = properyEntry.second; - AZStd::string propertyName{ properyEntry.first }; - Scope::FetchScriptName(property->m_attributes, propertyName); - WriteProperty(*fileHandle, 1, propertyName, *property, behaviorClass); - } - - for (const auto& methodEntry : behaviorClass->m_methods) - { - AZ::BehaviorMethod* method = methodEntry.second; - if (method && PythonProxyObjectManagement::IsMemberLike(*method, behaviorClass->m_typeId)) - { - AZStd::string baseMethodName{ methodEntry.first }; - Scope::FetchScriptName(method->m_attributes, baseMethodName); - WriteMethod(*fileHandle, baseMethodName, *method, behaviorClass); - } - } - } } } @@ -275,9 +168,10 @@ namespace EditorPythonBindings } } - void PythonLogSymbolsComponent::LogBus(const AZStd::string moduleName, const AZStd::string busName, const AZ::BehaviorEBus* behaviorEBus) + void PythonLogSymbolsComponent::LogBus( + const AZStd::string moduleName, const AZStd::string busName, const AZ::BehaviorEBus* behaviorEBus) { - if (behaviorEBus->m_events.empty()) + if (!behaviorEBus || behaviorEBus->m_events.empty()) { return; } @@ -285,136 +179,13 @@ namespace EditorPythonBindings auto fileHandle = OpenModuleAt(moduleName); if (fileHandle->IsValid()) { - AZStd::string buffer; - - const auto& eventSenderEntry = behaviorEBus->m_events.begin(); - const AZ::BehaviorEBusEventSender& sender = eventSenderEntry->second; - - AzFramework::StringFunc::Append(buffer, "def "); - AzFramework::StringFunc::Append(buffer, busName.data()); - bool isBroadcast = false; - if (sender.m_event) - { - AZStd::string addressType = FetchPythonTypeName(behaviorEBus->m_idParam); - if (addressType.empty()) - { - AzFramework::StringFunc::Append(buffer, "(busCallType: int, busEventName: str, address: Any, args: Tuple[Any])"); - } - else - { - AzFramework::StringFunc::Append(buffer, "(busCallType: int, busEventName: str, address: "); - AzFramework::StringFunc::Append(buffer, AZStd::string::format(AZ_STRING_FORMAT, AZ_STRING_ARG(addressType)).c_str()); - AzFramework::StringFunc::Append(buffer, ", args: Tuple[Any])"); - } - } - else - { - AzFramework::StringFunc::Append(buffer, "(busCallType: int, busEventName: str, args: Tuple[Any])"); - isBroadcast = true; - } - - AzFramework::StringFunc::Append(buffer, " -> Any:\n"); + AZStd::string buffer = m_pythonBehaviorDescription.BusDefinition(busName, behaviorEBus); AZ::IO::FileIOBase::GetInstance()->Write(*fileHandle, buffer.c_str(), buffer.size()); - buffer.clear(); - - auto eventInfoBuilder = [this](const AZ::BehaviorMethod* behaviorMethod, AZStd::string& inOutStrBuffer, [[maybe_unused]] TypeMap& typeCache) - { - AzFramework::StringFunc::Append(inOutStrBuffer, "("); - - size_t numArguments = behaviorMethod->GetNumArguments(); - - const AZ::BehaviorParameter* busIdArg = behaviorMethod->GetBusIdArgument(); - - for (size_t i = 0; i < numArguments; ++i) - { - const AZ::BehaviorParameter* argParam = behaviorMethod->GetArgument(i); - if (argParam == busIdArg) - { - // address argument is part of the bus call, skip from event argument list - continue; - } - - AZStd::string_view argType = FetchPythonTypeAndTraits(argParam->m_typeId, argParam->m_traits); - AzFramework::StringFunc::Append(inOutStrBuffer, argType.data()); - - if (i < (numArguments - 1)) - { - AzFramework::StringFunc::Append(inOutStrBuffer, ", "); - } - } - - const AZ::BehaviorParameter* resultParam = behaviorMethod->GetResult(); - AZStd::string returnType = FetchPythonTypeName(*resultParam); - AZStd::string returnTypeStr = AZStd::string::format(") -> " AZ_STRING_FORMAT" \n", AZ_STRING_ARG(returnType)); - AzFramework::StringFunc::Append(inOutStrBuffer, returnTypeStr.c_str()); - }; - - // record the event names the behavior can send, their parameters and return type - AZStd::string comment = behaviorEBus->m_toolTip; - - if (!behaviorEBus->m_events.empty()) - { - AzFramework::StringFunc::Append(comment, "The following bus Call types, Event names and Argument types are supported by this bus:\n"); - AZStd::vector events; - for (const auto& eventSenderEntry2 : behaviorEBus->m_events) - { - const AZStd::string& eventName = eventSenderEntry2.first; - AZStd::string eventNameStr = AZStd::string::format("'%s', ", eventName.c_str()); - - // prefer m_event info over m_broadcast - if (!isBroadcast && eventSenderEntry2.second.m_event != nullptr) - { - AZStd::string eventInfo; - AzFramework::StringFunc::Append(eventInfo, "bus.Event, "); - AzFramework::StringFunc::Append(eventInfo, eventNameStr.c_str()); - eventInfoBuilder(eventSenderEntry2.second.m_event, eventInfo, m_typeCache); - events.push_back(eventInfo); - } - else if (isBroadcast && eventSenderEntry2.second.m_broadcast != nullptr) - { - AZStd::string eventInfo; - AzFramework::StringFunc::Append(eventInfo, "bus.Broadcast, "); - AzFramework::StringFunc::Append(eventInfo, eventNameStr.c_str()); - eventInfoBuilder(eventSenderEntry2.second.m_broadcast, eventInfo, m_typeCache); - events.push_back(eventInfo); - } - else - { - AZ_Warning("python", false, "Event %s is expected to have valid event information.", eventName.c_str()); - } - } - - AZStd::sort(events.begin(), events.end()); - - for (auto& eventInfo : events) - { - Internal::Indent(1, comment); - AzFramework::StringFunc::Append(comment, eventInfo.c_str()); - } - } - - Internal::AddCommentBlock(1, comment, buffer); - - Internal::Indent(1, buffer); - AzFramework::StringFunc::Append(buffer, "pass\n\n"); - - AZ::IO::FileIOBase::GetInstance()->Write(*fileHandle, buffer.c_str(), buffer.size()); - - // can the EBus create & destroy a handler? - if (behaviorEBus->m_createHandler && behaviorEBus->m_destroyHandler) - { - buffer.clear(); - AzFramework::StringFunc::Append(buffer, "def "); - AzFramework::StringFunc::Append(buffer, busName.data()); - AzFramework::StringFunc::Append(buffer, "Handler() -> None:\n"); - Internal::Indent(1, buffer); - AzFramework::StringFunc::Append(buffer, "pass\n\n"); - AZ::IO::FileIOBase::GetInstance()->Write(*fileHandle, buffer.c_str(), buffer.size()); - } } } - void PythonLogSymbolsComponent::LogGlobalMethod(const AZStd::string moduleName, const AZStd::string methodName, const AZ::BehaviorMethod* behaviorMethod) + void PythonLogSymbolsComponent::LogGlobalMethod( + const AZStd::string moduleName, const AZStd::string methodName, const AZ::BehaviorMethod* behaviorMethod) { auto fileHandle = OpenModuleAt(moduleName); if (fileHandle->IsValid()) @@ -439,11 +210,9 @@ namespace EditorPythonBindings } void PythonLogSymbolsComponent::LogGlobalProperty( - const AZStd::string moduleName, - const AZStd::string propertyName, - const AZ::BehaviorProperty* behaviorProperty) + const AZStd::string moduleName, const AZStd::string propertyName, const AZ::BehaviorProperty* behaviorProperty) { - if (!behaviorProperty->m_getter || !behaviorProperty->m_getter->GetResult()) + if (!behaviorProperty || !behaviorProperty->m_getter || !behaviorProperty->m_getter->GetResult()) { return; } @@ -451,37 +220,13 @@ namespace EditorPythonBindings auto fileHandle = OpenModuleAt(moduleName); if (fileHandle->IsValid()) { - AZStd::string buffer; - - // add header + // add header AZ::u64 filesize = 0; AZ::IO::FileIOBase::GetInstance()->Size(fileHandle->m_handle, filesize); - if (filesize == 0) - { - AzFramework::StringFunc::Append(buffer, "class property():\n"); - } + bool needsHeader = (filesize == 0); - Internal::Indent(1, buffer); - AzFramework::StringFunc::Append(buffer, propertyName.data()); - AzFramework::StringFunc::Append(buffer, ": ClassVar["); - - const AZ::BehaviorParameter* resultParam = behaviorProperty->m_getter->GetResult(); - AZStd::string_view type = FetchPythonTypeAndTraits(resultParam->m_typeId, resultParam->m_traits); - if (type.empty()) - { - AzFramework::StringFunc::Append(buffer, "Any"); - } - else - { - AzFramework::StringFunc::Append(buffer, type.data()); - } - AzFramework::StringFunc::Append(buffer, "] = None"); - - if (behaviorProperty->m_getter && !behaviorProperty->m_setter) - { - AzFramework::StringFunc::Append(buffer, " # read only"); - } - AzFramework::StringFunc::Append(buffer, "\n"); + AZStd::string buffer = + m_pythonBehaviorDescription.GlobalPropertyDefinition(moduleName, propertyName, *behaviorProperty, needsHeader); AZ::IO::FileIOBase::GetInstance()->Write(*fileHandle, buffer.c_str(), buffer.size()); } @@ -520,173 +265,18 @@ namespace EditorPythonBindings const AZStd::string_view moduleName{ globalFunctionMapEntry.first }; const GlobalFunctionList& moduleFunctionList = globalFunctionMapEntry.second; - AZStd::transform(moduleFunctionList.begin(), moduleFunctionList.end(), AZStd::back_inserter(globalFunctionCollection), [moduleName](auto& entry) -> auto - { - const GlobalFunctionEntry& globalFunctionEntry = entry; - const AZ::BehaviorMethod* behaviorMethod = entry.first; - return AzToolsFramework::EditorPythonConsoleInterface::GlobalFunction({ moduleName, globalFunctionEntry.second, behaviorMethod->m_debugDescription }); - }); - } - } - - AZStd::string PythonLogSymbolsComponent::FetchListType(const AZ::TypeId& typeId) - { - AZStd::string type = "list"; - - AZStd::vector typeList = AZ::Utils::GetContainedTypes(typeId); - if (!typeList.empty()) - { - // trait info not available, so defaulting to TR_NONE - AZStd::string_view itemType = FetchPythonTypeAndTraits(typeList[0], AZ::BehaviorParameter::TR_NONE); - if (!itemType.empty()) - { - type = AZStd::string::format("List[" AZ_STRING_FORMAT "]", AZ_STRING_ARG(itemType)); - } - } - - return type; - } - - AZStd::string PythonLogSymbolsComponent::FetchMapType(const AZ::TypeId& typeId) - { - AZStd::string type = "dict"; - - AZStd::vector typeList = AZ::Utils::GetContainedTypes(typeId); - if (!typeList.empty()) - { - // trait info not available, so defaulting to TR_NONE - AZStd::string_view kType = FetchPythonTypeAndTraits(typeList[0], AZ::BehaviorParameter::TR_NONE); - AZStd::string_view vType = FetchPythonTypeAndTraits(typeList[1], AZ::BehaviorParameter::TR_NONE); - if (!kType.empty() && !vType.empty()) - { - type = AZStd::string::format("Dict[" AZ_STRING_FORMAT ", " AZ_STRING_FORMAT "]", - AZ_STRING_ARG(kType), AZ_STRING_ARG(vType)); - } - } - - return type; - } - - AZStd::string PythonLogSymbolsComponent::FetchOutcomeType(const AZ::TypeId& typeId) - { - AZStd::string type = "Outcome"; - AZStd::pair outcomeTypes = AZ::Utils::GetOutcomeTypes(typeId); - - // trait info not available, so defaulting to TR_NONE - AZStd::string_view valueT = FetchPythonTypeAndTraits(outcomeTypes.first, AZ::BehaviorParameter::TR_NONE); - AZStd::string_view errorT = FetchPythonTypeAndTraits(outcomeTypes.second, AZ::BehaviorParameter::TR_NONE); - if (!valueT.empty() && !errorT.empty()) - { - type = AZStd::string::format("Outcome[" AZ_STRING_FORMAT ", " AZ_STRING_FORMAT "]", - AZ_STRING_ARG(valueT), AZ_STRING_ARG(errorT)); - } - - return type; - } - - AZStd::string PythonLogSymbolsComponent::TypeNameFallback(const AZ::TypeId& typeId) - { - // fall back to class data m_name - AZ::SerializeContext* serializeContext = nullptr; - AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext); - - if (serializeContext) - { - auto classData = serializeContext->FindClassData(typeId); - if (classData) - { - return classData->m_name; - } - } - - return ""; - } - - AZStd::string_view PythonLogSymbolsComponent::FetchPythonTypeAndTraits(const AZ::TypeId& typeId, AZ::u32 traits) - { - if (m_typeCache.find(typeId) == m_typeCache.end()) - { - AZStd::string type; - if (AZ::AzTypeInfo::Uuid() == typeId || - AZ::AzTypeInfo::Uuid() == typeId) - { - type = "str"; - } - else if (AZ::AzTypeInfo::Uuid() == typeId && - traits & AZ::BehaviorParameter::TR_POINTER && - traits & AZ::BehaviorParameter::TR_CONST) - { - type = "str"; - } - else if (AZ::AzTypeInfo::Uuid() == typeId || - AZ::AzTypeInfo::Uuid() == typeId) - { - type = "float"; - } - else if (AZ::AzTypeInfo::Uuid() == typeId) - { - type = "bool"; - } - else if (AZ::AzTypeInfo::Uuid() == typeId || - AZ::AzTypeInfo::Uuid() == typeId || - AZ::AzTypeInfo::Uuid() == typeId || - AZ::AzTypeInfo::Uuid() == typeId || - AZ::AzTypeInfo::Uuid() == typeId || - AZ::AzTypeInfo::Uuid() == typeId || - AZ::AzTypeInfo::Uuid() == typeId || - AZ::AzTypeInfo::Uuid() == typeId) - { - type = "int"; - } - else if (AZ::AzTypeInfo>::Uuid() == typeId) - { - type = "bytes"; - } - else if (AZ::AzTypeInfo::Uuid() == typeId) - { - type = "object"; - } - else if (AZ::AzTypeInfo::Uuid() == typeId) - { - type = "None"; - } - else if (AZ::Utils::IsVectorContainerType(typeId)) - { - type = FetchListType(typeId); - } - else if (AZ::Utils::IsMapContainerType(typeId)) - { - type = FetchMapType(typeId); - } - else if (AZ::Utils::IsOutcomeType(typeId)) - { - type = FetchOutcomeType(typeId); - } - else - { - type = TypeNameFallback(typeId); - } - - m_typeCache[typeId] = type; - } - - return m_typeCache[typeId]; - } - - AZStd::string PythonLogSymbolsComponent::FetchPythonTypeName(const AZ::BehaviorParameter& param) - { - AZStd::string pythonType = FetchPythonTypeAndTraits(param.m_typeId, param.m_traits); - - if (pythonType.empty()) - { - if (AZ::StringFunc::Equal(param.m_name, "void")) - { - return "None"; - } - - return param.m_name; + AZStd::transform( + moduleFunctionList.begin(), + moduleFunctionList.end(), + AZStd::back_inserter(globalFunctionCollection), + [moduleName](auto& entry) -> auto + { + const GlobalFunctionEntry& globalFunctionEntry = entry; + const AZ::BehaviorMethod* behaviorMethod = entry.first; + return AzToolsFramework::EditorPythonConsoleInterface::GlobalFunction( + { moduleName, globalFunctionEntry.second, behaviorMethod->m_debugDescription }); + }); } - return pythonType; } PythonLogSymbolsComponent::FileHandlePtr PythonLogSymbolsComponent::OpenInitFileAt(AZStd::string_view moduleName) @@ -704,7 +294,7 @@ namespace EditorPythonBindings AzFramework::StringFunc::Path::Join(m_basePath.c_str(), modulePath.c_str(), initFile); AzFramework::StringFunc::Append(initFile, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING); AzFramework::StringFunc::Append(initFile, "__init__.pyi"); - + AZ::IO::OpenMode openMode = AZ::IO::OpenMode::ModeText | AZ::IO::OpenMode::ModeWrite; AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; AZ::IO::Result result = AZ::IO::FileIOBase::GetInstance()->Open(initFile.c_str(), openMode, fileHandle); @@ -775,4 +365,4 @@ namespace EditorPythonBindings return AZStd::make_shared(AZ::IO::InvalidHandle); } -} +} // namespace EditorPythonBindings diff --git a/Gems/EditorPythonBindings/Code/Source/PythonLogSymbolsComponent.h b/Gems/EditorPythonBindings/Code/Source/PythonLogSymbolsComponent.h index 7e89a41cd5c0..9f09de6f5af0 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonLogSymbolsComponent.h +++ b/Gems/EditorPythonBindings/Code/Source/PythonLogSymbolsComponent.h @@ -11,8 +11,8 @@ #include #include -#include -#include +#include +#include #include #include @@ -87,18 +87,13 @@ namespace EditorPythonBindings using GlobalFunctionEntry = AZStd::pair; using GlobalFunctionList = AZStd::vector; using GlobalFunctionMap = AZStd::unordered_map; - using TypeMap = AZStd::unordered_map; using FileHandlePtr = AZStd::shared_ptr; AZStd::string m_basePath; ModuleSet m_moduleSet; GlobalFunctionMap m_globalFunctionMap; - TypeMap m_typeCache; + Text::PythonBehaviorDescription m_pythonBehaviorDescription; - AZStd::string FetchListType(const AZ::TypeId& typeId); - AZStd::string FetchMapType(const AZ::TypeId& typeId); - AZStd::string FetchOutcomeType(const AZ::TypeId& typeId); - AZStd::string TypeNameFallback(const AZ::TypeId& typeId); FileHandlePtr OpenInitFileAt(AZStd::string_view moduleName); FileHandlePtr OpenModuleAt(AZStd::string_view moduleName); void WriteMethod(AZ::IO::HandleType handle, AZStd::string_view methodName, const AZ::BehaviorMethod& behaviorMethod, const AZ::BehaviorClass* behaviorClass); diff --git a/Gems/EditorPythonBindings/Code/Source/PythonMarshalComponent.cpp b/Gems/EditorPythonBindings/Code/Source/PythonMarshalComponent.cpp index 6227bb579c2a..275a419dc287 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonMarshalComponent.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonMarshalComponent.cpp @@ -6,9 +6,9 @@ * */ +#include #include #include -#include #include #include @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include diff --git a/Gems/EditorPythonBindings/Code/Source/PythonMarshalComponent.h b/Gems/EditorPythonBindings/Code/Source/PythonMarshalComponent.h index 606d6d5382c9..6e9a979e59d3 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonMarshalComponent.h +++ b/Gems/EditorPythonBindings/Code/Source/PythonMarshalComponent.h @@ -9,8 +9,8 @@ #include -#include -#include +#include +#include #include #include diff --git a/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp b/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp index 1cfb315c6aad..d88ef7f898ce 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp @@ -8,10 +8,10 @@ #include -#include +#include #include -#include +#include #include #include diff --git a/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.h b/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.h index 1e58e0093ec1..9c0b808e7535 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.h +++ b/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.h @@ -7,7 +7,7 @@ */ #pragma once -#include +#include #include namespace EditorPythonBindings diff --git a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp index 10cfbfae58dc..12524e20afa8 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp @@ -10,11 +10,11 @@ #include -#include -#include +#include +#include #include -#include #include +#include #include #include diff --git a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.h b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.h index c377d70d07d5..9ea203179455 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.h +++ b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.h @@ -12,7 +12,7 @@ #include #include -#include +#include #include namespace EditorPythonBindings diff --git a/Gems/EditorPythonBindings/Code/Source/PythonReflectionComponent.cpp b/Gems/EditorPythonBindings/Code/Source/PythonReflectionComponent.cpp index 992177fba33a..d3d9896c0deb 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonReflectionComponent.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonReflectionComponent.cpp @@ -9,13 +9,12 @@ #include #include - -#include -#include -#include +#include +#include #include #include #include +#include #include #include diff --git a/Gems/EditorPythonBindings/Code/Source/PythonReflectionComponent.h b/Gems/EditorPythonBindings/Code/Source/PythonReflectionComponent.h index 895c13bcc105..1aceacf89d08 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonReflectionComponent.h +++ b/Gems/EditorPythonBindings/Code/Source/PythonReflectionComponent.h @@ -11,7 +11,7 @@ #include #include -#include +#include #include namespace EditorPythonBindings diff --git a/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp b/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp index 45180b23d59b..90fb7d4e02f5 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp @@ -9,12 +9,12 @@ #include #include -#include +#include #include -#include +#include // for DELIM #include #include -#include // for DELIM +#include #include #include diff --git a/Gems/EditorPythonBindings/Code/Source/PythonTypeCasters.h b/Gems/EditorPythonBindings/Code/Source/PythonTypeCasters.h index 87d83de3d835..a5fa2d530ec1 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonTypeCasters.h +++ b/Gems/EditorPythonBindings/Code/Source/PythonTypeCasters.h @@ -7,9 +7,9 @@ */ #pragma once -#include -#include #include +#include +#include namespace pybind11 { diff --git a/Gems/EditorPythonBindings/Code/Source/PythonUtility.cpp b/Gems/EditorPythonBindings/Code/Source/PythonUtility.cpp index 3fcddb0f208a..ffb9113142a0 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonUtility.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonUtility.cpp @@ -6,14 +6,17 @@ * */ -#include +#include "AzCore/Script/ScriptContextAttributes.h" +#include +#include #include #include -#include #include #include #include +#include +#include #include #include #include @@ -22,7 +25,12 @@ namespace EditorPythonBindings { namespace Module { - pybind11::module DeterminePackageModule(PackageMapType& modulePackageMap, AZStd::string_view moduleName, pybind11::module parentModule, pybind11::module fallbackModule, [[maybe_unused]] bool alertUsingFallback) + pybind11::module DeterminePackageModule( + PackageMapType& modulePackageMap, + AZStd::string_view moduleName, + pybind11::module parentModule, + pybind11::module fallbackModule, + [[maybe_unused]] bool alertUsingFallback) { if (moduleName.empty() || !moduleName[0]) { @@ -75,7 +83,7 @@ namespace EditorPythonBindings } return currentModule; } - } + } // namespace Module namespace Internal { @@ -90,23 +98,27 @@ namespace EditorPythonBindings auto&& classInfo = serializeContext->FindClassData(typeId); if (classInfo) { - info = AZStd::string::format("name:%s version:%d isContainer:%s", - classInfo->m_name, classInfo->m_version, classInfo->m_container ? "true" : "false"); + info = AZStd::string::format( + "name:%s version:%d isContainer:%s", + classInfo->m_name, + classInfo->m_version, + classInfo->m_container ? "true" : "false"); } auto&& genericClassInfo = serializeContext->FindGenericClassInfo(typeId); if (genericClassInfo) { info += " generic:true"; - info += AZStd::string::format(" specialized typeId: %s", - genericClassInfo->GetSpecializedTypeId().ToString().c_str()); - info += AZStd::string::format(" generic typeId: %s", - genericClassInfo->GetGenericTypeId().ToString().c_str()); + info += AZStd::string::format( + " specialized typeId: %s", genericClassInfo->GetSpecializedTypeId().ToString().c_str()); + info += AZStd::string::format( + " generic typeId: %s", genericClassInfo->GetGenericTypeId().ToString().c_str()); size_t numTemplatedArguments = genericClassInfo->GetNumTemplatedArguments(); info += AZStd::string::format(" template arguments %zu", genericClassInfo->GetNumTemplatedArguments()); for (size_t index = 0; index < numTemplatedArguments; ++index) { - info += AZStd::string::format(" [%zu] template type: %s", + info += AZStd::string::format( + " [%zu] template type: %s", index, genericClassInfo->GetTemplatedTypeId(index).ToString().c_str()); } @@ -130,8 +142,9 @@ namespace EditorPythonBindings return AZStd::nullopt; } - template - bool ConvertPythonFromEnumClass(const AZ::TypeId& underlyingTypeId, AZ::BehaviorArgument& behaviorValue, AZ::s64& outboundPythonValue) + template + bool ConvertPythonFromEnumClass( + const AZ::TypeId& underlyingTypeId, AZ::BehaviorArgument& behaviorValue, AZ::s64& outboundPythonValue) { if (underlyingTypeId == AZ::AzTypeInfo::Uuid()) { @@ -152,12 +165,11 @@ namespace EditorPythonBindings { AZ::s64 outboundPythonValue = 0; - bool converted = - ConvertPythonFromEnumClass(underlyingTypeId, behaviorValue, outboundPythonValue) || + bool converted = ConvertPythonFromEnumClass(underlyingTypeId, behaviorValue, outboundPythonValue) || ConvertPythonFromEnumClass(underlyingTypeId, behaviorValue, outboundPythonValue) || ConvertPythonFromEnumClass(underlyingTypeId, behaviorValue, outboundPythonValue) || ConvertPythonFromEnumClass(underlyingTypeId, behaviorValue, outboundPythonValue) || - ConvertPythonFromEnumClass(underlyingTypeId, behaviorValue, outboundPythonValue) || + ConvertPythonFromEnumClass(underlyingTypeId, behaviorValue, outboundPythonValue) || ConvertPythonFromEnumClass(underlyingTypeId, behaviorValue, outboundPythonValue) || ConvertPythonFromEnumClass(underlyingTypeId, behaviorValue, outboundPythonValue) || ConvertPythonFromEnumClass(underlyingTypeId, behaviorValue, outboundPythonValue); @@ -168,7 +180,7 @@ namespace EditorPythonBindings return AZStd::nullopt; } - template + template bool ConvertBehaviorParameterEnum(pybind11::object obj, const AZ::TypeId& underlyingTypeId, AZ::BehaviorArgument& parameter) { if (underlyingTypeId == AZ::AzTypeInfo::Uuid()) @@ -189,7 +201,8 @@ namespace EditorPythonBindings return false; } - bool ConvertEnumClassFromPython(pybind11::object obj, const AZ::BehaviorParameter& behaviorArgument, AZ::BehaviorArgument& parameter) + bool ConvertEnumClassFromPython( + pybind11::object obj, const AZ::BehaviorParameter& behaviorArgument, AZ::BehaviorArgument& parameter) { if (behaviorArgument.m_azRtti) { @@ -202,15 +215,14 @@ namespace EditorPythonBindings parameter.m_traits = behaviorArgument.m_traits; parameter.m_typeId = behaviorArgument.m_typeId; - bool handled = - ConvertBehaviorParameterEnum(obj, underlyingTypeId, parameter) || + bool handled = ConvertBehaviorParameterEnum(obj, underlyingTypeId, parameter) || ConvertBehaviorParameterEnum(obj, underlyingTypeId, parameter) || ConvertBehaviorParameterEnum(obj, underlyingTypeId, parameter) || ConvertBehaviorParameterEnum(obj, underlyingTypeId, parameter) || - ConvertBehaviorParameterEnum(obj, underlyingTypeId, parameter) || + ConvertBehaviorParameterEnum(obj, underlyingTypeId, parameter) || ConvertBehaviorParameterEnum(obj, underlyingTypeId, parameter) || ConvertBehaviorParameterEnum(obj, underlyingTypeId, parameter) || - ConvertBehaviorParameterEnum(obj, underlyingTypeId, parameter) ; + ConvertBehaviorParameterEnum(obj, underlyingTypeId, parameter); AZ_Error("python", handled, "Enumeration backed by a non-numeric integer type."); return handled; @@ -222,39 +234,30 @@ namespace EditorPythonBindings // type checks bool IsPrimitiveType(const AZ::TypeId& typeId) { - return (typeId == AZ::AzTypeInfo::Uuid() || - typeId == AZ::AzTypeInfo::Uuid() || - typeId == AZ::AzTypeInfo::Uuid() || - typeId == AZ::AzTypeInfo::Uuid() || - typeId == AZ::AzTypeInfo::Uuid() || - typeId == AZ::AzTypeInfo::Uuid() || - typeId == AZ::AzTypeInfo::Uuid() || - typeId == AZ::AzTypeInfo::Uuid() || - typeId == AZ::AzTypeInfo::Uuid() || - typeId == AZ::AzTypeInfo::Uuid() || - typeId == AZ::AzTypeInfo::Uuid() || - typeId == AZ::AzTypeInfo::Uuid() ); + return ( + typeId == AZ::AzTypeInfo::Uuid() || typeId == AZ::AzTypeInfo::Uuid() || + typeId == AZ::AzTypeInfo::Uuid() || typeId == AZ::AzTypeInfo::Uuid() || + typeId == AZ::AzTypeInfo::Uuid() || typeId == AZ::AzTypeInfo::Uuid() || + typeId == AZ::AzTypeInfo::Uuid() || typeId == AZ::AzTypeInfo::Uuid() || + typeId == AZ::AzTypeInfo::Uuid() || typeId == AZ::AzTypeInfo::Uuid() || + typeId == AZ::AzTypeInfo::Uuid() || typeId == AZ::AzTypeInfo::Uuid()); } bool IsPointerType(const AZ::u32 traits) { - return (((traits & AZ::BehaviorParameter::TR_POINTER) == AZ::BehaviorParameter::TR_POINTER) || - ((traits & AZ::BehaviorParameter::TR_REFERENCE) == AZ::BehaviorParameter::TR_REFERENCE)); + return ( + ((traits & AZ::BehaviorParameter::TR_POINTER) == AZ::BehaviorParameter::TR_POINTER) || + ((traits & AZ::BehaviorParameter::TR_REFERENCE) == AZ::BehaviorParameter::TR_REFERENCE)); } // allocation patterns void StoreVariableCustomTypeDeleter( - CustomTypeBindingNotifications::ValueHandle handle, - AZ::TypeId typeId, - Convert::StackVariableAllocator& stackVariableAllocator) + CustomTypeBindingNotifications::ValueHandle handle, AZ::TypeId typeId, Convert::StackVariableAllocator& stackVariableAllocator) { auto deallocateValue = [typeId = std::move(typeId), handle]() mutable { - CustomTypeBindingNotificationBus::Event( - typeId, - &CustomTypeBindingNotificationBus::Events::CleanUpValue, - handle); + CustomTypeBindingNotificationBus::Event(typeId, &CustomTypeBindingNotificationBus::Events::CleanUpValue, handle); }; stackVariableAllocator.StoreVariableDeleter(deallocateValue); } @@ -272,13 +275,18 @@ namespace EditorPythonBindings } else { - AZ_Warning("python", behaviorClass->m_defaultConstructor, "Missing default constructor for AZ::BehaviorClass for typeId:%s", behaviorClass->m_name.c_str()); + AZ_Warning( + "python", + behaviorClass->m_defaultConstructor, + "Missing default constructor for AZ::BehaviorClass for typeId:%s", + behaviorClass->m_name.c_str()); } } return false; } - bool AllocateBehaviorValueParameter(const AZ::BehaviorMethod* behaviorMethod, AZ::BehaviorArgument& result, Convert::StackVariableAllocator& stackVariableAllocator) + bool AllocateBehaviorValueParameter( + const AZ::BehaviorMethod* behaviorMethod, AZ::BehaviorArgument& result, Convert::StackVariableAllocator& stackVariableAllocator) { if (const AZ::BehaviorParameter* resultType = behaviorMethod->GetResult()) { @@ -329,9 +337,7 @@ namespace EditorPythonBindings { CustomTypeBindingNotifications::AllocationHandle allocationHandleResult; CustomTypeBindingNotificationBus::EventResult( - allocationHandleResult, - result.m_typeId, - &CustomTypeBindingNotificationBus::Events::AllocateDefault); + allocationHandleResult, result.m_typeId, &CustomTypeBindingNotificationBus::Events::AllocateDefault); if (allocationHandleResult) { @@ -347,7 +353,10 @@ namespace EditorPythonBindings // so this code tries to pull out more type information about the typeId so that the user can get more human readable // information than a UUID LogSerializeTypeInfo(resultType->m_typeId); - AZ_Error("python", behaviorClass, "A behavior class is missing for %s!", + AZ_Error( + "python", + behaviorClass, + "A behavior class is missing for %s!", resultType->m_typeId.ToString().c_str()); } } @@ -378,7 +387,7 @@ namespace EditorPythonBindings behaviorClass->Destroy(behaviorObject); } } - } + } // namespace Internal namespace Convert { @@ -399,7 +408,8 @@ namespace EditorPythonBindings // from Python to BehaviorArgument - bool PythonProxyObjectToBehaviorValueParameter(const AZ::BehaviorParameter& behaviorArgument, pybind11::object pyObj, AZ::BehaviorArgument& parameter) + bool PythonProxyObjectToBehaviorValueParameter( + const AZ::BehaviorParameter& behaviorArgument, pybind11::object pyObj, AZ::BehaviorArgument& parameter) { auto behaviorObject = pybind11::cast(pyObj)->GetBehaviorObject(); if (behaviorObject) @@ -407,7 +417,11 @@ namespace EditorPythonBindings const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(behaviorObject.value()->m_typeId); if (!behaviorClass) { - AZ_Warning("python", false, "Missing BehaviorClass for typeId %s", behaviorObject.value()->m_typeId.ToString().c_str()); + AZ_Warning( + "python", + false, + "Missing BehaviorClass for typeId %s", + behaviorObject.value()->m_typeId.ToString().c_str()); return false; } @@ -469,11 +483,22 @@ namespace EditorPythonBindings return false; } - bool PythonToBehaviorValueParameter(const AZ::BehaviorParameter& behaviorArgument, pybind11::object pyObj, AZ::BehaviorArgument& parameter, Convert::StackVariableAllocator& stackVariableAllocator) + bool PythonToBehaviorValueParameter( + const AZ::BehaviorParameter& behaviorArgument, + pybind11::object pyObj, + AZ::BehaviorArgument& parameter, + Convert::StackVariableAllocator& stackVariableAllocator) { AZStd::optional result; - PythonMarshalTypeRequests::BehaviorTraits traits = static_cast(behaviorArgument.m_traits); - PythonMarshalTypeRequestBus::EventResult(result, behaviorArgument.m_typeId, &PythonMarshalTypeRequestBus::Events::PythonToBehaviorValueParameter, traits, pyObj, parameter); + PythonMarshalTypeRequests::BehaviorTraits traits = + static_cast(behaviorArgument.m_traits); + PythonMarshalTypeRequestBus::EventResult( + result, + behaviorArgument.m_typeId, + &PythonMarshalTypeRequestBus::Events::PythonToBehaviorValueParameter, + traits, + pyObj, + parameter); if (result && result.value().first) { auto deleter = AZStd::move(result.value().second); @@ -513,16 +538,13 @@ namespace EditorPythonBindings // from BehaviorArgument to Python - AZStd::optional CustomBehaviorToPython(AZ::BehaviorArgument& behaviorValue, Convert::StackVariableAllocator& stackVariableAllocator) + AZStd::optional CustomBehaviorToPython( + AZ::BehaviorArgument& behaviorValue, Convert::StackVariableAllocator& stackVariableAllocator) { AZStd::optional handle; PyObject* outPyObj = nullptr; CustomTypeBindingNotificationBus::EventResult( - handle, - behaviorValue.m_typeId, - &CustomTypeBindingNotificationBus::Events::BehaviorToPython, - behaviorValue, - outPyObj); + handle, behaviorValue.m_typeId, &CustomTypeBindingNotificationBus::Events::BehaviorToPython, behaviorValue, outPyObj); if (outPyObj != nullptr && handle) { @@ -533,7 +555,8 @@ namespace EditorPythonBindings return AZStd::nullopt; } - pybind11::object BehaviorValueParameterToPython(AZ::BehaviorArgument& behaviorValue, Convert::StackVariableAllocator& stackVariableAllocator) + pybind11::object BehaviorValueParameterToPython( + AZ::BehaviorArgument& behaviorValue, Convert::StackVariableAllocator& stackVariableAllocator) { auto pyValue = Internal::ConvertFromEnumClass(behaviorValue); if (pyValue.has_value()) @@ -542,7 +565,8 @@ namespace EditorPythonBindings } AZStd::optional result; - PythonMarshalTypeRequestBus::EventResult(result, behaviorValue.m_typeId, &PythonMarshalTypeRequestBus::Events::BehaviorValueParameterToPython, behaviorValue); + PythonMarshalTypeRequestBus::EventResult( + result, behaviorValue.m_typeId, &PythonMarshalTypeRequestBus::Events::BehaviorValueParameterToPython, behaviorValue); if (result.has_value()) { auto deleter = AZStd::move(result.value().second); @@ -560,7 +584,10 @@ namespace EditorPythonBindings { return PythonProxyObjectManagement::CreatePythonProxyObject(behaviorValue.m_typeId, behaviorValue.GetValueAddress()); } - AZ_Warning("python", false, "Cannot convert type %s", + AZ_Warning( + "python", + false, + "Cannot convert type %s", behaviorValue.m_name ? behaviorValue.m_name : behaviorValue.m_typeId.ToString().c_str()); return pybind11::cast(Py_None); } @@ -573,18 +600,24 @@ namespace EditorPythonBindings } return pybind11::cast(pybind11::str(pyObj.get_type())); } - } + } // namespace Convert namespace Call { constexpr size_t MaxBehaviorMethodArguments = 32; using BehaviorMethodArgumentArray = AZStd::array; - pybind11::object InvokeBehaviorMethodWithResult(AZ::BehaviorMethod* behaviorMethod, pybind11::args pythonInputArgs, AZ::BehaviorObject self, AZ::BehaviorArgument& result) + pybind11::object InvokeBehaviorMethodWithResult( + AZ::BehaviorMethod* behaviorMethod, pybind11::args pythonInputArgs, AZ::BehaviorObject self, AZ::BehaviorArgument& result) { if (behaviorMethod->GetNumArguments() > MaxBehaviorMethodArguments || pythonInputArgs.size() > MaxBehaviorMethodArguments) { - AZ_Error("python", false, "Too many arguments for class method; set:%zu max:%zu", behaviorMethod->GetMinNumberOfArguments(), MaxBehaviorMethodArguments); + AZ_Error( + "python", + false, + "Too many arguments for class method; set:%zu max:%zu", + behaviorMethod->GetMinNumberOfArguments(), + MaxBehaviorMethodArguments); return pybind11::cast(Py_None); } @@ -628,14 +661,25 @@ namespace EditorPythonBindings const AZ::BehaviorParameter* behaviorArgument = behaviorMethod->GetArgument(parameterCount); if (!behaviorArgument) { - AZ_Warning("python", false, "Missing argument at index %d in class method %s", parameterCount, behaviorMethod->m_name.c_str()); + AZ_Warning( + "python", + false, + "Missing argument at index %d in class method %s", + parameterCount, + behaviorMethod->m_name.c_str()); return pybind11::cast(Py_None); } - if (!Convert::PythonToBehaviorValueParameter(*behaviorArgument, currentPythonArg, parameters[parameterCount], stackVariableAllocator)) + if (!Convert::PythonToBehaviorValueParameter( + *behaviorArgument, currentPythonArg, parameters[parameterCount], stackVariableAllocator)) { - AZ_Warning("python", false, "BehaviorMethod %s: Parameter at [%d] index expects (%s:%s) for method but got type (%s)", - behaviorMethod->m_name.c_str(), parameterCount, - behaviorArgument->m_name, behaviorArgument->m_typeId.ToString().c_str(), + AZ_Warning( + "python", + false, + "BehaviorMethod %s: Parameter at [%d] index expects (%s:%s) for method but got type (%s)", + behaviorMethod->m_name.c_str(), + parameterCount, + behaviorArgument->m_name, + behaviorArgument->m_typeId.ToString().c_str(), Convert::GetPythonTypeName(currentPythonArg).c_str()); return pybind11::cast(Py_None); } @@ -644,15 +688,28 @@ namespace EditorPythonBindings } // did the Python script send the right amount of arguments? - const auto totalPythonArgs = pythonInputArgs.size() + (self.IsValid() ? 1 : 0); // +1 for the 'this' coming in from a marshaled Python/BehaviorObject + const auto totalPythonArgs = + pythonInputArgs.size() + (self.IsValid() ? 1 : 0); // +1 for the 'this' coming in from a marshaled Python/BehaviorObject if (totalPythonArgs < behaviorMethod->GetMinNumberOfArguments()) { - AZ_Warning("python", false, "Method %s requires at least %zu parameters got %zu", behaviorMethod->m_name.c_str(), behaviorMethod->GetMinNumberOfArguments(), totalPythonArgs); + AZ_Warning( + "python", + false, + "Method %s requires at least %zu parameters got %zu", + behaviorMethod->m_name.c_str(), + behaviorMethod->GetMinNumberOfArguments(), + totalPythonArgs); return pybind11::cast(Py_None); } else if (totalPythonArgs > behaviorMethod->GetNumArguments()) { - 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); + 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); } if (behaviorMethod->HasResult()) @@ -717,5 +774,569 @@ namespace EditorPythonBindings } return pybind11::cast(Py_None); } - } -} + } // namespace Call + + namespace Text + { + namespace Internal + { + AZStd::string ReadStringAttribute(const AZ::AttributeArray& attributes, const AZ::Crc32& attribute) + { + AZStd::string attributeValue = ""; + if (auto attributeItem = azrtti_cast*>(AZ::FindAttribute(attribute, attributes))) + { + attributeValue = attributeItem->Get(nullptr); + return attributeValue; + } + + if (auto attributeItem = azrtti_cast*>(AZ::FindAttribute(attribute, attributes))) + { + attributeValue = attributeItem->Get(nullptr); + return attributeValue; + } + return {}; + } + + AZStd::string TypeNameFallback(const AZ::TypeId& typeId) + { + // fall back to class data m_name + AZ::SerializeContext* serializeContext = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext); + + if (serializeContext) + { + auto classData = serializeContext->FindClassData(typeId); + if (classData) + { + return classData->m_name; + } + } + + return ""; + } + + void Indent(int level, AZStd::string& buffer) + { + buffer.append(level * 4, ' '); + } + + void AddCommentBlock(int level, const AZStd::string& comment, AZStd::string& buffer) + { + Indent(level, buffer); + AzFramework::StringFunc::Append(buffer, "\"\"\"\n"); + Indent(level, buffer); + AzFramework::StringFunc::Append(buffer, comment.c_str()); + Indent(level, buffer); + AzFramework::StringFunc::Append(buffer, "\"\"\"\n"); + } + } // namespace Internal + + AZStd::string PythonBehaviorDescription::FetchListType(const AZ::TypeId& typeId) + { + AZStd::string type = "list"; + + AZStd::vector typeList = AZ::Utils::GetContainedTypes(typeId); + if (!typeList.empty()) + { + // trait info not available, so defaulting to TR_NONE + AZStd::string_view itemType = FetchPythonTypeAndTraits(typeList[0], AZ::BehaviorParameter::TR_NONE); + if (!itemType.empty()) + { + type = AZStd::string::format("List[" AZ_STRING_FORMAT "]", AZ_STRING_ARG(itemType)); + } + } + + return type; + } + + AZStd::string PythonBehaviorDescription::FetchMapType(const AZ::TypeId& typeId) + { + AZStd::string type = "dict"; + + AZStd::vector typeList = AZ::Utils::GetContainedTypes(typeId); + if (!typeList.empty()) + { + // trait info not available, so defaulting to TR_NONE + AZStd::string_view kType = FetchPythonTypeAndTraits(typeList[0], AZ::BehaviorParameter::TR_NONE); + AZStd::string_view vType = FetchPythonTypeAndTraits(typeList[1], AZ::BehaviorParameter::TR_NONE); + if (!kType.empty() && !vType.empty()) + { + type = AZStd::string::format( + "Dict[" AZ_STRING_FORMAT ", " AZ_STRING_FORMAT "]", AZ_STRING_ARG(kType), AZ_STRING_ARG(vType)); + } + } + + return type; + } + + AZStd::string_view PythonBehaviorDescription::FetchPythonTypeAndTraits(const AZ::TypeId& typeId, AZ::u32 traits) + { + if (m_typeCache.find(typeId) == m_typeCache.end()) + { + AZStd::string type; + if (AZ::AzTypeInfo::Uuid() == typeId || AZ::AzTypeInfo::Uuid() == typeId) + { + type = "str"; + } + else if ( + AZ::AzTypeInfo::Uuid() == typeId && traits & AZ::BehaviorParameter::TR_POINTER && + traits & AZ::BehaviorParameter::TR_CONST) + { + type = "str"; + } + else if (AZ::AzTypeInfo::Uuid() == typeId || AZ::AzTypeInfo::Uuid() == typeId) + { + type = "float"; + } + else if (AZ::AzTypeInfo::Uuid() == typeId) + { + type = "bool"; + } + else if ( + AZ::AzTypeInfo::Uuid() == typeId || AZ::AzTypeInfo::Uuid() == typeId || + AZ::AzTypeInfo::Uuid() == typeId || AZ::AzTypeInfo::Uuid() == typeId || + AZ::AzTypeInfo::Uuid() == typeId || AZ::AzTypeInfo::Uuid() == typeId || + AZ::AzTypeInfo::Uuid() == typeId || AZ::AzTypeInfo::Uuid() == typeId) + { + type = "int"; + } + else if (AZ::AzTypeInfo>::Uuid() == typeId) + { + type = "bytes"; + } + else if (AZ::AzTypeInfo::Uuid() == typeId) + { + type = "object"; + } + else if (AZ::AzTypeInfo::Uuid() == typeId) + { + type = "None"; + } + else if (AZ::Utils::IsVectorContainerType(typeId)) + { + type = FetchListType(typeId); + } + else if (AZ::Utils::IsMapContainerType(typeId)) + { + type = FetchMapType(typeId); + } + else if (AZ::Utils::IsOutcomeType(typeId)) + { + type = FetchOutcomeType(typeId); + } + else + { + type = Internal::TypeNameFallback(typeId); + } + + m_typeCache[typeId] = type; + } + + return m_typeCache[typeId]; + } + + AZStd::string PythonBehaviorDescription::FetchPythonTypeName(const AZ::BehaviorParameter& param) + { + AZStd::string pythonType = FetchPythonTypeAndTraits(param.m_typeId, param.m_traits); + + if (pythonType.empty()) + { + if (AZ::StringFunc::Equal(param.m_name, "void")) + { + return "None"; + } + + return param.m_name; + } + return pythonType; + } + + AZStd::string PythonBehaviorDescription::FetchOutcomeType(const AZ::TypeId& typeId) + { + AZStd::string type = "Outcome"; + AZStd::pair outcomeTypes = AZ::Utils::GetOutcomeTypes(typeId); + + // trait info not available, so defaulting to TR_NONE + AZStd::string_view valueT = FetchPythonTypeAndTraits(outcomeTypes.first, AZ::BehaviorParameter::TR_NONE); + AZStd::string_view errorT = FetchPythonTypeAndTraits(outcomeTypes.second, AZ::BehaviorParameter::TR_NONE); + if (!valueT.empty() && !errorT.empty()) + { + type = AZStd::string::format( + "Outcome[" AZ_STRING_FORMAT ", " AZ_STRING_FORMAT "]", AZ_STRING_ARG(valueT), AZ_STRING_ARG(errorT)); + } + + return type; + } + + //! Creates a string containing bus events and documentation. + AZStd::string PythonBehaviorDescription::BusDefinition(const AZStd::string busName, const AZ::BehaviorEBus* behaviorEBus) + { + AZStd::string buffer; + if (!behaviorEBus || behaviorEBus->m_events.empty()) + { + return buffer; + } + + const auto& eventSenderEntry = behaviorEBus->m_events.begin(); + const AZ::BehaviorEBusEventSender& sender = eventSenderEntry->second; + + AzFramework::StringFunc::Append(buffer, "def "); + AzFramework::StringFunc::Append(buffer, busName.data()); + bool isBroadcast = false; + if (sender.m_event) + { + AZStd::string addressType = FetchPythonTypeName(behaviorEBus->m_idParam); + if (addressType.empty()) + { + AzFramework::StringFunc::Append(buffer, "(busCallType: int, busEventName: str, address: Any, args: Tuple[Any])"); + } + else + { + AzFramework::StringFunc::Append(buffer, "(busCallType: int, busEventName: str, address: "); + AzFramework::StringFunc::Append(buffer, AZStd::string::format(AZ_STRING_FORMAT, AZ_STRING_ARG(addressType)).c_str()); + AzFramework::StringFunc::Append(buffer, ", args: Tuple[Any])"); + } + } + else + { + AzFramework::StringFunc::Append(buffer, "(busCallType: int, busEventName: str, args: Tuple[Any])"); + isBroadcast = true; + } + AzFramework::StringFunc::Append(buffer, " -> Any:\n"); + + auto eventInfoBuilder = + [this](const AZ::BehaviorMethod* behaviorMethod, AZStd::string& inOutStrBuffer, [[maybe_unused]] TypeMap& typeCache) + { + AzFramework::StringFunc::Append(inOutStrBuffer, "("); + + size_t numArguments = behaviorMethod->GetNumArguments(); + + const AZ::BehaviorParameter* busIdArg = behaviorMethod->GetBusIdArgument(); + + for (size_t i = 0; i < numArguments; ++i) + { + const AZ::BehaviorParameter* argParam = behaviorMethod->GetArgument(i); + if (argParam == busIdArg) + { + // address argument is part of the bus call, skip from event argument list + continue; + } + + AZStd::string_view argType = FetchPythonTypeAndTraits(argParam->m_typeId, argParam->m_traits); + AzFramework::StringFunc::Append(inOutStrBuffer, argType.data()); + + if (i < (numArguments - 1)) + { + AzFramework::StringFunc::Append(inOutStrBuffer, ", "); + } + } + + const AZ::BehaviorParameter* resultParam = behaviorMethod->GetResult(); + AZStd::string returnType = FetchPythonTypeName(*resultParam); + AZStd::string returnTypeStr = AZStd::string::format(") -> " AZ_STRING_FORMAT " \n", AZ_STRING_ARG(returnType)); + AzFramework::StringFunc::Append(inOutStrBuffer, returnTypeStr.c_str()); + }; + + // record the event names the behavior can send, their parameters and return type + AZStd::string comment = behaviorEBus->m_toolTip; + if (comment.empty()) + { + comment = Internal::ReadStringAttribute(behaviorEBus->m_attributes, AZ::Script::Attributes::ToolTip); + } + + if (!behaviorEBus->m_events.empty()) + { + AzFramework::StringFunc::Append( + comment, "The following bus Call types, Event names and Argument types are supported by this bus:\n"); + AZStd::vector events; + for (const auto& eventSenderEntry2 : behaviorEBus->m_events) + { + const AZStd::string& eventName = eventSenderEntry2.first; + AZStd::string eventNameStr = AZStd::string::format("'%s', ", eventName.c_str()); + + // prefer m_event info over m_broadcast + if (!isBroadcast && eventSenderEntry2.second.m_event != nullptr) + { + AZStd::string eventInfo; + AzFramework::StringFunc::Append(eventInfo, "bus.Event, "); + AzFramework::StringFunc::Append(eventInfo, eventNameStr.c_str()); + eventInfoBuilder(eventSenderEntry2.second.m_event, eventInfo, m_typeCache); + events.push_back(eventInfo); + } + else if (isBroadcast && eventSenderEntry2.second.m_broadcast != nullptr) + { + AZStd::string eventInfo; + AzFramework::StringFunc::Append(eventInfo, "bus.Broadcast, "); + AzFramework::StringFunc::Append(eventInfo, eventNameStr.c_str()); + eventInfoBuilder(eventSenderEntry2.second.m_broadcast, eventInfo, m_typeCache); + events.push_back(eventInfo); + } + else + { + AZ_Warning("python", false, "Event %s is expected to have valid event information.", eventName.c_str()); + } + } + + AZStd::sort(events.begin(), events.end()); + + for (auto& eventInfo : events) + { + Internal::Indent(1, comment); + AzFramework::StringFunc::Append(comment, eventInfo.c_str()); + } + } + + Internal::AddCommentBlock(1, comment, buffer); + + Internal::Indent(1, buffer); + AzFramework::StringFunc::Append(buffer, "pass\n\n"); + + // can the EBus create & destroy a handler? + if (behaviorEBus->m_createHandler && behaviorEBus->m_destroyHandler) + { + AzFramework::StringFunc::Append(buffer, "def "); + AzFramework::StringFunc::Append(buffer, busName.data()); + AzFramework::StringFunc::Append(buffer, "Handler() -> None:\n"); + Internal::Indent(1, buffer); + AzFramework::StringFunc::Append(buffer, "pass\n\n"); + } + return buffer; + } + + //! Creates a string with class or global method definition and documentation. + AZStd::string PythonBehaviorDescription::MethodDefinition( + const AZStd::string methodName, + const AZ::BehaviorMethod& behaviorMethod, + const AZ::BehaviorClass* behaviorClass, + bool defineTooltip, + bool defineDebugDescription) + { + AZStd::string buffer; + AZStd::vector pythonArgs; + const bool isMemberLike = + behaviorClass ? PythonProxyObjectManagement::IsMemberLike(behaviorMethod, behaviorClass->m_typeId) : false; + + int indentLevel = 0; + if (isMemberLike) + { + indentLevel = 1; + Internal::Indent(indentLevel, buffer); + pythonArgs.emplace_back("self"); + } + + AzFramework::StringFunc::Append(buffer, "def "); + if (isMemberLike || !behaviorClass) + { + AzFramework::StringFunc::Append(buffer, methodName.data()); + } + else + { + AzFramework::StringFunc::Append(buffer, behaviorClass->m_name.c_str()); + AzFramework::StringFunc::Append(buffer, "_"); + AzFramework::StringFunc::Append(buffer, methodName.data()); + } + AzFramework::StringFunc::Append(buffer, "("); + + AZStd::string bufferArg; + for (size_t argIndex = 0; argIndex < behaviorMethod.GetNumArguments(); ++argIndex) + { + const AZStd::string* name = behaviorMethod.GetArgumentName(argIndex); + if (!name || name->empty()) + { + bufferArg = AZStd::string::format(" arg%zu", argIndex); + } + else + { + bufferArg = *name; + } + + AZStd::string type = FetchPythonTypeName(*behaviorMethod.GetArgument(argIndex)); + if (!type.empty()) + { + AzFramework::StringFunc::Append(bufferArg, ": "); + AzFramework::StringFunc::Append(bufferArg, type.data()); + } + + pythonArgs.push_back(bufferArg); + bufferArg.clear(); + } + + AZStd::string argsList; + AzFramework::StringFunc::Join(buffer, pythonArgs.begin(), pythonArgs.end(), ","); + AzFramework::StringFunc::Append(buffer, ") -> None:\n"); + + AZStd::string methodTooltipAndDebugDescription = ""; + + if (defineDebugDescription && behaviorMethod.m_debugDescription != nullptr) + { + AZStd::string debugDescription(behaviorMethod.m_debugDescription); + if (!debugDescription.empty()) + { + methodTooltipAndDebugDescription += debugDescription; + methodTooltipAndDebugDescription += "\n"; + } + } + if (defineTooltip) + { + AZStd::string methodTooltip = Internal::ReadStringAttribute(behaviorMethod.m_attributes, AZ::Script::Attributes::ToolTip); + if (!methodTooltip.empty()) + { + methodTooltipAndDebugDescription += methodTooltip; + methodTooltipAndDebugDescription += "\n"; + } + } + if (!methodTooltipAndDebugDescription.empty()) + { + Internal::AddCommentBlock(indentLevel + 1, methodTooltipAndDebugDescription, buffer); + } + + Internal::Indent(indentLevel + 1, buffer); + AzFramework::StringFunc::Append(buffer, "pass\n\n"); + return buffer; + } + + AZStd::string PythonBehaviorDescription::ClassDefinition( + const AZ::BehaviorClass* behaviorClass, + const AZStd::string className, + bool defineProperties, + bool defineMethods, + bool defineTooltip) + { + AZStd::string buffer; + AzFramework::StringFunc::Append(buffer, "class "); + AzFramework::StringFunc::Append(buffer, className.data()); + AzFramework::StringFunc::Append(buffer, ":\n"); + + if (behaviorClass->m_methods.empty() && behaviorClass->m_properties.empty()) + { + AZStd::string body; + if (defineProperties && defineMethods) + { + body = " # behavior class type with no methods or properties \n"; + } + else if (defineProperties) + { + body = " # behavior class type with no properties \n"; + } + else if (defineMethods) + { + body = " # behavior class type with no methods \n"; + } + else + { + body = ""; + } + if (defineTooltip) + { + AZStd::string classTooltip = + Internal::ReadStringAttribute(behaviorClass->m_attributes, AZ::Script::Attributes::ToolTip); + if (!classTooltip.empty()) + { + Internal::AddCommentBlock(1, classTooltip, body); + } + } + + Internal::Indent(1, body); + AzFramework::StringFunc::Append(body, "pass\n\n"); + AzFramework::StringFunc::Append(buffer, body.c_str()); + } + else + { + if (defineProperties) + { + for (const auto& propertyEntry : behaviorClass->m_properties) + { + AZ::BehaviorProperty* property = propertyEntry.second; + AZStd::string propertyName{ propertyEntry.first }; + Scope::FetchScriptName(property->m_attributes, propertyName); + AZStd::string propertyDef = PropertyDefinition(propertyName, 1, *property, behaviorClass); + AzFramework::StringFunc::Append(buffer, propertyDef.c_str()); + } + } + + if (defineMethods) + { + for (const auto& methodEntry : behaviorClass->m_methods) + { + AZ::BehaviorMethod* method = methodEntry.second; + if (method && PythonProxyObjectManagement::IsMemberLike(*method, behaviorClass->m_typeId)) + { + AZStd::string baseMethodName{ methodEntry.first }; + Scope::FetchScriptName(method->m_attributes, baseMethodName); + AZStd::string methodDef = MethodDefinition(baseMethodName, *method, behaviorClass, defineTooltip); + AzFramework::StringFunc::Append(buffer, methodDef.c_str()); + } + } + } + } + + return buffer; + } + + AZStd::string PythonBehaviorDescription::PropertyDefinition( + AZStd::string_view propertyName, int level, const AZ::BehaviorProperty& property, [[maybe_unused]] const AZ::BehaviorClass* behaviorClass) + { + AZStd::string buffer; + Internal::Indent(level, buffer); + AzFramework::StringFunc::Append(buffer, "@property\n"); + + Internal::Indent(level, buffer); + AzFramework::StringFunc::Append(buffer, "def "); + AzFramework::StringFunc::Append(buffer, propertyName.data()); + AzFramework::StringFunc::Append(buffer, "(self) -> "); + + AZStd::string_view type = FetchPythonTypeAndTraits(property.GetTypeId(), AZ::BehaviorParameter::TR_NONE); + if (type.empty()) + { + AzFramework::StringFunc::Append(buffer, "Any"); + } + else + { + AzFramework::StringFunc::Append(buffer, type.data()); + } + AzFramework::StringFunc::Append(buffer, ":\n"); + Internal::Indent(level + 1, buffer); + AzFramework::StringFunc::Append(buffer, "pass\n\n"); + return buffer; + } + + AZStd::string PythonBehaviorDescription::GlobalPropertyDefinition( + const AZStd::string moduleName, + const AZStd::string propertyName, + const AZ::BehaviorProperty& behaviorProperty, + bool needsHeader) + { + AZStd::string buffer; + + // add header + if (needsHeader) + { + AzFramework::StringFunc::Append(buffer, "class property():\n"); + } + + Internal::Indent(1, buffer); + AzFramework::StringFunc::Append(buffer, propertyName.data()); + AzFramework::StringFunc::Append(buffer, ": ClassVar["); + + const AZ::BehaviorParameter* resultParam = behaviorProperty.m_getter->GetResult(); + AZStd::string_view type = FetchPythonTypeAndTraits(resultParam->m_typeId, resultParam->m_traits); + if (type.empty()) + { + AzFramework::StringFunc::Append(buffer, "Any"); + } + else + { + AzFramework::StringFunc::Append(buffer, type.data()); + } + AzFramework::StringFunc::Append(buffer, "] = None"); + + if (behaviorProperty.m_getter && !behaviorProperty.m_setter) + { + AzFramework::StringFunc::Append(buffer, " # read only"); + } + AzFramework::StringFunc::Append(buffer, "\n"); + + return buffer; + } + } // namespace Text +} // namespace EditorPythonBindings diff --git a/Gems/EditorPythonBindings/Code/Tests/CustomTypeBindingBusTests.cpp b/Gems/EditorPythonBindings/Code/Tests/CustomTypeBindingBusTests.cpp index c42f3d46fe86..97f387d69d39 100644 --- a/Gems/EditorPythonBindings/Code/Tests/CustomTypeBindingBusTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/CustomTypeBindingBusTests.cpp @@ -5,11 +5,11 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include -#include -#include -#include "PythonTraceMessageSink.h" #include "PythonTestingUtility.h" +#include "PythonTraceMessageSink.h" +#include +#include +#include #include #include diff --git a/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp b/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp index 9dbbb34e6c52..05d2a02c1535 100644 --- a/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp @@ -5,7 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include +#include #include #include "PythonTraceMessageSink.h" diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonAssetTypesTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonAssetTypesTests.cpp index 8ed43673ac00..01d523f4c37f 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonAssetTypesTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonAssetTypesTests.cpp @@ -6,9 +6,9 @@ * */ -#include -#include +#include #include +#include #include "PythonTraceMessageSink.h" #include "PythonTestingUtility.h" diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonAssociativeTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonAssociativeTests.cpp index f4573ca012b4..de4754f95093 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonAssociativeTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonAssociativeTests.cpp @@ -6,9 +6,9 @@ * */ -#include -#include +#include #include +#include #include "PythonTraceMessageSink.h" #include "PythonTestingUtility.h" diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonBindingLibTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonBindingLibTests.cpp index 5aa4921287d9..e532f8ae4f67 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonBindingLibTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonBindingLibTests.cpp @@ -7,12 +7,12 @@ */ #include -#include -#include -#include "PythonTraceMessageSink.h" #include "PythonTestingUtility.h" -#include +#include "PythonTraceMessageSink.h" +#include +#include #include +#include #include #include diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonContainerAnyTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonContainerAnyTests.cpp index f89f9330a91d..5bdf151fa02d 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonContainerAnyTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonContainerAnyTests.cpp @@ -5,10 +5,10 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include #include -#include +#include #include +#include #include "PythonTraceMessageSink.h" #include "PythonTestingUtility.h" diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonDictionaryTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonDictionaryTests.cpp index a106d56d4073..75548485668c 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonDictionaryTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonDictionaryTests.cpp @@ -5,11 +5,11 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include -#include -#include -#include "PythonTraceMessageSink.h" #include "PythonTestingUtility.h" +#include "PythonTraceMessageSink.h" +#include +#include +#include #include #include diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonGlobalsTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonGlobalsTests.cpp index 89637c89a583..74560f0ee74a 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonGlobalsTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonGlobalsTests.cpp @@ -5,11 +5,11 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include -#include -#include -#include "PythonTraceMessageSink.h" #include "PythonTestingUtility.h" +#include "PythonTraceMessageSink.h" +#include +#include +#include #include #include diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonLogSymbolsComponentTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonLogSymbolsComponentTests.cpp index 79de3f1daf87..bfcdd2fcd364 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonLogSymbolsComponentTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonLogSymbolsComponentTests.cpp @@ -5,11 +5,11 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include -#include -#include -#include "PythonTraceMessageSink.h" #include "PythonTestingUtility.h" +#include "PythonTraceMessageSink.h" +#include +#include +#include #include #include diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonPairTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonPairTests.cpp index d1ae530e5f8c..37e69f75206c 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonPairTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonPairTests.cpp @@ -5,9 +5,9 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include -#include +#include #include +#include #include "PythonTraceMessageSink.h" #include "PythonTestingUtility.h" diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonPairTests.h b/Gems/EditorPythonBindings/Code/Tests/PythonPairTests.h index 6ed14f3bf8ec..9dc16bfb2ff6 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonPairTests.h +++ b/Gems/EditorPythonBindings/Code/Tests/PythonPairTests.h @@ -8,9 +8,9 @@ #pragma once -#include -#include +#include #include +#include #include "PythonTraceMessageSink.h" #include "PythonTestingUtility.h" diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonProxyBusTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonProxyBusTests.cpp index d7d617dc13e3..0cc144e677dd 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonProxyBusTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonProxyBusTests.cpp @@ -5,11 +5,11 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include -#include -#include -#include "PythonTraceMessageSink.h" #include "PythonTestingUtility.h" +#include "PythonTraceMessageSink.h" +#include +#include +#include #include #include diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonProxyObjectTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonProxyObjectTests.cpp index b990cea36484..e90ce4d35c53 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonProxyObjectTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonProxyObjectTests.cpp @@ -5,11 +5,11 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include -#include -#include -#include "PythonTraceMessageSink.h" #include "PythonTestingUtility.h" +#include "PythonTraceMessageSink.h" +#include +#include +#include #include #include diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonReflectionComponentTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonReflectionComponentTests.cpp index 3744c514ff11..bac87fc4e631 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonReflectionComponentTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonReflectionComponentTests.cpp @@ -5,9 +5,9 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include -#include +#include #include +#include #include "PythonTraceMessageSink.h" #include "PythonTestingUtility.h" diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonThreadingTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonThreadingTests.cpp index 0d5846aa7493..1e1a4913e785 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonThreadingTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonThreadingTests.cpp @@ -5,11 +5,11 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include -#include -#include -#include "PythonTraceMessageSink.h" #include "PythonTestingUtility.h" +#include "PythonTraceMessageSink.h" +#include +#include +#include #include #include diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonTupleTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonTupleTests.cpp index 657f8a001271..9e3019f90d9e 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonTupleTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonTupleTests.cpp @@ -5,9 +5,9 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include -#include +#include #include +#include #include "PythonTraceMessageSink.h" #include "PythonTestingUtility.h" diff --git a/Gems/EditorPythonBindings/Code/editorpythonbindings_common_files.cmake b/Gems/EditorPythonBindings/Code/editorpythonbindings_common_files.cmake index d5e0461e6659..3dbf9c7198eb 100644 --- a/Gems/EditorPythonBindings/Code/editorpythonbindings_common_files.cmake +++ b/Gems/EditorPythonBindings/Code/editorpythonbindings_common_files.cmake @@ -10,6 +10,8 @@ set(FILES Include/EditorPythonBindings/CustomTypeBindingBus.h Include/EditorPythonBindings/EditorPythonBindingsBus.h Include/EditorPythonBindings/EditorPythonBindingsSymbols.h + Include/EditorPythonBindings/PythonUtility.h + Include/EditorPythonBindings/PythonCommon.h Source/ActionManager/ActionManagerBus.h Source/ActionManager/MenuManagerBus.h Source/ActionManager/PythonActionManagerHandler.cpp @@ -17,7 +19,6 @@ set(FILES Source/ActionManager/PythonEditorAction.cpp Source/ActionManager/PythonEditorAction.h Source/ActionManager/ToolBarManagerBus.h - Source/PythonCommon.h Source/PythonLogSymbolsComponent.cpp Source/PythonLogSymbolsComponent.h Source/PythonMarshalComponent.cpp @@ -35,5 +36,4 @@ set(FILES Source/PythonSystemComponent.h Source/PythonTypeCasters.h Source/PythonUtility.cpp - Source/PythonUtility.h )