Передача io._BufferedReader в c-функцию - PullRequest
0 голосов
/ 09 декабря 2011

Этот вопрос является продолжением моего предыдущего вопроса , где я пытался скомпилировать python-yenc для Python3. После того, как мне сказали, что не было быстрого решения, я решил взять вызов и полностью переписать его.

Единственное, что я не могу понять, это как использовать PyArg_ParseTupleAndKeywords с io-объектами. Вот соответствующий код:

PyObject *in_file, *out_file;
static char *kwlist[] = { "infile", "outfile", NULL };

if(!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!|l", kwlist,\
                     &PyBytes_Type, &in_file,\
                     &PyBytes_Type, &out_file,\
                     &bytes)) return NULL;

что, очевидно, дает

Traceback (most recent call last):
  File "test.py", line 21, in <module>
    print(_yenc.decode_file(b), outfile=o)
TypeError: argument 1 must be bytes, not _io.BufferedReader

Как я могу передать _io.BufferedReader -объектам в мою функцию?

Спасибо, Мартейн

Ответы [ 3 ]

1 голос
/ 24 декабря 2011

Mzialla, вы не должны использовать «O!» в PyArg_ParseTupleAndKeywords. Это означает, что вы не можете передать любой объект, кроме указанного типа. Я считаю, что типичный подход к взаимодействию с файлами в расширениях Python 3.2 заключается не в том, чтобы предполагать какой-либо конкретный тип, а в программе против «протокола».

Так что вы должны сделать что-то вроде:

if(!PyArg_ParseTupleAndKeywords(args, kwds, "OO|l", kwlist,\
             &in_file, &out_file, &bytes))
    ...

И после этого у вас есть два варианта: либо вы взаимодействуете с объектом потока, используя его методы Python, либо получаете файловый дескриптор потока (PyObject_AsFileDescriptor) и работаете с ним, используя уровень ОС read / запись функций (или эквивалент).

Для подхода с использованием метода Python вы должны получить метод read и вызвать его вместо fread. Что-то вроде следующего (не проверено):

PyObject *read = PyObject_GetAttrString(in_file, "read");
if (!read) handle_error;
while(encoded < bytes || bytes == 0) {
    PyObject *bytes_obj= PyObject_CallFunction(read, "i", 1);
    if (!bytes || !PyBytes_Check(bytes_obj)) handle_error;
    char *s = PyBytes_AS_STRING(bytes_obj);
    ...
}

Тогда вам нужно будет сделать что-то подобное для стороны записи.

1 голос
/ 31 января 2018

Это работает:

PyObject *decode_file(PyObject *pPySelf, 
              PyObject* pPyArgs, 
              PyObject* pPyKeywords) {
  uLong     totDecodedBytes = 0;
  uInt      decodedBytes;
  uLong     bytesToRead;
  uLong     bytesToDecode = 0;
  Bool      escape = 0;
  Crc32     crc;
  PyObject *pPyInFile = NULL;
  PyObject *pPyOutFile = NULL;
  if (!PyArg_ParseTupleAndKeywords(pPyArgs,
                   pPyKeywords,
                   "OO|l",
                   argnames,
                   &pPyInFile,
                   &pPyOutFile,
                   &bytesToDecode)) {
    return NULL;
  }
  crc_init(&crc, 0xffffffffl);
  while (totDecodedBytes < bytesToDecode) {
    char      *readBuffer;
    Byte       writeBuffer[LONGBUFF];
    Py_ssize_t readBytes;
    PyObject  *pPyReadBuffer;
    bytesToRead = bytesToDecode - totDecodedBytes;
    if (bytesToRead > BLOCK) {
      bytesToRead = BLOCK;
    } else if (bytesToRead == 0) {
      break;
    }
    pPyReadBuffer = PyObject_CallMethod(pPyInFile, "read", "l", bytesToRead);
    if (NULL == pPyReadBuffer) {
      break;
    }
    PyBytes_AsStringAndSize(pPyReadBuffer, &readBuffer, &readBytes);
    decodedBytes = decode_buffer((Byte*)readBuffer,
                 writeBuffer,
                 readBytes,
                 &crc,
                 &escape);
    PyObject_CallMethod(pPyOutFile, "write", "y#", writeBuffer, decodedBytes);
    totDecodedBytes += decodedBytes;
  }
  return Py_BuildValue("(l,L)", totDecodedBytes, (long long)crc.crc);
}
1 голос
/ 19 декабря 2011

Я скачал yenc (https://bitbucket.org/dual75/yenc) и попробовал следующую тестовую программу, но безуспешно.

#include <stdio.h>
#include "_yenc.h"

int main()
{
    PyObject *in_file, *out_file;
    PyObject *args;
    PyObject *kwds;
    int bytes;
    static char *kwlist[] = { "infile", "outfile", NULL };

    if(!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!|l", kwlist,\
                 &PyBytes_Type, &in_file,\
                 &PyBytes_Type, &out_file,\
                 &bytes)) {
        printf("Yay!\n");
    } else {
        printf("Bad luck :( \n");
    }

    return 0;
}

Я скомпилировал приведенный выше код, используя следующее:

gcc test.c -I/usr/include/python2.7/

и столкнулся со следующими проблемами:

/tmp/ccRasW2G.o: In function `main':
/home/sangeeth/work/github/yenc/yenc/src/test.c:14: undefined reference to `PyString_Type'
/home/sangeeth/work/github/yenc/yenc/src/test.c:14: undefined reference to `PyString_Type'
/home/sangeeth/work/github/yenc/yenc/src/test.c:14: undefined reference to `PyArg_ParseTupleAndKeywords'
collect2: ld returned 1 exit status

Итак, не могли бы вы рассказать, как воспроизвести этот вопрос?

OTOH, я вижу

PyArg_ParseTupleAndKeywords (in src/_yenc.c) 

, который занимает оба

&PyFile_Type 

и

&PyString_Type 

и прекрасно компилируется (сборка python setup.py). Так в чем же проблема, которую вы видите?

...