3

I'm interested in loading a Python module that has its source embedded in a C extension. It should be possible to do something with Python's importlib machinery like importlib.util.spec_from_file_location so that the source code will appear if you are debugging. How would I implement an importlib.util.spec_from_string?

4
  • 2
    You want to embed Python code in a C extension for Python? Can you give an example of how that would be useful? I suspect this is more of a XY problem. Commented Jun 10, 2020 at 2:58
  • It's because it's a plugin that embeds Python into another program. Commented Jun 10, 2020 at 4:36
  • Is that "other program" also a Python program, or a C program? (or are you hoping to use the C extension / plugin in binary format?) I'm asking because you'll need Python itself to execute the Python code of course and if your other program isn't Python, that's not a trivial problem. If your other program is Python, I think there's better ways to resolve this than to embed the script into a C extension. Commented Jun 10, 2020 at 4:57
  • The implementation of github.com/python/cpython/blob/master/Lib/zipimport.py Commented Jun 20, 2020 at 22:52

2 Answers 2

8

Here's how to define a loader that takes the module's source from a string, and then creates and loads the module into sys.modules. It could be useful if the module's source is not in a file. If there is already a file then use https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly

Although inspect.getsource(module) works for a subclass of importlib.abc.InspectLoader for which it would only be necessary to define get_source, tracebacks and pdb don't appear to be willing to display the source code until you inherit from SourceLoader.

import sys
import importlib.abc, importlib.util

class StringLoader(importlib.abc.SourceLoader):
    def __init__(self, data):
        self.data = data

    def get_source(self, fullname):
        return self.data
    
    def get_data(self, path):
        return self.data.encode("utf-8")
    
    def get_filename(self, fullname):
        return "<not a real path>/" + fullname + ".py"
    
module_name = "testmodule"
with open("testmodule.py", "r") as module:
    loader = StringLoader(module.read())

spec = importlib.util.spec_from_loader(module_name, loader, origin="built-in")
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
Sign up to request clarification or add additional context in comments.

Comments

1

As a quick fix, you can dump it in a temporary module, import it using exec and delete the temp module when you're done.

Here's a toy example:

dummy_src =""" 
print("imported!") 
x = 5 
""" 

with open("temp.py", "w") as f: 
    f.write(dummy_src) 

exec("import temp") 
print(temp.x)

Output:

imported!
5

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.