2

I am using Python 3 within a C program. What I wish to do is "run" a single Python file (.py) which will be the main file for a larger Python project.

When I use import within this Python file, it works fine for other Python files in the same directory. In fact, the import works for files in sub-directories too whenever I run the .py file using "Python" in the terminal.

However, if I run it using PyRun_SimpleFile in C, I receive a "ModuleNotFoundError" error.

Here is my directory setup:

Project/
|-- Program.cpp
|-- Program.exe
|-- __init__.py
|-- bla.py
|-- Test/
|   |-- __init__.py
|   |-- bla2.py

Preferably, I do not want to use sys.path.append('./Test') so I can use the sub-directory names within the imports.

Here are the contents of all relevant files:


bla.py

import Test.bla2

bla2.py

print("in bla2.py now!")

Program.cpp

#include <Python.h>
#include <iostream>

int main(int argc, char **argv)
{
    Py_Initialize();

    FILE *file = _Py_fopen( "bla.py", "r" ); 
    PyRun_SimpleFile(file, "bla.py");

    Py_Finalize();
    return 0;
}
Griffort
  • 1,174
  • 1
  • 10
  • 26
  • Perhaps this can help you: https://stackoverflow.com/questions/3654652/why-does-the-python-c-api-crash-on-pyrun-simplefile – Pablo Feb 04 '18 at 17:12
  • @Pablo Thank you for the suggestion; however, it does not appear to be relevant to the question here. I did try some of the answers, but to no avail. Keep in mind the Python works perfectly for me, it's just the import statements that are causing issues only when run through the C program. – Griffort Feb 04 '18 at 18:14
  • 2
    Perhaps it would help you to use `setenv` and add the path of the modules in `PYTHONPATH` before calling `PyRun_SimpleFile`. – Pablo Feb 04 '18 at 18:29
  • 1
    [`PySys_SetArgvEx`](https://docs.python.org/3/c-api/init.html#c.PySys_SetArgvEx) might be what you want. – DavidW Feb 04 '18 at 22:05

1 Answers1

2

You just need to add

PySys_SetArgv(1, argv);

after Py_Initialize (documentation). This prepends the directory that your program is in to your Python path. (This is equivalent to prepending . to the path, rather than ./Test, but it should be more reliable, for example if your program is not started from its own directory. It gets the needed information on where your program is located from C++ argv)

It's probably a bad idea to call your module Test. There's a built-in Python module called test, and on a case-insensitive OS like Windows that might conflict and get loaded instead.

DavidW
  • 29,336
  • 6
  • 55
  • 86
  • Thanks, works like a charm! ^^ However, I did receive an error. I've resolved it by simply parsing argv to wchar_t**, but perhaps there is a better way to handle it? `error: cannot convert 'char**' to 'wchar_t**' for argument '2' to 'void PySys_SetArgv(int, wchar_t**)' PySys_SetArgv(1, argv);` – Griffort Feb 06 '18 at 01:24
  • 1
    Ah - I accidentally used Python 2 rather than 3 when I was testing so didn't see that error... I don't know of a anything better than just parsing it. – DavidW Feb 06 '18 at 07:18