Load Python Scripts from Memory

D

Dean Ellis

Hi

I'm new to python and I have a question. Is it possible to load a
script/module from memory. I have a requirement in the application I
am building for scripts to be downloaded from a web site and run
(these can be pre-compiled scripts). I would rather not have to save
these scripts to disk then load them if possible.

I would also like to add a feature where a compiled module could be
loaded directly from a resource file.

Anyone have any ideas on how this could be done?

Dean
 
A

Alex Martelli

Dean said:
I'm new to python and I have a question. Is it possible to load a
script/module from memory. I have a requirement in the application I
am building for scripts to be downloaded from a web site and run
(these can be pre-compiled scripts). I would rather not have to save
these scripts to disk then load them if possible.

Yes, you set your own function __import__ in lieu of the built-in
one, and in that function you can do absolutely anything you want.

If what you get from the website is simply the source, or the code
object to be executed in order to define your module (serialized and
deserialized as you prefer), then your life is reasonably easy; if
due to some weird constraint you have to get less easily dealt-with
things, such as the memory image of a .pyc file, you may have more
work to do, of course (basically, getting the code-object from the
"pyc in-memory image", after possibly checking and skipping its header),
but still, it _is_ a feasible task.

I would also like to add a feature where a compiled module could be
loaded directly from a resource file.

I'm not quite sure what a "resource file" _IS_. I know about the
"resources" that can be hidden in a Windows .EXE file, for example
(and I think win32all has code to let you get at them), but of
course those are NOT "a file", so I suspect you may be thinking of
something else.

Anyone have any ideas on how this could be done?

Once you clarify exactly what constraints you're operating under,
I'm sure some design can be found to satisfy them.

Suppose, for example, that what you want to do is as follows. If
the Python code at any time imports a module whose name starts with
_web_, then a pickled version of said module (either a source string
or a code object) is obtained (e.g. via urrlib, etc, etc) and must
be placed as a module in sys.modules &c -- and you also want to do
the usual optimization whereby a second import uses the module that
is already in sys.modules, etc, etc. Let's suppose further that you
need not deal with packages & their complications, reload, and so on.
Oh, and, if the name starts with _res_ instead, you will get its
pickled version via some other way (some kind of function that reads
stuff in memory from a "resource file", whatever THAT may be).

Oh, incidentally, we also assume that you trust the code you receive
*blindly* -- it has somehow been entirely validated, and you're quite
willing to let it do ANYthing the current user is authorized to do
(if that's not the case, then you do have a _serious_ problem...).

Under these conditions you might do something like...:

import __builtin__
base_importer = __builtin__.__import__

import cPickle, new, sys

def __import__(name, *args):
if name in sys.modules:
return sys.modules[name]
if name.startswith('_web_'):
pickled = get_pickled_from_web(name[5:])
elif name.startswith('_res_'):
pickled = get_pickled_from_resources(name[5:])
else:
return base_importer(name, *args)
module = new.module(name)
unpickled = cPickle.loads(pickled)
exec unpickled in module.__dict__
sys.modules[name] = module
return module

__builtin__.__import__ = __import__


Run this code once, e.g. at program startup, and assuming the
get_pickled_... functions know how to get a pickled sourcecode
string or codeobject from wherever, the rest should work (I'm
typing the code in from memory, not testing it, so if there's
any silly typo I apologize in advance... but still I hope this
can help!).

So, assuming there is something that stops you from using this
very simple solution, you can (with our collective help) move
on from here. Except that if the problem is that you do NOT
fully trust the code you have been sent, then the situation may
not be solvable to your safisfaction...


Alex
 
P

Paul Clinch

Hi

I'm new to python and I have a question. Is it possible to load a
script/module from memory. I have a requirement in the application I
am building for scripts to be downloaded from a web site and run
(these can be pre-compiled scripts). I would rather not have to save
these scripts to disk then load them if possible.

Possible to get clever with hooking into the import see
http://www.python.org/doc/2.3.2/whatsnew/section-pep302.html
But, mainly look at exec, execfile, eval() and compile().
Of course its up to you to determine this is safe, security-wise.
I would also like to add a feature where a compiled module could be
loaded directly from a resource file.

View the new import from zip file feature in Python2.3!
http://www.python.org/doc/2.3.2/whatsnew/node5.html
Anyone have any ideas on how this could be done?

Dean

Regards, Paul Clinch
 
J

John J. Lee

Alex Martelli said:
Dean Ellis wrote: [...]
Run this code once, e.g. at program startup, and assuming the
get_pickled_... functions know how to get a pickled sourcecode
string or codeobject from wherever, the rest should work (I'm
[...]

Why would you want to pickle the source for transit over the network?


John
 
A

Alex Martelli

John said:
Alex Martelli said:
Dean Ellis wrote: [...]
Run this code once, e.g. at program startup, and assuming the
get_pickled_... functions know how to get a pickled sourcecode
string or codeobject from wherever, the rest should work (I'm
[...]

Why would you want to pickle the source for transit over the network?

Just for uniformity with the codeobject. The OP had specified that
the incoming code "might be" compiled (implying it might also not be),
and pickle (or marshal, etc) is one simple way to let either source
code _or_ a codeobject be treated the same way on arrival (unpickle,
or unmarshal, then exec whatever has arrived).


Alex
 
D

Dean Ellis

Thanks for all the suggestions guys.

I have to admit writing my own import function sounds a bit daunting.
I think it might help if a clarify what I need to do.

Basically, my application is written in C/Delphi (what ever) and
provides a Host API to python to allow scripts to modify the
application objects. What we would like to support is a form of
automation, i.e we have a pre-compiled python script on our trusted
server, the application loads on the client machine detects there is a
new script and downloads it into memory, it then runs that script in
python. This could be done by saving the compiled script to a file
before running but I would prefer not to.

I did have a play with the PyCodeObject but I couldn't get it to work.

Dean
 
D

Dave Brueck

Dean said:
Thanks for all the suggestions guys.

I have to admit writing my own import function sounds a bit daunting.
I think it might help if a clarify what I need to do.

Basically, my application is written in C/Delphi (what ever) and
provides a Host API to python to allow scripts to modify the
application objects. What we would like to support is a form of
automation, i.e we have a pre-compiled python script on our trusted
server, the application loads on the client machine detects there is a
new script and downloads it into memory, it then runs that script in
python. This could be done by saving the compiled script to a file
before running but I would prefer not to.

I did have a play with the PyCodeObject but I couldn't get it to work.

I think Alex's and Paul's answers give you the same info that's in the code
below, but those solutions may be more general than you need. Hooking into the
normal import mechanism lets your custom imports be more transparent to the
rest of your application, but if you don't need that then you can strip down
the code they gave you to just something like this:

import new
module = new.module('ModuleName')
sourceCode = ObtainSourceCodeFromSomewhere()
exec sourceCode in module.__dict__
sys.modules['ModuleName'] = module

(error handling and security is left up to you)

Note that if Windows is involved anywhere in the process then you probably want
to do something like:

sourceCode = ObtainSourceCodeFromSomewhere().replace('\r', '')

(because Windows does \r\n for line endings, and IIRC exec doesn't like those)

-Dave

P.S. At my company we're doing something like what you describe and it has been
a _huge_ win for us. As long as you are careful about security it enables
functionality that statically-written-and-compiled applications have a tough
time competing with.
 
D

Dean Ellis

Hi

The great news is that I got the system working :-D.

I ended up using a number of methods, but the script can be loaded
from a string which I can get from anywhere (a file, the web, etc).

You were right about the \r\n, I had to go through and replace any
\r's with just the \n otherwise the import would fail.

Thanks for all your help. Now I just need to write the rest of the
application ;-)

Dean
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,432
Messages
2,571,681
Members
48,796
Latest member
Greg L.

Latest Threads

Top