Welcome, guest | Sign In | My Account | Store | Cart
import ctypes

class C_function:
	"""This class wraps a Python function into its C equivalent in the given library"""

	types = ctypes.__dict__

	@classmethod
	def register_type(cls, typename, obj):
		"""Add an acceptable argument or retval type to this."""
		cls.types[typename] = obj

	def __init__(self, dll, error_check=None):
		"""Set the library to reference the function name against."""
		self.library = ctypes.CDLL(dll)
		self.error_check = error_check
		
	def __call__(self, f):
		"""Performs the actual function wrapping.

		This extracts function details from annotations and signature.
		For example, to wrap puts, the following suffices (on Linux):

			>>> @C_function("libc.so.6")
			... def puts(my_string: "c_char_p") -> "c_int":
			... 	return puts.c_function(my_string)
			...
			>>> x = puts("hello, world!")
			hello, world!
			>>> x
			14

		Note that to call the actual C function, you just reference
		it by asking for the local function's .c_function attribute.
		"""

		# get the function's name, arguments, and arg types
		name = f.__name__
		args = f.__code__.co_varnames
		annotations = f.__annotations__

		# get the function itself
		function = self.library[name]

		# set the function's call signature
		argtypes = []
		for arg in args:
			annotation = annotations[arg]
			if annotation.startswith("*"):
				argtypes.append(ctypes.POINTER(self.types[annotation[1:]]))
			else:
				argtypes.append(self.types[annotation])
		function.argtypes = argtypes

		# and its return value
		restype = annotations['return']
		if restype.startswith("*"):
			function.restype = ctypes.POINTER(self.types[restype[1:]])
		else:
			function.restype = self.types[restype]

		# set its error handler
		if self.error_check:
			function.errcheck = self.error_check

		# set the c_function as an attribute of f
		f.c_function = function

		return f

History

  • revision 6 (12 years ago)
  • previous revisions are not available