Skip to content

Commit

Permalink
Allow writing generated code to a file-like object. (#115)
Browse files Browse the repository at this point in the history
This allows a caller to better control when and where the generated code is written, for issue #47.
  • Loading branch information
inklesspen authored Sep 2, 2024
1 parent 9ba9c1e commit 9e9dffb
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 1 deletion.
12 changes: 12 additions & 0 deletions doc/source/cdef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,12 @@ write). If you choose, you can include this .py file pre-packaged in
your own distributions: it is identical for any Python version (2 or
3).

*New in version 1.17.1:* ``filename`` can instead be a file-like object
(such as a StringIO instance). The generated code will be written to this
file-like object. However, if an error arises during generation, partial
code may be written; it is the caller's responsibility to clean up
if this occurs.

**ffibuilder.emit_c_code(filename):** generate the given .c file (for API
mode) without compiling it. Can be used if you have some other method
to compile it, e.g. if you want to integrate with some larger build
Expand All @@ -676,6 +682,12 @@ platform, the .c file itself is generic (it would be exactly the same
if produced on a different OS, with a different version of CPython, or
with PyPy; it is done with generating the appropriate ``#ifdef``).

*New in version 1.17.1:* ``filename`` can instead be a file-like object
(such as a StringIO instance). The generated code will be written to this
file-like object. However, if an error arises during generation, partial
code may be written; it is the caller's responsibility to clean up
if this occurs.

**ffibuilder.distutils_extension(tmpdir='build', verbose=True):** for
distutils-based ``setup.py`` files. Calling this creates the .c file
if needed in the given ``tmpdir``, and returns a
Expand Down
10 changes: 10 additions & 0 deletions src/cffi/recompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1419,13 +1419,20 @@ def write(self, s):
s = s.encode('ascii')
super(NativeIO, self).write(s)

def _is_file_like(maybefile):
# compare to xml.etree.ElementTree._get_writer
return hasattr(maybefile, 'write')

def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose):
if verbose:
print("generating %s" % (target_file,))
recompiler = Recompiler(ffi, module_name,
target_is_python=(preamble is None))
recompiler.collect_type_table()
recompiler.collect_step_tables()
if _is_file_like(target_file):
recompiler.write_source_to_f(target_file, preamble)
return True
f = NativeIO()
recompiler.write_source_to_f(f, preamble)
output = f.getvalue()
Expand Down Expand Up @@ -1526,6 +1533,9 @@ def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True,
if ffi._windows_unicode:
ffi._apply_windows_unicode(kwds)
if preamble is not None:
if call_c_compiler and _is_file_like(c_file):
raise TypeError("Writing to file-like objects is not supported "
"with call_c_compiler=True")
embedding = (ffi._embedding is not None)
if embedding:
ffi._apply_embedding_fix(kwds)
Expand Down
9 changes: 8 additions & 1 deletion testing/cffi1/test_new_ffi_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import cffi
from testing.udir import udir
from testing.support import *
from cffi.recompiler import recompile
from cffi.recompiler import recompile, NativeIO
from cffi.cffi_opcode import PRIMITIVE_TO_INDEX

SIZE_OF_INT = ctypes.sizeof(ctypes.c_int)
Expand Down Expand Up @@ -1776,6 +1776,13 @@ def test_emit_c_code(self):
ffi.emit_c_code(c_file)
assert os.path.isfile(c_file)

def test_emit_c_code_to_file_obj(self):
ffi = cffi.FFI()
ffi.set_source("foobar", "??")
fileobj = NativeIO()
ffi.emit_c_code(fileobj)
assert 'foobar' in fileobj.getvalue()

def test_import_from_lib(self):
ffi2 = cffi.FFI()
ffi2.cdef("int myfunc(int); extern int myvar;\n#define MYFOO ...\n")
Expand Down

0 comments on commit 9e9dffb

Please sign in to comment.