25

I want to call a Python script from C, passing some arguments that are needed in the script.

The script I want to use is mrsync, or multicast remote sync. I got this working from command line, by calling:

python mrsync.py -m /tmp/targets.list -s /tmp/sourcedata -t /tmp/targetdata

-m is the list containing the target ip-addresses. -s is the directory that contains the files to be synced. -t is the directory on the target machines where the files will be put.

So far I managed to run a Python script without parameters, by using the following C program:

Py_Initialize();
FILE* file = fopen("/tmp/myfile.py", "r");
PyRun_SimpleFile(file, "/tmp/myfile.py");
Py_Finalize();

This works fine. However, I can't find how I can pass these argument to the PyRun_SimpleFile(..) method.

2 Answers 2

44
+50

Seems like you're looking for an answer using the python development APIs from Python.h. Here's an example for you that should work:

#My python script called mypy.py
import sys

if len(sys.argv) != 2:
  sys.exit("Not enough args")
ca_one = str(sys.argv[1])
ca_two = str(sys.argv[2])

print "My command line args are " + ca_one + " and " + ca_two

And then the C code to pass these args:

//My code file
#include <stdio.h>
#include <python2.7/Python.h>

void main()
{
    FILE* file;
    int argc;
    char * argv[3];

    argc = 3;
    argv[0] = "mypy.py";
    argv[1] = "-m";
    argv[2] = "/tmp/targets.list";

    Py_SetProgramName(argv[0]);
    Py_Initialize();
    PySys_SetArgv(argc, argv);
    file = fopen("mypy.py","r");
    PyRun_SimpleFile(file, "mypy.py");
    Py_Finalize();

    return;
}

If you can pass the arguments into your C function this task becomes even easier:

void main(int argc, char *argv[])
{
    FILE* file;

    Py_SetProgramName(argv[0]);
    Py_Initialize();
    PySys_SetArgv(argc, argv);
    file = fopen("mypy.py","r");
    PyRun_SimpleFile(file, "mypy.py");
    Py_Finalize();

    return;
}

You can just pass those straight through. Now my solutions only used 2 command line args for the sake of time, but you can use the same concept for all 6 that you need to pass... and of course there's cleaner ways to capture the args on the python side too, but that's just the basic idea.

Hope it helps!

Sign up to request clarification or add additional context in comments.

5 Comments

Note that in Python 3 you'll have to first convert to wide chars (wchar_t). Try mbstowcs_s (#include <stdlib.h>) for this.
Hi @Mike, could you please have a look at this situation, where I need to pass the arguments to a python package app (not just a py file): stackoverflow.com/questions/21036106/…
@JayZ - Sorry, just got back on today, looks like you got your answer at the other link, but if you need more help let me know.
I revisited this example - while CPython seems to use argc as well, I'd still use a NULL pointer at the end of self-constructed argv, as is in the standard C argv.
@Mike where to put the '-m' option ? how to set flags in Python/C api ?
9

You have two options.

  1. Call

    system("python mrsync.py -m /tmp/targets.list -s /tmp/sourcedata -t /tmp/targetdata")
    

    in your C code.

  2. Actually use the API that mrsync (hopefully) defines. This is more flexible, but much more complicated. The first step would be to work out how you would perform the above operation as a Python function call. If mrsync has been written nicely, there will be a function mrsync.sync (say) that you call as

    mrsync.sync("/tmp/targets.list", "/tmp/sourcedata", "/tmp/targetdata")
    

    Once you've worked out how to do that, you can call the function directly from the C code using the Python API.

4 Comments

+1 Your first method is working indeed, thank you for that. However, I'm still curious to know if it can't be achieved with the Python Interpreter that I described in my first post. Your second method will unfortunately not work in this situation, because mrsync doesn't provide an API.
Actually, it might be possible to initialise the interpreter with argv and then have it exec a file. You'd have to try it and see, though, I don't know.
Option 1 only working with the assumption that "python" is guaranteed to be in the system path
Using the system function gets always a downvote from me. The string provided to system is subject to shell interpretation and thus the function is quite dangerous. The proper way to execute commands from C in POSIX systems are the exec* functions. Thus: execlp("python", "python", "mrsync.py", "-m", ..., "/tmp/targetdata", NULL); If you want to link against a specific python interpreter, you can use Mike's answer. Please note however that if you update the python your application will break much easier, which necessarily is not the case when using just exec.

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.