Skip to content

Commit cc798f7

Browse files
committed
Implement wxInputStream.
Can now be used to load images from a io.BytesIO.
1 parent 5ac1cc8 commit cc798f7

File tree

3 files changed

+119
-2
lines changed

3 files changed

+119
-2
lines changed

etg/cffi/stream.py

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,59 @@
1+
import etgtools
2+
import etgtools.tweaker_tools as tools
3+
4+
from etgtools.extractors import ClassDef, CppMethodDef_cffi, ParamDef
5+
16
def run(module):
2-
# TODO: replicate functions added in etg/sip/stream.py
3-
pass
7+
# Include a C++ class that can wrap a Python file-like object so it can
8+
# be used as a wxInputStream
9+
c = ClassDef(name='wxPyInputStream')
10+
c.includeCppCode('src/cffi/stream_input.cpp')
11+
module.addItem(c)
12+
c.addItem(CppMethodDef_cffi(
13+
c.name, isCtor=True,
14+
pyArgs=etgtools.ArgsString('(WL_Self self, WL_Object file)'),
15+
pyBody="""\
16+
with wrapper_lib.get_refcounted_handle(file) as handle:
17+
self._cpp_obj = call(handle)
18+
""",
19+
cReturnType='void*',
20+
cArgsString='(void *handle)',
21+
cBody="return new WL_CLASS_NAME(handle);",
22+
originalCppArgs=etgtools.ArgsString('(void *handle)'),
23+
))
24+
25+
# Add the following code to the class body to initialize the callback
26+
module.addCdef_cffi("""\
27+
size_t (*PyInputStream_OnSysRead)(void*, void*, size_t);
28+
int (*PyInputStream_IsSeekable)(void*);
29+
size_t (*PyInputStream_TellI)(void*);
30+
size_t (*PyInputStream_SeekI)(void*, size_t, int);
31+
""")
32+
c.pyCode_cffi = """\
33+
@ffi.callback('size_t(*)(void*, void*, size_t)')
34+
def _PyInputStream_OnSysRead(handle, buffer, bufsize):
35+
file = ffi.from_handle(handle)
36+
data = file.read(bufsize)
37+
ffi.cast('char*', buffer)[0:len(data)] = data
38+
return len(data)
39+
clib.PyInputStream_OnSysRead = _PyInputStream_OnSysRead
40+
41+
@ffi.callback('int(*)(void*)')
42+
def _PyInputStream_IsSeekable(handle):
43+
file = ffi.from_handle(handle)
44+
return hasattr(file, 'seek')
45+
clib.PyInputStream_IsSeekable = _PyInputStream_IsSeekable
46+
47+
@ffi.callback('size_t(*)(void*)')
48+
def _PyInputStream_TellI(handle):
49+
file = ffi.from_handle(handle)
50+
return file.tell()
51+
clib.PyInputStream_TellI = _PyInputStream_TellI
52+
53+
@ffi.callback('size_t(*)(void*, size_t, int)')
54+
def _PyInputStream_SeekI(handle, pos, whence):
55+
file = ffi.from_handle(handle)
56+
return file.seek(pos, whence)
57+
clib.PyInputStream_SeekI = _PyInputStream_SeekI
58+
"""
59+

etg/stream.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ def run():
8585
*sipCppPtr = new wxPyInputStream(sipPy);
8686
return sipGetState(sipTransferObj);
8787
"""
88+
c.convertFromPyObject_cffi = """\
89+
return PyInputStream(py_obj)
90+
"""
91+
c.instanceCheck_cffi = """\
92+
return hasattr(py_obj, 'read')
93+
"""
8894

8995

9096

src/cffi/stream_input.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
extern "C" size_t (*PyInputStream_OnSysRead)(void*, void*, size_t);
2+
extern "C" int (*PyInputStream_IsSeekable)(void*);
3+
extern "C" size_t (*PyInputStream_TellI)(void*);
4+
extern "C" size_t (*PyInputStream_SeekI)(void*, size_t, int);
5+
6+
// This class can wrap a Python file-like object and allow it to be used
7+
// as a wxInputStream.
8+
class wxPyInputStream : public wxInputStream
9+
{
10+
private:
11+
void* m_file_handle;
12+
13+
public:
14+
15+
wxPyInputStream(void* file_handle)
16+
: m_file_handle(file_handle)
17+
{
18+
WL_ADJUST_REFCOUNT(m_file_handle, 1);
19+
}
20+
21+
virtual ~wxPyInputStream()
22+
{
23+
WL_ADJUST_REFCOUNT(m_file_handle, -1);
24+
}
25+
26+
wxPyInputStream(const wxPyInputStream& other)
27+
: m_file_handle(other.m_file_handle)
28+
{
29+
WL_ADJUST_REFCOUNT(m_file_handle, 1);
30+
}
31+
32+
protected:
33+
34+
// implement base class virtuals
35+
virtual size_t OnSysRead(void *buffer, size_t size)
36+
{
37+
return (*PyInputStream_OnSysRead)(m_file_handle, buffer, size);
38+
}
39+
40+
bool IsSeekable() const
41+
{
42+
return (*PyInputStream_IsSeekable)(m_file_handle);
43+
}
44+
45+
wxFileOffset TellI() const
46+
{
47+
return (*PyInputStream_TellI)(m_file_handle);
48+
}
49+
50+
wxFileOffset SeekI(wxFileOffset pos, wxSeekMode mode = wxFromStart)
51+
{
52+
return (*PyInputStream_SeekI)(m_file_handle, pos, mode);
53+
}
54+
55+
};

0 commit comments

Comments
 (0)