1
$\begingroup$

The following code creates a dictionary and prints it at regular intervals:

import bpy
import json

class ModalTimerOperator(bpy.types.Operator):
    """Operator which runs its self from a timer"""
    bl_idname = "wm.modal_timer_operator"
    bl_label = "Modal Timer Operator"

    _timer = None

    def modal(self, context, event):
        if event.type in {'RIGHTMOUSE', 'ESC'}:
            self.cancel(context)
            return {'CANCELLED'}

        if event.type == 'TIMER':
            dict = {"a":4}

            msg = 'testDict = '+json.dumps(dict)+'\n'
            msg += 'list = ["a","b","c"]\n'

            msg += 'def printTestFunc(num1,num2):\n'
            msg += '    print(num1)\n'
            msg += '    print(num2)\n'

            msg += 'for i in range(5):\n'
            msg += '    [printTestFunc(0,testDict[key]) for key in list if key in testDict]\n'
            exec(msg)

        return {'PASS_THROUGH'}

    def execute(self, context):
        wm = context.window_manager
        self._timer = wm.event_timer_add(0.1, window=context.window)
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        wm = context.window_manager
        wm.event_timer_remove(self._timer)


def register():
    bpy.utils.register_class(ModalTimerOperator)


def unregister():
    bpy.utils.unregister_class(ModalTimerOperator)


if __name__ == "__main__":
    register()

    # test call
    bpy.ops.wm.modal_timer_operator()

Unfortunately, it returns the following error:

line 28, in modal File "", line 7, in File "", line 7, in NameError: name 'testDict' is not defined

However, when I write the following code using Python without using Blender's ModalOperator there is no error:

import json

dict = {"a":4}

msg = 'testDict = '+json.dumps(dict)+'\n'
msg += 'list = ["a","b","c"]\n'

msg += 'def printTestFunc(num1,num2):\n'
msg += '    print(num1)\n'
msg += '    print(num2)\n'

msg += 'for i in range(5):\n'
msg += '    [printTestFunc(0,testDict[key]) for key in list if key in testDict]\n'
exec(msg)

I think this is a Modal Operator specific trouble. Why does this happen?

$\endgroup$
  • 1
    $\begingroup$ While it's certainly interesting why this doesn't work, I'm wondering why you're dynamically creating code and using exec() in the first place? Is there any specific reason why you can't simply call a function and pass the the json data as an argument? $\endgroup$ – Robert Gützkow Jan 25 at 12:42
  • $\begingroup$ I'm writing code that generates commands on other machines and sends them to Blender over TCP / IP. Therefore, I use exec() in this sample. $\endgroup$ – taichi Jan 25 at 12:55
  • $\begingroup$ Oh no. Please DO NOT do that. You're basically creating a remote code execution vulnerability as a "feature". $\endgroup$ – Robert Gützkow Jan 25 at 12:57
  • $\begingroup$ As a security measure, I wrote a process that will not execute if the sent string contains the string "import". Then I can only use Blender's bpy command, but is it still dangerous? $\endgroup$ – taichi Jan 25 at 13:04
  • 2
    $\begingroup$ Yes it's still very dangerous. First of all the question is whether or not your check works properly and even if it does, the attacker can still use any module you've already imported. Given that you're opening sockets that's likely a lot more than just bpy. Please inform yourself about the security risks of such code and how to properly implement a server API. You never want a client to be able to execute arbitrary code on your server. $\endgroup$ – Robert Gützkow Jan 25 at 13:21
2
$\begingroup$

Pass a globals dict to exec

Not sure of the reason why it doesn't work.. only have some inklings.

However can populate a dictionary and pass as globals to the exec function.

        if event.type == 'TIMER':
            mydict = {"a":4}
            myglobals = {"testDict": eval(json.dumps(mydict)),
                       "mylist": ["a","b","c"] }

            msg = 'def printTestFunc(num1,num2):\n'
            msg += '    print(num1)\n'
            msg += '    print(num2)\n'

            msg += 'for i in range(5):\n'
            msg += '    [printTestFunc(0,testDict[key]) for key in mylist if key in testDict]\n'
            exec(msg, myglobals, None)

        return {'PASS_THROUGH'}

Alternatively could define vars in the method and pass locals() which will include self, context and event

Note: Good idea not to use builtins like list and dict for variable names. EDIT: Oops did just that with globals

| improve this answer | |
$\endgroup$
  • $\begingroup$ Thanks, the code worked fine! $\endgroup$ – taichi Jan 25 at 14:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.