diff --git a/install/scripts/builtin/brush.py b/install/scripts/builtin/brush.py index 75c51b2d8..82d3f4620 100644 --- a/install/scripts/builtin/brush.py +++ b/install/scripts/builtin/brush.py @@ -1,11 +1,23 @@ -import dr as darkradiant +import darkradiant as dr +from internal import CommandUtil +# Python commands would be classes with static members class ResizeSelectedBrushesToBounds: - def execute(self, min: dr.Vector3, max: dr.Vector3, material: str): + # We can use type hints to make it possible to register its Signature in the CommandSystem + @staticmethod + def execute(min: dr.Vector3, max: dr.Vector3, material: str): print(min) print(max) print(material) -import inspect -for name, obj in inspect.getmembers(sys.modules[__name__], inspect.isclass): - print(name) \ No newline at end of file + @staticmethod + def canExecute(): + return true + +print('We have the module: ' + __name__) + +# Proves that it's possible to access previously registered DR interfaces +print(dr.ScriptingSystemInterface) +print(dr.ScriptingSystem) + +CommandUtil.registerModule(__name__) \ No newline at end of file diff --git a/install/scripts/builtin/internal.py b/install/scripts/builtin/internal.py new file mode 100644 index 000000000..0c21f32a0 --- /dev/null +++ b/install/scripts/builtin/internal.py @@ -0,0 +1,21 @@ +import darkradiant as dr +import inspect + +class CommandUtil: + @staticmethod + def registerModule(moduleName): + for name, obj in inspect.getmembers(sys.modules[moduleName], inspect.isclass): + print(name + ' ' + str(obj)) + registerClass(name, obj) + + @staticmethod + def registerClass(name, obj): + sig = inspect.signature(obj.execute) + print(sig) + + for paramName in sig.parameters: + print("Parameter {0} has type {1}".format(paramName, sig.parameters[paramName].annotation)) + # Prove that we can identify DR types in the signature + if sig.parameters[paramName].annotation == dr.Vector3: + print('This parametr is a Vec3') + #dr.ScriptingSystem.registerBuiltinScriptCommand(...) \ No newline at end of file diff --git a/plugins/script/PythonModule.cpp b/plugins/script/PythonModule.cpp index b0fbfecfc..61b30a350 100644 --- a/plugins/script/PythonModule.cpp +++ b/plugins/script/PythonModule.cpp @@ -35,6 +35,14 @@ PythonModule::~PythonModule() { _namedInterfaces.clear(); + for (auto& module : _builtInModules) + { + module.dec_ref(); + module.release(); + } + + _builtInModules.clear(); + // Release the references to trigger the internal cleanup before Py_Finalize _module.dec_ref(); _module.release(); @@ -324,6 +332,36 @@ ScriptCommand::Ptr PythonModule::createScriptCommand(const std::string& scriptBa } } +void PythonModule::registerBuiltInModulePath(const std::string& scriptBasePath) +{ + // Import module from the base path + auto sys = py::module::import("sys"); + sys.attr("path").attr("insert")(1, os::getDirectory(scriptBasePath)); +} + +void PythonModule::initialiseBuiltInModule(const std::string& moduleFilename) +{ + try + { + auto moduleName = os::removeExtension(os::getFilename(moduleFilename)); + auto builtInModule = py::module::import(moduleName.c_str()); + _builtInModules.push_back(builtInModule); + } + catch (const py::error_already_set& ex) + { + rError() << "Script file " << moduleFilename << " is not a valid command:" << std::endl; + rError() << ex.what() << std::endl; + } +} + +void PythonModule::refreshBuiltInModules() +{ + for (auto& module : _builtInModules) + { + module.reload(); + } +} + PythonModule* PythonModule::_instance = nullptr; } diff --git a/plugins/script/PythonModule.h b/plugins/script/PythonModule.h index 7e38cdbe0..2094b8230 100644 --- a/plugins/script/PythonModule.h +++ b/plugins/script/PythonModule.h @@ -26,6 +26,8 @@ class PythonModule final // List of registered interfaces NamedInterfaces _namedInterfaces; + std::vector _builtInModules; + PythonModule(const PythonModule& other) = delete; PythonModule& operator=(const PythonModule& other) = delete; @@ -63,6 +65,10 @@ class PythonModule final // Will return an empty object if the file path is not a valid file ScriptCommand::Ptr createScriptCommand(const std::string& scriptBasePath, const std::string& relativeScriptPath); + void registerBuiltInModulePath(const std::string& scriptBasePath); + void initialiseBuiltInModule(const std::string& moduleFilename); + void refreshBuiltInModules(); + private: // Register the darkradiant module with the inittab pointing to InitModule void registerModule(); diff --git a/plugins/script/ScriptingSystem.cpp b/plugins/script/ScriptingSystem.cpp index c91ef307f..9708e62e9 100644 --- a/plugins/script/ScriptingSystem.cpp +++ b/plugins/script/ScriptingSystem.cpp @@ -101,6 +101,9 @@ void ScriptingSystem::initialise() // Start the init script executeScriptFile(INIT_SCRIPT_FILENAME); + // Initialise the python script files containing built-in commands + registerBuiltInCommands(); + // Search script folder for commands reloadScripts(); } @@ -221,6 +224,8 @@ void ScriptingSystem::registerBuiltInCommands() return; } + _pythonModule->registerBuiltInModulePath(start.string()); + for (fs::recursive_directory_iterator it(start); it != fs::recursive_directory_iterator(); ++it) { // Get the candidate @@ -236,7 +241,7 @@ void ScriptingSystem::registerBuiltInCommands() void ScriptingSystem::initialiseBuiltInCommandFile(const std::string& scriptFilename) { - //_pythonModule->createScriptCommand(_scriptPath, scriptFilename); + _pythonModule->initialiseBuiltInModule(scriptFilename); } // RegisterableModule implementation @@ -315,8 +320,10 @@ void ScriptingSystem::initialiseModule(const IApplicationContext& ctx) { cmd::ARGTYPE_STRING } ); - // Initialise the python script files containing built-in commands - registerBuiltInCommands(); + GlobalCommandSystem().addCommand( + "RefreshScriptModules", + [this](const auto& args) { _pythonModule->refreshBuiltInModules(); } + ); SceneNodeBuffer::Instance().clear(); } diff --git a/plugins/script/interfaces/ScriptingSystemInterface.cpp b/plugins/script/interfaces/ScriptingSystemInterface.cpp index 25dc27515..b3edd69a5 100644 --- a/plugins/script/interfaces/ScriptingSystemInterface.cpp +++ b/plugins/script/interfaces/ScriptingSystemInterface.cpp @@ -3,9 +3,14 @@ namespace script { -void ScriptingSystemInterface::registerBuiltinScriptCommand() +void ScriptingSystemInterface::registerBuiltinScriptCommand(py::object& cls) { - + auto inspect = py::module::import("inspect"); + + auto execute = cls.attr("execute"); + + auto result = inspect.attr("signature")(execute); + py::print(result); } void ScriptingSystemInterface::registerInterface(py::module& scope, py::dict& globals) @@ -17,6 +22,9 @@ void ScriptingSystemInterface::registerInterface(py::module& scope, py::dict& gl // Now point the Python variable "GlobalScriptingSystem" to this instance globals["GlobalScriptingSystem"] = this; + + // Define a ScriptingSystem property in the darkradiant module + scope.attr("ScriptingSystem") = this; } } diff --git a/plugins/script/interfaces/ScriptingSystemInterface.h b/plugins/script/interfaces/ScriptingSystemInterface.h index 8c84cbe74..7cc81b5a6 100644 --- a/plugins/script/interfaces/ScriptingSystemInterface.h +++ b/plugins/script/interfaces/ScriptingSystemInterface.h @@ -1,6 +1,7 @@ #pragma once #include "iscriptinterface.h" +#include namespace script { @@ -21,7 +22,7 @@ class ScriptingSystemInterface : _scriptingSystem(scriptingSystem) {} - void registerBuiltinScriptCommand(); + void registerBuiltinScriptCommand(py::object& cls); // IScriptInterface implementation void registerInterface(py::module& scope, py::dict& globals) override;