Thanks for your great reply. I even augmented the reloading with the same dict 
by clearing all of the non-standard symbols from the dict. This effectively 
resets the dict:

        # try to clear out the module by deleting all global refs               
                                        
        d = self.module.__dict__
        for k in dict(d).keys():
            if not k in ['__spec__', '__name__', '__loader__', '__package__', 
'__doc__', '__builtins__']:
                del d[k]
        self.module.__dict__['sendMessage'] = self.sendMessage
        try:
            exec(self.source, self.module.__dict__)
        except Exception:
            import traceback
            traceback.print_exc(file=sys.stdout)


Is there a better and more secure way to do the python-within-python in order 
allow users to automate your app?

Thanks!


> On Nov 23, 2014, at 12:24 AM, Chris Angelico <ros...@gmail.com> wrote:
> 
> On Sun, Nov 23, 2014 at 4:48 PM, Patrick Stinson <patrickk...@gmail.com> 
> wrote:
>> I am writing a python app (using PyQt, but that’s not important here), and 
>> want my users to be able to write their own scripts to automate the app’s 
>> functioning using an engine API hat I expose. I have extensive experience 
>> doing this in a C++ app with the CPython api, but have no idea how to do 
>> this outside of calling exec() from with in Python :)
>> 
>> Ideally their script would compile when the source changes and retain it’s 
>> state and respond to callbacks from the api object. It appears this won’t 
>> work with exec() because the script’s definitions and state disappear as 
>> soon as the exec() call is complete, and the script doesn’t seem to be able 
>> to access it’s own defined functions and classes.
>> 
>> Thoughts? Fun stuff!
> 
> First off, a cautionary note: Security-wise, this is absolutely
> equivalent to your users editing your source code. Be aware that
> you're giving them complete control.
> 
> What you should be able to do is exec the script in a specific global
> dictionary. Here's some example code (Python 3.4):
> 
>>>> script = """
> def init():
>    print("Initializing")
> 
> def on_some_event(status):
>    trigger_some_action("Status is now "+status)
> """
>>>> def trigger_some_action(msg):
>    print("Action triggered.",msg)
>>>> globl = {"trigger_some_action":trigger_some_action}
>>>> exec(script,globl)
>>>> globl["init"]()
> Initializing
>>>> globl["on_some_event"]("Something happened")
> Action triggered. Status is now Something happened
> 
> You can provide globals like this, or you can create an importable
> module for the scripts to call on. (Or both. Create a module, and
> pre-import it automatically.) The script defines functions with
> specific names and/or calls your functions to register hooks; you can
> reach into the globals to trigger functions.
> 
> One way to handle updates to the code would be to exec it in the same
> globals dictionary. That has its complexities (for instance, if you
> rename a function, the old version will still exist under the old name
> unless you explicitly del it), but it can be very convenient.
> Alternatively, you could have the new version run in a new dictionary,
> but important state can be kept in separate dictionaries. That's how I
> manage things with a Pike program - all code gets reloaded cleanly,
> but retained state is stored separately in a mutable object that gets
> passed around.
> 
> There are several ways this sort of thing can be done. It's reasonably
> easy, as long as you take a bit of care across the reload boundaries.
> 
> ChrisA
> -- 
> https://mail.python.org/mailman/listinfo/python-list

-- 
https://mail.python.org/mailman/listinfo/python-list

Reply via email to