4

Im trying to figure out how to include a .pyc file in a python script.

For example my script is called:

myscript.py

and the script I would like to include is called:

included_script.pyc

So, do I just use:

import included_script

And will that automatically execute the included_script.pyc ? Or is there something further I need to do, to get my included_script.pyc to run inside the myscript.py?

Do I need to pass the variables used in included_script.pyc also? If so, how might this be achieved?

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
Ke.
  • 2,484
  • 8
  • 40
  • 78
  • 1
    No that won't execute it automatically. Check out - http://stackoverflow.com/questions/1027714/how-to-execute-a-file-within-the-python-interpreter – Nikhil Parmar Jan 10 '16 at 18:49

1 Answers1

12

Unfortunately, no, this cannot be done automatically. You can, of course, do it manually in a gritty ugly way.


Setup:

For demonstration purposes, I'll first generate a .pyc file. In order to do that, we first need a .py file for it. Our sample test.py file will look like:

def foo():
    print("In foo")

if __name__ == "__main__":
    print("Hello World")

Super simple. Generating the .pyc file can done with the py_compile module found in the standard library. We simply pass in the name of the .py file and the name for our .pyc file in the following way:

 py_compile.compile('test.py', 'mypyc.pyc')

This will place mypyc.pyc in our current working directory.


Getting the code from .pyc files:

Now, .pyc files contain bytes that are structured in the following way:

  • First 4 bytes signalling a 'magic number'
  • Next 4 bytes holding a modification timestamp
  • Rest of the contents are a marshalled code object.

What we're after is that marshalled code object, so we need to import marshal to un-marshall it and execute it. Additionally, we really don't care/need the 8 first bytes, and un-marshalling the .pyc file with them is disallowed, so we'll ignore them (seek past them):

import marshal

s = open('mypyc.pyc', 'rb')
s.seek(8)  # go past first eight bytes
code_obj = marshal.load(s)

So, now we have our fancy code object for test.py which is valid and ready to be executed as we wish. We have two options here:

  1. Execute it in the current global namespace. This will bind all definitions inside our .pyc file in the current namespace and will act as a sort of: from file import * statement.

  2. Create a new module object and execute the code inside the module. This will be like the import file statement.


Emulating from file import * like behaviour:

Performing this is pretty simple, just do:

exec(code_obj)

This will execute the code contained inside code_obj in the current namespace and bind everything there. After the call we can call foo like any other funtion:

foo()
# prints: In foo!

Note: exec() is a built-in.


Emulating import file like behaviour:

This includes another requirement, the types module. This contains the type for ModuleType which we can use to create a new module object. It takes two arguments, the name for the module (mandatory) and the documentation for it (optional):

m = types.ModuleType("Fancy Name", "Fancy Documentation")

print(m)
<module 'Fancy Name' (built-in)>

Now that we have our module object, we can again use exec to execute the code contained in code_obj inside the module namespace (namely, m.__dict__):

exec(code_obj, m.__dict__)

Now, our module m has everything defined in code_obj, you can verify this by running:

m.foo() 
# prints: In foo

These are the ways you can 'include' a .pyc file in your module. At least, the ways I can think of. I don't really see the practicality in this but hey, I'm not here to judge.

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
  • 1
    While I don't have much confidence, but Python 3.3+ seems to requires another 4 bytes in a head magic munbers, so s.seek(8) needs to be s.seek(12). https://qiita.com/amedama/items/698a7c4dbdd34b03b427 – dkato Mar 23 '18 at 08:22
  • Am having .pyc file with some like this: def myfunc(in_str = "Hello World"): print(in_str) And I run it using a separate script like this; import marshal s = open('script2.cpython-37.pyc', 'rb') s.seek(12) code_obj = marshal.load(s) exec(code_obj) myfunc() myfunc("Testing from script4") It is giving me error: (base) C:\Users\ashish\Desktop\code_20210128_1200\2>python script4.py Traceback (most recent call last): File "script4.py", line 5, in code_obj = marshal.load(s) ValueError: bad marshal data (unknown type code) – Ashish Jain Jan 28 '21 at 08:40
  • 2
    In python 3.7 and above, I believe it should be s.seek(16). https://www.python.org/dev/peps/pep-0552/ – Starbuck5 Aug 05 '21 at 05:26