Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you use the python3 c api for a command line driven app?

Tags:

c

python-3.x

I've been using a custom build as a replacement for virtualenv for a while now, and it's brillant. It takes longer to build, but it actually works, and it never screws up.

Part of this in a simple python wrapper that adds some specific folders to the library path, which I've found very useful. The code for it is trivial:

#include <stdio.h>
#include <n/text/StringUtils.h>
#include <Python.h>

int main(int argc, char *argv[]) {

  /* Setup */
  Py_SetProgramName(argv[0]);
  Py_Initialize();
  PySys_SetArgv(argc, argv);

  /* Add local path */
  PyObject *sys = PyImport_ImportModule("sys");
  PyObject *path = PyObject_GetAttrString(sys, "path");

  /* Custom path */
  char *cwd = nrealpath(argv[0]);
  char *libdir = nstrpath(cwd, "python_lib", NULL);
  PyList_Append(path, PyString_FromString(libdir));
  free(cwd);
  free(libdir);

  /* Run the 'main' module */
  int rtn = Py_Main(argc, argv); // <-- Notice the command line arguments.
  Py_Finalize();

  return rtn;
}

So, moving to python3 is good right? So...

I dutifully replaced the call to PyString_FromString() with PyByte_FromString() and tried to recompile, but it raises errors:

/Users/doug/env/src/main.c:8:21: error: incompatible pointer types passing 'char *' to parameter of type 'wchar_t *' (aka 'int *')
      [-Werror,-Wincompatible-pointer-types]
  Py_SetProgramName(argv[0]);
                    ^~~~~~~
/Users/doug/projects/py-sdl2/py3/include/python3.3m/pythonrun.h:25:45: note: passing argument to parameter here
PyAPI_FUNC(void) Py_SetProgramName(wchar_t *);
                                            ^
/Users/doug/env/src/main.c:10:23: error: incompatible pointer types passing 'char **' to parameter of type 'wchar_t **' (aka 'int **')
      [-Werror,-Wincompatible-pointer-types]
  PySys_SetArgv(argc, argv);
                      ^~~~
/Users/doug/projects/py-sdl2/py3/include/python3.3m/sysmodule.h:12:47: note: passing argument to parameter here
PyAPI_FUNC(void) PySys_SetArgv(int, wchar_t **);
                                              ^
/Users/doug/env/src/main.c:24:27: error: incompatible pointer types passing 'char **' to parameter of type 'wchar_t **' (aka 'int **')
      [-Werror,-Wincompatible-pointer-types]
  int rtn = Py_Main(argc, argv);
                          ^~~~
/Users/doug/projects/py-sdl2/py3/include/python3.3m/pythonrun.h:148:45: note: passing argument to parameter 'argv' here
PyAPI_FUNC(int) Py_Main(int argc, wchar_t **argv);
                                            ^
3 errors generated.
make[2]: *** [CMakeFiles/python.dir/src/main.c.o] Error 1
make[1]: *** [CMakeFiles/python.dir/all] Error 2
make: *** [all] Error 2

As you can see from the error, wchar_t is used instead of char *.

How are you supposed to use this api?

I see there are a few examples of doing this, for example: http://svn.python.org/projects/python/tags/r32rc2/Python/frozenmain.c

seriously?

My 29 line program has to become a 110 line monster full of #ifdefs?

Am I misunderstanding, or has the python3 c api really become this ridiculously difficult to use?

Surely I'm missing some obvious convenience function which does this for you, in a simple, portable and cross platform way?

like image 682
Doug Avatar asked Aug 15 '13 01:08

Doug


People also ask

What is the Python C API?

The Python/C API allows for compiled pieces of code to be called from Python programs or executed within the CPython interpreter. This process of producing compiled code for use by CPython is generally known as "extending" Python and the compiled pieces of code to be used are known as "extension modules".

What is a command line utility in Python?

Command line utilities are tools that you can run on the command line of a computer. We most often see these on Linux and MacOS computers using the 'bash' shell, but Windows users have options like CMD, git-bash and powershell too. These tools allow you to instruct the computer to do things using text alone.


1 Answers

The official recommended way of converting from char to wchar_t is by using Py_DecodeLocale. Like this:

wchar_t *program = Py_DecodeLocale(argv[0], NULL);
Py_SetProgramName(program);
like image 125
qft Avatar answered Nov 04 '22 20:11

qft