Hello! First time here, starting to work with Python scripting for the editor Sorry for the long post!
I encountered a problem/crash in the way python classes inheriting unreal.Object are reloaded when reexecuting the .py file containing them with the py <file.py> commandlet.
- Builtin unreal python
- I have a FPythonBridge UClass in C++ that is subclassed in my init_unreal.py, that I use for binding a python function to a button in the editor, like it’s done in the Shotgun plugin.
- In init_unreal.py, I import a module containing a class *Derived *from which I create an object to do some work when the button is clicked
- This class is inheriting one from a parent module, let’s say Base.
Be able to reload all my classes in order to easily iterate on my python scripts without the need to restart the editor every time I modify one file.
Failed attempt #1
I tried using the “reload” python function after my imports in *init_unreal.py *but the instantiated classes stay the same in memory and do not get reloaded when I execute
How I finally achieved it
By making Base inherit unreal.Object, and decorating both Base and Derived with @unreal.uclass, when I issue commands
- py “path/to/init_unreal.py”
- *py “my/module/base.py”
- py “my/module/derived.py”*
it does indeed reload the classes and their method implementation for example got updated when I test them. **HOWEVER…
Basically, hot reloading a python base uclass the first time is OK, but the second time you do it, it crashes. In other words,
- When the editor starts and executes init_unreal.py the first time, all is well and works as expected.
- When I modify Derived’s methods implementation then run
again all is well and the updated code does get executed when I test. Yay!
THEN if I re-run
CRASH when executing the second line.
I traced it all to the C++ editor code that tries to reparent in-memory derived classes from Base to the new Base that’s being “reloaded” from the command. It finds Derived as a child of the old Base, but for some reasons passes the name of Derived instead of the object representing the class to the FPythonGeneratedClassBuilder constructor, and this guy for some reason when executing its FindObject line to find the OldClass object again (that the calling context already has!), returns null… And so, when the library calls CopyFunctionsFromOldClass() on the FPythonGeneratedClassBuilder instance that we’ve just constructed, it crashes because OldClass is nullptr…
For info, call stack to the crash (most recent call first) relevant to this process in the Python plugin source code:
FPythonGeneratedClassBuilder::CopyFunctionsFromOldClass() Line 1540 C++ UPythonGeneratedClass::ReparentClass(UPythonGeneratedClass * InOldClass, UPythonGeneratedClass * InNewParent) Line 1786 C++ UPythonGeneratedClass::ReparentDerivedClasses(UPythonGeneratedClass * InOldParent, UPythonGeneratedClass * InNewParent) Line 1773 C++ FPythonGeneratedClassBuilder::Finalize(TPyPtr<_object> InPyPostInitFunction) Line 1166 C++
Any idea of what I’m doing wrong here? is this a bug I should submit? How do you guys handle python scripts and classes reloading?