Possible to execute Python bytecode from a script?
Question: Say I have a running CPython session,

Is there a way to run the data (`bytes`) from a `pyc` file directly? _(without
having the data on-disk necessarily, and without having to write a temporary
pyc file)_

Example script to show a simple use-case:

    
    
    if foo:
        data = read_data_from_somewhere()
    else:
        data = open("bar.pyc", 'rb').read()
    
    assert(type(data) is bytes)
    
    code = bytes_to_code(data)
    
    # call a method from the loaded code
    code.call_function()
    

* * *

Exact use isn't so important, but generating code dynamically and copying over
a network to execute is one use-case.

* * *

Here are some example use-cases, which made me curious to know how this can be
done:

  * Checking Python scripts for malicious code.  
_If a single command can access a larger body of code hidden in binary data,
what would that command look like?_

  * Dynamically generate code and cache it for re-use (not necessarily on disk, could use a data-base for example).
  * Ability to send pre-compiled byte-code to a process, _control an application which embeds Python for eg_.


Answer: > Is there a way to run the data from a pyc file directly?

Yes, but the exact functions involved depend on the use case. An easy way to
do this is with [exec](https://docs.python.org/3/library/functions.html#exec).
Here a small example:

    
    
    spam = 'print(3)'
    eggs = compile(spam, '<string>', 'exec')
    exec(eggs)
    

edit:

The compiled code object can be saved using `marshal`

    
    
    import marshal
    bytes = marshal.dumps( eggs )
    

the bytes can be converted back to a code object

    
    
    eggs = marshal.loads( bytes )
    exec( eggs )
    

According to [Ned
Batchelder](http://nedbatchelder.com/blog/200804/the_structure_of_pyc_files.html)
a pyc file is basically just a marshaled code object.

* * *

Note, the link references Python2, but its almost the same in Python3, the
`pyc` header size is just 12 instead of 8 bytes. This needs to be skipped
before passing the data to `marshal.loads`.

