cffi-0.8.2/ 0000755 0000764 0000144 00000000000 12306266401 012714 5 ustar arigo users 0000000 0000000 cffi-0.8.2/MANIFEST.in 0000644 0000764 0000144 00000000341 12244375414 014456 0 ustar arigo users 0000000 0000000 recursive-include cffi *.py
recursive-include c *.c *.h *.asm *.py win64.obj
recursive-include testing *.py
recursive-include doc *.py *.rst Makefile *.bat
recursive-include demo py.cleanup *.py
include LICENSE setup_base.py
cffi-0.8.2/setup.cfg 0000644 0000764 0000144 00000000073 12306266401 014535 0 ustar arigo users 0000000 0000000 [egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
cffi-0.8.2/PKG-INFO 0000644 0000764 0000144 00000001632 12306266401 014013 0 ustar arigo users 0000000 0000000 Metadata-Version: 1.1
Name: cffi
Version: 0.8.2
Summary: Foreign Function Interface for Python calling C code.
Home-page: http://cffi.readthedocs.org
Author: Armin Rigo, Maciej Fijalkowski
Author-email: python-cffi@googlegroups.com
License: MIT
Description:
CFFI
====
Foreign Function Interface for Python calling C code.
Please see the `Documentation `_.
Contact
-------
`Mailing list `_
Platform: UNKNOWN
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
cffi-0.8.2/setup_base.py 0000644 0000764 0000144 00000001604 12067103443 015421 0 ustar arigo users 0000000 0000000 import sys, os
from setup import include_dirs, sources, libraries, define_macros
from setup import library_dirs, extra_compile_args, extra_link_args
if __name__ == '__main__':
from distutils.core import setup
from distutils.extension import Extension
standard = '__pypy__' not in sys.modules
setup(packages=['cffi'],
requires=['pycparser'],
ext_modules=[Extension(name = '_cffi_backend',
include_dirs=include_dirs,
sources=sources,
libraries=libraries,
define_macros=define_macros,
library_dirs=library_dirs,
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
)] * standard)
cffi-0.8.2/cffi.egg-info/ 0000755 0000764 0000144 00000000000 12306266401 015315 5 ustar arigo users 0000000 0000000 cffi-0.8.2/cffi.egg-info/dependency_links.txt 0000644 0000764 0001750 00000000001 12306266400 021326 0 ustar arigo arigo 0000000 0000000
cffi-0.8.2/cffi.egg-info/top_level.txt 0000644 0000764 0001750 00000000023 12306266400 020005 0 ustar arigo arigo 0000000 0000000 _cffi_backend
cffi
cffi-0.8.2/cffi.egg-info/requires.txt 0000644 0000764 0001750 00000000011 12306266400 017650 0 ustar arigo arigo 0000000 0000000 pycparser cffi-0.8.2/cffi.egg-info/PKG-INFO 0000644 0000764 0001750 00000001632 12306266400 016357 0 ustar arigo arigo 0000000 0000000 Metadata-Version: 1.1
Name: cffi
Version: 0.8.2
Summary: Foreign Function Interface for Python calling C code.
Home-page: http://cffi.readthedocs.org
Author: Armin Rigo, Maciej Fijalkowski
Author-email: python-cffi@googlegroups.com
License: MIT
Description:
CFFI
====
Foreign Function Interface for Python calling C code.
Please see the `Documentation `_.
Contact
-------
`Mailing list `_
Platform: UNKNOWN
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
cffi-0.8.2/cffi.egg-info/not-zip-safe 0000644 0000764 0000144 00000000001 12026557304 017547 0 ustar arigo users 0000000 0000000
cffi-0.8.2/cffi.egg-info/SOURCES.txt 0000644 0000764 0001750 00000006131 12306266401 017146 0 ustar arigo arigo 0000000 0000000 LICENSE
MANIFEST.in
setup.py
setup_base.py
c/_cffi_backend.c
c/check__thread.c
c/file_emulator.h
c/malloc_closure.h
c/minibuffer.h
c/misc_thread.h
c/misc_win32.h
c/test_c.py
c/wchar_helper.h
c/x.py
c/libffi_msvc/ffi.c
c/libffi_msvc/ffi.h
c/libffi_msvc/ffi_common.h
c/libffi_msvc/fficonfig.h
c/libffi_msvc/ffitarget.h
c/libffi_msvc/prep_cif.c
c/libffi_msvc/types.c
c/libffi_msvc/win32.c
c/libffi_msvc/win64.asm
c/libffi_msvc/win64.obj
cffi/__init__.py
cffi/api.py
cffi/backend_ctypes.py
cffi/commontypes.py
cffi/cparser.py
cffi/ffiplatform.py
cffi/gc_weakref.py
cffi/lock.py
cffi/model.py
cffi/vengine_cpy.py
cffi/vengine_gen.py
cffi/verifier.py
cffi.egg-info/PKG-INFO
cffi.egg-info/SOURCES.txt
cffi.egg-info/dependency_links.txt
cffi.egg-info/not-zip-safe
cffi.egg-info/requires.txt
cffi.egg-info/top_level.txt
demo/_csvmodule.py
demo/_curses.py
demo/api.py
demo/bsdopendirtype.py
demo/btrfs-snap.py
demo/cffi-cocoa.py
demo/fastcsv.py
demo/gmp.py
demo/image.py
demo/pwuid.py
demo/py.cleanup
demo/pyobj.py
demo/readdir.py
demo/readdir2.py
demo/readdir_ctypes.py
demo/sarvi.py
demo/setup.py
demo/syslog.py
demo/ui.py
demo/winclipboard.py
demo/xclient.py
demo/y.py
doc/Makefile
doc/design.rst
doc/make.bat
doc/source/conf.py
doc/source/index.rst
testing/__init__.py
testing/backend_tests.py
testing/callback_in_thread.py
testing/support.py
testing/test_cdata.py
testing/test_ctypes.py
testing/test_ffi_backend.py
testing/test_function.py
testing/test_model.py
testing/test_ownlib.py
testing/test_parsing.py
testing/test_platform.py
testing/test_unicode_literals.py
testing/test_verify.py
testing/test_verify2.py
testing/test_version.py
testing/test_vgen.py
testing/test_vgen2.py
testing/test_zdistutils.py
testing/test_zintegration.py
testing/udir.py
testing/snippets/distutils_module/setup.py
testing/snippets/distutils_module/snip_basic_verify.py
testing/snippets/distutils_module/build/lib.linux-x86_64-3.2/snip_basic_verify.py
testing/snippets/distutils_package_1/setup.py
testing/snippets/distutils_package_1/build/lib.linux-x86_64-3.2/snip_basic_verify1/__init__.py
testing/snippets/distutils_package_1/snip_basic_verify1/__init__.py
testing/snippets/distutils_package_2/setup.py
testing/snippets/distutils_package_2/build/lib.linux-x86_64-3.2/snip_basic_verify2/__init__.py
testing/snippets/distutils_package_2/snip_basic_verify2/__init__.py
testing/snippets/infrastructure/setup.py
testing/snippets/infrastructure/build/lib/snip_infrastructure/__init__.py
testing/snippets/infrastructure/snip_infrastructure/__init__.py
testing/snippets/setuptools_module/setup.py
testing/snippets/setuptools_module/snip_setuptools_verify.py
testing/snippets/setuptools_module/build/lib.linux-x86_64-3.2/snip_setuptools_verify.py
testing/snippets/setuptools_package_1/setup.py
testing/snippets/setuptools_package_1/build/lib.linux-x86_64-3.2/snip_setuptools_verify1/__init__.py
testing/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py
testing/snippets/setuptools_package_2/setup.py
testing/snippets/setuptools_package_2/build/lib.linux-x86_64-3.2/snip_setuptools_verify2/__init__.py
testing/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py cffi-0.8.2/setup.py 0000644 0000764 0000144 00000011727 12306266214 014440 0 ustar arigo users 0000000 0000000 import sys, os
import subprocess
import errno
sources = ['c/_cffi_backend.c']
libraries = ['ffi']
include_dirs = ['/usr/include/ffi',
'/usr/include/libffi'] # may be changed by pkg-config
define_macros = []
library_dirs = []
extra_compile_args = []
extra_link_args = []
def _ask_pkg_config(resultlist, option, result_prefix='', sysroot=False):
pkg_config = os.environ.get('PKG_CONFIG','pkg-config')
try:
p = subprocess.Popen([pkg_config, option, 'libffi'],
stdout=subprocess.PIPE)
except OSError as e:
if e.errno != errno.ENOENT:
raise
else:
t = p.stdout.read().decode().strip()
if p.wait() == 0:
res = t.split()
# '-I/usr/...' -> '/usr/...'
for x in res:
assert x.startswith(result_prefix)
res = [x[len(result_prefix):] for x in res]
#print 'PKG_CONFIG:', option, res
#
sysroot = sysroot and os.environ.get('PKG_CONFIG_SYSROOT_DIR', '')
if sysroot:
# old versions of pkg-config don't support this env var,
# so here we emulate its effect if needed
res = [path if path.startswith(sysroot)
else sysroot + path
for path in res]
#
resultlist[:] = res
def ask_supports_thread():
if sys.platform == "darwin":
sys.stderr.write("OS/X: confusion between 'cc' versus 'gcc'")
sys.stderr.write(" (see issue 123)\n")
sys.stderr.write("will not use '__thread' in the C code\n")
return
import distutils.errors
from distutils.ccompiler import new_compiler
compiler = new_compiler(force=1)
try:
compiler.compile(['c/check__thread.c'])
except distutils.errors.CompileError:
sys.stderr.write("the above error message can be safely ignored;\n")
sys.stderr.write("will not use '__thread' in the C code\n")
else:
define_macros.append(('USE__THREAD', None))
try:
os.unlink('c/check__thread.o')
except OSError:
pass
def use_pkg_config():
_ask_pkg_config(include_dirs, '--cflags-only-I', '-I', sysroot=True)
_ask_pkg_config(extra_compile_args, '--cflags-only-other')
_ask_pkg_config(library_dirs, '--libs-only-L', '-L', sysroot=True)
_ask_pkg_config(extra_link_args, '--libs-only-other')
_ask_pkg_config(libraries, '--libs-only-l', '-l')
if sys.platform == 'win32':
COMPILE_LIBFFI = 'c/libffi_msvc' # from the CPython distribution
else:
COMPILE_LIBFFI = None
if COMPILE_LIBFFI:
assert os.path.isdir(COMPILE_LIBFFI), "directory not found!"
include_dirs[:] = [COMPILE_LIBFFI]
libraries[:] = []
_filenames = [filename.lower() for filename in os.listdir(COMPILE_LIBFFI)]
_filenames = [filename for filename in _filenames
if filename.endswith('.c')]
if sys.maxsize > 2**32:
# 64-bit: unlist win32.c, and add instead win64.obj. If the obj
# happens to get outdated at some point in the future, you need to
# rebuild it manually from win64.asm.
_filenames.remove('win32.c')
extra_link_args.append(os.path.join(COMPILE_LIBFFI, 'win64.obj'))
sources.extend(os.path.join(COMPILE_LIBFFI, filename)
for filename in _filenames)
else:
use_pkg_config()
ask_supports_thread()
if __name__ == '__main__':
from setuptools import setup, Extension
ext_modules = []
if '__pypy__' not in sys.modules:
ext_modules.append(Extension(
name='_cffi_backend',
include_dirs=include_dirs,
sources=sources,
libraries=libraries,
define_macros=define_macros,
library_dirs=library_dirs,
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
))
setup(
name='cffi',
description='Foreign Function Interface for Python calling C code.',
long_description="""
CFFI
====
Foreign Function Interface for Python calling C code.
Please see the `Documentation `_.
Contact
-------
`Mailing list `_
""",
version='0.8.2',
packages=['cffi'],
zip_safe=False,
url='http://cffi.readthedocs.org',
author='Armin Rigo, Maciej Fijalkowski',
author_email='python-cffi@googlegroups.com',
license='MIT',
ext_modules=ext_modules,
install_requires=[
'pycparser',
],
classifiers=[
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
],
)
cffi-0.8.2/LICENSE 0000644 0000764 0000144 00000002416 11767605344 013741 0 ustar arigo users 0000000 0000000
Except when otherwise stated (look for LICENSE files in directories or
information at the beginning of each file) all software and
documentation is licensed as follows:
The MIT License
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
cffi-0.8.2/doc/ 0000755 0000764 0000144 00000000000 12306266401 013461 5 ustar arigo users 0000000 0000000 cffi-0.8.2/doc/source/ 0000755 0000764 0000144 00000000000 12306266401 014761 5 ustar arigo users 0000000 0000000 cffi-0.8.2/doc/source/conf.py 0000644 0000764 0000144 00000014266 12306266214 016273 0 ustar arigo users 0000000 0000000 # -*- coding: utf-8 -*-
#
# CFFI documentation build configuration file, created by
# sphinx-quickstart on Thu Jun 14 16:37:47 2012.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.append(os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'CFFI'
copyright = u'2012, Armin Rigo, Maciej Fijalkowski'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.8'
# The full version, including alpha/beta/rc tags.
release = '0.8.2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
#unused_docs = []
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = []
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_use_modindex = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
# Output file base name for HTML help builder.
htmlhelp_basename = 'CFFIdoc'
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'CFFI.tex', u'CFFI Documentation',
u'Armin Rigo, Maciej Fijalkowski', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_use_modindex = True
cffi-0.8.2/doc/source/index.rst 0000644 0000764 0000144 00000206700 12306266243 016633 0 ustar arigo users 0000000 0000000 CFFI documentation
================================
Foreign Function Interface for Python calling C code. The aim of this project
is to provide a convenient and reliable way of calling C code from Python.
The interface is based on `LuaJIT's FFI`_ and follows a few principles:
* The goal is to call C code from Python. You should be able to do so
without learning a 3rd language: every alternative requires you to learn
their own language (Cython_, SWIG_) or API (ctypes_). So we tried to
assume that you know Python and C and minimize the extra bits of API that
you need to learn.
* Keep all the Python-related logic in Python so that you don't need to
write much C code (unlike `CPython native C extensions`_).
* Work either at the level of the ABI (Application Binary Interface)
or the API (Application Programming Interface). Usually, C
libraries have a specified C API but often not an ABI (e.g. they may
document a "struct" as having at least these fields, but maybe more).
(ctypes_ works at the ABI level, whereas Cython_ and `native C extensions`_
work at the API level.)
* We try to be complete. For now some C99 constructs are not supported,
but all C89 should be, including macros (and including macro "abuses",
which you can `manually wrap`_ in saner-looking C functions).
* We attempt to support both PyPy and CPython, with a reasonable path
for other Python implementations like IronPython and Jython.
* Note that this project is **not** about embedding executable C code in
Python, unlike `Weave`_. This is about calling existing C libraries
from Python.
.. _`LuaJIT's FFI`: http://luajit.org/ext_ffi.html
.. _`Cython`: http://www.cython.org
.. _`SWIG`: http://www.swig.org/
.. _`CPython native C extensions`: http://docs.python.org/extending/extending.html
.. _`native C extensions`: http://docs.python.org/extending/extending.html
.. _`ctypes`: http://docs.python.org/library/ctypes.html
.. _`Weave`: http://www.scipy.org/Weave
.. _`manually wrap`: `The verification step`_
Installation and Status
=======================================================
Quick installation:
* ``pip install cffi``
* or get the source code via the `Python Package Index`__.
.. __: http://pypi.python.org/pypi/cffi
In more details:
This code has been developed on Linux but should work on any POSIX
platform as well as on Win32. There are some Windows-specific issues
left.
It supports CPython 2.6; 2.7; 3.x (tested with 3.2 and 3.3);
and PyPy 2.0 beta2 or later.
Its speed is comparable to ctypes on CPython (a bit faster but a higher
warm-up time). It is already faster on PyPy (1.5x-2x), but not yet
*much* faster; stay tuned.
Requirements:
* CPython 2.6 or 2.7 or 3.x, or PyPy 2.0 beta2
* on CPython you need to build the C extension module, so you need
``python-dev`` and ``libffi-dev`` (for Windows, libffi is included
with CFFI).
* pycparser >= 2.06: https://github.com/eliben/pycparser
* a C compiler is required to use CFFI during development, but not to run
correctly-installed programs that use CFFI.
* `py.test`_ is needed to run the tests of CFFI.
.. _`py.test`: http://pypi.python.org/pypi/pytest
Download and Installation:
* http://pypi.python.org/packages/source/c/cffi/cffi-0.8.2.tar.gz
- Or grab the most current version by following the instructions below.
- MD5: ...
- SHA: ...
* Or get it from the `Bitbucket page`_:
``hg clone https://bitbucket.org/cffi/cffi``
* ``python setup.py install`` or ``python setup_base.py install``
(should work out of the box on Linux or Windows; see below for
`MacOS X`_ or `Windows 64`_.)
* or you can directly import and use ``cffi``, but if you don't
compile the ``_cffi_backend`` extension module, it will fall back
to using internally ``ctypes`` (much slower; we recommend not to use it).
* running the tests: ``py.test c/ testing/`` (if you didn't
install cffi yet, you may need ``python setup_base.py build``
and ``PYTHONPATH=build/lib.xyz.../``)
.. _`Bitbucket page`: https://bitbucket.org/cffi/cffi
Demos:
* The `demo`_ directory contains a number of small and large demos
of using ``cffi``.
* The documentation below is sketchy on the details; for now the
ultimate reference is given by the tests, notably
`testing/test_verify.py`_ and `testing/backend_tests.py`_.
.. _`demo`: https://bitbucket.org/cffi/cffi/src/default/demo
.. _`testing/backend_tests.py`: https://bitbucket.org/cffi/cffi/src/default/testing/backend_tests.py
.. _`testing/test_verify.py`: https://bitbucket.org/cffi/cffi/src/default/testing/test_verify.py
Platform-specific instructions
------------------------------
``libffi`` is notoriously messy to install and use --- to the point that
CPython includes its own copy to avoid relying on external packages.
CFFI does the same for Windows, but not for other platforms (which should
have their own working libffi's).
Modern Linuxes work out of the box thanks to ``pkg-config``. Here are some
(user-supplied) instructions for other platforms.
MacOS X
+++++++
**Homebrew** (Thanks David Griffin for this)
1) Install homebrew: http://brew.sh
2) Run the following commands in a terminal
::
brew install pkg-config libffi
export PKG_CONFIG_PATH=/usr/local/Cellar/libffi/3.0.13/lib/pkgconfig/ # May change with libffi version
pip install cffi
Aternatively, **on OS/X 10.6** (Thanks Juraj Sukop for this)
For building libffi you can use the default install path, but then, in
``setup.py`` you need to change::
include_dirs = []
to::
include_dirs = ['/usr/local/lib/libffi-3.0.11/include']
Then running ``python setup.py build`` complains about "fatal error: error writing to -: Broken pipe", which can be fixed by running::
ARCHFLAGS="-arch i386 -arch x86_64" python setup.py build
as described here_.
.. _here: http://superuser.com/questions/259278/python-2-6-1-pycrypto-2-3-pypi-package-broken-pipe-during-build
Windows 64
++++++++++
Win32 works and is tested at least each official release.
Status: Win64 received very basic testing and we applied a few essential
fixes in cffi 0.7. Please report any other issue.
Note as usual that this is only about running the 64-bit version of
Python on the 64-bit OS. If you're running the 32-bit version (the
common case apparently), then you're running Win32 as far as we're
concerned.
.. _`issue 9`: https://bitbucket.org/cffi/cffi/issue/9
.. _`Python issue 7546`: http://bugs.python.org/issue7546
=======================================================
Examples
=======================================================
Simple example (ABI level)
--------------------------
.. code-block:: python
>>> from cffi import FFI
>>> ffi = FFI()
>>> ffi.cdef("""
... int printf(const char *format, ...); // copy-pasted from the man page
... """)
>>> C = ffi.dlopen(None) # loads the entire C namespace
>>> arg = ffi.new("char[]", "world") # equivalent to C code: char arg[] = "world";
>>> C.printf("hi there, %s!\n", arg) # call printf
hi there, world!
Note that on Python 3 you need to pass byte strings to ``char *``
arguments. In the above example it would be ``b"world"`` and ``b"hi
there, %s!\n"``. In general it is ``somestring.encode(myencoding)``.
Real example (API level)
------------------------
.. code-block:: python
from cffi import FFI
ffi = FFI()
ffi.cdef(""" // some declarations from the man page
struct passwd {
char *pw_name;
...;
};
struct passwd *getpwuid(int uid);
""")
C = ffi.verify(""" // passed to the real C compiler
#include
#include
""", libraries=[]) # or a list of libraries to link with
p = C.getpwuid(0)
assert ffi.string(p.pw_name) == 'root' # on Python 3: b'root'
Note that the above example works independently of the exact layout of
``struct passwd``. It requires a C compiler the first time you run it,
unless the module is distributed and installed according to the
`Distributing modules using CFFI`_ intructions below. See also the
note about `Cleaning up the __pycache__ directory`_.
You will find a number of larger examples using ``verify()`` in the
`demo`_ directory.
Struct/Array Example
--------------------
.. code-block:: python
from cffi import FFI
ffi = FFI()
ffi.cdef("""
typedef struct {
unsigned char r, g, b;
} pixel_t;
""")
image = ffi.new("pixel_t[]", 800*600)
f = open('data', 'rb') # binary mode -- important
f.readinto(ffi.buffer(image))
f.close()
image[100].r = 255
image[100].g = 192
image[100].b = 128
f = open('data', 'wb')
f.write(ffi.buffer(image))
f.close()
This can be used as a more flexible replacement of the struct_ and
array_ modules. You could also call ``ffi.new("pixel_t[600][800]")``
and get a two-dimensional array.
.. _struct: http://docs.python.org/library/struct.html
.. _array: http://docs.python.org/library/array.html
What actually happened?
-----------------------
The CFFI interface operates on the same level as C - you declare types
and functions using the same syntax as you would define them in C. This
means that most of the documentation or examples can be copied straight
from the man pages.
The declarations can contain types, functions and global variables. The
cdef in the above examples are just that - they declared "there is a
function in the C level with this given signature", or "there is a
struct type with this shape".
The ``dlopen()`` line loads libraries. C has multiple namespaces - a
global one and local ones per library. In this example we load the
global one (``None`` as argument to ``dlopen()``) which always contains
the standard C library. You get as a result a ```` object
that has as attributes all symbols declared in the ``cdef()`` and coming
from this library.
The ``verify()`` line in the second example is an alternative: instead
of doing a ``dlopen``, it generates and compiles a piece of C code.
When using ``verify()`` you have the advantage that you can use "``...``"
at various places in the ``cdef()``, and the missing information will
be completed with the help of the C compiler. It also does checking,
to verify that your declarations are correct. If the C compiler gives
warnings or errors, they are reported here.
Finally, the ``ffi.new()`` lines allocate C objects. They are filled
with zeroes initially, unless the optional second argument is used.
If specified, this argument gives an "initializer", like you can use
with C code to initialize global variables.
The actual function calls should be obvious. It's like C.
=======================================================
Distributing modules using CFFI
=======================================================
If you use CFFI and ``verify()`` in a project that you plan to
distribute, other users will install it on machines that may not have a
C compiler. Here is how to write a ``setup.py`` script using
``distutils`` in such a way that the extension modules are listed too.
This lets normal ``setup.py`` commands compile and package the C
extension modules too.
Example::
from setuptools import setup
--OR--
from distutils.core import setup
# you must import at least the module(s) that define the ffi's
# that you use in your application
import yourmodule
setup(...
zip_safe=False, # with setuptools only
ext_modules=[yourmodule.ffi.verifier.get_extension()])
Warning: with ``setuptools``, you have to say ``zip_safe=False``,
otherwise it might or might not work, depending on which verifier engine
is used! (I tried to find either workarounds or proper solutions but
failed so far.)
.. versionadded:: 0.4
If your ``setup.py`` installs a whole package, you can put the extension
in it too:
::
setup(...
zip_safe=False,
ext_package='yourpackage', # but see below!
ext_modules=[yourmodule.ffi.verifier.get_extension()])
However in this case you must also give the same ``ext_package``
argument to the original call to ``ffi.verify()``::
ffi.verify("...", ext_package='yourpackage')
Usually that's all you need, but see the `Reference: verifier`_ section
for more details about the ``verifier`` object.
Cleaning up the __pycache__ directory
-------------------------------------
During development, every time you change the C sources that you pass to
``cdef()`` or ``verify()``, then the latter will create a new module
file name, based on two CRC32 hashes computed from these strings.
This creates more
and more files in the ``__pycache__`` directory. It is recommended that
you clean it up from time to time. A nice way to do that is to add, in
your test suite, a call to ``cffi.verifier.cleanup_tmpdir()``.
Alternatively, you can just completely remove the ``__pycache__``
directory.
=======================================================
Reference
=======================================================
As a guideline: you have already seen in the above examples all the
major pieces except maybe ``ffi.cast()``. The rest of this
documentation gives a more complete reference.
Declaring types and functions
-----------------------------
``ffi.cdef(source)`` parses the given C source. This should be done
first. It registers all the functions, types, and global variables in
the C source. The types can be used immediately in ``ffi.new()`` and
other functions. Before you can access the functions and global
variables, you need to give ``ffi`` another piece of information: where
they actually come from (which you do with either ``ffi.dlopen()`` or
``ffi.verify()``).
The C source is parsed internally (using ``pycparser``). This code
cannot contain ``#include``. It should typically be a self-contained
piece of declarations extracted from a man page. The only things it
can assume to exist are the standard types:
* char, short, int, long, long long (both signed and unsigned)
* float, double, long double
* intN_t, uintN_t (for N=8,16,32,64), intptr_t, uintptr_t, ptrdiff_t,
size_t, ssize_t
* wchar_t (if supported by the backend)
* *New in version 0.4:* _Bool. If not directly supported by the C compiler,
this is declared with the size of ``unsigned char``.
* *New in version 0.6:* bool. In CFFI 0.4 or 0.5, you had to manually say
``typedef _Bool bool;``. Now such a line is optional.
* *New in version 0.4:* FILE. You can declare C functions taking a
``FILE *`` argument and call them with a Python file object. If needed,
you can also do ``c_f = ffi.cast("FILE *", fileobj)`` and then pass around
``c_f``.
* *New in version 0.6:* all `common Windows types`_ are defined if you run
on Windows (``DWORD``, ``LPARAM``, etc.).
.. _`common Windows types`: http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751%28v=vs.85%29.aspx
.. "versionadded:: 0.4": _Bool
.. "versionadded:: 0.6": bool
.. "versionadded:: 0.4": FILE
.. "versionadded:: 0.6": Wintypes
As we will see on `the verification step`_ below, the declarations can
also contain "``...``" at various places; these are placeholders that will
be completed by a call to ``verify()``.
.. versionadded:: 0.6
The standard type names listed above are now handled as *defaults*
only (apart from the ones that are keywords in the C language).
If your ``cdef`` contains an explicit typedef that redefines one of
the types above, then the default described above is ignored. (This
is a bit hard to implement cleanly, so in some corner cases it might
fail, notably with the error ``Multiple type specifiers with a type
tag``. Please report it as a bug if it does.)
Loading libraries
-----------------
``ffi.dlopen(libpath, [flags])``: this function opens a shared library and
returns a module-like library object. You need to use *either*
``ffi.dlopen()`` *or* ``ffi.verify()``, documented below_.
You can use the library object to call the functions previously declared
by ``ffi.cdef()``, and to read or write global variables. Note that you
can use a single ``cdef()`` to declare functions from multiple
libraries, as long as you load each of them with ``dlopen()`` and access
the functions from the correct one.
The ``libpath`` is the file name of the shared library, which can
contain a full path or not (in which case it is searched in standard
locations, as described in ``man dlopen``), with extensions or not.
Alternatively, if ``libpath`` is None, it returns the standard C library
(which can be used to access the functions of glibc, on Linux).
This gives ABI-level access to the library: you need to have all types
declared manually exactly as they were while the library was made. No
checking is done. For this reason, we recommend to use ``ffi.verify()``
instead when possible.
Note that only functions and global variables are in library objects;
types exist in the ``ffi`` instance independently of library objects.
This is due to the C model: the types you declare in C are not tied to a
particular library, as long as you ``#include`` their headers; but you
cannot call functions from a library without linking it in your program,
as ``dlopen()`` does dynamically in C.
For the optional ``flags`` argument, see ``man dlopen`` (ignored on
Windows). It defaults to ``ffi.RTLD_NOW``.
This function returns a "library" object that gets closed when it goes
out of scope. Make sure you keep the library object around as long as
needed.
.. _below:
The verification step
---------------------
``ffi.verify(source, tmpdir=.., ext_package=.., modulename=.., **kwargs)``:
verifies that the current ffi signatures
compile on this machine, and return a dynamic library object. The
dynamic library can be used to call functions and access global
variables declared by a previous ``ffi.cdef()``. You don't need to use
``ffi.dlopen()`` in this case.
The returned library is a custom one, compiled just-in-time by the C
compiler: it gives you C-level API compatibility (including calling
macros, as long as you declared them as functions in ``ffi.cdef()``).
This differs from ``ffi.dlopen()``, which requires ABI-level
compatibility and must be called several times to open several shared
libraries.
On top of CPython, the new library is actually a CPython C extension
module.
The arguments to ``ffi.verify()`` are:
* ``source``: C code that is pasted verbatim in the generated code (it
is *not* parsed internally). It should contain at least the
necessary ``#include``. It can also contain the complete
implementation of some functions declared in ``cdef()``; this is
useful if you really need to write a piece of C code, e.g. to access
some advanced macros (see the example of ``getyx()`` in
`demo/_curses.py`_).
* ``sources``, ``include_dirs``,
``define_macros``, ``undef_macros``, ``libraries``,
``library_dirs``, ``extra_objects``, ``extra_compile_args``,
``extra_link_args`` (keyword arguments): these are used when
compiling the C code, and are passed directly to distutils_. You
typically need at least ``libraries=['foo']`` in order to link with
``libfoo.so`` or ``libfoo.so.X.Y``, or ``foo.dll`` on Windows. The
``sources`` is a list of extra .c files compiled and linked together. See
the distutils documentation for `more information about the other
arguments`__.
.. __: http://docs.python.org/distutils/setupscript.html#library-options
.. _distutils: http://docs.python.org/distutils/setupscript.html#describing-extension-modules
.. _`demo/_curses.py`: https://bitbucket.org/cffi/cffi/src/default/demo/_curses.py
On the plus side, this solution gives more "C-like" flexibility:
* functions taking or returning integer or float-point arguments can be
misdeclared: if e.g. a function is declared by ``cdef()`` as taking a
``int``, but actually takes a ``long``, then the C compiler handles the
difference.
* other arguments are checked: you get a compilation warning or error
if you pass a ``int *`` argument to a function expecting a ``long *``.
Moreover, you can use "``...``" in the following places in the ``cdef()``
for leaving details unspecified, which are then completed by the C
compiler during ``verify()``:
* structure declarations: any ``struct`` that ends with "``...;``" is
partial: it may be missing fields and/or have them declared out of order.
This declaration will be corrected by the compiler. (But note that you
can only access fields that you declared, not others.) Any ``struct``
declaration which doesn't use "``...``" is assumed to be exact, but this is
checked: you get a ``VerificationError`` if it is not.
* unknown types: the syntax "``typedef ... foo_t;``" declares the type
``foo_t`` as opaque. Useful mainly for when the API takes and returns
``foo_t *`` without you needing to look inside the ``foo_t``. Also
works with "``typedef ... *foo_p;``" which declares the pointer type
``foo_p`` without giving a name to the opaque type itself. Note that
such an opaque struct has no known size, which prevents some operations
from working (mostly like in C). *You cannot use this syntax to
declare a specific type, like an integer type! It declares opaque
types only.* In some cases you need to say that
``foo_t`` is not opaque, but you just don't know any field in it; then
you would use "``typedef struct { ...; } foo_t;``".
* array lengths: when used as structure fields or in global variables,
arrays can have an unspecified length, as in "``int n[...];``". The
length is completed by the C compiler. (Only the outermost array
may have an unknown length, in case of array-of-array.)
You can also use the syntax "``int n[];``".
.. versionchanged:: 0.8
"``int n[];``" asks for an array of unknown length whose length must
*not* be completed by the C compiler. See `variable-length array`_
below. If the structure does not contain the syntax ``...`` anywhere,
it will be not be considered to have a partial layout to complete by
the compiler.
* enums: if you don't know the exact order (or values) of the declared
constants, then use this syntax: "``enum foo { A, B, C, ... };``"
(with a trailing "``...``"). The C compiler will be used to figure
out the exact values of the constants. An alternative syntax is
"``enum foo { A=..., B, C };``" or even
"``enum foo { A=..., B=..., C=... };``". Like
with structs, an ``enum`` without "``...``" is assumed to
be exact, and this is checked.
* integer macros: you can write in the ``cdef`` the line
"``#define FOO ...``", with any macro name FOO. Provided the macro
is defined to be an integer value, this value will be available via
an attribute of the library object returned by ``verify()``. The
same effect can be achieved by writing a declaration
``static const int FOO;``. The latter is more general because it
supports other types than integer types (note: the syntax is then
to write the ``const`` together with the variable name, as in
``static char *const FOO;``).
Currently, it is not supported to find automatically which of the
various integer or float types you need at which place. In the case of
function arguments or return type, when it is a simple integer/float
type, it may be misdeclared (if you misdeclare a function ``void
f(long)`` as ``void f(int)``, it still works, but you have to call it
with arguments that fit an int). But it doesn't work any longer for
more complex types (e.g. you cannot misdeclare a ``int *`` argument as
``long *``) or in other locations (e.g. a global array ``int a[5];``
must not be declared ``long a[5];``). CFFI considers all types listed
above__ as primitive (so ``long long a[5];`` and ``int64_t a[5]`` are
different declarations).
.. __: `Declaring types and functions`_
Note the following hack to find explicitly the size of any type, in
bytes::
ffi.cdef("const int mysize;")
lib = ffi.verify("const int mysize = sizeof(THE_TYPE);")
print lib.mysize
Note that ``verify()`` is meant to call C libraries that are *not* using
``#include ``. The C functions are called without the GIL,
and afterwards we don't check if they set a Python exception, for
example. You may work around it, but mixing CFFI with ``Python.h`` is
not recommended.
.. versionadded:: 0.4
Unions used to crash ``verify()``. Fixed.
.. versionadded:: 0.4
The ``tmpdir`` argument to ``verify()`` controls where the C
files are created and compiled. By default it is
``directory_containing_the_py_file/__pycache__``, using the
directory name of the .py file that contains the actual call to
``ffi.verify()``. (This is a bit of a hack but is generally
consistent with the location of the .pyc files for your library.
The name ``__pycache__`` itself comes from Python 3.)
The ``ext_package`` argument controls in which package the
compiled extension module should be looked from. This is
only useful after `distributing modules using CFFI`_.
The ``tag`` argument gives an extra string inserted in the
middle of the extension module's name: ``_cffi__``.
Useful to give a bit more context, e.g. when debugging.
.. _`warning about modulename`:
.. versionadded:: 0.5
The ``modulename`` argument can be used to force a specific module
name, overriding the name ``_cffi__``. Use with care,
e.g. if you are passing variable information to ``verify()`` but
still want the module name to be always the same (e.g. absolute
paths to local files). In this case, no hash is computed and if
the module name already exists it will be reused without further
check. Be sure to have other means of clearing the ``tmpdir``
whenever you change your sources.
This function returns a "library" object that gets closed when it goes
out of scope. Make sure you keep the library object around as long as
needed.
Working with pointers, structures and arrays
--------------------------------------------
The C code's integers and floating-point values are mapped to Python's
regular ``int``, ``long`` and ``float``. Moreover, the C type ``char``
corresponds to single-character strings in Python. (If you want it to
map to small integers, use either ``signed char`` or ``unsigned char``.)
Similarly, the C type ``wchar_t`` corresponds to single-character
unicode strings, if supported by the backend. Note that in some
situations (a narrow Python build with an underlying 4-bytes wchar_t
type), a single wchar_t character may correspond to a pair of
surrogates, which is represented as a unicode string of length 2. If
you need to convert such a 2-chars unicode string to an integer,
``ord(x)`` does not work; use instead ``int(ffi.cast('wchar_t', x))``.
Pointers, structures and arrays are more complex: they don't have an
obvious Python equivalent. Thus, they correspond to objects of type
``cdata``, which are printed for example as
````.
``ffi.new(ctype, [initializer])``: this function builds and returns a
new cdata object of the given ``ctype``. The ctype is usually some
constant string describing the C type. It must be a pointer or array
type. If it is a pointer, e.g. ``"int *"`` or ``struct foo *``, then
it allocates the memory for one ``int`` or ``struct foo``. If it is
an array, e.g. ``int[10]``, then it allocates the memory for ten
``int``. In both cases the returned cdata is of type ``ctype``.
The memory is initially filled with zeros. An initializer can be given
too, as described later.
Example::
>>> ffi.new("char *")
>>> ffi.new("int *")
>>> ffi.new("int[10]")
.. versionchanged:: 0.2
Note that this changed from CFFI version 0.1: what used to be
``ffi.new("int")`` is now ``ffi.new("int *")``.
Unlike C, the returned pointer object has *ownership* on the allocated
memory: when this exact object is garbage-collected, then the memory is
freed. If, at the level of C, you store a pointer to the memory
somewhere else, then make sure you also keep the object alive for as
long as needed. (This also applies if you immediately cast the returned
pointer to a pointer of a different type: only the original object has
ownership, so you must keep it alive. As soon as you forget it, then
the casted pointer will point to garbage! In other words, the ownership
rules are attached to the *wrapper* cdata objects: they are not, and
cannot, be attached to the underlying raw memory.) Example::
global_weakkeydict = weakref.WeakKeyDictionary()
s1 = ffi.new("struct foo *")
fld1 = ffi.new("struct bar *")
fld2 = ffi.new("struct bar *")
s1.thefield1 = fld1
s1.thefield2 = fld2
# here the 'fld1' and 'fld2' object must not go away,
# otherwise 's1.thefield1/2' will point to garbage!
global_weakkeydict[s1] = (fld1, fld2)
# now 's1' keeps alive 'fld1' and 'fld2'. When 's1' goes
# away, then the weak dictionary entry will be removed.
The cdata objects support mostly the same operations as in C: you can
read or write from pointers, arrays and structures. Dereferencing a
pointer is done usually in C with the syntax ``*p``, which is not valid
Python, so instead you have to use the alternative syntax ``p[0]``
(which is also valid C). Additionally, the ``p.x`` and ``p->x``
syntaxes in C both become ``p.x`` in Python.
.. versionchanged:: 0.2
You will find ``ffi.NULL`` to use in the same places as the C ``NULL``.
Like the latter, it is actually defined to be ``ffi.cast("void *", 0)``.
In version 0.1, reading a NULL pointer used to return None;
now it returns a regular ````, which you can
check for e.g. by comparing it with ``ffi.NULL``.
There is no general equivalent to the ``&`` operator in C (because it
would not fit nicely in the model, and it does not seem to be needed
here). But see ``ffi.addressof()`` below__.
__ `Misc methods on ffi`_
Any operation that would in C return a pointer or array or struct type
gives you a fresh cdata object. Unlike the "original" one, these fresh
cdata objects don't have ownership: they are merely references to
existing memory.
As an exception to the above rule, dereferencing a pointer that owns a
*struct* or *union* object returns a cdata struct or union object
that "co-owns" the same memory. Thus in this case there are two
objects that can keep the same memory alive. This is done for cases where
you really want to have a struct object but don't have any convenient
place to keep alive the original pointer object (returned by
``ffi.new()``).
Example::
ffi.cdef("void somefunction(int *);")
lib = ffi.verify("#include ")
x = ffi.new("int *") # allocate one int, and return a pointer to it
x[0] = 42 # fill it
lib.somefunction(x) # call the C function
print x[0] # read the possibly-changed value
The equivalent of C casts are provided with ``ffi.cast("type", value)``.
They should work in the same cases as they do in C. Additionally, this
is the only way to get cdata objects of integer or floating-point type::
>>> x = ffi.cast("int", 42)
>>> x
>>> int(x)
42
To cast a pointer to an int, cast it to ``intptr_t`` or ``uintptr_t``,
which are defined by C to be large enough integer types (example on 32
bits)::
>>> int(ffi.cast("intptr_t", pointer_cdata)) # signed
-1340782304
>>> int(ffi.cast("uintptr_t", pointer_cdata)) # unsigned
2954184992L
The initializer given as the optional second argument to ``ffi.new()``
can be mostly anything that you would use as an initializer for C code,
with lists or tuples instead of using the C syntax ``{ .., .., .. }``.
Example::
typedef struct { int x, y; } foo_t;
foo_t v = { 1, 2 }; // C syntax
v = ffi.new("foo_t *", [1, 2]) # CFFI equivalent
foo_t v = { .y=1, .x=2 }; // C99 syntax
v = ffi.new("foo_t *", {'y': 1, 'x': 2}) # CFFI equivalent
Like C, arrays of chars can also be initialized from a string, in
which case a terminating null character is appended implicitly::
>>> x = ffi.new("char[]", "hello")
>>> x
>>> len(x) # the actual size of the array
6
>>> x[5] # the last item in the array
'\x00'
>>> x[0] = 'H' # change the first item
>>> ffi.string(x) # interpret 'x' as a regular null-terminated string
'Hello'
Similarly, arrays of wchar_t can be initialized from a unicode string,
and calling ``ffi.string()`` on the cdata object returns the current unicode
string stored in the wchar_t array (encoding and decoding surrogates as
needed if necessary).
Note that unlike Python lists or tuples, but like C, you *cannot* index in
a C array from the end using negative numbers.
More generally, the C array types can have their length unspecified in C
types, as long as their length can be derived from the initializer, like
in C::
int array[] = { 1, 2, 3, 4 }; // C syntax
array = ffi.new("int[]", [1, 2, 3, 4]) # CFFI equivalent
As an extension, the initializer can also be just a number, giving
the length (in case you just want zero-initialization)::
int array[1000]; // C syntax
array = ffi.new("int[1000]") # CFFI 1st equivalent
array = ffi.new("int[]", 1000) # CFFI 2nd equivalent
This is useful if the length is not actually a constant, to avoid things
like ``ffi.new("int[%d]" % x)``. Indeed, this is not recommended:
``ffi`` normally caches the string ``"int[]"`` to not need to re-parse
it all the time.
.. versionadded:: 0.9
The ``ffi.cdef()`` call takes an optional argument ``packed``: if
True, then all structs declared within this cdef are "packed". This
has a meaning similar to ``__attribute__((packed))`` in GCC. It
specifies that all structure fields should have an alignment of one
byte. (Note that the packed attribute has no effect on bit fields so
far, which mean that they may be packed differently than on GCC.)
Python 3 support
----------------
Python 3 is supported, but the main point to note is that the ``char`` C
type corresponds to the ``bytes`` Python type, and not ``str``. It is
your responsibility to encode/decode all Python strings to bytes when
passing them to or receiving them from CFFI.
This only concerns the ``char`` type and derivative types; other parts
of the API that accept strings in Python 2 continue to accept strings in
Python 3.
An example of calling a main-like thing
---------------------------------------
Imagine we have something like this:
.. code-block:: python
from cffi import FFI
ffi = FFI()
ffi.cdef("""
int main_like(int argv, char *argv[]);
""")
lib = ffi.dlopen("some_library.so")
Now, everything is simple, except, how do we create the ``char**`` argument
here?
The first idea:
.. code-block:: python
lib.main_like(2, ["arg0", "arg1"])
does not work, because the initializer receives two Python ``str`` objects
where it was expecting ```` objects. You need to use
``ffi.new()`` explicitly to make these objects:
.. code-block:: python
lib.main_like(2, [ffi.new("char[]", "arg0"),
ffi.new("char[]", "arg1")])
Note that the two ```` objects are kept alive for the
duration of the call: they are only freed when the list itself is freed,
and the list is only freed when the call returns.
If you want instead to build an "argv" variable that you want to reuse,
then more care is needed:
.. code-block:: python
# DOES NOT WORK!
argv = ffi.new("char *[]", [ffi.new("char[]", "arg0"),
ffi.new("char[]", "arg1")])
In the above example, the inner "arg0" string is deallocated as soon
as "argv" is built. You have to make sure that you keep a reference
to the inner "char[]" objects, either directly or by keeping the list
alive like this:
.. code-block:: python
argv_keepalive = [ffi.new("char[]", "arg0"),
ffi.new("char[]", "arg1")]
argv = ffi.new("char *[]", argv_keepalive)
.. versionchanged:: 0.3
In older versions, passing a list as the ``char *[]`` argument did
not work; you needed to make an ``argv_keepalive`` and an ``argv``
in all cases.
Function calls
--------------
When calling C functions, passing arguments follows mostly the same
rules as assigning to structure fields, and the return value follows the
same rules as reading a structure field. For example::
ffi.cdef("""
int foo(short a, int b);
""")
lib = ffi.verify("#include ")
n = lib.foo(2, 3) # returns a normal integer
lib.foo(40000, 3) # raises OverflowError
As an extension, you can pass to ``char *`` arguments a normal Python
string (but don't pass a normal Python string to functions that take a
``char *`` argument and may mutate it!)::
ffi.cdef("""
size_t strlen(const char *);
""")
C = ffi.dlopen(None)
assert C.strlen("hello") == 5
You can also pass unicode strings as ``wchar_t *`` arguments. Note that
in general, there is no difference between C argument declarations that
use ``type *`` or ``type[]``. For example, ``int *`` is fully
equivalent to ``int[]`` or ``int[5]``. So you can pass an ``int *`` as
a list of integers::
ffi.cdef("""
void do_something_with_array(int *array);
""")
lib.do_something_with_array([1, 2, 3, 4, 5])
CFFI supports passing and returning structs to functions and callbacks.
Example (sketch)::
>>> ffi.cdef("""
... struct foo_s { int a, b; };
... struct foo_s function_returning_a_struct(void);
... """)
>>> lib = ffi.verify("#include ")
>>> lib.function_returning_a_struct()
There are a few (obscure) limitations to the argument types and return
type. You cannot pass directly as argument a union (but a **pointer**
to a union is fine), nor a struct which uses bitfields (but a
**pointer** to such a struct is fine). If you pass a struct (not a
**pointer** to a struct), the struct type cannot have been declared with
"``...;``" and completed with ``verify()``; you need to declare it
completely in ``cdef()``. You can work around these limitations by
writing a C function with a simpler signature in the code passed to
``ffi.verify()``, which calls the real C function.
Aside from these limitations, functions and callbacks can return structs.
CPython only: for performance, ``ffi.verify()`` returns functions as
objects of type ````. They are not ````, so
you cannot e.g. pass them to some other C function expecting a function
pointer argument. Only ``ffi.typeof()`` works on them. If you really
need a pointer to the function, use the following workaround::
ffi.cdef(""" int (*foo)(int a, int b); """)
i.e. declare them as pointer-to-function in the cdef (even if they are
regular functions in the C code).
Variadic function calls
-----------------------
Variadic functions in C (which end with "``...``" as their last
argument) can be declared and called normally, with the exception that
all the arguments passed in the variable part *must* be cdata objects.
This is because it would not be possible to guess, if you wrote this::
C.printf("hello, %d\n", 42)
that you really meant the 42 to be passed as a C ``int``, and not a
``long`` or ``long long``. The same issue occurs with ``float`` versus
``double``. So you have to force cdata objects of the C type you want,
if necessary with ``ffi.cast()``::
C.printf("hello, %d\n", ffi.cast("int", 42))
C.printf("hello, %ld\n", ffi.cast("long", 42))
C.printf("hello, %f\n", ffi.cast("double", 42))
C.printf("hello, %s\n", ffi.new("char[]", "world"))
Callbacks
---------
C functions can also be viewed as ``cdata`` objects, and so can be
passed as callbacks. To make new C callback objects that will invoke a
Python function, you need to use::
>>> def myfunc(x, y):
... return x + y
...
>>> ffi.callback("int(int, int)", myfunc)
>
.. versionadded:: 0.4
Or equivalently as a decorator:
>>> @ffi.callback("int(int, int)")
... def myfunc(x, y):
... return x + y
Note that you can also use a C *function pointer* type like ``"int(*)(int,
int)"`` (as opposed to a C *function* type like ``"int(int, int)"``). It
is equivalent here.
Warning: like ffi.new(), ffi.callback() returns a cdata that has
ownership of its C data. (In this case, the necessary C data contains
the libffi data structures to do a callback.) This means that the
callback can only be invoked as long as this cdata object is alive. If
you store the function pointer into C code, then make sure you also keep this
object alive for as long as the callback may be invoked. (If you want
the callback to remain valid forever, store the object in a fresh global
variable somewhere.)
Note that callbacks of a variadic function type are not supported. A
workaround is to add custom C code. In the following example, a
callback gets a first argument that counts how many extra ``int``
arguments are passed::
ffi.cdef("""
int (*python_callback)(int how_many, int *values);
void *const c_callback; /* pass this ptr to C routines */
""")
lib = ffi.verify("""
#include
#include
static int (*python_callback)(int how_many, int *values);
static int c_callback(int how_many, ...) {
va_list ap;
/* collect the "..." arguments into the values[] array */
int i, *values = alloca(how_many * sizeof(int));
va_start(ap, how_many);
for (i=0; i>> ffi.callback("int(int, int)", myfunc, error=42)
In all cases the exception is printed to stderr, so this should be
used only as a last-resort solution.
Misc methods on ffi
-------------------
``ffi.include(other_ffi)``: includes the typedefs, structs, unions and
enum types defined in another FFI instance. Usage is similar to a
``#include`` in C, where a part of the program might include types
defined in another part for its own usage. Note that the include()
method has no effect on functions, constants and global variables, which
must anyway be accessed directly from the ``lib`` object returned by the
original FFI instance. *Note that you should only use one ffi object
per library; the intended usage of ffi.include() is if you want to
interface with several inter-dependent libraries.* For only one
library, make one ``ffi`` object. (If the source becomes too large,
split it up e.g. by collecting the cdef/verify strings from multiple
Python modules, as long as you call ``ffi.verify()`` only once.) *New
in version 0.5.*
.. "versionadded:: 0.5" --- inlined in the previous paragraph
``ffi.errno``: the value of ``errno`` received from the most recent C call
in this thread, and passed to the following C call, is available via
reads and writes of the property ``ffi.errno``.
``ffi.getwinerror(code=-1)``: on Windows, in addition to ``errno`` we
also save and restore the ``GetLastError()`` value across function
calls. This function returns this error code as a tuple ``(code,
message)``, adding a readable message like Python does when raising
WindowsError. If the argument ``code`` is given, format that code into
a message instead of using ``GetLastError()``. *New in version 0.8.*
(Note that it is also possible to declare and call the ``GetLastError()``
function as usual.)
.. "versionadded:: 0.8" --- inlined in the previous paragraph
``ffi.string(cdata, [maxlen])``: return a Python string (or unicode
string) from the 'cdata'. *New in version 0.3.*
.. "versionadded:: 0.3" --- inlined in the previous paragraph
- If 'cdata' is a pointer or array of characters or bytes, returns the
null-terminated string. The returned string extends until the first
null character, or at most 'maxlen' characters. If 'cdata' is an
array then 'maxlen' defaults to its length. See ``ffi.buffer()`` below
for a way to continue past the first null character. *Python 3:* this
returns a ``bytes``, not a ``str``.
- If 'cdata' is a pointer or array of wchar_t, returns a unicode string
following the same rules.
- If 'cdata' is a single character or byte or a wchar_t, returns it as a
byte string or unicode string. (Note that in some situation a single
wchar_t may require a Python unicode string of length 2.)
- If 'cdata' is an enum, returns the value of the enumerator as a string.
If the value is out of range, it is simply returned as the stringified
integer.
``ffi.buffer(cdata, [size])``: return a buffer object that references
the raw C data pointed to by the given 'cdata', of 'size' bytes. The
'cdata' must be a pointer or an array. If unspecified, the size of the
buffer is either the size of what ``cdata`` points to, or the whole size
of the array. Getting a buffer is useful because you can read from it
without an extra copy, or write into it to change the original value;
you can use for example ``file.write()`` and ``file.readinto()`` with
such a buffer (for files opened in binary mode). (Remember that like in
C, you use ``array + index`` to get the pointer to the index'th item of
an array.)
.. versionchanged:: 0.4
The returned object is not a built-in buffer nor memoryview object,
because these objects' API changes too much across Python versions.
Instead it has the following Python API (a subset of ``buffer``):
- ``buf[:]`` or ``bytes(buf)``: fetch a copy as a regular byte string (or
``buf[start:end]`` for a part)
- ``buf[:] = newstr``: change the original content (or ``buf[start:end]
= newstr``)
- ``len(buf), buf[index], buf[index] = newchar``: access as a sequence
of characters.
.. versionchanged:: 0.5
The buffer object returned by ``ffi.buffer(cdata)`` keeps alive the
``cdata`` object: if it was originally an owning cdata, then its
owned memory will not be freed as long as the buffer is alive.
Moreover buffer objects now support weakrefs to them.
.. versionchanged:: 0.9
Before version 0.9, ``bytes(buf)`` was supported in Python 3 to get
the content of the buffer, but on Python 2 it would return the repr
``<_cffi_backend.buffer object>``. This has been fixed. But you
should avoid using ``str(buf)``: it now gives inconsistent results
between Python 2 and Python 3 (this is similar to how ``str()``
gives inconsistent results on regular byte strings).
``ffi.typeof("C type" or cdata object)``: return an object of type
```` corresponding to the parsed string, or to the C type of the
cdata instance. Usually you don't need to call this function or to
explicitly manipulate ```` objects in your code: any place that
accepts a C type can receive either a string or a pre-parsed ``ctype``
object (and because of caching of the string, there is no real
performance difference). It can still be useful in writing typechecks,
e.g.::
def myfunction(ptr):
assert ffi.typeof(ptr) is ffi.typeof("foo_t*")
...
.. versionadded:: 0.4
``ffi.CData, ffi.CType``: the Python type of the objects referred to
as ```` and ```` in the rest of this document. Note
that some cdata objects may be actually of a subclass of
``ffi.CData``, and similarly with ctype, so you should check with
``if isinstance(x, ffi.CData)``. Also, ```` objects have
a number of attributes for introspection: ``kind`` and ``cname`` are
always present, and depending on the kind they may also have
``item``, ``length``, ``fields``, ``args``, ``result``, ``ellipsis``,
``abi``, ``elements`` and ``relements``.
``ffi.sizeof("C type" or cdata object)``: return the size of the
argument in bytes. The argument can be either a C type, or a cdata object,
like in the equivalent ``sizeof`` operator in C.
``ffi.alignof("C type")``: return the alignment of the C type.
Corresponds to the ``__alignof__`` operator in GCC.
``ffi.offsetof("C struct type", "fieldname")``: return the offset within
the struct of the given field. Corresponds to ``offsetof()`` in C.
``ffi.getctype("C type" or , extra="")``: return the string
representation of the given C type. If non-empty, the "extra" string is
appended (or inserted at the right place in more complicated cases); it
can be the name of a variable to declare, or an extra part of the type
like ``"*"`` or ``"[5]"``. For example
``ffi.getctype(ffi.typeof(x), "*")`` returns the string representation
of the C type "pointer to the same type than x"; and
``ffi.getctype("char[80]", "a") == "char a[80]"``.
``ffi.gc(cdata, destructor)``: return a new cdata object that points to the
same data. Later, when this new cdata object is garbage-collected,
``destructor(old_cdata_object)`` will be called. Example of usage:
``ptr = ffi.gc(lib.malloc(42), lib.free)``. Note that like objects
returned by ``ffi.new()``, the returned pointer objects have *ownership*,
which means the destructor is called as soon as *this* exact returned
object is garbage-collected. *New in version 0.3* (together
with the fact that any cdata object can be weakly referenced).
Note that this should be avoided for large memory allocations or
for limited resources. This is particularly true on PyPy: its GC does
not know how much memory or how many resources the returned ``ptr``
holds. It will only run its GC when enough memory it knows about has
been allocated (and thus run the destructor possibly later than you
would expect). Moreover, the destructor is called in whatever thread
PyPy is at that moment, which might be a problem for some C libraries.
In these cases, consider writing a wrapper class with custom ``__enter__()``
and ``__exit__()`` methods that allocate and free the C data at known
points in time, and using it in a ``with`` statement.
.. "versionadded:: 0.3" --- inlined in the previous paragraph
``ffi.new_handle(python_object)``: return a non-NULL cdata of type
``void *`` that contains an opaque reference to ``python_object``. You
can pass it around to C functions or store it into C structures. Later,
you can use ``ffi.from_handle(p)`` to retrive the original
``python_object`` from a value with the same ``void *`` pointer.
*Calling ffi.from_handle(p) is invalid and will likely crash if
the cdata object returned by new_handle() is not kept alive!*
*New in version 0.7.*
Note that ``from_handle()`` conceptually works like this: it searches in
the list of cdata objects made by ``new_handle()`` the one which has got
the same ``void *`` value; and then it fetches in that cdata object the
corresponding Python object. The cdata object keeps the Python object
alive, similar to how ``ffi.new()`` returns a cdata object that keeps a
piece of memory alive. If the cdata object *itself* is not alive any
more, then the association ``void * -> python_object`` is dead and
``from_handle()`` will crash.
.. "versionadded:: 0.7" --- inlined in the previous paragraph
``ffi.addressof(cdata, field=None)``: from a cdata whose type is
``struct foo_s``, return its "address", as a cdata whose type is
``struct foo_s *``. Also works on unions, but not on any other type.
(It would be difficult because only structs and unions are internally
stored as an indirect pointer to the data. If you need a C int whose
address can be taken, use ``ffi.new("int[1]")`` in the first place;
similarly, if it's a C pointer, use ``ffi.new("foo_t *[1]")``.)
If ``field`` is given,
returns the address of that field in the structure. The returned
pointer is only valid as long as the original ``cdata`` object is; be
sure to keep it alive if it was obtained directly from ``ffi.new()``.
*New in version 0.4.*
.. "versionadded:: 0.4" --- inlined in the previous paragraph
Unimplemented features
----------------------
All of the ANSI C declarations should be supported, and some of C99.
Known missing features that are GCC or MSVC extensions:
* Any ``__attribute__`` or ``#pragma pack(n)``
* Additional types: complex numbers, special-size floating and fixed
point types, vector types, and so on. You might be able to access an
array of complex numbers by declaring it as an array of ``struct
my_complex { double real, imag; }``, but in general you should declare
them as ``struct { ...; }`` and cannot access them directly. This
means that you cannot call any function which has an argument or
return value of this type (this would need added support in libffi).
You need to write wrapper functions in C, e.g. ``void
foo_wrapper(struct my_complex c) { foo(c.real + c.imag*1j); }``, and
call ``foo_wrapper`` rather than ``foo`` directly.
* Thread-local variables (access them via getter/setter functions)
.. versionadded:: 0.4
Now supported: the common GCC extension of anonymous nested
structs/unions inside structs/unions.
.. versionadded:: 0.6
Enum types follow the GCC rules: they are defined as the first of
``unsigned int``, ``int``, ``unsigned long`` or ``long`` that fits
all numeric values. Note that the first choice is unsigned. In CFFI
0.5 and before, enums were always ``int``. *Unimplemented: if the enum
has very large values in C not declared in CFFI, the enum will incorrectly
be considered as an int even though it is really a long! Work around
this by naming the largest value. A similar but less important problem
involves negative values.*
.. _`variable-length array`:
.. versionadded:: 0.8
Now supported: variable-length structures, i.e. whose last field is
a variable-length array.
Note that since version 0.8, declarations like ``int field[];`` in
structures are interpreted as variable-length structures. When used for
structures that are not, in fact, variable-length, it works too; in this
case, the difference with using ``int field[...];`` is that, as CFFI
believes it cannot ask the C compiler for the length of the array, you
get reduced safety checks: for example, you risk overwriting the
following fields by passing too many array items in the constructor.
Debugging dlopen'ed C libraries
-------------------------------
A few C libraries are actually hard to use correctly in a ``dlopen()``
setting. This is because most C libraries are intented for, and tested
with, a situation where they are *linked* with another program, using
either static linking or dynamic linking --- but from a program written
in C, at start-up, using the linker's capabilities instead of
``dlopen()``.
This can occasionally create issues. You would have the same issues in
another setting than CFFI, like with ``ctypes`` or even plain C code that
calls ``dlopen()``. This section contains a few generally useful
environment variables (on Linux) that can help when debugging these
issues.
**export LD_TRACE_LOADED_OBJECTS=all**
provides a lot of information, sometimes too much depending on the
setting. Output verbose debugging information about the dynamic
linker. If set to ``all`` prints all debugging information it has, if
set to ``help`` prints a help message about which categories can be
specified in this environment variable
**export LD_VERBOSE=1**
(glibc since 2.1) If set to a nonempty string, output symbol
versioning information about the program if querying information
about the program (i.e., either ``LD_TRACE_LOADED_OBJECTS`` has been set,
or ``--list`` or ``--verify`` options have been given to the dynamic
linker).
**export LD_WARN=1**
(ELF only)(glibc since 2.1.3) If set to a nonempty string, warn
about unresolved symbols.
Reference: conversions
----------------------
This section documents all the conversions that are allowed when
*writing into* a C data structure (or passing arguments to a function
call), and *reading from* a C data structure (or getting the result of a
function call). The last column gives the type-specific operations
allowed.
+---------------+------------------------+------------------+----------------+
| C type | writing into | reading from |other operations|
+===============+========================+==================+================+
| integers | an integer or anything | a Python int or | int() |
| and enums | on which int() works | long, depending | |
| `(*****)` | (but not a float!). | on the type | |
| | Must be within range. | | |
+---------------+------------------------+------------------+----------------+
| ``char`` | a string of length 1 | a string of | int() |
| | or another | length 1 | |
+---------------+------------------------+------------------+----------------+
| ``wchar_t`` | a unicode of length 1 | a unicode of | |
| | (or maybe 2 if | length 1 | int() |
| | surrogates) or | (or maybe 2 if | |
| | another | surrogates) | |
+---------------+------------------------+------------------+----------------+
| ``float``, | a float or anything on | a Python float | float(), int() |
| ``double`` | which float() works | | |
+---------------+------------------------+------------------+----------------+
|``long double``| another with | a , to | float(), int() |
| | a ``long double``, or | avoid loosing | |
| | anything on which | precision `(***)`| |
| | float() works | | |
+---------------+------------------------+------------------+----------------+
| pointers | another with | a |``[]`` `(****)`,|
| | a compatible type (i.e.| |``+``, ``-``, |
| | same type or ``char*`` | |bool() |
| | or ``void*``, or as an | | |
| | array instead) `(*)` | | |
+---------------+------------------------+ | |
| ``void *``, | another with | | |
| ``char *`` | any pointer or array | | |
| | type | | |
+---------------+------------------------+ +----------------+
| pointers to | same as pointers | | ``[]``, ``+``, |
| structure or | | | ``-``, bool(), |
| union | | | and read/write |
| | | | struct fields |
+---------------+------------------------+ +----------------+
| function | same as pointers | | bool(), |
| pointers | | | call `(**)` |
+---------------+------------------------+------------------+----------------+
| arrays | a list or tuple of | a |len(), iter(), |
| | items | |``[]`` `(****)`,|
| | | |``+``, ``-`` |
+---------------+------------------------+ +----------------+
| ``char[]`` | same as arrays, or a | | len(), iter(), |
| | Python string | | ``[]``, ``+``, |
| | | | ``-`` |
+---------------+------------------------+ +----------------+
| ``wchar_t[]`` | same as arrays, or a | | len(), iter(), |
| | Python unicode | | ``[]``, |
| | | | ``+``, ``-`` |
| | | | |
+---------------+------------------------+------------------+----------------+
| structure | a list or tuple or | a | read/write |
| | dict of the field | | fields |
| | values, or a same-type | | |
| | | | |
+---------------+------------------------+ +----------------+
| union | same as struct, but | | read/write |
| | with at most one field | | fields |
+---------------+------------------------+------------------+----------------+
.. versionchanged:: 0.3
`(*)` Note that when calling a function, as per C, a ``item *`` argument
is identical to a ``item[]`` argument. So you can pass an argument that
is accepted by either C type, like for example passing a Python string
to a ``char *`` argument (because it works for ``char[]`` arguments)
or a list of integers to a ``int *`` argument (it works for ``int[]``
arguments). Note that even if you want to pass a single ``item``,
you need to specify it in a list of length 1; for example, a ``struct
foo *`` argument might be passed as ``[[field1, field2...]]``.
As an optimization, the CPython version of CFFI assumes that a function
with a ``char *`` argument to which you pass a Python string will not
actually modify the array of characters passed in, and so passes directly
a pointer inside the Python string object.
.. versionchanged:: 0.3
`(**)` C function calls are now done with the GIL released.
.. versionadded:: 0.3
`(***)` ``long double`` support.
Such a number is passed around in a cdata object to avoid loosing
precision, because a normal Python floating-point number only contains
enough precision for a ``double``. To convert it to a regular float,
call ``float()``. If you want to operate on such numbers
without any precision loss, you need to define and use a family of C
functions like ``long double add(long double a, long double b);``.
.. versionadded:: 0.6
`(****)` Supports simple slices as well: ``x[start:stop]`` gives another
cdata object that is a "view" of all items from ``start`` to ``stop``.
It is a cdata of type "array" (so e.g. passing it as an argument to a
C function would just convert it to a pointer to the ``start`` item).
This makes cdata's of type "array" behave more like a Python list, but
``start`` and ``stop`` are not optional and a ``step`` is not supported.
As with indexing, negative bounds mean really negative indices, like in
C. As for slice assignment, it accepts any iterable, including a list
of items or another array-like cdata object, but the length must match.
(Note that this behavior differs from initialization: e.g. if you pass
a string when assigning to a slice of a ``char`` array, it must be of
the correct length; no implicit null character is added.)
.. versionchanged:: 0.6
`(*****)` Enums are now handled like ints (unsigned or signed, int or
long, like GCC; note that the first choice is unsigned). In previous
versions, you would get the enum's value as a string. Now we follow the C
convention and treat them as really equivalent to integers. To compare
their value symbolically, use code like ``if x.field == lib.FOO``.
If you really want to get their value as a string, use
``ffi.string(ffi.cast("the_enum_type", x.field))``.
Reference: verifier
-------------------
For advanced use cases, the ``Verifier`` class from ``cffi.verifier``
can be instantiated directly. It is normally instantiated for you by
``ffi.verify()``, and the instance is attached as ``ffi.verifier``.
- ``Verifier(ffi, preamble, tmpdir=.., ext_package='', modulename=None,
tag='', **kwds)``:
instantiate the class with an
FFI object and a preamble, which is C text that will be pasted into
the generated C source. The value of ``tmpdir`` defaults to the
directory ``directory_of_the_caller/__pycache__``. The value of
``ext_package`` is used when looking up an already-compiled, already-
installed version of the extension module. The module name is
``_cffi__``, unless overridden with ``modulename``
(see the `warning about modulename`_ above).
The other keyword arguments are passed directly
to `distutils when building the Extension object.`__
.. __: http://docs.python.org/distutils/setupscript.html#describing-extension-module
``Verifier`` objects have the following public attributes and methods:
- ``sourcefilename``: name of a C file. Defaults to
``tmpdir/_cffi_CRCHASH.c``, with the ``CRCHASH`` part computed
from the strings you passed to cdef() and verify() as well as the
version numbers of Python and CFFI. Can be changed before calling
``write_source()`` if you want to write the source somewhere else.
- ``modulefilename``: name of the ``.so`` file (or ``.pyd`` on Windows).
Defaults to ``tmpdir/_cffi_CRCHASH.so``. Can be changed before
calling ``compile_module()``.
- ``get_module_name()``: extract the module name from ``modulefilename``.
- ``write_source(file=None)``: produces the C source of the extension
module. If ``file`` is specified, write it in that file (or file-like)
object rather than to ``sourcefilename``.
- ``compile_module()``: writes the C source code (if not done already)
and compiles it. This produces a dynamic link library whose file is
given by ``modulefilename``.
- ``load_library()``: loads the C module (if necessary, making it
first; it looks for the existing module based on the checksum of the
strings passed to ``ffi.cdef()`` and ``preamble``, either in the
directory ``tmpdir`` or in the directory of the package ``ext_package``).
Returns an instance of a FFILibrary class that behaves like
the objects returned by ffi.dlopen(), but that delegates all
operations to the C module. This is what is returned by
``ffi.verify()``.
- ``get_extension()``: returns a distutils-compatible ``Extension`` instance.
The following are global functions in the ``cffi.verifier`` module:
- ``set_tmpdir(dirname)``: sets the temporary directory to use instead of
``directory_containing_the_py_file/__pycache__``. This is a global, so
avoid it in production code.
- ``cleanup_tmpdir(tmpdir=...)``: cleans up the temporary directory by
removing all files in it called ``_cffi_*.{c,so}`` as well as all
files in the ``build`` subdirectory. By default it will clear
``directory_containing_the_py_file/__pycache__``. This is the .py
file containing the actual call to ``cleanup_tmpdir()``.
=================
Comments and bugs
=================
The best way to contact us is on the IRC ``#pypy`` channel of
``irc.freenode.net``. Feel free to discuss matters either there or in
the `mailing list`_. Please report to the `issue tracker`_ any bugs.
As a general rule, when there is a design issue to resolve, we pick the
solution that is the "most C-like". We hope that this module has got
everything you need to access C code and nothing more.
--- the authors, Armin Rigo and Maciej Fijalkowski
.. _`issue tracker`: https://bitbucket.org/cffi/cffi/issues
.. _`mailing list`: https://groups.google.com/forum/#!forum/python-cffi
Indices and tables
==================
* :ref:`genindex`
* :ref:`search`
cffi-0.8.2/doc/make.bat 0000644 0000764 0000144 00000005777 11766377660 015131 0 ustar arigo users 0000000 0000000 @ECHO OFF
REM Command file for Sphinx documentation
set SPHINXBUILD=sphinx-build
set BUILDDIR=build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^` where ^ is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. changes to make an overview over all changed/added/deprecated items
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\CFFI.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\CFFI.ghc
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
:end
cffi-0.8.2/doc/Makefile 0000644 0000764 0000144 00000006066 11766377660 015154 0 ustar arigo users 0000000 0000000 # Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
help:
@echo "Please use \`make ' where is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/CFFI.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/CFFI.qhc"
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
"run these through (pdf)latex."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
cffi-0.8.2/doc/design.rst 0000644 0000764 0000144 00000005333 11764325060 015474 0 ustar arigo users 0000000 0000000 ================
Design decisions
================
* Generally follow LuaJIT's ffi: http://luajit.org/ext_ffi.html
* Be explicit: almost no automatic conversions. Here is the set
of automatic conversions: the various C integer types are
automatically wrapped and unwrapped to regular applevel integers. The
type ``char`` might correspond to single-character strings instead;
for integer correspondance you would use ``signed char`` or ``unsigned
char``. We might also decide that ``const char *`` automatically maps
to strings; for cases where you don't want that, use ``char *``.
* Integers are not automatically converted when passed as vararg
arguments. You have to use explicitly ``ffi.new("int", 42)`` or
``ffi.new("long", 42)`` to resolve the ambiguity. Floats would be
fine (varargs in C can only accept ``double``, not ``float``), but
there is again ambiguity between characters and strings. Even with
floats the result is a bit strange because passing a float works
but passing an integer not. I would fix this once and for all by
saying that varargs must *always* be a cdata (from ``ffi.new()``).
The possibly acceptable exception would be None (for ``NULL``).
* The internal class ``blob`` is used for raw-malloced data. You only
get a class that has internally a ``blob`` instance (or maybe is a
subclass of ``blob``) by calling ``ffi.new(struct-or-array-type)``.
The other cases, namely the cases where the type is a pointer or a
primitive, don't need a blob because it's not possible to take their
raw address.
* It would be possible to add a debug mode: when we cast ``struct foo``
to ``struct foo *`` or store it in some other struct, then we would
additionally record a weakref to the original ``struct foo`` blob.
If later we try to access the ``struct foo *`` but the weakref shows
that the blob was freed, we complain. This is a difference with
ctypes, which in these cases would store a strong reference and
keep the blob alive. "Explicit is better than implicit", so we ask
the user to keep a reference to the original blob alive as long as
it may be used (instead of doing the right things in 90% of the cases
but still crashing in the remaining 10%).
* LuaJIT uses ``struct foo &`` for a number of things, like for ``p[0]``
if ``p`` is a ``struct foo *``. I suppose it's not a bad idea at least
to have internally such types, even if you can't specify them through
pycparser. Basically ``struct foo &`` is a type that doesn't own a
blob, whereas ``struct foo`` is the type that does.
* LuaJIT uses ``int[?]`` which pycparser doesn't accept. I propose
instead to use ``int[]`` for the same purpose (its use is anyway quite
close to the C standard's use of ``int[]``).
cffi-0.8.2/c/ 0000755 0000764 0000144 00000000000 12306266401 013136 5 ustar arigo users 0000000 0000000 cffi-0.8.2/c/check__thread.c 0000644 0000764 0000144 00000000053 12242413161 016037 0 ustar arigo users 0000000 0000000 __thread int some_threadlocal_variable_42;
cffi-0.8.2/c/misc_win32.h 0000644 0000764 0000144 00000013037 12240432017 015263 0 ustar arigo users 0000000 0000000
/************************************************************/
/* errno and GetLastError support */
struct cffi_errno_s {
int saved_errno;
int saved_lasterror;
};
static DWORD cffi_tls_index;
static void init_errno(void)
{
cffi_tls_index = TlsAlloc();
if (cffi_tls_index == TLS_OUT_OF_INDEXES)
PyErr_SetString(PyExc_WindowsError, "TlsAlloc() failed");
}
static struct cffi_errno_s *_geterrno_object(void)
{
LPVOID p = TlsGetValue(cffi_tls_index);
if (p == NULL) {
/* XXX this malloc() leaks */
p = malloc(sizeof(struct cffi_errno_s));
if (p == NULL)
return NULL;
memset(p, 0, sizeof(struct cffi_errno_s));
TlsSetValue(cffi_tls_index, p);
}
return (struct cffi_errno_s *)p;
}
static void save_errno(void)
{
int current_err = errno;
int current_lasterr = GetLastError();
struct cffi_errno_s *p;
p = _geterrno_object();
if (p != NULL) {
p->saved_errno = current_err;
p->saved_lasterror = current_lasterr;
}
/* else: cannot report the error */
}
static void save_errno_only(void)
{
int current_err = errno;
struct cffi_errno_s *p;
p = _geterrno_object();
if (p != NULL) {
p->saved_errno = current_err;
}
/* else: cannot report the error */
}
static void restore_errno(void)
{
struct cffi_errno_s *p;
p = _geterrno_object();
if (p != NULL) {
SetLastError(p->saved_lasterror);
errno = p->saved_errno;
}
/* else: cannot report the error */
}
static void restore_errno_only(void)
{
struct cffi_errno_s *p;
p = _geterrno_object();
if (p != NULL) {
errno = p->saved_errno;
}
/* else: cannot report the error */
}
#if PY_MAJOR_VERSION >= 3
static PyObject *b_getwinerror(PyObject *self, PyObject *args)
{
int err = -1;
int len;
WCHAR *s_buf = NULL; /* Free via LocalFree */
PyObject *v, *message;
if (!PyArg_ParseTuple(args, "|i", &err))
return NULL;
if (err == -1) {
struct cffi_errno_s *p;
p = _geterrno_object();
if (p == NULL)
return PyErr_NoMemory();
err = p->saved_lasterror;
}
len = FormatMessageW(
/* Error API error */
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, /* no message source */
err,
MAKELANGID(LANG_NEUTRAL,
SUBLANG_DEFAULT), /* Default language */
(LPWSTR) &s_buf,
0, /* size not used */
NULL); /* no args */
if (len==0) {
/* Only seen this in out of mem situations */
message = PyUnicode_FromFormat("Windows Error 0x%X", err);
} else {
/* remove trailing cr/lf and dots */
while (len > 0 && (s_buf[len-1] <= L' ' || s_buf[len-1] == L'.'))
s_buf[--len] = L'\0';
message = PyUnicode_FromWideChar(s_buf, len);
}
if (message != NULL)
v = Py_BuildValue("(iO)", err, message);
else
v = NULL;
LocalFree(s_buf);
return v;
}
#else
static PyObject *b_getwinerror(PyObject *self, PyObject *args)
{
int err = -1;
int len;
char *s;
char *s_buf = NULL; /* Free via LocalFree */
char s_small_buf[28]; /* Room for "Windows Error 0xFFFFFFFF" */
PyObject *v;
if (!PyArg_ParseTuple(args, "|i", &err))
return NULL;
if (err == -1) {
struct cffi_errno_s *p;
p = _geterrno_object();
if (p == NULL)
return PyErr_NoMemory();
err = p->saved_lasterror;
}
len = FormatMessage(
/* Error API error */
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, /* no message source */
err,
MAKELANGID(LANG_NEUTRAL,
SUBLANG_DEFAULT), /* Default language */
(LPTSTR) &s_buf,
0, /* size not used */
NULL); /* no args */
if (len==0) {
/* Only seen this in out of mem situations */
sprintf(s_small_buf, "Windows Error 0x%X", err);
s = s_small_buf;
s_buf = NULL;
} else {
s = s_buf;
/* remove trailing cr/lf and dots */
while (len > 0 && (s[len-1] <= ' ' || s[len-1] == '.'))
s[--len] = '\0';
}
v = Py_BuildValue("(is)", err, s);
LocalFree(s_buf);
return v;
}
#endif
/************************************************************/
/* Emulate dlopen()&co. from the Windows API */
#define RTLD_LAZY 0
#define RTLD_NOW 0
#define RTLD_GLOBAL 0
#define RTLD_LOCAL 0
static void *dlopen(const char *filename, int flag)
{
return (void *)LoadLibrary(filename);
}
static void *dlsym(void *handle, const char *symbol)
{
return GetProcAddress((HMODULE)handle, symbol);
}
static void dlclose(void *handle)
{
FreeLibrary((HMODULE)handle);
}
static const char *dlerror(void)
{
static char buf[32];
DWORD dw = GetLastError();
if (dw == 0)
return NULL;
sprintf(buf, "error 0x%x", (unsigned int)dw);
return buf;
}
/************************************************************/
/* types */
typedef __int8 int8_t;
typedef __int16 int16_t;
typedef __int32 int32_t;
typedef __int64 int64_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
typedef unsigned char _Bool;
/************************************************************/
/* obscure */
#define ffi_prep_closure(a,b,c,d) ffi_prep_closure_loc(a,b,c,d,a)
cffi-0.8.2/c/file_emulator.h 0000644 0000764 0000144 00000004004 12176750421 016141 0 ustar arigo users 0000000 0000000
/* Emulation of PyFile_Check() and PyFile_AsFile() for Python 3. */
static PyObject *PyIOBase_TypeObj;
static int init_file_emulator(void)
{
PyObject *io = PyImport_ImportModule("_io");
if (io == NULL)
return -1;
PyIOBase_TypeObj = PyObject_GetAttrString(io, "_IOBase");
if (PyIOBase_TypeObj == NULL)
return -1;
return 0;
}
#define PyFile_Check(p) PyObject_IsInstance(p, PyIOBase_TypeObj)
static void _close_file_capsule(PyObject *ob_capsule)
{
FILE *f = (FILE *)PyCapsule_GetPointer(ob_capsule, "FILE");
if (f != NULL)
fclose(f);
}
static FILE *PyFile_AsFile(PyObject *ob_file)
{
PyObject *ob, *ob_capsule = NULL, *ob_mode = NULL;
FILE *f = NULL;
int fd;
char *mode;
ob = PyObject_CallMethod(ob_file, "flush", NULL);
if (ob == NULL)
goto fail;
Py_DECREF(ob);
ob_capsule = PyObject_GetAttrString(ob_file, "__cffi_FILE");
if (ob_capsule == NULL) {
PyErr_Clear();
fd = PyObject_AsFileDescriptor(ob_file);
if (fd < 0)
goto fail;
ob_mode = PyObject_GetAttrString(ob_file, "mode");
if (ob_mode == NULL)
goto fail;
mode = PyText_AsUTF8(ob_mode);
if (mode == NULL)
goto fail;
fd = dup(fd);
if (fd < 0) {
PyErr_SetFromErrno(PyExc_OSError);
goto fail;
}
f = fdopen(fd, mode);
if (f == NULL) {
close(fd);
PyErr_SetFromErrno(PyExc_OSError);
goto fail;
}
setbuf(f, NULL); /* non-buffered */
Py_DECREF(ob_mode);
ob_mode = NULL;
ob_capsule = PyCapsule_New(f, "FILE", _close_file_capsule);
if (ob_capsule == NULL) {
fclose(f);
goto fail;
}
if (PyObject_SetAttrString(ob_file, "__cffi_FILE", ob_capsule) < 0)
goto fail;
}
return PyCapsule_GetPointer(ob_capsule, "FILE");
fail:
Py_XDECREF(ob_mode);
Py_XDECREF(ob_capsule);
return NULL;
}
cffi-0.8.2/c/test_c.py 0000644 0000764 0000144 00000337537 12306266214 015015 0 ustar arigo users 0000000 0000000 import py
def _setup_path():
import os, sys
if '__pypy__' in sys.builtin_module_names:
py.test.skip("_cffi_backend.c: not tested on top of pypy, "
"use pypy/module/_cffi_backend/test/ instead.")
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
_setup_path()
from _cffi_backend import *
from _cffi_backend import _testfunc, _get_types, __version__
# ____________________________________________________________
import sys
if sys.version_info < (3,):
type_or_class = "type"
mandatory_b_prefix = ''
mandatory_u_prefix = 'u'
bytechr = chr
bitem2bchr = lambda x: x
class U(object):
def __add__(self, other):
return eval('u'+repr(other).replace(r'\\u', r'\u')
.replace(r'\\U', r'\U'))
u = U()
str2bytes = str
else:
type_or_class = "class"
long = int
unicode = str
unichr = chr
mandatory_b_prefix = 'b'
mandatory_u_prefix = ''
bytechr = lambda n: bytes([n])
bitem2bchr = bytechr
u = ""
str2bytes = lambda s: bytes(s, "ascii")
def size_of_int():
BInt = new_primitive_type("int")
return sizeof(BInt)
def size_of_long():
BLong = new_primitive_type("long")
return sizeof(BLong)
def size_of_ptr():
BInt = new_primitive_type("int")
BPtr = new_pointer_type(BInt)
return sizeof(BPtr)
def find_and_load_library(name, flags=RTLD_NOW):
import ctypes.util
if name is None:
path = None
else:
path = ctypes.util.find_library(name)
return load_library(path, flags)
def test_load_library():
x = find_and_load_library('c')
assert repr(x).startswith(""
def check_dir(p, expected):
got = set(name for name in dir(p) if not name.startswith('_'))
assert got == set(expected)
def test_inspect_primitive_type():
p = new_primitive_type("signed char")
assert p.kind == "primitive"
assert p.cname == "signed char"
check_dir(p, ['cname', 'kind'])
def test_cast_to_signed_char():
p = new_primitive_type("signed char")
x = cast(p, -65 + 17*256)
assert repr(x) == ""
assert repr(type(x)) == "<%s '_cffi_backend.CData'>" % type_or_class
assert int(x) == -65
x = cast(p, -66 + (1<<199)*256)
assert repr(x) == ""
assert int(x) == -66
assert (x == cast(p, -66)) is False
assert (x != cast(p, -66)) is True
q = new_primitive_type("short")
assert (x == cast(q, -66)) is False
assert (x != cast(q, -66)) is True
def test_sizeof_type():
py.test.raises(TypeError, sizeof, 42.5)
p = new_primitive_type("short")
assert sizeof(p) == 2
def test_integer_types():
for name in ['signed char', 'short', 'int', 'long', 'long long']:
p = new_primitive_type(name)
size = sizeof(p)
min = -(1 << (8*size-1))
max = (1 << (8*size-1)) - 1
assert int(cast(p, min)) == min
assert int(cast(p, max)) == max
assert int(cast(p, min - 1)) == max
assert int(cast(p, max + 1)) == min
py.test.raises(TypeError, cast, p, None)
assert long(cast(p, min - 1)) == max
assert int(cast(p, b'\x08')) == 8
assert int(cast(p, u+'\x08')) == 8
for name in ['char', 'short', 'int', 'long', 'long long']:
p = new_primitive_type('unsigned ' + name)
size = sizeof(p)
max = (1 << (8*size)) - 1
assert int(cast(p, 0)) == 0
assert int(cast(p, max)) == max
assert int(cast(p, -1)) == max
assert int(cast(p, max + 1)) == 0
assert long(cast(p, -1)) == max
assert int(cast(p, b'\xFE')) == 254
assert int(cast(p, u+'\xFE')) == 254
def test_no_float_on_int_types():
p = new_primitive_type('long')
py.test.raises(TypeError, float, cast(p, 42))
py.test.raises(TypeError, complex, cast(p, 42))
def test_float_types():
INF = 1E200 * 1E200
for name in ["float", "double"]:
p = new_primitive_type(name)
assert bool(cast(p, 0))
assert bool(cast(p, INF))
assert bool(cast(p, -INF))
assert int(cast(p, -150)) == -150
assert int(cast(p, 61.91)) == 61
assert long(cast(p, 61.91)) == 61
assert type(int(cast(p, 61.91))) is int
assert type(int(cast(p, 1E22))) is long
assert type(long(cast(p, 61.91))) is long
assert type(long(cast(p, 1E22))) is long
py.test.raises(OverflowError, int, cast(p, INF))
py.test.raises(OverflowError, int, cast(p, -INF))
assert float(cast(p, 1.25)) == 1.25
assert float(cast(p, INF)) == INF
assert float(cast(p, -INF)) == -INF
if name == "float":
assert float(cast(p, 1.1)) != 1.1 # rounding error
assert float(cast(p, 1E200)) == INF # limited range
assert cast(p, -1.1) != cast(p, -1.1)
assert repr(float(cast(p, -0.0))) == '-0.0'
assert float(cast(p, b'\x09')) == 9.0
assert float(cast(p, u+'\x09')) == 9.0
assert float(cast(p, True)) == 1.0
py.test.raises(TypeError, cast, p, None)
def test_complex_types():
py.test.skip("later")
INF = 1E200 * 1E200
for name in ["float", "double"]:
p = new_primitive_type("_Complex " + name)
assert bool(cast(p, 0))
assert bool(cast(p, INF))
assert bool(cast(p, -INF))
assert bool(cast(p, 0j))
assert bool(cast(p, INF*1j))
assert bool(cast(p, -INF*1j))
py.test.raises(TypeError, int, cast(p, -150))
py.test.raises(TypeError, long, cast(p, -150))
py.test.raises(TypeError, float, cast(p, -150))
assert complex(cast(p, 1.25)) == 1.25
assert complex(cast(p, 1.25j)) == 1.25j
assert float(cast(p, INF*1j)) == INF*1j
assert float(cast(p, -INF)) == -INF
if name == "float":
assert complex(cast(p, 1.1j)) != 1.1j # rounding error
assert complex(cast(p, 1E200+3j)) == INF+3j # limited range
assert complex(cast(p, 3+1E200j)) == 3+INF*1j # limited range
assert cast(p, -1.1j) != cast(p, -1.1j)
assert repr(complex(cast(p, -0.0)).real) == '-0.0'
assert repr(complex(cast(p, -0j))) == '-0j'
assert complex(cast(p, '\x09')) == 9.0
assert complex(cast(p, True)) == 1.0
py.test.raises(TypeError, cast, p, None)
#
py.test.raises(cast, new_primitive_type(name), 1+2j)
py.test.raises(cast, new_primitive_type("int"), 1+2j)
def test_character_type():
p = new_primitive_type("char")
assert bool(cast(p, '\x00'))
assert cast(p, '\x00') != cast(p, -17*256)
assert int(cast(p, 'A')) == 65
assert long(cast(p, 'A')) == 65
assert type(int(cast(p, 'A'))) is int
assert type(long(cast(p, 'A'))) is long
assert str(cast(p, 'A')) == repr(cast(p, 'A'))
assert repr(cast(p, 'A')) == "" % mandatory_b_prefix
assert repr(cast(p, 255)) == r"" % mandatory_b_prefix
assert repr(cast(p, 0)) == r"" % mandatory_b_prefix
def test_pointer_type():
p = new_primitive_type("int")
assert repr(p) == ""
p = new_pointer_type(p)
assert repr(p) == ""
p = new_pointer_type(p)
assert repr(p) == ""
p = new_pointer_type(p)
assert repr(p) == ""
def test_inspect_pointer_type():
p1 = new_primitive_type("int")
p2 = new_pointer_type(p1)
assert p2.kind == "pointer"
assert p2.cname == "int *"
assert p2.item is p1
check_dir(p2, ['cname', 'kind', 'item'])
p3 = new_pointer_type(p2)
assert p3.item is p2
def test_pointer_to_int():
BInt = new_primitive_type("int")
py.test.raises(TypeError, newp, BInt)
py.test.raises(TypeError, newp, BInt, None)
BPtr = new_pointer_type(BInt)
p = newp(BPtr)
assert repr(p) == "" % size_of_int()
p = newp(BPtr, None)
assert repr(p) == "" % size_of_int()
p = newp(BPtr, 5000)
assert repr(p) == "" % size_of_int()
q = cast(BPtr, p)
assert repr(q).startswith("" % size_of_ptr()
def test_reading_pointer_to_int():
BInt = new_primitive_type("int")
BPtr = new_pointer_type(BInt)
p = newp(BPtr, None)
assert p[0] == 0
p = newp(BPtr, 5000)
assert p[0] == 5000
py.test.raises(IndexError, "p[1]")
py.test.raises(IndexError, "p[-1]")
def test_reading_pointer_to_float():
BFloat = new_primitive_type("float")
py.test.raises(TypeError, newp, BFloat, None)
BPtr = new_pointer_type(BFloat)
p = newp(BPtr, None)
assert p[0] == 0.0 and type(p[0]) is float
p = newp(BPtr, 1.25)
assert p[0] == 1.25 and type(p[0]) is float
p = newp(BPtr, 1.1)
assert p[0] != 1.1 and abs(p[0] - 1.1) < 1E-5 # rounding errors
def test_cast_float_to_int():
for type in ["int", "unsigned int", "long", "unsigned long",
"long long", "unsigned long long"]:
p = new_primitive_type(type)
assert int(cast(p, 4.2)) == 4
py.test.raises(TypeError, newp, new_pointer_type(p), 4.2)
def test_newp_integer_types():
for name in ['signed char', 'short', 'int', 'long', 'long long']:
p = new_primitive_type(name)
pp = new_pointer_type(p)
size = sizeof(p)
min = -(1 << (8*size-1))
max = (1 << (8*size-1)) - 1
assert newp(pp, min)[0] == min
assert newp(pp, max)[0] == max
py.test.raises(OverflowError, newp, pp, min - 1)
py.test.raises(OverflowError, newp, pp, max + 1)
for name in ['char', 'short', 'int', 'long', 'long long']:
p = new_primitive_type('unsigned ' + name)
pp = new_pointer_type(p)
size = sizeof(p)
max = (1 << (8*size)) - 1
assert newp(pp, 0)[0] == 0
assert newp(pp, max)[0] == max
py.test.raises(OverflowError, newp, pp, -1)
py.test.raises(OverflowError, newp, pp, max + 1)
def test_reading_pointer_to_char():
BChar = new_primitive_type("char")
py.test.raises(TypeError, newp, BChar, None)
BPtr = new_pointer_type(BChar)
p = newp(BPtr, None)
assert p[0] == b'\x00'
p = newp(BPtr, b'A')
assert p[0] == b'A'
py.test.raises(TypeError, newp, BPtr, 65)
py.test.raises(TypeError, newp, BPtr, b"foo")
py.test.raises(TypeError, newp, BPtr, u+"foo")
c = cast(BChar, b'A')
assert str(c) == repr(c)
assert int(c) == ord(b'A')
py.test.raises(TypeError, cast, BChar, b'foo')
py.test.raises(TypeError, cast, BChar, u+'foo')
def test_reading_pointer_to_pointer():
BVoidP = new_pointer_type(new_void_type())
BCharP = new_pointer_type(new_primitive_type("char"))
BInt = new_primitive_type("int")
BIntPtr = new_pointer_type(BInt)
BIntPtrPtr = new_pointer_type(BIntPtr)
q = newp(BIntPtr, 42)
assert q[0] == 42
p = newp(BIntPtrPtr, None)
assert p[0] is not None
assert p[0] == cast(BVoidP, 0)
assert p[0] == cast(BCharP, 0)
assert p[0] != None
assert repr(p[0]) == ""
p[0] = q
assert p[0] != cast(BVoidP, 0)
assert p[0] != cast(BCharP, 0)
assert p[0][0] == 42
q[0] += 1
assert p[0][0] == 43
p = newp(BIntPtrPtr, q)
assert p[0][0] == 43
def test_load_standard_library():
if sys.platform == "win32":
py.test.raises(OSError, find_and_load_library, None)
return
x = find_and_load_library(None)
BVoidP = new_pointer_type(new_void_type())
assert x.load_function(BVoidP, 'strcpy')
py.test.raises(KeyError, x.load_function,
BVoidP, 'xxx_this_function_does_not_exist')
# the next one is from 'libm', not 'libc', but we assume
# that it is already loaded too, so it should work
assert x.load_function(BVoidP, 'sqrt')
def test_hash_differences():
BChar = new_primitive_type("char")
BInt = new_primitive_type("int")
BFloat = new_primitive_type("float")
for i in range(1, 20):
x1 = cast(BChar, chr(i))
x2 = cast(BInt, i)
if hash(x1) != hash(x2):
break
else:
raise AssertionError("hashes are equal")
for i in range(1, 20):
if hash(cast(BFloat, i)) != hash(float(i)):
break
else:
raise AssertionError("hashes are equal")
def test_no_len_on_nonarray():
p = new_primitive_type("int")
py.test.raises(TypeError, len, cast(p, 42))
def test_cmp_none():
p = new_primitive_type("int")
x = cast(p, 42)
assert (x == None) is False
assert (x != None) is True
assert (x == ["hello"]) is False
assert (x != ["hello"]) is True
y = cast(p, 0)
assert (y == None) is False
def test_invalid_indexing():
p = new_primitive_type("int")
x = cast(p, 42)
py.test.raises(TypeError, "p[0]")
def test_default_str():
BChar = new_primitive_type("char")
x = cast(BChar, 42)
assert str(x) == repr(x)
BInt = new_primitive_type("int")
x = cast(BInt, 42)
assert str(x) == repr(x)
BArray = new_array_type(new_pointer_type(BInt), 10)
x = newp(BArray, None)
assert str(x) == repr(x)
def test_default_unicode():
BInt = new_primitive_type("int")
x = cast(BInt, 42)
assert unicode(x) == unicode(repr(x))
BArray = new_array_type(new_pointer_type(BInt), 10)
x = newp(BArray, None)
assert unicode(x) == unicode(repr(x))
def test_cast_from_cdataint():
BInt = new_primitive_type("int")
x = cast(BInt, 0)
y = cast(new_pointer_type(BInt), x)
assert bool(y) is False
#
x = cast(BInt, 42)
y = cast(BInt, x)
assert int(y) == 42
y = cast(new_primitive_type("char"), x)
assert int(y) == 42
y = cast(new_primitive_type("float"), x)
assert float(y) == 42.0
#
z = cast(BInt, 42.5)
assert int(z) == 42
z = cast(BInt, y)
assert int(z) == 42
def test_void_type():
p = new_void_type()
assert p.kind == "void"
assert p.cname == "void"
check_dir(p, ['kind', 'cname'])
def test_array_type():
p = new_primitive_type("int")
assert repr(p) == ""
#
py.test.raises(TypeError, new_array_type, new_pointer_type(p), "foo")
py.test.raises(ValueError, new_array_type, new_pointer_type(p), -42)
#
p1 = new_array_type(new_pointer_type(p), None)
assert repr(p1) == ""
py.test.raises(ValueError, new_array_type, new_pointer_type(p1), 42)
#
p1 = new_array_type(new_pointer_type(p), 42)
p2 = new_array_type(new_pointer_type(p1), 25)
assert repr(p2) == ""
p2 = new_array_type(new_pointer_type(p1), None)
assert repr(p2) == ""
#
py.test.raises(OverflowError,
new_array_type, new_pointer_type(p), sys.maxsize+1)
py.test.raises(OverflowError,
new_array_type, new_pointer_type(p), sys.maxsize // 3)
def test_inspect_array_type():
p = new_primitive_type("int")
p1 = new_array_type(new_pointer_type(p), None)
assert p1.kind == "array"
assert p1.cname == "int[]"
assert p1.item is p
assert p1.length is None
check_dir(p1, ['cname', 'kind', 'item', 'length'])
p1 = new_array_type(new_pointer_type(p), 42)
assert p1.kind == "array"
assert p1.cname == "int[42]"
assert p1.item is p
assert p1.length == 42
check_dir(p1, ['cname', 'kind', 'item', 'length'])
def test_array_instance():
LENGTH = 1423
p = new_primitive_type("int")
p1 = new_array_type(new_pointer_type(p), LENGTH)
a = newp(p1, None)
assert repr(a) == "" % (
LENGTH, LENGTH * size_of_int())
assert len(a) == LENGTH
for i in range(LENGTH):
assert a[i] == 0
py.test.raises(IndexError, "a[LENGTH]")
py.test.raises(IndexError, "a[-1]")
for i in range(LENGTH):
a[i] = i * i + 1
for i in range(LENGTH):
assert a[i] == i * i + 1
e = py.test.raises(IndexError, "a[LENGTH+100] = 500")
assert ('(expected %d < %d)' % (LENGTH+100, LENGTH)) in str(e.value)
py.test.raises(TypeError, int, a)
def test_array_of_unknown_length_instance():
p = new_primitive_type("int")
p1 = new_array_type(new_pointer_type(p), None)
py.test.raises(TypeError, newp, p1, None)
py.test.raises(ValueError, newp, p1, -42)
a = newp(p1, 42)
assert len(a) == 42
for i in range(42):
a[i] -= i
for i in range(42):
assert a[i] == -i
py.test.raises(IndexError, "a[42]")
py.test.raises(IndexError, "a[-1]")
py.test.raises(IndexError, "a[42] = 123")
py.test.raises(IndexError, "a[-1] = 456")
def test_array_of_unknown_length_instance_with_initializer():
p = new_primitive_type("int")
p1 = new_array_type(new_pointer_type(p), None)
a = newp(p1, list(range(42)))
assert len(a) == 42
a = newp(p1, tuple(range(142)))
assert len(a) == 142
def test_array_initializer():
p = new_primitive_type("int")
p1 = new_array_type(new_pointer_type(p), None)
a = newp(p1, list(range(100, 142)))
for i in range(42):
assert a[i] == 100 + i
#
p2 = new_array_type(new_pointer_type(p), 43)
a = newp(p2, tuple(range(100, 142)))
for i in range(42):
assert a[i] == 100 + i
assert a[42] == 0 # extra uninitialized item
def test_array_add():
p = new_primitive_type("int")
p1 = new_array_type(new_pointer_type(p), 5) # int[5]
p2 = new_array_type(new_pointer_type(p1), 3) # int[3][5]
a = newp(p2, [list(range(n, n+5)) for n in [100, 200, 300]])
assert repr(a) == "" % (
3*5*size_of_int(),)
assert repr(a + 0).startswith(""
BStruct = new_struct_type("struct foo")
assert repr(BStruct) == ""
BPtr = new_pointer_type(BStruct)
assert repr(BPtr) == ""
py.test.raises(ValueError, sizeof, BStruct)
py.test.raises(ValueError, alignof, BStruct)
def test_new_union_type():
BUnion = new_union_type("union foo")
assert repr(BUnion) == ""
BPtr = new_pointer_type(BUnion)
assert repr(BPtr) == ""
def test_complete_struct():
BLong = new_primitive_type("long")
BChar = new_primitive_type("char")
BShort = new_primitive_type("short")
BStruct = new_struct_type("struct foo")
assert BStruct.kind == "struct"
assert BStruct.cname == "struct foo"
assert BStruct.fields is None
check_dir(BStruct, ['cname', 'kind', 'fields'])
#
complete_struct_or_union(BStruct, [('a1', BLong, -1),
('a2', BChar, -1),
('a3', BShort, -1)])
d = BStruct.fields
assert len(d) == 3
assert d[0][0] == 'a1'
assert d[0][1].type is BLong
assert d[0][1].offset == 0
assert d[0][1].bitshift == -1
assert d[0][1].bitsize == -1
assert d[1][0] == 'a2'
assert d[1][1].type is BChar
assert d[1][1].offset == sizeof(BLong)
assert d[1][1].bitshift == -1
assert d[1][1].bitsize == -1
assert d[2][0] == 'a3'
assert d[2][1].type is BShort
assert d[2][1].offset == sizeof(BLong) + sizeof(BShort)
assert d[2][1].bitshift == -1
assert d[2][1].bitsize == -1
assert sizeof(BStruct) == 2 * sizeof(BLong)
assert alignof(BStruct) == alignof(BLong)
def test_complete_union():
BLong = new_primitive_type("long")
BChar = new_primitive_type("char")
BUnion = new_union_type("union foo")
assert BUnion.kind == "union"
assert BUnion.cname == "union foo"
assert BUnion.fields is None
complete_struct_or_union(BUnion, [('a1', BLong, -1),
('a2', BChar, -1)])
d = BUnion.fields
assert len(d) == 2
assert d[0][0] == 'a1'
assert d[0][1].type is BLong
assert d[0][1].offset == 0
assert d[1][0] == 'a2'
assert d[1][1].type is BChar
assert d[1][1].offset == 0
assert sizeof(BUnion) == sizeof(BLong)
assert alignof(BUnion) == alignof(BLong)
def test_struct_instance():
BInt = new_primitive_type("int")
BStruct = new_struct_type("struct foo")
BStructPtr = new_pointer_type(BStruct)
p = cast(BStructPtr, 0)
py.test.raises(AttributeError, "p.a1") # opaque
complete_struct_or_union(BStruct, [('a1', BInt, -1),
('a2', BInt, -1)])
p = newp(BStructPtr, None)
s = p[0]
assert s.a1 == 0
s.a2 = 123
assert s.a1 == 0
assert s.a2 == 123
py.test.raises(OverflowError, "s.a1 = sys.maxsize+1")
assert s.a1 == 0
py.test.raises(AttributeError, "p.foobar")
py.test.raises(AttributeError, "s.foobar")
def test_union_instance():
BInt = new_primitive_type("int")
BUInt = new_primitive_type("unsigned int")
BUnion = new_union_type("union bar")
complete_struct_or_union(BUnion, [('a1', BInt, -1), ('a2', BUInt, -1)])
p = newp(new_pointer_type(BUnion), [-42])
bigval = -42 + (1 << (8*size_of_int()))
assert p.a1 == -42
assert p.a2 == bigval
p = newp(new_pointer_type(BUnion), {'a2': bigval})
assert p.a1 == -42
assert p.a2 == bigval
py.test.raises(OverflowError, newp, new_pointer_type(BUnion),
{'a1': bigval})
p = newp(new_pointer_type(BUnion), [])
assert p.a1 == p.a2 == 0
def test_struct_pointer():
BInt = new_primitive_type("int")
BStruct = new_struct_type("struct foo")
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a1', BInt, -1),
('a2', BInt, -1)])
p = newp(BStructPtr, None)
assert p.a1 == 0 # read/write via the pointer (C equivalent: '->')
p.a2 = 123
assert p.a1 == 0
assert p.a2 == 123
def test_struct_init_list():
BVoidP = new_pointer_type(new_void_type())
BInt = new_primitive_type("int")
BIntPtr = new_pointer_type(BInt)
BStruct = new_struct_type("struct foo")
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a1', BInt, -1),
('a2', BInt, -1),
('a3', BInt, -1),
('p4', BIntPtr, -1)])
s = newp(BStructPtr, [123, 456])
assert s.a1 == 123
assert s.a2 == 456
assert s.a3 == 0
assert s.p4 == cast(BVoidP, 0)
assert s.p4 != 0
#
s = newp(BStructPtr, {'a2': 41122, 'a3': -123})
assert s.a1 == 0
assert s.a2 == 41122
assert s.a3 == -123
assert s.p4 == cast(BVoidP, 0)
#
py.test.raises(KeyError, newp, BStructPtr, {'foobar': 0})
#
p = newp(BIntPtr, 14141)
s = newp(BStructPtr, [12, 34, 56, p])
assert s.p4 == p
assert s.p4
#
s = newp(BStructPtr, [12, 34, 56, cast(BVoidP, 0)])
assert s.p4 == cast(BVoidP, 0)
assert not s.p4
#
py.test.raises(TypeError, newp, BStructPtr, [12, 34, 56, None])
def test_array_in_struct():
BInt = new_primitive_type("int")
BStruct = new_struct_type("struct foo")
BArrayInt5 = new_array_type(new_pointer_type(BInt), 5)
complete_struct_or_union(BStruct, [('a1', BArrayInt5, -1)])
s = newp(new_pointer_type(BStruct), [[20, 24, 27, 29, 30]])
assert s.a1[2] == 27
assert repr(s.a1).startswith(""
BFunc2 = new_function_type((), BFunc, False)
assert repr(BFunc2) == ""
def test_inspect_function_type():
BInt = new_primitive_type("int")
BFunc = new_function_type((BInt, BInt), BInt, False)
assert BFunc.kind == "function"
assert BFunc.cname == "int(*)(int, int)"
assert BFunc.args == (BInt, BInt)
assert BFunc.result is BInt
assert BFunc.ellipsis is False
assert BFunc.abi == FFI_DEFAULT_ABI
def test_function_type_taking_struct():
BChar = new_primitive_type("char")
BShort = new_primitive_type("short")
BStruct = new_struct_type("struct foo")
complete_struct_or_union(BStruct, [('a1', BChar, -1),
('a2', BShort, -1)])
BFunc = new_function_type((BStruct,), BShort, False)
assert repr(BFunc) == ""
def test_function_void_result():
BVoid = new_void_type()
BInt = new_primitive_type("int")
BFunc = new_function_type((BInt, BInt), BVoid, False)
assert repr(BFunc) == ""
def test_function_void_arg():
BVoid = new_void_type()
BInt = new_primitive_type("int")
py.test.raises(TypeError, new_function_type, (BVoid,), BInt, False)
def test_call_function_0():
BSignedChar = new_primitive_type("signed char")
BFunc0 = new_function_type((BSignedChar, BSignedChar), BSignedChar, False)
f = cast(BFunc0, _testfunc(0))
assert f(40, 2) == 42
assert f(-100, -100) == -200 + 256
py.test.raises(OverflowError, f, 128, 0)
py.test.raises(OverflowError, f, 0, 128)
def test_call_function_1():
BInt = new_primitive_type("int")
BLong = new_primitive_type("long")
BFunc1 = new_function_type((BInt, BLong), BLong, False)
f = cast(BFunc1, _testfunc(1))
assert f(40, 2) == 42
assert f(-100, -100) == -200
int_max = (1 << (8*size_of_int()-1)) - 1
long_max = (1 << (8*size_of_long()-1)) - 1
if int_max == long_max:
assert f(int_max, 1) == - int_max - 1
else:
assert f(int_max, 1) == int_max + 1
def test_call_function_2():
BLongLong = new_primitive_type("long long")
BFunc2 = new_function_type((BLongLong, BLongLong), BLongLong, False)
f = cast(BFunc2, _testfunc(2))
longlong_max = (1 << (8*sizeof(BLongLong)-1)) - 1
assert f(longlong_max - 42, 42) == longlong_max
assert f(43, longlong_max - 42) == - longlong_max - 1
def test_call_function_3():
BFloat = new_primitive_type("float")
BDouble = new_primitive_type("double")
BFunc3 = new_function_type((BFloat, BDouble), BDouble, False)
f = cast(BFunc3, _testfunc(3))
assert f(1.25, 5.1) == 1.25 + 5.1 # exact
res = f(1.3, 5.1)
assert res != 6.4 and abs(res - 6.4) < 1E-5 # inexact
def test_call_function_4():
BFloat = new_primitive_type("float")
BDouble = new_primitive_type("double")
BFunc4 = new_function_type((BFloat, BDouble), BFloat, False)
f = cast(BFunc4, _testfunc(4))
res = f(1.25, 5.1)
assert res != 6.35 and abs(res - 6.35) < 1E-5 # inexact
def test_call_function_5():
BVoid = new_void_type()
BFunc5 = new_function_type((), BVoid, False)
f = cast(BFunc5, _testfunc(5))
f() # did not crash
def test_call_function_6():
BInt = new_primitive_type("int")
BIntPtr = new_pointer_type(BInt)
BFunc6 = new_function_type((BIntPtr,), BIntPtr, False)
f = cast(BFunc6, _testfunc(6))
x = newp(BIntPtr, 42)
res = f(x)
assert typeof(res) is BIntPtr
assert res[0] == 42 - 1000
#
BIntArray = new_array_type(BIntPtr, None)
BFunc6bis = new_function_type((BIntArray,), BIntPtr, False)
f = cast(BFunc6bis, _testfunc(6))
#
res = f([142])
assert typeof(res) is BIntPtr
assert res[0] == 142 - 1000
#
res = f((143,))
assert typeof(res) is BIntPtr
assert res[0] == 143 - 1000
#
x = newp(BIntArray, [242])
res = f(x)
assert typeof(res) is BIntPtr
assert res[0] == 242 - 1000
#
py.test.raises(TypeError, f, 123456)
py.test.raises(TypeError, f, "foo")
py.test.raises(TypeError, f, u+"bar")
def test_call_function_7():
BChar = new_primitive_type("char")
BShort = new_primitive_type("short")
BStruct = new_struct_type("struct foo")
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a1', BChar, -1),
('a2', BShort, -1)])
BFunc7 = new_function_type((BStruct,), BShort, False)
f = cast(BFunc7, _testfunc(7))
res = f({'a1': b'A', 'a2': -4042})
assert res == -4042 + ord(b'A')
#
x = newp(BStructPtr, {'a1': b'A', 'a2': -4042})
res = f(x[0])
assert res == -4042 + ord(b'A')
def test_call_function_20():
BChar = new_primitive_type("char")
BShort = new_primitive_type("short")
BStruct = new_struct_type("struct foo")
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a1', BChar, -1),
('a2', BShort, -1)])
BFunc20 = new_function_type((BStructPtr,), BShort, False)
f = cast(BFunc20, _testfunc(20))
x = newp(BStructPtr, {'a1': b'A', 'a2': -4042})
# can't pass a 'struct foo'
py.test.raises(TypeError, f, x[0])
def test_call_function_21():
BInt = new_primitive_type("int")
BStruct = new_struct_type("struct foo")
complete_struct_or_union(BStruct, [('a', BInt, -1),
('b', BInt, -1),
('c', BInt, -1),
('d', BInt, -1),
('e', BInt, -1),
('f', BInt, -1),
('g', BInt, -1),
('h', BInt, -1),
('i', BInt, -1),
('j', BInt, -1)])
BFunc21 = new_function_type((BStruct,), BInt, False)
f = cast(BFunc21, _testfunc(21))
res = f(list(range(13, 3, -1)))
lst = [(n << i) for (i, n) in enumerate(range(13, 3, -1))]
assert res == sum(lst)
def test_call_function_22():
BInt = new_primitive_type("int")
BArray10 = new_array_type(new_pointer_type(BInt), 10)
BStruct = new_struct_type("struct foo")
BStructP = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a', BArray10, -1)])
BFunc22 = new_function_type((BStruct, BStruct), BStruct, False)
f = cast(BFunc22, _testfunc(22))
p1 = newp(BStructP, {'a': list(range(100, 110))})
p2 = newp(BStructP, {'a': list(range(1000, 1100, 10))})
res = f(p1[0], p2[0])
for i in range(10):
assert res.a[i] == p1.a[i] - p2.a[i]
def test_call_function_23():
BVoid = new_void_type() # declaring the function as int(void*)
BVoidP = new_pointer_type(BVoid)
BInt = new_primitive_type("int")
BFunc23 = new_function_type((BVoidP,), BInt, False)
f = cast(BFunc23, _testfunc(23))
res = f(b"foo")
assert res == 1000 * ord(b'f')
res = f(cast(BVoidP, 0)) # NULL
assert res == -42
py.test.raises(TypeError, f, None)
py.test.raises(TypeError, f, 0)
py.test.raises(TypeError, f, 0.0)
def test_call_function_23_bis():
# declaring the function as int(unsigned char*)
BUChar = new_primitive_type("unsigned char")
BUCharP = new_pointer_type(BUChar)
BInt = new_primitive_type("int")
BFunc23 = new_function_type((BUCharP,), BInt, False)
f = cast(BFunc23, _testfunc(23))
res = f(b"foo")
assert res == 1000 * ord(b'f')
def test_cannot_pass_struct_with_array_of_length_0():
BInt = new_primitive_type("int")
BArray0 = new_array_type(new_pointer_type(BInt), 0)
BStruct = new_struct_type("struct foo")
complete_struct_or_union(BStruct, [('a', BArray0)])
py.test.raises(NotImplementedError, new_function_type,
(BStruct,), BInt, False)
py.test.raises(NotImplementedError, new_function_type,
(BInt,), BStruct, False)
def test_call_function_9():
BInt = new_primitive_type("int")
BFunc9 = new_function_type((BInt,), BInt, True) # vararg
f = cast(BFunc9, _testfunc(9))
assert f(0) == 0
assert f(1, cast(BInt, 42)) == 42
assert f(2, cast(BInt, 40), cast(BInt, 2)) == 42
py.test.raises(TypeError, f, 1, 42)
py.test.raises(TypeError, f, 2, None)
# promotion of chars and shorts to ints
BSChar = new_primitive_type("signed char")
BUChar = new_primitive_type("unsigned char")
BSShort = new_primitive_type("short")
assert f(3, cast(BSChar, -3), cast(BUChar, 200), cast(BSShort, -5)) == 192
def test_cannot_call_with_a_autocompleted_struct():
BSChar = new_primitive_type("signed char")
BDouble = new_primitive_type("double")
BStruct = new_struct_type("struct foo")
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('c', BDouble, -1, 8),
('a', BSChar, -1, 2),
('b', BSChar, -1, 0)])
e = py.test.raises(TypeError, new_function_type, (BStruct,), BDouble)
msg ='cannot pass as an argument a struct that was completed with verify()'
assert msg in str(e.value)
def test_new_charp():
BChar = new_primitive_type("char")
BCharP = new_pointer_type(BChar)
BCharA = new_array_type(BCharP, None)
x = newp(BCharA, 42)
assert len(x) == 42
x = newp(BCharA, b"foobar")
assert len(x) == 7
def test_load_and_call_function():
BChar = new_primitive_type("char")
BCharP = new_pointer_type(BChar)
BLong = new_primitive_type("long")
BFunc = new_function_type((BCharP,), BLong, False)
ll = find_and_load_library('c')
strlen = ll.load_function(BFunc, "strlen")
input = newp(new_array_type(BCharP, None), b"foobar")
assert strlen(input) == 6
#
assert strlen(b"foobarbaz") == 9
#
BVoidP = new_pointer_type(new_void_type())
strlenaddr = ll.load_function(BVoidP, "strlen")
assert strlenaddr == cast(BVoidP, strlen)
def test_read_variable():
## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard
## https://bugs.pypy.org/issue1643
if sys.platform == 'win32' or sys.platform == 'darwin' or sys.platform.startswith('freebsd'):
py.test.skip("untested")
BVoidP = new_pointer_type(new_void_type())
ll = find_and_load_library('c')
stderr = ll.read_variable(BVoidP, "stderr")
assert stderr == cast(BVoidP, _testfunc(8))
def test_read_variable_as_unknown_length_array():
## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard
## https://bugs.pypy.org/issue1643
if sys.platform == 'win32' or sys.platform == 'darwin' or sys.platform.startswith('freebsd'):
py.test.skip("untested")
BCharP = new_pointer_type(new_primitive_type("char"))
BArray = new_array_type(BCharP, None)
ll = find_and_load_library('c')
stderr = ll.read_variable(BArray, "stderr")
assert repr(stderr).startswith(":
Traceback (most recent call last):
File "$", line $, in Zcb1
$
File "$", line $, in check_value
$
ValueError: 42
""")
sys.stderr = cStringIO.StringIO()
bigvalue = 20000
assert f(bigvalue) == -42
assert matches(sys.stderr.getvalue(), """\
From callback :
Trying to convert the result back to C:
OverflowError: integer 60000 does not fit 'short'
""")
finally:
sys.stderr = orig_stderr
linecache.getline = orig_getline
def test_callback_return_type():
for rettype in ["signed char", "short", "int", "long", "long long",
"unsigned char", "unsigned short", "unsigned int",
"unsigned long", "unsigned long long"]:
BRet = new_primitive_type(rettype)
def cb(n):
return n + 1
BFunc = new_function_type((BRet,), BRet)
f = callback(BFunc, cb, 42)
assert f(41) == 42
if rettype.startswith("unsigned "):
min = 0
max = (1 << (8*sizeof(BRet))) - 1
else:
min = -(1 << (8*sizeof(BRet)-1))
max = (1 << (8*sizeof(BRet)-1)) - 1
assert f(min) == min + 1
assert f(max - 1) == max
assert f(max) == 42
def test_a_lot_of_callbacks():
BIGNUM = 10000
if 'PY_DOT_PY' in globals(): BIGNUM = 100 # tests on py.py
#
BInt = new_primitive_type("int")
BFunc = new_function_type((BInt,), BInt, False)
def make_callback(m):
def cb(n):
return n + m
return callback(BFunc, cb, 42) # 'cb' and 'BFunc' go out of scope
#
flist = [make_callback(i) for i in range(BIGNUM)]
for i, f in enumerate(flist):
assert f(-142) == -142 + i
def test_callback_receiving_tiny_struct():
BSChar = new_primitive_type("signed char")
BInt = new_primitive_type("int")
BStruct = new_struct_type("struct foo")
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a', BSChar, -1),
('b', BSChar, -1)])
def cb(s):
return s.a + 10 * s.b
BFunc = new_function_type((BStruct,), BInt)
f = callback(BFunc, cb)
p = newp(BStructPtr, [-2, -4])
n = f(p[0])
assert n == -42
def test_callback_returning_tiny_struct():
BSChar = new_primitive_type("signed char")
BInt = new_primitive_type("int")
BStruct = new_struct_type("struct foo")
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a', BSChar, -1),
('b', BSChar, -1)])
def cb(n):
return newp(BStructPtr, [-n, -3*n])[0]
BFunc = new_function_type((BInt,), BStruct)
f = callback(BFunc, cb)
s = f(10)
assert typeof(s) is BStruct
assert repr(s) == ""
assert s.a == -10
assert s.b == -30
def test_callback_receiving_struct():
BSChar = new_primitive_type("signed char")
BInt = new_primitive_type("int")
BDouble = new_primitive_type("double")
BStruct = new_struct_type("struct foo")
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a', BSChar, -1),
('b', BDouble, -1)])
def cb(s):
return s.a + int(s.b)
BFunc = new_function_type((BStruct,), BInt)
f = callback(BFunc, cb)
p = newp(BStructPtr, [-2, 44.444])
n = f(p[0])
assert n == 42
def test_callback_returning_struct():
BSChar = new_primitive_type("signed char")
BInt = new_primitive_type("int")
BDouble = new_primitive_type("double")
BStruct = new_struct_type("struct foo")
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a', BSChar, -1),
('b', BDouble, -1)])
def cb(n):
return newp(BStructPtr, [-n, 1E-42])[0]
BFunc = new_function_type((BInt,), BStruct)
f = callback(BFunc, cb)
s = f(10)
assert typeof(s) is BStruct
assert repr(s) in ["",
""]
assert s.a == -10
assert s.b == 1E-42
def test_callback_receiving_big_struct():
BInt = new_primitive_type("int")
BStruct = new_struct_type("struct foo")
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a', BInt, -1),
('b', BInt, -1),
('c', BInt, -1),
('d', BInt, -1),
('e', BInt, -1),
('f', BInt, -1),
('g', BInt, -1),
('h', BInt, -1),
('i', BInt, -1),
('j', BInt, -1)])
def cb(s):
for i, name in enumerate("abcdefghij"):
assert getattr(s, name) == 13 - i
return 42
BFunc = new_function_type((BStruct,), BInt)
f = callback(BFunc, cb)
p = newp(BStructPtr, list(range(13, 3, -1)))
n = f(p[0])
assert n == 42
def test_callback_returning_big_struct():
BInt = new_primitive_type("int")
BStruct = new_struct_type("struct foo")
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a', BInt, -1),
('b', BInt, -1),
('c', BInt, -1),
('d', BInt, -1),
('e', BInt, -1),
('f', BInt, -1),
('g', BInt, -1),
('h', BInt, -1),
('i', BInt, -1),
('j', BInt, -1)])
def cb():
return newp(BStructPtr, list(range(13, 3, -1)))[0]
BFunc = new_function_type((), BStruct)
f = callback(BFunc, cb)
s = f()
assert typeof(s) is BStruct
assert repr(s) in ["",
""]
for i, name in enumerate("abcdefghij"):
assert getattr(s, name) == 13 - i
def test_callback_returning_void():
BVoid = new_void_type()
BFunc = new_function_type((), BVoid, False)
def cb():
seen.append(42)
f = callback(BFunc, cb)
seen = []
f()
assert seen == [42]
py.test.raises(TypeError, callback, BFunc, cb, -42)
def test_enum_type():
BUInt = new_primitive_type("unsigned int")
BEnum = new_enum_type("foo", (), (), BUInt)
assert repr(BEnum) == ""
assert BEnum.kind == "enum"
assert BEnum.cname == "foo"
assert BEnum.elements == {}
#
BInt = new_primitive_type("int")
BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt)
assert BEnum.kind == "enum"
assert BEnum.cname == "enum foo"
assert BEnum.elements == {-20: 'ab', 0: 'def', 1: 'c'}
# 'elements' is not the real dict, but merely a copy
BEnum.elements[2] = '??'
assert BEnum.elements == {-20: 'ab', 0: 'def', 1: 'c'}
#
BEnum = new_enum_type("enum bar", ('ab', 'cd'), (5, 5), BUInt)
assert BEnum.elements == {5: 'ab'}
assert BEnum.relements == {'ab': 5, 'cd': 5}
def test_cast_to_enum():
BInt = new_primitive_type("int")
BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt)
assert sizeof(BEnum) == sizeof(BInt)
e = cast(BEnum, 0)
assert repr(e) == ""
assert repr(cast(BEnum, -42)) == ""
assert repr(cast(BEnum, -20)) == ""
assert string(e) == 'def'
assert string(cast(BEnum, -20)) == 'ab'
assert int(cast(BEnum, 1)) == 1
assert int(cast(BEnum, 0)) == 0
assert int(cast(BEnum, -242 + 2**128)) == -242
assert string(cast(BEnum, -242 + 2**128)) == '-242'
#
BUInt = new_primitive_type("unsigned int")
BEnum = new_enum_type("enum bar", ('def', 'c', 'ab'), (0, 1, 20), BUInt)
e = cast(BEnum, -1)
assert repr(e) == "" # unsigned int
#
BLong = new_primitive_type("long")
BEnum = new_enum_type("enum baz", (), (), BLong)
assert sizeof(BEnum) == sizeof(BLong)
e = cast(BEnum, -1)
assert repr(e) == ""
def test_enum_with_non_injective_mapping():
BInt = new_primitive_type("int")
BEnum = new_enum_type("enum foo", ('ab', 'cd'), (7, 7), BInt)
e = cast(BEnum, 7)
assert repr(e) == ""
assert string(e) == 'ab'
def test_enum_in_struct():
BInt = new_primitive_type("int")
BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt)
BStruct = new_struct_type("struct bar")
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a1', BEnum, -1)])
p = newp(BStructPtr, [-20])
assert p.a1 == -20
p = newp(BStructPtr, [12])
assert p.a1 == 12
e = py.test.raises(TypeError, newp, BStructPtr, [None])
msg = str(e.value)
assert ("an integer is required" in msg or # CPython
"unsupported operand type for int(): 'NoneType'" in msg or # old PyPys
"expected integer, got NoneType object" in msg) # newer PyPys
py.test.raises(TypeError, 'p.a1 = "def"')
if sys.version_info < (3,):
BEnum2 = new_enum_type(unicode("foo"), (unicode('abc'),), (5,), BInt)
assert string(cast(BEnum2, 5)) == 'abc'
assert type(string(cast(BEnum2, 5))) is str
def test_enum_overflow():
max_uint = 2 ** (size_of_int()*8) - 1
max_int = max_uint // 2
max_ulong = 2 ** (size_of_long()*8) - 1
max_long = max_ulong // 2
for BPrimitive in [new_primitive_type("int"),
new_primitive_type("unsigned int"),
new_primitive_type("long"),
new_primitive_type("unsigned long")]:
for x in [max_uint, max_int, max_ulong, max_long]:
for testcase in [x, x+1, -x-1, -x-2]:
if int(cast(BPrimitive, testcase)) == testcase:
# fits
BEnum = new_enum_type("foo", ("AA",), (testcase,),
BPrimitive)
assert int(cast(BEnum, testcase)) == testcase
else:
# overflows
py.test.raises(OverflowError, new_enum_type,
"foo", ("AA",), (testcase,), BPrimitive)
def test_callback_returning_enum():
BInt = new_primitive_type("int")
BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20), BInt)
def cb(n):
if n & 1:
return cast(BEnum, n)
else:
return n
BFunc = new_function_type((BInt,), BEnum)
f = callback(BFunc, cb)
assert f(0) == 0
assert f(1) == 1
assert f(-20) == -20
assert f(20) == 20
assert f(21) == 21
def test_callback_returning_enum_unsigned():
BInt = new_primitive_type("int")
BUInt = new_primitive_type("unsigned int")
BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, 20), BUInt)
def cb(n):
if n & 1:
return cast(BEnum, n)
else:
return n
BFunc = new_function_type((BInt,), BEnum)
f = callback(BFunc, cb)
assert f(0) == 0
assert f(1) == 1
assert f(-21) == 2**32 - 21
assert f(20) == 20
assert f(21) == 21
def test_callback_returning_char():
BInt = new_primitive_type("int")
BChar = new_primitive_type("char")
def cb(n):
return bytechr(n)
BFunc = new_function_type((BInt,), BChar)
f = callback(BFunc, cb)
assert f(0) == b'\x00'
assert f(255) == b'\xFF'
def _hacked_pypy_uni4():
pyuni4 = {1: True, 2: False}[len(u+'\U00012345')]
return 'PY_DOT_PY' in globals() and not pyuni4
def test_callback_returning_wchar_t():
BInt = new_primitive_type("int")
BWChar = new_primitive_type("wchar_t")
def cb(n):
if n == -1:
return u+'\U00012345'
if n == -2:
raise ValueError
return unichr(n)
BFunc = new_function_type((BInt,), BWChar)
f = callback(BFunc, cb)
assert f(0) == unichr(0)
assert f(255) == unichr(255)
assert f(0x1234) == u+'\u1234'
if sizeof(BWChar) == 4 and not _hacked_pypy_uni4():
assert f(-1) == u+'\U00012345'
assert f(-2) == u+'\x00' # and an exception printed to stderr
def test_struct_with_bitfields():
BLong = new_primitive_type("long")
BStruct = new_struct_type("struct foo")
LONGBITS = 8 * sizeof(BLong)
complete_struct_or_union(BStruct, [('a1', BLong, 1),
('a2', BLong, 2),
('a3', BLong, 3),
('a4', BLong, LONGBITS - 5)])
d = BStruct.fields
assert d[0][1].offset == d[1][1].offset == d[2][1].offset == 0
assert d[3][1].offset == sizeof(BLong)
def f(m, r):
if sys.byteorder == 'little':
return r
else:
return LONGBITS - m - r
assert d[0][1].bitshift == f(1, 0)
assert d[0][1].bitsize == 1
assert d[1][1].bitshift == f(2, 1)
assert d[1][1].bitsize == 2
assert d[2][1].bitshift == f(3, 3)
assert d[2][1].bitsize == 3
assert d[3][1].bitshift == f(LONGBITS - 5, 0)
assert d[3][1].bitsize == LONGBITS - 5
assert sizeof(BStruct) == 2 * sizeof(BLong)
assert alignof(BStruct) == alignof(BLong)
def test_bitfield_instance():
BInt = new_primitive_type("int")
BUnsignedInt = new_primitive_type("unsigned int")
BStruct = new_struct_type("struct foo")
complete_struct_or_union(BStruct, [('a1', BInt, 1),
('a2', BUnsignedInt, 2),
('a3', BInt, 3)])
p = newp(new_pointer_type(BStruct), None)
p.a1 = -1
assert p.a1 == -1
p.a1 = 0
py.test.raises(OverflowError, "p.a1 = 2")
assert p.a1 == 0
#
p.a1 = -1
p.a2 = 3
p.a3 = -4
py.test.raises(OverflowError, "p.a3 = 4")
e = py.test.raises(OverflowError, "p.a3 = -5")
assert str(e.value) == ("value -5 outside the range allowed by the "
"bit field width: -4 <= x <= 3")
assert p.a1 == -1 and p.a2 == 3 and p.a3 == -4
#
# special case for convenience: "int x:1", while normally signed,
# allows also setting the value "1" (it still gets read back as -1)
p.a1 = 1
assert p.a1 == -1
e = py.test.raises(OverflowError, "p.a1 = -2")
assert str(e.value) == ("value -2 outside the range allowed by the "
"bit field width: -1 <= x <= 1")
def test_bitfield_instance_init():
BInt = new_primitive_type("int")
BStruct = new_struct_type("struct foo")
complete_struct_or_union(BStruct, [('a1', BInt, 1)])
p = newp(new_pointer_type(BStruct), [-1])
assert p.a1 == -1
p = newp(new_pointer_type(BStruct), {'a1': -1})
assert p.a1 == -1
#
BUnion = new_union_type("union bar")
complete_struct_or_union(BUnion, [('a1', BInt, 1)])
p = newp(new_pointer_type(BUnion), [-1])
assert p.a1 == -1
def test_weakref():
import _weakref
BInt = new_primitive_type("int")
BPtr = new_pointer_type(BInt)
rlist = [_weakref.ref(BInt),
_weakref.ref(newp(BPtr, 42)),
_weakref.ref(cast(BPtr, 42)),
_weakref.ref(cast(BInt, 42)),
_weakref.ref(buffer(newp(BPtr, 42))),
]
for i in range(5):
import gc; gc.collect()
if [r() for r in rlist] == [None for r in rlist]:
break
def test_no_inheritance():
BInt = new_primitive_type("int")
try:
class foo(type(BInt)): pass
except TypeError:
pass
else:
raise AssertionError
x = cast(BInt, 42)
try:
class foo(type(x)): pass
except TypeError:
pass
else:
raise AssertionError
def test_assign_string():
BChar = new_primitive_type("char")
BArray1 = new_array_type(new_pointer_type(BChar), 5)
BArray2 = new_array_type(new_pointer_type(BArray1), 5)
a = newp(BArray2, [b"abc", b"de", b"ghij"])
assert string(a[1]) == b"de"
assert string(a[2]) == b"ghij"
a[2] = b"."
assert string(a[2]) == b"."
a[2] = b"12345"
assert string(a[2]) == b"12345"
e = py.test.raises(IndexError, 'a[2] = b"123456"')
assert 'char[5]' in str(e.value)
assert 'got 6 characters' in str(e.value)
def test_add_error():
x = cast(new_primitive_type("int"), 42)
py.test.raises(TypeError, "x + 1")
py.test.raises(TypeError, "x - 1")
def test_void_errors():
py.test.raises(ValueError, alignof, new_void_type())
py.test.raises(TypeError, newp, new_pointer_type(new_void_type()), None)
def test_too_many_items():
BChar = new_primitive_type("char")
BArray = new_array_type(new_pointer_type(BChar), 5)
py.test.raises(IndexError, newp, BArray, tuple(b'123456'))
py.test.raises(IndexError, newp, BArray, list(b'123456'))
py.test.raises(IndexError, newp, BArray, b'123456')
BStruct = new_struct_type("struct foo")
complete_struct_or_union(BStruct, [])
py.test.raises(TypeError, newp, new_pointer_type(BStruct), b'')
py.test.raises(ValueError, newp, new_pointer_type(BStruct), [b'1'])
def test_more_type_errors():
BInt = new_primitive_type("int")
BChar = new_primitive_type("char")
BArray = new_array_type(new_pointer_type(BChar), 5)
py.test.raises(TypeError, newp, BArray, 12.34)
BArray = new_array_type(new_pointer_type(BInt), 5)
py.test.raises(TypeError, newp, BArray, 12.34)
BFloat = new_primitive_type("float")
py.test.raises(TypeError, cast, BFloat, newp(BArray, None))
def test_more_overflow_errors():
BUInt = new_primitive_type("unsigned int")
py.test.raises(OverflowError, newp, new_pointer_type(BUInt), -1)
py.test.raises(OverflowError, newp, new_pointer_type(BUInt), 2**32)
def test_newp_copying():
"""Test that we can do newp(, ) for most
types, with the exception of arrays, like in C.
"""
BInt = new_primitive_type("int")
p = newp(new_pointer_type(BInt), cast(BInt, 42))
assert p[0] == 42
#
BUInt = new_primitive_type("unsigned int")
p = newp(new_pointer_type(BUInt), cast(BUInt, 42))
assert p[0] == 42
#
BChar = new_primitive_type("char")
p = newp(new_pointer_type(BChar), cast(BChar, '!'))
assert p[0] == b'!'
#
BFloat = new_primitive_type("float")
p = newp(new_pointer_type(BFloat), cast(BFloat, 12.25))
assert p[0] == 12.25
#
BStruct = new_struct_type("struct foo_s")
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a1', BInt, -1)])
s1 = newp(BStructPtr, [42])
p1 = newp(new_pointer_type(BStructPtr), s1)
assert p1[0] == s1
#
BArray = new_array_type(new_pointer_type(BInt), None)
a1 = newp(BArray, [1, 2, 3, 4])
py.test.raises(TypeError, newp, BArray, a1)
BArray6 = new_array_type(new_pointer_type(BInt), 6)
a1 = newp(BArray6, None)
py.test.raises(TypeError, newp, BArray6, a1)
#
s1 = newp(BStructPtr, [42])
s2 = newp(BStructPtr, s1[0])
assert s2.a1 == 42
#
BUnion = new_union_type("union foo_u")
BUnionPtr = new_pointer_type(BUnion)
complete_struct_or_union(BUnion, [('a1', BInt, -1)])
u1 = newp(BUnionPtr, [42])
u2 = newp(BUnionPtr, u1[0])
assert u2.a1 == 42
#
BFunc = new_function_type((BInt,), BUInt)
p1 = cast(BFunc, 42)
p2 = newp(new_pointer_type(BFunc), p1)
assert p2[0] == p1
def test_string():
BChar = new_primitive_type("char")
assert string(cast(BChar, 42)) == b'*'
assert string(cast(BChar, 0)) == b'\x00'
BCharP = new_pointer_type(BChar)
BArray = new_array_type(BCharP, 10)
a = newp(BArray, b"hello")
assert len(a) == 10
assert string(a) == b"hello"
p = a + 2
assert string(p) == b"llo"
assert string(newp(new_array_type(BCharP, 4), b"abcd")) == b"abcd"
py.test.raises(RuntimeError, string, cast(BCharP, 0))
assert string(a, 4) == b"hell"
assert string(a, 5) == b"hello"
assert string(a, 6) == b"hello"
def test_string_byte():
BByte = new_primitive_type("signed char")
assert string(cast(BByte, 42)) == b'*'
assert string(cast(BByte, 0)) == b'\x00'
BArray = new_array_type(new_pointer_type(BByte), None)
a = newp(BArray, [65, 66, 67])
assert type(string(a)) is bytes and string(a) == b'ABC'
#
BByte = new_primitive_type("unsigned char")
assert string(cast(BByte, 42)) == b'*'
assert string(cast(BByte, 0)) == b'\x00'
BArray = new_array_type(new_pointer_type(BByte), None)
a = newp(BArray, [65, 66, 67])
assert type(string(a)) is bytes and string(a) == b'ABC'
if 'PY_DOT_PY' not in globals() and sys.version_info < (3,):
assert string(a, 8).startswith(b'ABC') # may contain additional garbage
def test_string_wchar():
BWChar = new_primitive_type("wchar_t")
assert string(cast(BWChar, 42)) == u+'*'
assert string(cast(BWChar, 0x4253)) == u+'\u4253'
assert string(cast(BWChar, 0)) == u+'\x00'
BArray = new_array_type(new_pointer_type(BWChar), None)
a = newp(BArray, [u+'A', u+'B', u+'C'])
assert type(string(a)) is unicode and string(a) == u+'ABC'
if 'PY_DOT_PY' not in globals() and sys.version_info < (3,):
try:
# may contain additional garbage
assert string(a, 8).startswith(u+'ABC')
except ValueError: # garbage contains values > 0x10FFFF
assert sizeof(BWChar) == 4
def test_string_typeerror():
BShort = new_primitive_type("short")
BArray = new_array_type(new_pointer_type(BShort), None)
a = newp(BArray, [65, 66, 67])
py.test.raises(TypeError, string, a)
def test_bug_convert_to_ptr():
BChar = new_primitive_type("char")
BCharP = new_pointer_type(BChar)
BDouble = new_primitive_type("double")
x = cast(BDouble, 42)
py.test.raises(TypeError, newp, new_pointer_type(BCharP), x)
def test_set_struct_fields():
BChar = new_primitive_type("char")
BCharP = new_pointer_type(BChar)
BCharArray10 = new_array_type(BCharP, 10)
BStruct = new_struct_type("struct foo")
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a1', BCharArray10, -1)])
p = newp(BStructPtr, None)
assert string(p.a1) == b''
p.a1 = b'foo'
assert string(p.a1) == b'foo'
assert list(p.a1) == [b'f', b'o', b'o'] + [b'\x00'] * 7
p.a1 = [b'x', b'y']
assert string(p.a1) == b'xyo'
def test_invalid_function_result_types():
BFunc = new_function_type((), new_void_type())
BArray = new_array_type(new_pointer_type(BFunc), 5) # works
new_function_type((), BFunc) # works
new_function_type((), new_primitive_type("int"))
new_function_type((), new_pointer_type(BFunc))
BUnion = new_union_type("union foo_u")
complete_struct_or_union(BUnion, [])
py.test.raises(NotImplementedError, new_function_type, (), BUnion)
py.test.raises(TypeError, new_function_type, (), BArray)
def test_struct_return_in_func():
BChar = new_primitive_type("char")
BShort = new_primitive_type("short")
BFloat = new_primitive_type("float")
BDouble = new_primitive_type("double")
BInt = new_primitive_type("int")
BStruct = new_struct_type("struct foo_s")
complete_struct_or_union(BStruct, [('a1', BChar, -1),
('a2', BShort, -1)])
BFunc10 = new_function_type((BInt,), BStruct)
f = cast(BFunc10, _testfunc(10))
s = f(40)
assert repr(s) == ""
assert s.a1 == bytechr(40)
assert s.a2 == 40 * 40
#
BStruct11 = new_struct_type("struct test11")
complete_struct_or_union(BStruct11, [('a1', BInt, -1),
('a2', BInt, -1)])
BFunc11 = new_function_type((BInt,), BStruct11)
f = cast(BFunc11, _testfunc(11))
s = f(40)
assert repr(s) == ""
assert s.a1 == 40
assert s.a2 == 40 * 40
#
BStruct12 = new_struct_type("struct test12")
complete_struct_or_union(BStruct12, [('a1', BDouble, -1),
])
BFunc12 = new_function_type((BInt,), BStruct12)
f = cast(BFunc12, _testfunc(12))
s = f(40)
assert repr(s) == ""
assert s.a1 == 40.0
#
BStruct13 = new_struct_type("struct test13")
complete_struct_or_union(BStruct13, [('a1', BInt, -1),
('a2', BInt, -1),
('a3', BInt, -1)])
BFunc13 = new_function_type((BInt,), BStruct13)
f = cast(BFunc13, _testfunc(13))
s = f(40)
assert repr(s) == ""
assert s.a1 == 40
assert s.a2 == 40 * 40
assert s.a3 == 40 * 40 * 40
#
BStruct14 = new_struct_type("struct test14")
complete_struct_or_union(BStruct14, [('a1', BFloat, -1),
])
BFunc14 = new_function_type((BInt,), BStruct14)
f = cast(BFunc14, _testfunc(14))
s = f(40)
assert repr(s) == ""
assert s.a1 == 40.0
#
BStruct15 = new_struct_type("struct test15")
complete_struct_or_union(BStruct15, [('a1', BFloat, -1),
('a2', BInt, -1)])
BFunc15 = new_function_type((BInt,), BStruct15)
f = cast(BFunc15, _testfunc(15))
s = f(40)
assert repr(s) == ""
assert s.a1 == 40.0
assert s.a2 == 40 * 40
#
BStruct16 = new_struct_type("struct test16")
complete_struct_or_union(BStruct16, [('a1', BFloat, -1),
('a2', BFloat, -1)])
BFunc16 = new_function_type((BInt,), BStruct16)
f = cast(BFunc16, _testfunc(16))
s = f(40)
assert repr(s) == ""
assert s.a1 == 40.0
assert s.a2 == -40.0
#
BStruct17 = new_struct_type("struct test17")
complete_struct_or_union(BStruct17, [('a1', BInt, -1),
('a2', BFloat, -1)])
BFunc17 = new_function_type((BInt,), BStruct17)
f = cast(BFunc17, _testfunc(17))
s = f(40)
assert repr(s) == ""
assert s.a1 == 40
assert s.a2 == 40.0 * 40.0
#
BStruct17Ptr = new_pointer_type(BStruct17)
BFunc18 = new_function_type((BStruct17Ptr,), BInt)
f = cast(BFunc18, _testfunc(18))
x = f([[40, 2.5]])
assert x == 42
x = f([{'a2': 43.1}])
assert x == 43
def test_cast_with_functionptr():
BFunc = new_function_type((), new_void_type())
BFunc2 = new_function_type((), new_primitive_type("short"))
BCharP = new_pointer_type(new_primitive_type("char"))
BIntP = new_pointer_type(new_primitive_type("int"))
BStruct = new_struct_type("struct foo")
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a1', BFunc, -1)])
newp(BStructPtr, [cast(BFunc, 0)])
newp(BStructPtr, [cast(BCharP, 0)])
py.test.raises(TypeError, newp, BStructPtr, [cast(BIntP, 0)])
py.test.raises(TypeError, newp, BStructPtr, [cast(BFunc2, 0)])
def test_wchar():
BWChar = new_primitive_type("wchar_t")
BInt = new_primitive_type("int")
pyuni4 = {1: True, 2: False}[len(u+'\U00012345')]
wchar4 = {2: False, 4: True}[sizeof(BWChar)]
assert str(cast(BWChar, 0x45)) == "" % (
mandatory_u_prefix,)
assert str(cast(BWChar, 0x1234)) == "" % (
mandatory_u_prefix,)
if wchar4:
if not _hacked_pypy_uni4():
x = cast(BWChar, 0x12345)
assert str(x) == "" % (
mandatory_u_prefix,)
assert int(x) == 0x12345
else:
assert not pyuni4
#
BWCharP = new_pointer_type(BWChar)
BStruct = new_struct_type("struct foo_s")
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a1', BWChar, -1),
('a2', BWCharP, -1)])
s = newp(BStructPtr)
s.a1 = u+'\x00'
assert s.a1 == u+'\x00'
py.test.raises(TypeError, "s.a1 = b'a'")
py.test.raises(TypeError, "s.a1 = bytechr(0xFF)")
s.a1 = u+'\u1234'
assert s.a1 == u+'\u1234'
if pyuni4:
assert wchar4
s.a1 = u+'\U00012345'
assert s.a1 == u+'\U00012345'
elif wchar4:
if not _hacked_pypy_uni4():
s.a1 = cast(BWChar, 0x12345)
assert s.a1 == u+'\ud808\udf45'
s.a1 = u+'\ud807\udf44'
assert s.a1 == u+'\U00011f44'
else:
py.test.raises(TypeError, "s.a1 = u+'\U00012345'")
#
BWCharArray = new_array_type(BWCharP, None)
a = newp(BWCharArray, u+'hello \u1234 world')
assert len(a) == 14 # including the final null
assert string(a) == u+'hello \u1234 world'
a[13] = u+'!'
assert string(a) == u+'hello \u1234 world!'
assert str(a) == repr(a)
assert a[6] == u+'\u1234'
a[6] = u+'-'
assert string(a) == u+'hello - world!'
assert str(a) == repr(a)
#
if wchar4 and not _hacked_pypy_uni4():
u1 = u+'\U00012345\U00012346\U00012347'
a = newp(BWCharArray, u1)
assert len(a) == 4
assert string(a) == u1
assert len(list(a)) == 4
expected = [u+'\U00012345', u+'\U00012346', u+'\U00012347', unichr(0)]
assert list(a) == expected
got = [a[i] for i in range(4)]
assert got == expected
py.test.raises(IndexError, 'a[4]')
#
w = cast(BWChar, 'a')
assert repr(w) == "" % mandatory_u_prefix
assert str(w) == repr(w)
assert string(w) == u+'a'
assert int(w) == ord('a')
w = cast(BWChar, 0x1234)
assert repr(w) == "" % mandatory_u_prefix
assert str(w) == repr(w)
assert string(w) == u+'\u1234'
assert int(w) == 0x1234
w = cast(BWChar, u+'\u8234')
assert repr(w) == "" % mandatory_u_prefix
assert str(w) == repr(w)
assert string(w) == u+'\u8234'
assert int(w) == 0x8234
w = cast(BInt, u+'\u1234')
assert repr(w) == ""
if wchar4 and not _hacked_pypy_uni4():
w = cast(BWChar, u+'\U00012345')
assert repr(w) == "" % (
mandatory_u_prefix,)
assert str(w) == repr(w)
assert string(w) == u+'\U00012345'
assert int(w) == 0x12345
w = cast(BInt, u+'\U00012345')
assert repr(w) == ""
py.test.raises(TypeError, cast, BInt, u+'')
py.test.raises(TypeError, cast, BInt, u+'XX')
assert int(cast(BInt, u+'a')) == ord('a')
#
a = newp(BWCharArray, u+'hello - world')
p = cast(BWCharP, a)
assert string(p) == u+'hello - world'
p[6] = u+'\u2345'
assert string(p) == u+'hello \u2345 world'
#
s = newp(BStructPtr, [u+'\u1234', p])
assert s.a1 == u+'\u1234'
assert s.a2 == p
assert str(s.a2) == repr(s.a2)
assert string(s.a2) == u+'hello \u2345 world'
#
q = cast(BWCharP, 0)
assert str(q) == repr(q)
py.test.raises(RuntimeError, string, q)
#
def cb(p):
assert repr(p).startswith(""
q = p[0]
assert repr(q) == ""
q.a1 = 123456
assert p.a1 == 123456
r = cast(BStructPtr, p)
assert repr(r[0]).startswith(""
assert q.a1 == 123456
def test_nokeepalive_struct():
BStruct = new_struct_type("struct foo")
BStructPtr = new_pointer_type(BStruct)
BStructPtrPtr = new_pointer_type(BStructPtr)
complete_struct_or_union(BStruct, [('a1', new_primitive_type("int"), -1)])
p = newp(BStructPtr)
pp = newp(BStructPtrPtr)
pp[0] = p
s = pp[0][0]
assert repr(s).startswith(""
assert sizeof(p) == 28
#
BArray = new_array_type(new_pointer_type(BInt), 7) # int[7]
p = newp(BArray, None)
assert repr(p) == ""
assert sizeof(p) == 28
def test_cannot_dereference_void():
BVoidP = new_pointer_type(new_void_type())
p = cast(BVoidP, 123456)
py.test.raises(TypeError, "p[0]")
p = cast(BVoidP, 0)
if 'PY_DOT_PY' in globals(): py.test.skip("NULL crashes early on py.py")
py.test.raises(TypeError, "p[0]")
def test_iter():
BInt = new_primitive_type("int")
BIntP = new_pointer_type(BInt)
BArray = new_array_type(BIntP, None) # int[]
p = newp(BArray, 7)
assert list(p) == list(iter(p)) == [0] * 7
#
py.test.raises(TypeError, iter, cast(BInt, 5))
py.test.raises(TypeError, iter, cast(BIntP, 123456))
def test_cmp():
BInt = new_primitive_type("int")
BIntP = new_pointer_type(BInt)
BVoidP = new_pointer_type(new_void_type())
p = newp(BIntP, 123)
q = cast(BInt, 124)
py.test.raises(TypeError, "p < q")
py.test.raises(TypeError, "p <= q")
assert (p == q) is False
assert (p != q) is True
py.test.raises(TypeError, "p > q")
py.test.raises(TypeError, "p >= q")
r = cast(BVoidP, p)
assert (p < r) is False
assert (p <= r) is True
assert (p == r) is True
assert (p != r) is False
assert (p > r) is False
assert (p >= r) is True
s = newp(BIntP, 125)
assert (p == s) is False
assert (p != s) is True
assert (p < s) is (p <= s) is (s > p) is (s >= p)
assert (p > s) is (p >= s) is (s < p) is (s <= p)
assert (p < s) ^ (p > s)
def test_buffer():
try:
import __builtin__
except ImportError:
import builtins as __builtin__
BShort = new_primitive_type("short")
s = newp(new_pointer_type(BShort), 100)
assert sizeof(s) == size_of_ptr()
assert sizeof(BShort) == 2
assert len(buffer(s)) == 2
#
BChar = new_primitive_type("char")
BCharArray = new_array_type(new_pointer_type(BChar), None)
c = newp(BCharArray, b"hi there")
#
buf = buffer(c)
assert repr(buf).startswith('<_cffi_backend.buffer object at 0x')
assert bytes(buf) == b"hi there\x00"
if sys.version_info < (3,):
assert str(buf) == "hi there\x00"
assert unicode(buf) == u+"hi there\x00"
else:
assert str(buf) == repr(buf)
# --mb_length--
assert len(buf) == len(b"hi there\x00")
# --mb_item--
for i in range(-12, 12):
try:
expected = b"hi there\x00"[i]
except IndexError:
py.test.raises(IndexError, "buf[i]")
else:
assert buf[i] == bitem2bchr(expected)
# --mb_slice--
assert buf[:] == b"hi there\x00"
for i in range(-12, 12):
assert buf[i:] == b"hi there\x00"[i:]
assert buf[:i] == b"hi there\x00"[:i]
for j in range(-12, 12):
assert buf[i:j] == b"hi there\x00"[i:j]
# --misc--
assert list(buf) == list(map(bitem2bchr, b"hi there\x00"))
# --mb_as_buffer--
if hasattr(__builtin__, 'buffer'): # Python <= 2.7
py.test.raises(TypeError, __builtin__.buffer, c)
bf1 = __builtin__.buffer(buf)
assert len(bf1) == len(buf) and bf1[3] == "t"
if hasattr(__builtin__, 'memoryview'): # Python >= 2.7
py.test.raises(TypeError, memoryview, c)
mv1 = memoryview(buf)
assert len(mv1) == len(buf) and mv1[3] in (b"t", ord(b"t"))
# --mb_ass_item--
expected = list(map(bitem2bchr, b"hi there\x00"))
for i in range(-12, 12):
try:
expected[i] = bytechr(i & 0xff)
except IndexError:
py.test.raises(IndexError, "buf[i] = bytechr(i & 0xff)")
else:
buf[i] = bytechr(i & 0xff)
assert list(buf) == expected
# --mb_ass_slice--
buf[:] = b"hi there\x00"
assert list(buf) == list(c) == list(map(bitem2bchr, b"hi there\x00"))
py.test.raises(ValueError, 'buf[:] = b"shorter"')
py.test.raises(ValueError, 'buf[:] = b"this is much too long!"')
buf[4:2] = b"" # no effect, but should work
assert buf[:] == b"hi there\x00"
buf[:2] = b"HI"
assert buf[:] == b"HI there\x00"
buf[:2] = b"hi"
expected = list(map(bitem2bchr, b"hi there\x00"))
x = 0
for i in range(-12, 12):
for j in range(-12, 12):
start = i if i >= 0 else i + len(buf)
stop = j if j >= 0 else j + len(buf)
start = max(0, min(len(buf), start))
stop = max(0, min(len(buf), stop))
sample = bytechr(x & 0xff) * (stop - start)
x += 1
buf[i:j] = sample
expected[i:j] = map(bitem2bchr, sample)
assert list(buf) == expected
def test_getcname():
BUChar = new_primitive_type("unsigned char")
BArray = new_array_type(new_pointer_type(BUChar), 123)
assert getcname(BArray, "<-->") == "unsigned char<-->[123]"
def test_errno():
BVoid = new_void_type()
BFunc5 = new_function_type((), BVoid)
f = cast(BFunc5, _testfunc(5))
set_errno(50)
f()
assert get_errno() == 65
f(); f()
assert get_errno() == 95
def test_errno_callback():
if globals().get('PY_DOT_PY') == '2.5':
py.test.skip("cannot run this test on py.py with Python 2.5")
set_errno(95)
def cb():
e = get_errno()
set_errno(e - 6)
BVoid = new_void_type()
BFunc5 = new_function_type((), BVoid)
f = callback(BFunc5, cb)
f()
assert get_errno() == 89
f(); f()
assert get_errno() == 77
def test_abi():
assert isinstance(FFI_DEFAULT_ABI, int)
def test_cast_to_array():
# not valid in C! extension to get a non-owning
BInt = new_primitive_type("int")
BIntP = new_pointer_type(BInt)
BArray = new_array_type(BIntP, 3)
x = cast(BArray, 0)
assert repr(x) == ""
def test_cast_invalid():
BStruct = new_struct_type("struct foo")
complete_struct_or_union(BStruct, [])
p = cast(new_pointer_type(BStruct), 123456)
s = p[0]
py.test.raises(TypeError, cast, BStruct, s)
def test_bug_float_convertion():
BDouble = new_primitive_type("double")
BDoubleP = new_pointer_type(BDouble)
py.test.raises(TypeError, newp, BDoubleP, "foobar")
def test_bug_delitem():
BChar = new_primitive_type("char")
BCharP = new_pointer_type(BChar)
x = newp(BCharP)
py.test.raises(TypeError, "del x[0]")
def test_bug_delattr():
BLong = new_primitive_type("long")
BStruct = new_struct_type("struct foo")
complete_struct_or_union(BStruct, [('a1', BLong, -1)])
x = newp(new_pointer_type(BStruct))
py.test.raises(AttributeError, "del x.a1")
def test_variable_length_struct():
py.test.skip("later")
BLong = new_primitive_type("long")
BArray = new_array_type(new_pointer_type(BLong), None)
BStruct = new_struct_type("struct foo")
BStructP = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a1', BLong, -1),
('a2', BArray, -1)])
assert sizeof(BStruct) == size_of_long()
assert alignof(BStruct) == alignof(BLong)
#
py.test.raises(TypeError, newp, BStructP, None)
x = newp(BStructP, 5)
assert sizeof(x) == 6 * size_of_long()
x[4] = 123
assert x[4] == 123
py.test.raises(IndexError, "x[5]")
assert len(x.a2) == 5
#
py.test.raises(TypeError, newp, BStructP, [123])
x = newp(BStructP, [123, 5])
assert x.a1 == 123
assert len(x.a2) == 5
assert list(x.a2) == [0] * 5
#
x = newp(BStructP, {'a2': 5})
assert x.a1 == 0
assert len(x.a2) == 5
assert list(x.a2) == [0] * 5
#
x = newp(BStructP, [123, (4, 5)])
assert x.a1 == 123
assert len(x.a2) == 2
assert list(x.a2) == [4, 5]
#
x = newp(BStructP, {'a2': (4, 5)})
assert x.a1 == 0
assert len(x.a2) == 2
assert list(x.a2) == [4, 5]
def test_autocast_int():
BInt = new_primitive_type("int")
BIntPtr = new_pointer_type(BInt)
BLongLong = new_primitive_type("long long")
BULongLong = new_primitive_type("unsigned long long")
BULongLongPtr = new_pointer_type(BULongLong)
x = newp(BIntPtr, cast(BInt, 42))
assert x[0] == 42
x = newp(BIntPtr, cast(BLongLong, 42))
assert x[0] == 42
x = newp(BIntPtr, cast(BULongLong, 42))
assert x[0] == 42
x = newp(BULongLongPtr, cast(BInt, 42))
assert x[0] == 42
py.test.raises(OverflowError, newp, BULongLongPtr, cast(BInt, -42))
x = cast(BInt, cast(BInt, 42))
assert int(x) == 42
x = cast(BInt, cast(BLongLong, 42))
assert int(x) == 42
x = cast(BInt, cast(BULongLong, 42))
assert int(x) == 42
x = cast(BULongLong, cast(BInt, 42))
assert int(x) == 42
x = cast(BULongLong, cast(BInt, -42))
assert int(x) == 2 ** 64 - 42
x = cast(BIntPtr, cast(BInt, 42))
assert int(cast(BInt, x)) == 42
def test_autocast_float():
BFloat = new_primitive_type("float")
BDouble = new_primitive_type("float")
BFloatPtr = new_pointer_type(BFloat)
x = newp(BFloatPtr, cast(BDouble, 12.5))
assert x[0] == 12.5
x = cast(BFloat, cast(BDouble, 12.5))
assert float(x) == 12.5
def test_longdouble():
py_py = 'PY_DOT_PY' in globals()
BInt = new_primitive_type("int")
BLongDouble = new_primitive_type("long double")
BLongDoublePtr = new_pointer_type(BLongDouble)
BLongDoubleArray = new_array_type(BLongDoublePtr, None)
a = newp(BLongDoubleArray, 1)
x = a[0]
if not py_py:
assert repr(x).startswith(" sizeof(new_primitive_type("double")):
assert float(lstart) != start
assert repr(lstart).startswith(""
s = p[0]
assert repr(s) == ""
a = rawaddressof(BStructPtr, s)
assert repr(a).startswith("= (3,):
try:
import posix, io
posix.fdopen = io.open
except ImportError:
pass # win32
def test_FILE():
if sys.platform == "win32":
py.test.skip("testing FILE not implemented")
#
BFILE = new_struct_type("struct _IO_FILE")
BFILEP = new_pointer_type(BFILE)
BChar = new_primitive_type("char")
BCharP = new_pointer_type(BChar)
BInt = new_primitive_type("int")
BFunc = new_function_type((BCharP, BFILEP), BInt, False)
BFunc2 = new_function_type((BFILEP, BCharP), BInt, True)
ll = find_and_load_library('c')
fputs = ll.load_function(BFunc, "fputs")
fscanf = ll.load_function(BFunc2, "fscanf")
#
import posix
fdr, fdw = posix.pipe()
fr1 = posix.fdopen(fdr, 'rb', 256)
fw1 = posix.fdopen(fdw, 'wb', 256)
#
fw1.write(b"X")
res = fputs(b"hello world\n", fw1)
assert res >= 0
fw1.flush() # should not be needed
#
p = newp(new_array_type(BCharP, 100), None)
res = fscanf(fr1, b"%s\n", p)
assert res == 1
assert string(p) == b"Xhello"
fr1.close()
fw1.close()
def test_FILE_only_for_FILE_arg():
if sys.platform == "win32":
py.test.skip("testing FILE not implemented")
#
B_NOT_FILE = new_struct_type("struct NOT_FILE")
B_NOT_FILEP = new_pointer_type(B_NOT_FILE)
BChar = new_primitive_type("char")
BCharP = new_pointer_type(BChar)
BInt = new_primitive_type("int")
BFunc = new_function_type((BCharP, B_NOT_FILEP), BInt, False)
ll = find_and_load_library('c')
fputs = ll.load_function(BFunc, "fputs")
#
import posix
fdr, fdw = posix.pipe()
fr1 = posix.fdopen(fdr, 'r')
fw1 = posix.fdopen(fdw, 'w')
#
e = py.test.raises(TypeError, fputs, b"hello world\n", fw1)
assert str(e.value).startswith(
"initializer for ctype 'struct NOT_FILE *' must "
"be a cdata pointer, not ")
def test_FILE_object():
if sys.platform == "win32":
py.test.skip("testing FILE not implemented")
#
BFILE = new_struct_type("FILE")
BFILEP = new_pointer_type(BFILE)
BChar = new_primitive_type("char")
BCharP = new_pointer_type(BChar)
BInt = new_primitive_type("int")
BFunc = new_function_type((BCharP, BFILEP), BInt, False)
BFunc2 = new_function_type((BFILEP,), BInt, False)
ll = find_and_load_library('c')
fputs = ll.load_function(BFunc, "fputs")
fileno = ll.load_function(BFunc2, "fileno")
#
import posix
fdr, fdw = posix.pipe()
fw1 = posix.fdopen(fdw, 'wb', 256)
#
fw1p = cast(BFILEP, fw1)
fw1.write(b"X")
fw1.flush()
res = fputs(b"hello\n", fw1p)
assert res >= 0
res = fileno(fw1p)
assert (res == fdw) == (sys.version_info < (3,))
fw1.close()
#
data = posix.read(fdr, 256)
assert data == b"Xhello\n"
posix.close(fdr)
def test_GetLastError():
if sys.platform != "win32":
py.test.skip("GetLastError(): only for Windows")
#
lib = find_and_load_library('KERNEL32.DLL')
BInt = new_primitive_type("int")
BVoid = new_void_type()
BFunc1 = new_function_type((BInt,), BVoid, False)
BFunc2 = new_function_type((), BInt, False)
SetLastError = lib.load_function(BFunc1, "SetLastError")
GetLastError = lib.load_function(BFunc2, "GetLastError")
#
SetLastError(42)
# a random function that will reset the real GetLastError() to 0
import nt; nt.stat('.')
#
res = GetLastError()
assert res == 42
#
SetLastError(2)
code, message = getwinerror()
assert code == 2
assert message == "The system cannot find the file specified"
#
code, message = getwinerror(1155)
assert code == 1155
assert message == ("No application is associated with the "
"specified file for this operation")
def test_nonstandard_integer_types():
for typename in ['int8_t', 'uint8_t', 'int16_t', 'uint16_t', 'int32_t',
'uint32_t', 'int64_t', 'uint64_t', 'intptr_t',
'uintptr_t', 'ptrdiff_t', 'size_t', 'ssize_t']:
new_primitive_type(typename) # works
def test_cannot_convert_unicode_to_charp():
BCharP = new_pointer_type(new_primitive_type("char"))
BCharArray = new_array_type(BCharP, None)
py.test.raises(TypeError, newp, BCharArray, u+'foobar')
def test_buffer_keepalive():
BCharP = new_pointer_type(new_primitive_type("char"))
BCharArray = new_array_type(BCharP, None)
buflist = []
for i in range(20):
c = newp(BCharArray, str2bytes("hi there %d" % i))
buflist.append(buffer(c))
import gc; gc.collect()
for i in range(20):
buf = buflist[i]
assert buf[:] == str2bytes("hi there %d\x00" % i)
def test_slice():
BIntP = new_pointer_type(new_primitive_type("int"))
BIntArray = new_array_type(BIntP, None)
c = newp(BIntArray, 5)
assert len(c) == 5
assert repr(c) == ""
d = c[1:4]
assert len(d) == 3
assert repr(d) == ""
d[0] = 123
d[2] = 456
assert c[1] == 123
assert c[3] == 456
assert d[2] == 456
py.test.raises(IndexError, "d[3]")
py.test.raises(IndexError, "d[-1]")
def test_slice_ptr():
BIntP = new_pointer_type(new_primitive_type("int"))
BIntArray = new_array_type(BIntP, None)
c = newp(BIntArray, 5)
d = (c+1)[0:2]
assert len(d) == 2
assert repr(d) == ""
d[1] += 50
assert c[2] == 50
def test_slice_array_checkbounds():
BIntP = new_pointer_type(new_primitive_type("int"))
BIntArray = new_array_type(BIntP, None)
c = newp(BIntArray, 5)
c[0:5]
assert len(c[5:5]) == 0
py.test.raises(IndexError, "c[-1:1]")
cp = c + 0
cp[-1:1]
def test_nonstandard_slice():
BIntP = new_pointer_type(new_primitive_type("int"))
BIntArray = new_array_type(BIntP, None)
c = newp(BIntArray, 5)
e = py.test.raises(IndexError, "c[:5]")
assert str(e.value) == "slice start must be specified"
e = py.test.raises(IndexError, "c[4:]")
assert str(e.value) == "slice stop must be specified"
e = py.test.raises(IndexError, "c[1:2:3]")
assert str(e.value) == "slice with step not supported"
e = py.test.raises(IndexError, "c[1:2:1]")
assert str(e.value) == "slice with step not supported"
e = py.test.raises(IndexError, "c[4:2]")
assert str(e.value) == "slice start > stop"
e = py.test.raises(IndexError, "c[6:6]")
assert str(e.value) == "index too large (expected 6 <= 5)"
def test_setslice():
BIntP = new_pointer_type(new_primitive_type("int"))
BIntArray = new_array_type(BIntP, None)
c = newp(BIntArray, 5)
c[1:3] = [100, 200]
assert list(c) == [0, 100, 200, 0, 0]
cp = c + 3
cp[-1:1] = [300, 400]
assert list(c) == [0, 100, 300, 400, 0]
cp[-1:1] = iter([500, 600])
assert list(c) == [0, 100, 500, 600, 0]
py.test.raises(ValueError, "cp[-1:1] = [1000]")
assert list(c) == [0, 100, 1000, 600, 0]
py.test.raises(ValueError, "cp[-1:1] = (700, 800, 900)")
assert list(c) == [0, 100, 700, 800, 0]
def test_setslice_array():
BIntP = new_pointer_type(new_primitive_type("int"))
BIntArray = new_array_type(BIntP, None)
c = newp(BIntArray, 5)
d = newp(BIntArray, [10, 20, 30])
c[1:4] = d
assert list(c) == [0, 10, 20, 30, 0]
#
BShortP = new_pointer_type(new_primitive_type("short"))
BShortArray = new_array_type(BShortP, None)
d = newp(BShortArray, [40, 50])
c[1:3] = d
assert list(c) == [0, 40, 50, 30, 0]
def test_cdata_name_module_doc():
p = new_primitive_type("signed char")
x = cast(p, 17)
assert x.__module__ == '_cffi_backend'
assert x.__name__ == ''
assert hasattr(x, '__doc__')
def test_different_types_of_ptr_equality():
BVoidP = new_pointer_type(new_void_type())
BIntP = new_pointer_type(new_primitive_type("int"))
x = cast(BVoidP, 12345)
assert x == cast(BIntP, 12345)
assert x != cast(BIntP, 12344)
assert hash(x) == hash(cast(BIntP, 12345))
def test_new_handle():
import _weakref
BVoidP = new_pointer_type(new_void_type())
BCharP = new_pointer_type(new_primitive_type("char"))
class mylist(list):
pass
o = mylist([2, 3, 4])
x = newp_handle(BVoidP, o)
assert repr(x) == ""
assert x
assert from_handle(x) is o
assert from_handle(cast(BCharP, x)) is o
wr = _weakref.ref(o)
del o
import gc; gc.collect()
assert wr() is not None
assert from_handle(x) == list((2, 3, 4))
assert from_handle(cast(BCharP, x)) == list((2, 3, 4))
del x
for i in range(3):
if wr() is not None:
import gc; gc.collect()
assert wr() is None
py.test.raises(RuntimeError, from_handle, cast(BCharP, 0))
def test_new_handle_cycle():
import _weakref
BVoidP = new_pointer_type(new_void_type())
class A(object):
pass
o = A()
o.cycle = newp_handle(BVoidP, o)
wr = _weakref.ref(o)
del o
for i in range(3):
if wr() is not None:
import gc; gc.collect()
assert wr() is None
def _test_bitfield_details(flag):
BChar = new_primitive_type("char")
BShort = new_primitive_type("short")
BInt = new_primitive_type("int")
BUInt = new_primitive_type("unsigned int")
BStruct = new_struct_type("struct foo1")
complete_struct_or_union(BStruct, [('a', BChar, -1),
('b1', BInt, 9),
('b2', BUInt, 7),
('c', BChar, -1)], -1, -1, -1, flag)
if not (flag & SF_MSVC_BITFIELDS): # gcc, any variant
assert typeoffsetof(BStruct, 'c') == (BChar, 3)
assert sizeof(BStruct) == 4
else: # msvc
assert typeoffsetof(BStruct, 'c') == (BChar, 8)
assert sizeof(BStruct) == 12
assert alignof(BStruct) == 4
#
p = newp(new_pointer_type(BStruct), None)
p.a = b'A'
p.b1 = -201
p.b2 = 99
p.c = b'\x9D'
raw = buffer(p)[:]
if sys.byteorder == 'little':
if flag & SF_MSVC_BITFIELDS:
assert raw == b'A\x00\x00\x007\xC7\x00\x00\x9D\x00\x00\x00'
elif flag & SF_GCC_LITTLE_ENDIAN:
assert raw == b'A7\xC7\x9D'
elif flag & SF_GCC_BIG_ENDIAN:
assert raw == b'A\xE3\x9B\x9D'
else:
raise AssertionError("bad flag")
else:
if flag & SF_MSVC_BITFIELDS:
assert raw == b'A\x00\x00\x00\x00\x00\xC77\x9D\x00\x00\x00'
elif flag & SF_GCC_LITTLE_ENDIAN:
assert raw == b'A\xC77\x9D'
elif flag & SF_GCC_BIG_ENDIAN:
assert raw == b'A\x9B\xE3\x9D'
else:
raise AssertionError("bad flag")
#
BStruct = new_struct_type("struct foo2")
complete_struct_or_union(BStruct, [('a', BChar, -1),
('', BShort, 9),
('c', BChar, -1)], -1, -1, -1, flag)
assert typeoffsetof(BStruct, 'c') == (BChar, 4)
if flag & SF_MSVC_BITFIELDS:
assert sizeof(BStruct) == 6
assert alignof(BStruct) == 2
elif flag & SF_GCC_X86_BITFIELDS:
assert sizeof(BStruct) == 5
assert alignof(BStruct) == 1
elif flag & SF_GCC_ARM_BITFIELDS:
assert sizeof(BStruct) == 6
assert alignof(BStruct) == 2
else:
raise AssertionError("bad flag")
#
BStruct = new_struct_type("struct foo2")
complete_struct_or_union(BStruct, [('a', BChar, -1),
('', BInt, 0),
('', BInt, 0),
('c', BChar, -1)], -1, -1, -1, flag)
if flag & SF_MSVC_BITFIELDS:
assert typeoffsetof(BStruct, 'c') == (BChar, 1)
assert sizeof(BStruct) == 2
assert alignof(BStruct) == 1
elif flag & SF_GCC_X86_BITFIELDS:
assert typeoffsetof(BStruct, 'c') == (BChar, 4)
assert sizeof(BStruct) == 5
assert alignof(BStruct) == 1
elif flag & SF_GCC_ARM_BITFIELDS:
assert typeoffsetof(BStruct, 'c') == (BChar, 4)
assert sizeof(BStruct) == 8
assert alignof(BStruct) == 4
else:
raise AssertionError("bad flag")
SF_MSVC_BITFIELDS = 0x01
SF_GCC_ARM_BITFIELDS = 0x02
SF_GCC_X86_BITFIELDS = 0x10
SF_GCC_BIG_ENDIAN = 0x04
SF_GCC_LITTLE_ENDIAN = 0x40
SF_PACKED = 0x08
def test_bitfield_as_x86_gcc():
_test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_LITTLE_ENDIAN)
def test_bitfield_as_msvc():
_test_bitfield_details(flag=SF_MSVC_BITFIELDS|SF_GCC_LITTLE_ENDIAN)
def test_bitfield_as_arm_gcc():
_test_bitfield_details(flag=SF_GCC_ARM_BITFIELDS|SF_GCC_LITTLE_ENDIAN)
def test_bitfield_as_ppc_gcc():
# PowerPC uses the same format as X86, but is big-endian
_test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_BIG_ENDIAN)
def test_struct_array_no_length():
BInt = new_primitive_type("int")
BIntP = new_pointer_type(BInt)
BArray = new_array_type(BIntP, None)
BStruct = new_struct_type("foo")
py.test.raises(TypeError, complete_struct_or_union,
BStruct, [('x', BArray),
('y', BInt)])
#
BStruct = new_struct_type("foo")
complete_struct_or_union(BStruct, [('x', BInt),
('y', BArray)])
assert sizeof(BStruct) == size_of_int()
d = BStruct.fields
assert len(d) == 2
assert d[0][0] == 'x'
assert d[0][1].type is BInt
assert d[0][1].offset == 0
assert d[0][1].bitshift == -1
assert d[0][1].bitsize == -1
assert d[1][0] == 'y'
assert d[1][1].type is BArray
assert d[1][1].offset == size_of_int()
assert d[1][1].bitshift == -1
assert d[1][1].bitsize == -1
#
p = newp(new_pointer_type(BStruct))
p.x = 42
assert p.x == 42
assert typeof(p.y) is BIntP
assert p.y == cast(BIntP, p) + 1
#
p = newp(new_pointer_type(BStruct), [100])
assert p.x == 100
#
# Tests for
# ffi.new("struct_with_var_array *", [field.., [the_array_items..]])
# ffi.new("struct_with_var_array *", [field.., array_size])
plist = []
for i in range(20):
if i % 2 == 0:
p = newp(new_pointer_type(BStruct), [100, [200, i, 400]])
else:
p = newp(new_pointer_type(BStruct), [100, 3])
p.y[1] = i
p.y[0] = 200
assert p.y[2] == 0
p.y[2] = 400
plist.append(p)
for i in range(20):
p = plist[i]
assert p.x == 100
assert p.y[0] == 200
assert p.y[1] == i
assert p.y[2] == 400
assert list(p.y[0:3]) == [200, i, 400]
#
# the following assignment works, as it normally would, for any array field
p.y = [500, 600]
assert list(p.y[0:3]) == [500, 600, 400]
#
# error cases
py.test.raises(TypeError, "p.y = cast(BIntP, 0)")
py.test.raises(TypeError, "p.y = 15")
py.test.raises(TypeError, "p.y = None")
#
# accepting this may be specified by the C99 standard,
# or a GCC strangeness...
BStruct2 = new_struct_type("bar")
complete_struct_or_union(BStruct2, [('f', BStruct),
('n', BInt)])
p = newp(new_pointer_type(BStruct2), {'n': 42})
assert p.n == 42
#
# more error cases
py.test.raises(TypeError, newp, new_pointer_type(BStruct), [100, None])
BArray4 = new_array_type(BIntP, 4)
BStruct4 = new_struct_type("test4")
complete_struct_or_union(BStruct4, [('a', BArray4)]) # not varsized
py.test.raises(TypeError, newp, new_pointer_type(BStruct4), [None])
py.test.raises(TypeError, newp, new_pointer_type(BStruct4), [4])
p = newp(new_pointer_type(BStruct4), [[10, 20, 30]])
assert p.a[0] == 10
assert p.a[1] == 20
assert p.a[2] == 30
assert p.a[3] == 0
def test_struct_array_no_length_explicit_position():
BInt = new_primitive_type("int")
BIntP = new_pointer_type(BInt)
BArray = new_array_type(BIntP, None)
BStruct = new_struct_type("foo")
complete_struct_or_union(BStruct, [('x', BArray, -1, 0), # actually 3 items
('y', BInt, -1, 12)])
p = newp(new_pointer_type(BStruct), [[10, 20], 30])
assert p.x[0] == 10
assert p.x[1] == 20
assert p.x[2] == 0
assert p.y == 30
p = newp(new_pointer_type(BStruct), {'x': [40], 'y': 50})
assert p.x[0] == 40
assert p.x[1] == 0
assert p.x[2] == 0
assert p.y == 50
p = newp(new_pointer_type(BStruct), {'y': 60})
assert p.x[0] == 0
assert p.x[1] == 0
assert p.x[2] == 0
assert p.y == 60
#
# This "should" work too, allocating a larger structure
# (a bit strange in this case, but useful in general)
plist = []
for i in range(20):
p = newp(new_pointer_type(BStruct), [[10, 20, 30, 40, 50, 60, 70]])
plist.append(p)
for i in range(20):
p = plist[i]
assert p.x[0] == 10
assert p.x[1] == 20
assert p.x[2] == 30
assert p.x[3] == 40 == p.y
assert p.x[4] == 50
assert p.x[5] == 60
assert p.x[6] == 70
def test_ass_slice():
BChar = new_primitive_type("char")
BArray = new_array_type(new_pointer_type(BChar), None)
p = newp(BArray, b"foobar")
p[2:5] = [b"*", b"Z", b"T"]
p[1:3] = b"XY"
assert list(p) == [b"f", b"X", b"Y", b"Z", b"T", b"r", b"\x00"]
py.test.raises(TypeError, "p[1:5] = u+'XYZT'")
py.test.raises(TypeError, "p[1:5] = [1, 2, 3, 4]")
#
BUniChar = new_primitive_type("wchar_t")
BArray = new_array_type(new_pointer_type(BUniChar), None)
p = newp(BArray, u+"foobar")
p[2:5] = [u+"*", u+"Z", u+"T"]
p[1:3] = u+"XY"
assert list(p) == [u+"f", u+"X", u+"Y", u+"Z", u+"T", u+"r", u+"\x00"]
py.test.raises(TypeError, "p[1:5] = b'XYZT'")
py.test.raises(TypeError, "p[1:5] = [1, 2, 3, 4]")
def test_void_p_arithmetic():
BVoid = new_void_type()
BInt = new_primitive_type("intptr_t")
p = cast(new_pointer_type(BVoid), 100000)
assert int(cast(BInt, p)) == 100000
assert int(cast(BInt, p + 42)) == 100042
assert int(cast(BInt, p - (-42))) == 100042
assert (p + 42) - p == 42
q = cast(new_pointer_type(new_primitive_type("char")), 100000)
py.test.raises(TypeError, "p - q")
py.test.raises(TypeError, "q - p")
py.test.raises(TypeError, "p + cast(new_primitive_type('int'), 42)")
py.test.raises(TypeError, "p - cast(new_primitive_type('int'), 42)")
def test_sizeof_sliced_array():
BInt = new_primitive_type("int")
BArray = new_array_type(new_pointer_type(BInt), 10)
p = newp(BArray, None)
assert sizeof(p[2:9]) == 7 * sizeof(BInt)
def test_packed():
BLong = new_primitive_type("long")
BChar = new_primitive_type("char")
BShort = new_primitive_type("short")
BStruct = new_struct_type("struct foo")
complete_struct_or_union(BStruct, [('a1', BLong, -1),
('a2', BChar, -1),
('a3', BShort, -1)],
None, -1, -1, SF_PACKED)
d = BStruct.fields
assert len(d) == 3
assert d[0][0] == 'a1'
assert d[0][1].type is BLong
assert d[0][1].offset == 0
assert d[0][1].bitshift == -1
assert d[0][1].bitsize == -1
assert d[1][0] == 'a2'
assert d[1][1].type is BChar
assert d[1][1].offset == sizeof(BLong)
assert d[1][1].bitshift == -1
assert d[1][1].bitsize == -1
assert d[2][0] == 'a3'
assert d[2][1].type is BShort
assert d[2][1].offset == sizeof(BLong) + sizeof(BChar)
assert d[2][1].bitshift == -1
assert d[2][1].bitsize == -1
assert sizeof(BStruct) == sizeof(BLong) + sizeof(BChar) + sizeof(BShort)
assert alignof(BStruct) == 1
def test_packed_with_bitfields():
if sys.platform == "win32":
py.test.skip("testing gcc behavior")
BLong = new_primitive_type("long")
BChar = new_primitive_type("char")
BStruct = new_struct_type("struct foo")
py.test.raises(NotImplementedError,
complete_struct_or_union,
BStruct, [('a1', BLong, 30),
('a2', BChar, 5)],
None, -1, -1, SF_PACKED)
def test_version():
# this test is here mostly for PyPy
assert __version__ == "0.8.2"
cffi-0.8.2/c/x.py 0000644 0000764 0000144 00000001015 12172012547 013755 0 ustar arigo users 0000000 0000000 import gc
import sys
import cffi
def make_callback():
container = [data]
callback = ffi.callback('int()', lambda: len(vars(container)))
container.append(callback)
# Ref cycle: callback -> lambda (closure) -> container -> callback
return callback
ffi = cffi.FFI()
data = 'something'
initial_refcount = sys.getrefcount(data)
callback = make_callback()
assert sys.getrefcount(data) == initial_refcount + 1
del callback
gc.collect()
assert sys.getrefcount(data) == initial_refcount # Fails, data is leaked
cffi-0.8.2/c/wchar_helper.h 0000644 0000764 0000144 00000006572 11776620115 015772 0 ustar arigo users 0000000 0000000 /*
* wchar_t helpers
*/
#if (Py_UNICODE_SIZE == 2) && (SIZEOF_WCHAR_T == 4)
# define CONVERT_WCHAR_TO_SURROGATES
#endif
#ifdef CONVERT_WCHAR_TO_SURROGATES
/* Before Python 2.7, PyUnicode_FromWideChar is not able to convert
wchar_t values greater than 65535 into two-unicode-characters surrogates.
But even the Python 2.7 version doesn't detect wchar_t values that are
out of range(1114112), and just returns nonsense.
*/
static PyObject *
_my_PyUnicode_FromWideChar(register const wchar_t *w,
Py_ssize_t size)
{
PyObject *unicode;
register Py_ssize_t i;
Py_ssize_t alloc;
const wchar_t *orig_w;
if (w == NULL) {
PyErr_BadInternalCall();
return NULL;
}
alloc = size;
orig_w = w;
for (i = size; i > 0; i--) {
if (*w > 0xFFFF)
alloc++;
w++;
}
w = orig_w;
unicode = PyUnicode_FromUnicode(NULL, alloc);
if (!unicode)
return NULL;
/* Copy the wchar_t data into the new object */
{
register Py_UNICODE *u;
u = PyUnicode_AS_UNICODE(unicode);
for (i = size; i > 0; i--) {
if (((unsigned int)*w) > 0xFFFF) {
wchar_t ordinal;
if (((unsigned int)*w) > 0x10FFFF) {
PyErr_Format(PyExc_ValueError,
"wchar_t out of range for "
"conversion to unicode: 0x%x", (int)*w);
Py_DECREF(unicode);
return NULL;
}
ordinal = *w++;
ordinal -= 0x10000;
*u++ = 0xD800 | (ordinal >> 10);
*u++ = 0xDC00 | (ordinal & 0x3FF);
}
else
*u++ = *w++;
}
}
return unicode;
}
#else
# define _my_PyUnicode_FromWideChar PyUnicode_FromWideChar
#endif
#define IS_SURROGATE(u) (0xD800 <= (u)[0] && (u)[0] <= 0xDBFF && \
0xDC00 <= (u)[1] && (u)[1] <= 0xDFFF)
#define AS_SURROGATE(u) (0x10000 + (((u)[0] - 0xD800) << 10) + \
((u)[1] - 0xDC00))
static int _my_PyUnicode_AsSingleWideChar(PyObject *unicode, wchar_t *result)
{
Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode);
if (PyUnicode_GET_SIZE(unicode) == 1) {
*result = (wchar_t)(u[0]);
return 0;
}
#ifdef CONVERT_WCHAR_TO_SURROGATES
if (PyUnicode_GET_SIZE(unicode) == 2 && IS_SURROGATE(u)) {
*result = AS_SURROGATE(u);
return 0;
}
#endif
return -1;
}
static Py_ssize_t _my_PyUnicode_SizeAsWideChar(PyObject *unicode)
{
Py_ssize_t length = PyUnicode_GET_SIZE(unicode);
Py_ssize_t result = length;
#ifdef CONVERT_WCHAR_TO_SURROGATES
Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode);
Py_ssize_t i;
for (i=0; i