9

The following code works in Python 2:

from ctypes import *

## Setup python file -> c 'FILE *' conversion :
class FILE(Structure):
    pass
FILE_P = POINTER(FILE)
PyFile_AsFile = pythonapi.PyFile_AsFile # problem here
PyFile_AsFile.argtypes = [py_object]
PyFile_AsFile.restype = FILE_P
fp = open(filename,'wb')
gd.gdImagePng(img, PyFile_AsFile(fp))

But in Python 3, there is no PyFile_AsFile in pythonapi.

The code is an except from testPixelOps.py.

Janus Troelsen
  • 20,267
  • 14
  • 135
  • 196
  • Looks like there's a solution [here](http://www.salstar.sk/pub/svplayer/vlc.py), but it segfaults for me. – Janus Troelsen Apr 21 '13 at 10:39
  • Ah, I know why it segfaulted. I was using the file descriptor as a FILE*... – Janus Troelsen Apr 21 '13 at 10:46
  • It is perfectly fine answer your own question and accept it, if of course there are no other better answers – jamylak Apr 21 '13 at 10:50
  • @jamylak: I didn't find the answer. I just found out I did something wrong. I don't think it's actually a solution any more, cause they just import different API functions depending on the version, but they don't actually use them. – Janus Troelsen Apr 21 '13 at 11:07
  • Why do you want to call `PyFile_AsFile` using ctypes? You use ctypes to reach things that are not available using Python. – David Heffernan Apr 21 '13 at 14:15
  • @DavidHeffernan: I just needed a way to convert a file object to a ctypes FILE* so that I can pass it to GD. I.e. I am just looking for a Python 3 equivalent of the above. PyFile_AsFile is indeed available using Python 2, as this piece of code demonstrates. – Janus Troelsen Apr 21 '13 at 14:33
  • I certainly never said that `PyFile_AsFile` was not available. Just that it was an odd thing to be using in the first place. – David Heffernan Apr 21 '13 at 14:40
  • Check this answer: http://stackoverflow.com/a/9629271/1046299 – MasterMind Sep 03 '14 at 15:48
  • Python 3 still has something similar to `PyFile_AsFile`, but it is a two-step process now. What used to be `FILE *fp = PyFile_AsFile(p)` is now [`FILE *fp = fdopen(PyObject_AsFileDescriptor(p), "r")`](https://docs.python.org/3.7/c-api/file.html#c.PyObject_AsFileDescriptor). – Henri Menke Jul 11 '19 at 05:37

2 Answers2

13

I just needed a way to convert a file object to a ctypes FILE* so that I can pass it to GD.

You are out of luck. That was possible in Python 2.x, but is not possible in Python 3.x. The documentation explains why not:

These APIs are a minimal emulation of the Python 2 C API for built-in file objects, which used to rely on the buffered I/O (FILE*) support from the C standard library. In Python 3, files and streams use the new io module, which defines several layers over the low-level unbuffered I/O of the operating system.

If you want a FILE* you are going to have to make one yourself, using the C standard library directly.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
3

I didn't find a real answer to the problem, but I found out that if you don't need to convert the Python file object to a FILE* (i.e., you don't need to "share" the opened file), you can just use ctypes to call fopen from libc and get the FILE* like that.

Janus Troelsen
  • 20,267
  • 14
  • 135
  • 196