Search code examples
pythonpython-3.xblender

Python 3.X exec + compile - pass command line arguments


I see answers on how to pass command line arguments to exec but not if compile is nestled inside.

Provided with the code:

filename = 'C:\\temp\\script.py'
exec(compile(open(filename).read(),filename, 'exec'))

How can I pass command line arguments or input to the calling code?


Additional context (you don't have to read):

A program called Blender has a built-in text editor / interpreter. And I want to inject code using the statement above. I tried using exec by itself without success. That's why I need exec and compile together. Code structure looks like this:

import bpy

# Create a new material
mat_ref = bpy.data.materials.new("Mat for joined object")

# Use shader nodes to render the material
mat_ref.use_nodes = True
nodes = mat_ref.node_tree.nodes

# Start from fresh state
mat_ref.node_tree.links.clear()
mat_ref.node_tree.nodes.clear()

# Create the Shader nodes
node_diffuse = nodes.new( type = 'ShaderNodeBsdfDiffuse' )
node_matoutput = nodes.new( type = 'ShaderNodeOutputMaterial' )
node_voronoi = nodes.new( type = 'ShaderNodeTexVoronoi' )

# Link the shader nodes
mat_ref.node_tree.links.new( node_diffuse.outputs['BSDF'], node_matoutput.inputs['Surface'] )
mat_ref.node_tree.links.new( node_voronoi.outputs['Distance'], node_matoutput.inputs['Displacement'] )

# Change properties of shader nodes
mat_ref.node_tree.nodes["Voronoi Texture"].inputs[2].default_value = 300

Solution

  • I'm not sure if what you're trying to do is the best way to achieve your actual goal - you may have a bit of an XY problem here.

    However, the question is valid and the answer straightforward. exec() will execute your code in the current context, so whatever your current arguments are in the calling script will be the arguments available to the called script.

    So, if script.py is:

    import sys
    
    print('Script: ', sys.argv[0])
    
    if len(sys.argv) > 1:
        print('Argument(s): ', ' '.join(sys.argv[1:]))
    

    And you run this compile_exec.py:

    import sys
    
    with open('script.py') as f:
        content = f.read()
    
    compile(content, 'script.py', 'exec')
    
    sys.argv[1:] = ['Hello', 'world']  # overriding any other arguments, careful
    
    exec(content)
    

    That will output:

    Script:  < path to your script >\compile_exec.py
    Argument(s):  Hello world
    

    Or, as the oneliner you asked about:

    import sys
    
    sys.argv[1:] = ['Hello', 'world']
    exec(compile(open('script.py').read(), 'script.py', 'exec'))