{LYN-2185} Helios - Added GraphObjectProxy::GetMethodList() function (#392)

* {LYN-2185} Helios - Added GraphObjectProxy::GetMethodList() function

* Updated the EditorPythonConsoleInterface to get Python type name info
* Added PythonBehaviorInfo to the GraphObjectProxy object to reflect abstract calls that can be invoked
* removed SCENE_DATA_API from Reflect() function

Jira: https://jira.agscollab.com/browse/LYN-2185
Tests: Added tests GraphObjectProxy_GetClassInfo_Loads & GraphObjectProxy_GetClassInfo_CorrectFormats to regress test

* fix compile error

* etchPythonTypeName() returns AZStd::string

* put None into a const char
main
jackalbe 5 years ago committed by GitHub
parent 790657f8c0
commit 8b1251c9f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -13,6 +13,12 @@
#include <AzCore/EBus/EBus.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/std/string/string.h>
namespace AZ
{
struct BehaviorParameter;
}
namespace AzToolsFramework
{
@ -40,6 +46,8 @@ namespace AzToolsFramework
};
using GlobalFunctionCollection = AZStd::vector<GlobalFunction>;
virtual void GetGlobalFunctionList(GlobalFunctionCollection& globalFunctionCollection) const = 0;
virtual AZStd::string FetchPythonTypeName(const AZ::BehaviorParameter& param) = 0;
};
//! Interface to signal the phases for the Python virtual machine

@ -13,9 +13,135 @@
#include <SceneAPI/SceneCore/Containers/GraphObjectProxy.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorPythonConsoleBus.h>
namespace AZ
{
namespace Python
{
static const char* const None = "None";
class PythonBehaviorInfo final
{
public:
AZ_RTTI(PythonBehaviorInfo, "{8055BD03-5B3B-490D-AEC5-1B1E2616D529}");
AZ_CLASS_ALLOCATOR(PythonBehaviorInfo, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* context);
PythonBehaviorInfo(const AZ::BehaviorClass* behaviorClass);
PythonBehaviorInfo() = delete;
protected:
bool IsMemberLike(const AZ::BehaviorMethod& method, const AZ::TypeId& typeId) const;
AZStd::string FetchPythonType(const AZ::BehaviorParameter& param) const;
void WriteMethod(AZStd::string_view methodName, const AZ::BehaviorMethod& behaviorMethod);
private:
const AZ::BehaviorClass* m_behaviorClass = nullptr;
AZStd::vector<AZStd::string> m_methodList;
};
PythonBehaviorInfo::PythonBehaviorInfo(const AZ::BehaviorClass* behaviorClass)
: m_behaviorClass(behaviorClass)
{
AZ_Assert(m_behaviorClass, "PythonBehaviorInfo requires a valid behaviorClass pointer");
for (const auto& entry : behaviorClass->m_methods)
{
WriteMethod(entry.first, *entry.second);
}
}
void PythonBehaviorInfo::Reflect(AZ::ReflectContext* context)
{
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->Class<PythonBehaviorInfo>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "scene.graph")
->Property("className", [](const PythonBehaviorInfo& self)
{ return self.m_behaviorClass->m_name; }, nullptr)
->Property("classUuid", [](const PythonBehaviorInfo& self)
{ return self.m_behaviorClass->m_typeId.ToString<AZStd::string>(); }, nullptr)
->Property("methodList", BehaviorValueGetter(&PythonBehaviorInfo::m_methodList), nullptr);
}
}
bool PythonBehaviorInfo::IsMemberLike(const AZ::BehaviorMethod& method, const AZ::TypeId& typeId) const
{
return method.IsMember() || (method.GetNumArguments() > 0 && method.GetArgument(0)->m_typeId == typeId);
}
AZStd::string PythonBehaviorInfo::FetchPythonType(const AZ::BehaviorParameter& param) const
{
using namespace AzToolsFramework;
EditorPythonConsoleInterface* editorPythonConsoleInterface = AZ::Interface<EditorPythonConsoleInterface>::Get();
if (editorPythonConsoleInterface)
{
return editorPythonConsoleInterface->FetchPythonTypeName(param);
}
return None;
}
void PythonBehaviorInfo::WriteMethod(AZStd::string_view methodName, const AZ::BehaviorMethod& behaviorMethod)
{
// if the method is a static method then it is not a part of the abstract class
const bool isMemberLike = IsMemberLike(behaviorMethod, m_behaviorClass->m_typeId);
if (isMemberLike == false)
{
return;
}
AZStd::string buffer;
AZStd::vector<AZStd::string> pythonArgs;
AzFramework::StringFunc::Append(buffer, "def ");
AzFramework::StringFunc::Append(buffer, methodName.data());
AzFramework::StringFunc::Append(buffer, "(");
pythonArgs.emplace_back("self");
AZStd::string bufferArg;
for (size_t argIndex = 1; 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_view type = FetchPythonType(*behaviorMethod.GetArgument(argIndex));
if (!type.empty())
{
AzFramework::StringFunc::Append(bufferArg, ": ");
AzFramework::StringFunc::Append(bufferArg, type.data());
}
pythonArgs.push_back(bufferArg);
bufferArg.clear();
}
AZStd::string resultValue{ None };
if (behaviorMethod.HasResult() && behaviorMethod.GetResult())
{
resultValue = FetchPythonType(*behaviorMethod.GetResult());
}
AZStd::string argsList;
AzFramework::StringFunc::Join(buffer, pythonArgs.begin(), pythonArgs.end(), ",");
AzFramework::StringFunc::Append(buffer, ") -> ");
AzFramework::StringFunc::Append(buffer, resultValue.c_str());
m_methodList.emplace_back(buffer);
}
}
namespace SceneAPI
{
namespace Containers
@ -25,6 +151,8 @@ namespace AZ
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
Python::PythonBehaviorInfo::Reflect(context);
behaviorContext->Class<DataTypes::IGraphObject>();
behaviorContext->Class<GraphObjectProxy>()
@ -33,6 +161,19 @@ namespace AZ
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Method("CastWithTypeName", &GraphObjectProxy::CastWithTypeName)
->Method("Invoke", &GraphObjectProxy::Invoke)
->Method("GetClassInfo", [](GraphObjectProxy& self) -> Python::PythonBehaviorInfo*
{
if (self.m_pythonBehaviorInfo)
{
return self.m_pythonBehaviorInfo.get();
}
if (self.m_behaviorClass)
{
self.m_pythonBehaviorInfo = AZStd::make_shared<Python::PythonBehaviorInfo>(self.m_behaviorClass);
return self.m_pythonBehaviorInfo.get();
}
return nullptr;
})
;
}
}

@ -16,6 +16,11 @@
namespace AZ
{
namespace Python
{
class PythonBehaviorInfo;
}
namespace SceneAPI
{
namespace Containers
@ -44,6 +49,7 @@ namespace AZ
private:
AZStd::shared_ptr<const DataTypes::IGraphObject> m_graphObject;
const AZ::BehaviorClass* m_behaviorClass = nullptr;
AZStd::shared_ptr<Python::PythonBehaviorInfo> m_pythonBehaviorInfo;
};
} // Containers

@ -20,6 +20,7 @@
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/Serialization/Json/JsonSystemComponent.h>
#include <AzToolsFramework/API/EditorPythonConsoleBus.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
@ -378,6 +379,25 @@ namespace AZ
MOCK_CONST_METHOD1(QueryApplicationType, void(AZ::ApplicationTypeQuery&));
};
class MockEditorPythonConsoleInterface final
: public AzToolsFramework::EditorPythonConsoleInterface
{
public:
MockEditorPythonConsoleInterface()
{
AZ::Interface<AzToolsFramework::EditorPythonConsoleInterface>::Register(this);
}
~MockEditorPythonConsoleInterface()
{
AZ::Interface<AzToolsFramework::EditorPythonConsoleInterface>::Unregister(this);
}
MOCK_CONST_METHOD1(GetModuleList, void(AZStd::vector<AZStd::string_view>&));
MOCK_CONST_METHOD1(GetGlobalFunctionList, void(GlobalFunctionCollection&));
MOCK_METHOD1(FetchPythonTypeName, AZStd::string(const AZ::BehaviorParameter&));
};
//
// SceneGraphBehaviorScriptTest
//
@ -386,6 +406,7 @@ namespace AZ
{
public:
AZStd::unique_ptr<MockSceneComponentApplication> m_componentApplication;
AZStd::unique_ptr<MockEditorPythonConsoleInterface> m_editorPythonConsoleInterface;
AZStd::unique_ptr<AZ::ScriptContext> m_scriptContext;
AZStd::unique_ptr<AZ::BehaviorContext> m_behaviorContext;
AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
@ -454,6 +475,15 @@ namespace AZ
{
return this->m_serializeContext.get();
}));
m_editorPythonConsoleInterface = AZStd::make_unique<MockEditorPythonConsoleInterface>();
}
void SetupEditorPythonConsoleInterface()
{
EXPECT_CALL(*m_editorPythonConsoleInterface, FetchPythonTypeName(::testing::_))
.Times(4)
.WillRepeatedly(::testing::Invoke([](const AZ::BehaviorParameter&) {return "int"; }));
}
void TearDown() override
@ -562,6 +592,38 @@ namespace AZ
ExpectExecute("TestExpectEquals(value, 17)");
}
TEST_F(SceneGraphBehaviorScriptTest, GraphObjectProxy_GetClassInfo_Loads)
{
SetupEditorPythonConsoleInterface();
ExpectExecute("builder = MockBuilder()");
ExpectExecute("builder:BuildSceneGraph()");
ExpectExecute("scene = builder:GetScene()");
ExpectExecute("nodeG = scene.graph:FindWithPath('A.C.E.G')");
ExpectExecute("proxy = scene.graph:GetNodeContent(nodeG)");
ExpectExecute("TestExpectTrue(proxy:CastWithTypeName('MockIGraphObject'))");
ExpectExecute("info = proxy:GetClassInfo()");
ExpectExecute("TestExpectTrue(info ~= nil)");
}
TEST_F(SceneGraphBehaviorScriptTest, GraphObjectProxy_GetClassInfo_CorrectFormats)
{
SetupEditorPythonConsoleInterface();
ExpectExecute("builder = MockBuilder()");
ExpectExecute("builder:BuildSceneGraph()");
ExpectExecute("scene = builder:GetScene()");
ExpectExecute("nodeG = scene.graph:FindWithPath('A.C.E.G')");
ExpectExecute("proxy = scene.graph:GetNodeContent(nodeG)");
ExpectExecute("TestExpectTrue(proxy:CastWithTypeName('MockIGraphObject'))");
ExpectExecute("info = proxy:GetClassInfo()");
ExpectExecute("TestExpectTrue(info.className == 'MockIGraphObject')");
ExpectExecute("TestExpectTrue(info.classUuid == '{66A082CC-851D-4E1F-ABBD-45B58A216CFA}')");
ExpectExecute("TestExpectTrue(info.methodList[1] == 'def GetId(self) -> int')");
ExpectExecute("TestExpectTrue(info.methodList[2] == 'def SetId(self, arg1: int) -> None')");
ExpectExecute("TestExpectTrue(info.methodList[3] == 'def AddAndSet(self, arg1: int, arg2: int) -> None')");
}
//
// SceneManifestBehaviorScriptTest is meant to test the script abilities of the SceneManifest
//

@ -31,7 +31,7 @@ namespace AZ
public:
AZ_RTTI(BlendShapeData, "{FF875C22-2E4F-4CE3-BA49-09BF78C70A09}", SceneAPI::DataTypes::IBlendShapeData)
SCENE_DATA_API static void Reflect(ReflectContext* context);
static void Reflect(ReflectContext* context);
// Maximum number of color sets matches limitation set in assImp (AI_MAX_NUMBER_OF_COLOR_SETS)
static constexpr AZ::u8 MaxNumColorSets = 8;

@ -161,7 +161,7 @@ namespace EditorPythonBindings
bufferArg = *name;
}
AZStd::string_view type = FetchPythonType(*behaviorMethod.GetArgument(argIndex));
AZStd::string type = FetchPythonTypeName(*behaviorMethod.GetArgument(argIndex));
if (!type.empty())
{
AzFramework::StringFunc::Append(bufferArg, ": ");
@ -289,7 +289,7 @@ namespace EditorPythonBindings
bool isBroadcast = false;
if (sender.m_event)
{
AZStd::string_view addressType = FetchPythonType(behaviorEBus->m_idParam);
AZStd::string addressType = FetchPythonTypeName(behaviorEBus->m_idParam);
if (addressType.empty())
{
AzFramework::StringFunc::Append(buffer, "(busCallType: int, busEventName: str, address: Any, args: Tuple[Any])");
@ -338,7 +338,7 @@ namespace EditorPythonBindings
}
const AZ::BehaviorParameter* resultParam = behaviorMethod->GetResult();
AZStd::string_view returnType = FetchPythonType(*resultParam);
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());
};
@ -664,9 +664,9 @@ namespace EditorPythonBindings
return m_typeCache[typeId];
}
AZStd::string_view PythonLogSymbolsComponent::FetchPythonType(const AZ::BehaviorParameter& param)
AZStd::string PythonLogSymbolsComponent::FetchPythonTypeName(const AZ::BehaviorParameter& param)
{
AZStd::string_view pythonType = FetchPythonTypeAndTraits(param.m_typeId, param.m_traits);
AZStd::string pythonType = FetchPythonTypeAndTraits(param.m_typeId, param.m_traits);
if (pythonType.empty())
{

@ -62,6 +62,7 @@ namespace EditorPythonBindings
void LogGlobalMethod(AZStd::string_view moduleName, AZStd::string_view methodName, AZ::BehaviorMethod* behaviorMethod) override;
void LogGlobalProperty(AZStd::string_view moduleName, AZStd::string_view propertyName, AZ::BehaviorProperty* behaviorProperty) override;
void Finalize() override;
AZStd::string FetchPythonTypeName(const AZ::BehaviorParameter& param) override;
////////////////////////////////////////////////////////////////////////
// EditorPythonConsoleInterface
@ -71,7 +72,6 @@ namespace EditorPythonBindings
////////////////////////////////////////////////////////////////////////
// Python type deduction
AZStd::string_view FetchPythonTypeAndTraits(const AZ::TypeId& typeId, AZ::u32 traits);
AZStd::string_view FetchPythonType(const AZ::BehaviorParameter& param);
private:
using ModuleSet = AZStd::unordered_set<AZStd::string>;

@ -41,9 +41,9 @@ namespace UnitTest
return FetchPythonTypeAndTraits(typeId, traits);
}
AZStd::string_view FetchPythonTypeWrapper(const AZ::BehaviorParameter& param)
AZStd::string FetchPythonTypeWrapper(const AZ::BehaviorParameter& param)
{
return FetchPythonType(param);
return FetchPythonTypeName(param);
}
};

Loading…
Cancel
Save