pushyrpc / pushy

Easy-as RPC. Zero-server RPC for Python and Java.
http://github.com/pushyrpc/pushy
45 stars 18 forks source link

executing code with imports fails #39

Closed bradharmon closed 11 years ago

bradharmon commented 11 years ago

Maybe I'm missing something really basic, but executing code with imports fails with an error saying that the import is not defined

import pushy

source = """
import math
def sqrt(x)
   return math.sqrt(x)
"""

conn = pushy.connect("local:")

namespace = {}
conn.execute(source, locals=namespace, globals=None)
print namespace["sqrt"](144)

conn.close()

pushy.protocol.proxy.ExceptionProxy: global name 'math' is not defined

However if I run it with 'source' modified slightly, it works fine:

source = """
import math
def sqrt(x)
   global math
   return math.sqrt(x)
"""

I would like to be able to get rid of the extra 'global' declaration, however nothing I've tried seems to work.

axw commented 11 years ago

Hi Brad,

Please try this:

namespace = conn.eval("{}")
conn.execute(source, globals=namespace) # locals defaults to globals

The CPython runtime doesn't like it when you try to use a foreign dictionary object as the globals parameter to eval. The change I've suggested creates a dictionary in the remote process, and then calls eval with that; thus when eval is called in the remote process it is operating on a dictionary local to its interpreter (hope that makes sense!)

Thanks for filing the issue. Please let me know how you get on.

Cheers, Andrew

bradharmon commented 11 years ago

This worked great! Thanks

Along the same lines, I'm having difficulty loading remote code into a local module:

source = """
import math
def sqrt(x):
   return math.sqrt(x)
"""

module = conn.modules.imp.new_module('test')
conn.execute(source, globals=module.__dict__)
module.sqrt(144)

pushy.protocol.proxy.ExceptionProxy: 'module' object has no attribute 'sqrt'

I've also tried to execute it with 'globals=namespace' and then load that dict into a local module, but that has issues as well:

module = imp.new_module('test')
namespace = conn.eval("{}")
conn.execute(source, globals=namespace)

module.__dict__ = namespace
module.__dict__ = namespace

TypeError: readonly attribute

What are your suggestions for this use case? I really appreciate the help.

Thanks,

Brad

bradharmon commented 11 years ago

manually copying all values from the namespace dict into module.dict seems to work. Is this the suggested method for accomplishing this?

import pushy
import imp

source = """
import math
def sqrt(x):
   return math.sqrt(x)
"""

conn = pushy.connect("local:")

namespace = conn.eval("{}")
conn.execute(source, globals=namespace)

#create new module and copy namespace dict into module dict
module = imp.new_module('test')
for key, value in namespace.items():
   module.__dict__[key] = value

del namespace
print module.sqrt(144)

conn.close()
axw commented 11 years ago

Just so I'm clear on the intentions of the code: you're trying to create a module in process A that contains methods that will be executed in process B?

It's a bit ugly IMHO, but that's pretty much how it goes. Ideally you'd just set globals=module.dict, but then you have the problem of it not being a real dict in the remote process.

bradharmon commented 11 years ago

Yes, that's the goal. I want the ability to remotely import local code. I have code where multiple classes need to interact on a remote box and this seems like the easiest way

axw commented 11 years ago

Okey dokey. I haven't personally done (exactly) this before, so you may come against more ugliness along the way. I'll leave this issue open a while longer in case you want some more input.

axw commented 11 years ago

I assume this can be closed now, please reopen if you encounter any more problems.