Skip to content

Commit

Permalink
Port fileobj implementation to C to enable conditional compilation
Browse files Browse the repository at this point in the history
  • Loading branch information
althonos committed Aug 3, 2023
1 parent 8554a9d commit a7951c2
Show file tree
Hide file tree
Showing 12 changed files with 432 additions and 274 deletions.
12 changes: 6 additions & 6 deletions include/libhmmer/p7_hmmfile.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ cdef extern from "hmmer.h" nogil:
char[eslERRBUFSIZE] errbuf


int p7_hmmfile_OpenE (const char *filename, char *env, P7_HMMFILE **ret_hfp, char *errbuf)
int p7_hmmfile_OpenENoDB(const char *filename, char *env, P7_HMMFILE **ret_hfp, char *errbuf)
int p7_hmmfile_Open (const char *filename, char *env, P7_HMMFILE **ret_hfp) # Deprecated
int p7_hmmfile_OpenNoDB (const char *filename, char *env, P7_HMMFILE **ret_hfp) # Deprecated
int p7_hmmfile_OpenBuffer(const char *buffer, int size, P7_HMMFILE **ret_hfp)
void p7_hmmfile_Close(P7_HMMFILE *hfp)
int p7_hmmfile_OpenE (const char *filename, char *env, P7_HMMFILE **ret_hfp, char *errbuf) except *
int p7_hmmfile_OpenENoDB(const char *filename, char *env, P7_HMMFILE **ret_hfp, char *errbuf) except *
int p7_hmmfile_Open (const char *filename, char *env, P7_HMMFILE **ret_hfp) except * # Deprecated
int p7_hmmfile_OpenNoDB (const char *filename, char *env, P7_HMMFILE **ret_hfp) except * # Deprecated
int p7_hmmfile_OpenBuffer(const char *buffer, int size, P7_HMMFILE **ret_hfp) except *
void p7_hmmfile_Close(P7_HMMFILE *hfp) except *

int p7_hmmfile_WriteBinary(FILE *fp, int format, P7_HMM *hmm) except *
int p7_hmmfile_WriteASCII (FILE *fp, int format, P7_HMM *hmm) except *
Expand Down
23 changes: 10 additions & 13 deletions pyhmmer/easel.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,6 @@ to facilitate the development of biological software in C. It is used by
"""

# --- C declarations ---------------------------------------------------------

IF UNAME_SYSNAME == "Linux":
include "fileobj/linux.pxi"
ELIF UNAME_SYSNAME == "Darwin" or UNAME_SYSNAME.endswith("BSD"):
include "fileobj/bsd.pxi"

# --- C imports --------------------------------------------------------------

cimport cython
Expand All @@ -26,7 +19,7 @@ from cpython.memoryview cimport PyMemoryView_FromMemory
from cpython.ref cimport Py_INCREF
from cpython.tuple cimport PyTuple_New, PyTuple_SET_ITEM
from libc.stdint cimport int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t, SIZE_MAX
from libc.stdio cimport fclose
from libc.stdio cimport fclose, FILE
from libc.stdlib cimport calloc, malloc, realloc, free
from libc.string cimport memcmp, memcpy, memmove, memset, strdup, strlen, strncpy
from posix.types cimport off_t
Expand Down Expand Up @@ -94,8 +87,12 @@ from .reexports.esl_sqio_ascii cimport (
fileheader_hmmpgmd,
)

include "exceptions.pxi"
if PLATFORM_UNAME_SYSTEM == "Linux":
from .fileobj.linux cimport fileobj_linux_open as fopen_obj
elif PLATFORM_UNAME_SYSTEM == "Darwin" or PLATFORM_UNAME_SYSTEM.endswith("BSD"):
from .fileobj.bsd cimport fileobj_bsd_open as fopen_obj

include "exceptions.pxi"

# --- Python imports ---------------------------------------------------------

Expand Down Expand Up @@ -3465,7 +3462,7 @@ cdef class MSA:
raise InvalidParameter("format", format, choices=list(MSA_FILE_FORMATS))

fmt = MSA_FILE_FORMATS[format]
file = fopen_obj(fh, mode="w")
file = fopen_obj(fh, "w")
status = libeasel.msafile.esl_msafile_Write(file, self._msa, fmt)
fclose(file)

Expand Down Expand Up @@ -4080,7 +4077,7 @@ cdef class MSAFile:
cdef int status
cdef ESL_BUFFER* buffer = NULL
cdef ESL_MSAFILE* msaf = NULL
cdef FILE* fp = fopen_obj(fh)
cdef FILE* fp = fopen_obj(fh, "r")
cdef bytes fh_repr = repr(fh).encode("ascii")

try:
Expand Down Expand Up @@ -4818,7 +4815,7 @@ cdef class Sequence:
assert self._sq != NULL

cdef int status
cdef FILE* file = fopen_obj(fh, mode="w")
cdef FILE* file = fopen_obj(fh, "w")

status = libeasel.sqio.ascii.esl_sqascii_WriteFasta(file, self._sq, False)
fclose(file)
Expand Down Expand Up @@ -6108,7 +6105,7 @@ cdef class SequenceFile:
cdef int status
cdef ESL_SQFILE* sqfp = NULL
cdef ESL_SQASCII_DATA* ascii = NULL
cdef FILE* fp = fopen_obj(fh)
cdef FILE* fp = fopen_obj(fh, "r")
cdef bytes fh_repr = repr(fh).encode("ascii")

# bail out early if format is not supported
Expand Down
Empty file added pyhmmer/fileobj/__init__.pxd
Empty file.
188 changes: 188 additions & 0 deletions pyhmmer/fileobj/bsd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
#ifndef _PYHMMER_FILEOBJ_LINUX
#define _PYHMMER_FILEOBJ_LINUX

#include <stdio.h>

#include <Python.h>
#include "util.h"

#define _COOKIE_ERROR_CLOSE -1
#define _COOKIE_ERROR_WRITE -1
#define _COOKIE_ERROR_READ -1
#define _COOKIE_ERROR_SEEK -1

int fileobj_bsd_write(void* cookie, const char* buf, int size) {
PyObject* file = (PyObject*) cookie;

PyObject* out = PyObject_CallMethod(file, "write", "y#", buf, (Py_ssize_t) size);
if (out == NULL)
return _COOKIE_ERROR_WRITE;

if (!PyLong_Check(out)) {
Py_DECREF(out);
PyErr_SetString(PyExc_TypeError, "Expected int");
return _COOKIE_ERROR_WRITE;
}

int n = PyLong_AsLongLong(out);
Py_DECREF(out);
return n;
}

int fileobj_bsd_read(void* cookie, char* buf, int size) {
PyObject* file = (PyObject*) cookie;

PyObject* chunk = PyObject_CallMethod(file, "read", "n", size);
if (chunk == NULL)
return _COOKIE_ERROR_READ;

const char* data = PyBytes_AsString(chunk);
if (data == NULL) {
Py_DECREF(chunk);
return _COOKIE_ERROR_READ;
}

Py_ssize_t len = PyBytes_Size(chunk);
if (len > size) {
Py_DECREF(chunk);
PyErr_SetString(PyExc_BufferError, "buffer too small to store `read` result");
return _COOKIE_ERROR_READ;
}

memcpy(buf, data, len);

Py_DECREF(chunk);
return len;
}

int fileobj_bsd_readinto(void* cookie, char* buf, int size) {
PyObject* file = (PyObject*) cookie;

PyObject* mem = PyMemoryView_FromMemory(buf, (Py_ssize_t) size, PyBUF_WRITE);
if (mem == NULL)
return _COOKIE_ERROR_READ;

PyObject* out = PyObject_CallMethod(file, "readinto", "O", mem);
if (out == NULL) {
Py_DECREF(mem);
return _COOKIE_ERROR_READ;
}

if (!PyLong_Check(out)) {
Py_DECREF(out);
Py_DECREF(mem);
PyErr_SetString(PyExc_TypeError, "Expected int");
return _COOKIE_ERROR_WRITE;
}

Py_ssize_t len = PyLong_AsSize_t(out);
Py_DECREF(out);
Py_DECREF(mem);
return len;
}

int fileobj_bsd_seek(void* cookie, off64_t* offset, int whence) {
PyObject* file = (PyObject*) cookie;

PyObject* out = PyObject_CallMethod(file, "seek", "Li", *offset, whence);
if (out == NULL)
return _COOKIE_ERROR_SEEK;

if (!PyLong_Check(out)) {
Py_DECREF(out);
PyErr_SetString(PyExc_TypeError, "Expected int");
return _COOKIE_ERROR_SEEK;
}

*offset = PyLong_AsLongLong(out);
Py_DECREF(out);
return 0;
}

int fileobj_bsd_close(void* cookie) {
PyObject* file = (PyObject*) cookie;
Py_DECREF(file);
return _COOKIE_ERROR_CLOSE;
}

FILE* fileobj_bsd_open(PyObject* obj, const char* mode) {
Py_INCREF(obj);

PyTypeObject* ty = Py_TYPE(obj);

readfn_t readfn;
writefn_t writefn;
seekfn_t seekfn;

PyObject* readable = PyObject_CallMethod(obj, "readable", NULL);
if (readable == NULL)
return NULL;
switch (PyObject_IsTrue(readable)) {
case 1:
Py_DECREF(readable);
functions.read = ((is_cpython() == 1) && PyObject_HasAttrString(obj, "readinto")) ? fileobj_linux_readinto : fileobj_linux_read;
break;
case 0:
Py_DECREF(readable);
readfn = NULL;
break;
default:
Py_DECREF(readable);
PyErr_Format(PyExc_TypeError, "Expected `io.IOBase` instance, found %s", ty->tp_name);
return NULL;
}

PyObject* seekable = PyObject_CallMethod(obj, "seekable", NULL);
if (seekable == NULL)
return NULL;
switch (PyObject_IsTrue(seekable)) {
case 1:
Py_DECREF(seekable);
seekfn = fileobj_linux_seek;
break;
case 0:
Py_DECREF(seekable);
seekfn = NULL;
break;
default:
Py_DECREF(seekable);
PyErr_Format(PyExc_TypeError, "Expected `io.IOBase` instance, found %s", ty->tp_name);
return NULL;
}

PyObject* writable = PyObject_CallMethod(obj, "writable", NULL);
if (writable == NULL)
return NULL;
switch (PyObject_IsTrue(writable)) {
case 1:
Py_DECREF(writable);
writefn = fileobj_linux_write;
break;
case 0:
Py_DECREF(writable);
writefn = NULL;
break;
default:
Py_DECREF(writable);
PyErr_Format(PyExc_TypeError, "Expected `io.IOBase` instance, found %s", ty->tp_name);
return NULL;
}

FILE* file = funopen(
(void*) obj,
readfn,
writefn,
seekfn,
fileobj_bsd_close
);
if (file == NULL) {
PyErr_SetString(PyExc_RuntimeError, "Failed to open file-like object");
Py_DECREF(obj);
}

return file;
}



#endif
4 changes: 4 additions & 0 deletions pyhmmer/fileobj/bsd.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from libc.stdio cimport FILE

cdef extern from "fileobj/linux.h":
FILE* fileobj_bsd_open(object obj, const char* mode) except NULL
Loading

0 comments on commit a7951c2

Please sign in to comment.