Skip to content

Commit

Permalink
Merge pull request #16 from JensGM/py3-2
Browse files Browse the repository at this point in the history
Make cwrap Python3 compatible
  • Loading branch information
JensGM authored Feb 8, 2018
2 parents ac51b4f + cbf7ed9 commit a0e01d8
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 84 deletions.
7 changes: 7 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
language: python
python:
- "2.7"
- "3.6"

os:
- linux
# - osx

install:
- pip install -r requirements.txt
Expand Down
8 changes: 4 additions & 4 deletions cwrap/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,23 @@
try: from .version import version as __version__
except ImportError: __version__ = '0.0.0'

__author__ = 'Jean-Paul Balabanian, Joakim Hove, and PG Drange'
__author__ = 'Statoil ASA'
__copyright__ = 'Copyright 2016, Statoil ASA'
__credits__ = __author__
__license__ = 'GPL'
__maintainer__ = __author__
__email__ = __author__
__email__ = 'fg_gpl@statoil.com'
__status__ = 'Prototype'

from .basecclass import BaseCClass
from .basecenum import BaseCEnum
from .basecvalue import BaseCValue

from .cfile import CFILE
from .cfile import CFILE, copen as open
from .clib import load, lib_name

from .metacwrap import MetaCWrap
from .prototype import REGISTERED_TYPES, Prototype, PrototypeError

__all__ = ['BaseCClass', 'BaseCEnum', 'BaseCValue', 'CFILE',
__all__ = ['BaseCClass', 'BaseCEnum', 'BaseCValue', 'CFILE', 'open',
'MetaCWrap', 'Prototype', 'load', 'lib_name']
176 changes: 139 additions & 37 deletions cwrap/cfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,57 +14,159 @@
# See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
# for more details.

import ctypes
import six
from .prototype import Prototype, PrototypeError
import sys
from .prototype import Prototype
from .basecclass import BaseCClass

class CFILE(BaseCClass):
"""
Utility class to map a Python file handle <-> FILE* in C
"""
TYPE_NAME = "FILE"

_as_file = Prototype(ctypes.pythonapi, "void* PyFile_AsFile(py_object)")
if six.PY2:
import ctypes

def __init__(self, py_file):
def copen(filename, mode='r'):
"""
Takes a python file handle and looks up the underlying FILE *
This is a compatibility layer for functions taking FILE* pointers, and
should not be used unless absolutely needed.
The purpose of the CFILE class is to be able to use python
file handles when calling C functions which expect a FILE
pointer. A CFILE instance should be created based on the
Python file handle, and that should be passed to the function
expecting a FILE pointer.
In Python 2 this function is simply an alias for open. In Python 3,
however, it returns an instance of CWrapFile, a very light weight
wrapper around a FILE* instance.
"""
return open(filename, mode)

class CFILE(BaseCClass):
"""
Utility class to map a Python file handle <-> FILE* in C
"""
TYPE_NAME = "FILE"

_as_file = Prototype(ctypes.pythonapi, "void* PyFile_AsFile(py_object)")

def __init__(self, py_file):
"""
Takes a python file handle and looks up the underlying FILE *
The purpose of the CFILE class is to be able to use python
file handles when calling C functions which expect a FILE
pointer. A CFILE instance should be created based on the
Python file handle, and that should be passed to the function
expecting a FILE pointer.
The implementation is based on the ctypes object
pythonapi which is ctypes wrapping of the CPython api.
C-function:
void fprintf_hello(FILE * stream , const char * msg);
Python wrapper:
lib = clib.load( "lib.so" )
fprintf_hello = Prototype(lib, "void fprintf_hello( FILE , char* )")
Python use:
py_fileH = open("file.txt" , "w")
fprintf_hello( CFILE( py_fileH ) , "Message ...")
py_fileH.close()
If the supplied argument is not of type py_file the function
will raise a TypeException.
Examples: ecl.ecl.ecl_kw.EclKW.fprintf_grdecl()
"""
c_ptr = self._as_file(py_file)
try:
super(CFILE, self).__init__(c_ptr)
except ValueError:
raise TypeError("Sorry - the supplied argument is not a valid "
" Python file handle!")

self.py_file = py_file

The implementation is based on the ctypes object
pythonapi which is ctypes wrapping of the CPython api.
def __del__(self):
pass

C-function:
void fprintf_hello(FILE * stream , const char * msg);

Python wrapper:
lib = clib.load( "lib.so" )
fprintf_hello = Prototype(lib, "void fprintf_hello( FILE , char* )")
if six.PY3:
from .clib import load as cwrapload

Python use:
py_fileH = open("file.txt" , "w")
fprintf_hello( CFILE( py_fileH ) , "Message ...")
py_fileH.close()
class LibcPrototype(Prototype):
lib = cwrapload(None)

If the supplied argument is not of type py_file the function
will raise a TypeException.
def __init__(self, prototype, bind=False, allow_attribute_error=False):
super(LibcPrototype, self).__init__(
LibcPrototype.lib,
prototype,
bind=bind,
allow_attribute_error=allow_attribute_error)

Examples: ecl.ecl.ecl_kw.EclKW.fprintf_grdecl()
def copen(filename, mode='r'):
"""
c_ptr = self._as_file(py_file)
try:
super(CFILE, self).__init__(c_ptr)
except ValueError as e:
raise TypeError("Sorry - the supplied argument is not a valid Python file handle!")
This is a compatibility layer for functions taking FILE* pointers, and
should not be used unless absolutely needed.
In Python 2 this function is simply an alias for open. In Python 3,
however, it returns an instance of CWrapFile, a very lightweight
wrapper around a FILE* instance.
"""
return CWrapFile(filename, mode)

class CWrapFile(BaseCClass):
"""
This is a compatibility layer for functions taking FILE* pointers, and
should not be used unless absolutely needed.
CWrapFile is a very lightweight wrapper around FILE* instances. It is
meant be used inplace of python file objects that are to be passed to
foreign function calls under python 3.
Example:
with cwrap.open('filename', 'mode') as f:
foreign_function_call(f)
"""

TYPE_NAME = "FILE"

_fopen = LibcPrototype("void* fopen (char*, char*)")
_fclose = LibcPrototype("int fclose (FILE)", bind=True)
_fflush = LibcPrototype("int fflush (FILE)", bind=True)

def __init__(self, fname, mode):
c_ptr = self._fopen(fname, mode)
self._mode = mode
self._fname = fname
self._closed = False

try:
super(CWrapFile, self).__init__(c_ptr)
except ValueError:
self._closed = True
raise IOError('Could not open file "{}" in mode {}'
.format(fname, mode))

def close(self):
if not self._closed:
self._fflush()
cs = self._fclose()
if (cs != 0):
raise IOError("Failed to close file")
self._closed = True

def __enter__(self):
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
return exc_type is None

self.py_file = py_file
def free(self):
self.close()

def __del__(self):
self.close()

def __del__(self):
pass
def CFILE(f):
if not isinstance(f, CWrapFile):
raise TypeError("This function requires the use of CWrapFile, "
"not {} when running Python 3. See "
"help(cwrap.open) for more info"
.format(type(f).__name__))
return f
5 changes: 0 additions & 5 deletions cwrap/metacwrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,3 @@ def __init__(cls, name, bases, attrs):
if isinstance(attr, Prototype):
attr.resolve()
attr.__name__ = key

if attr.shouldBeBound():
method = MethodType(attr, None, cls)
#method = six.create_bound_method(attr, cls)
setattr(cls, key, method)
Loading

0 comments on commit a0e01d8

Please sign in to comment.