CSlug API Reference

Module contents

class cslug.CSlug(path, *sources, headers=(), links=(), flags=())[source]

Compiles and loads C code in a relatively safe and streamlined manner.

__init__(path, *sources, headers=(), links=(), flags=())[source]
Parameters
  • path (str or os.PathLike or list) –

  • *sources (str or os.PathLike or io.TextIOBase) – C source code files. May also be lists of files.

  • headers (cslug.Header or list[cslug.Header]) – cslug generated headers - not just *.h files (which cslug doesn't need to see anywhere).

  • links (str or list[str]) – Other C libraries to link to via the -l compiler switch. Should not contain the -l prefix or platform suffix.

  • flags (str or list[str]) – Additional flags to be passed directly to the C compiler. Can also be configured using the CC_FLAGS environment variable. Inspect using compile_command.

Changed in version 0.3.0: Add flags parameter and the CC_FLAGS variable.

close(all=True)[source]

Close the library stored in CSlug.dll.

Parameters

all (bool) – Close any other existing handles to the same file.

Closing is required before a recompile to avoid permission errors or dangling pointers. If the library is already closed, this function silently does nothing.

compile()[source]

Recompile C code only.

Returns

True if the compile succeeded. However this is redundant because an error is raised if it didn't.

Return type

bool

Raises

exceptions.BuildError – Any compiler errors are propagated as Python exceptions.

Warns

exceptions.BuildWarning – Any build warnings from the compiler are propagated as Python warnings.

compile_command(_cc=None, _cc_version=None)[source]

Get the compile command invoked by compile.

I hope to eventually make this function configurable.

property dll

The open C library.

Returns

Return type

ctypes.CDLL

This attribute is lazily loaded. On first access, this property will:

  • Check the library has been compiled and invoke a full compile with make if it hasn't.

  • Open the library.

  • Initialise type information for all known symbols (functions).

Use close to reset.

make()[source]

Invoke a full recompile and refresh of everything.

Returns

True if the build succeeded.

Return type

bool

Everything here is defined as the following sequence in the following order:

  1. close any open handles.

  2. Rebuild each Header in headers using Header.make.

  3. Recompile the shared library using :meth`compile`.

  4. Rescan C source code for type information and write it to a json file.

The C library is loaded back into Python on next access of dll.

cslug.anchor(*paths)[source]

Replace relative paths with frozen paths relative to __file__'s parent.

Parameters

paths (str or os.PathLike or io.IOBase) – Path(s) to freeze or pseudo files.

Returns

List of modified paths.

Return type

list

Pseudo files (io.IOBase) and absolute paths are left unchanged. Use this function to make your code working-dir independent.

class cslug.Header(path, *sources, includes=(), defines=())[source]

Automatically generate a header file.

For every function in every source file, generate a prototype for it. Use to automate the unfortunate copy/pasting required for multiple source files to interact with each other.

Using a header like this globalises every function. Whilst this type of namespace collapsing would normally be discouraged, a shared library does not allow naming collisions anyway so there is little to no advantage in keeping namespaces separate.

__init__(path, *sources, includes=(), defines=())[source]
Parameters
  • path – A file to write the header to.

  • *sources – C source files to extract functions from.

  • includes (str or list[str]) – Other headers to #include.

  • defines (dict or enum.Enum or list[dict or enum.Enum]) – Constants classes to #define.

For local includes wrap in double quotes includes='"header.h"' or leave as is includes='header.h'. For library includes use angle brackets includes='<stdint.h>'.

make()[source]

Reread sources and write to self.path.

write(path=<_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>)[source]

Reread sources and write to a file or stream.

class cslug.Types(path, *sources, headers=(), compact=True)[source]

Manages type information which is not found in a shared libraryAn executable file containing definitions (function/variables/...) but no main method..

  • Scans source code for:

    • The name, argument and return types of each function.

    • The name, field names, types and bit-field sizes of structures.

  • Stores the above in a portable and quickly deserializable json file.

  • Sets the types for the contents of a ctypes.CDLL.

__init__(path, *sources, headers=(), compact=True)[source]
Parameters

Note the distinction between sources and headers. A function prototype such as int foo(); will be ignored if found in sources but included if it were in headers. A true function definition such as int foo() {}, as well as structure definitions would be collected in either case.

apply(dll, strict=False)[source]

Set the type information for the contents of dll.

Parameters

For every structure in self.structs, turn it into a ctypes.Structure and set it as an attribute of dll. For every function in self.functions, get it from dll then set its argtypes and restype attributes.

Note

Structures don't normally go in shared librariesAn executable file containing definitions (function/variables/...) but no main method. but cslug lobs them in there for simplicity.

property functions: dict

All functions (from true definitions of prototypes).

The format is:

function_name: [return_type, [arg_type, arg_type, ...]]

All types are strings - either names of structures or attribute names of ctypes.

init_from_json()[source]

Initialise types by source code.

init_from_source()[source]

Initialise types by scanning source code.

json_path: Union[io.TextIOBase, pathlib.Path]

File to read or write the json.

make()[source]

Initialise from source then write to file.

property structs: dict

All structures defined using typedef struct {…} name.

The format is:

name: [(field_name, field_type), ...]

Or for bit-field structs:

name: [(field_name, field_type, bit_length), ...]
types: dict

All type information collected. This is broken out into functions and structs.

Note that this attribute is not set automatically. You must explicitly call either init_from_source or init_from_json before accessing.

write(path=<_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>)[source]

Serialise contents to path.

Parameters

path (str or os.PathLike or io.TextIOBase) – A filename or stream to write to. Defaults to sys.stdout.

cslug.ptr(bytes_like)[source]

Get the raw address of any Python object supporting the C buffer protocol.

A bytes-like object is anything for which calling memoryview(obj) doesn't raise a TypeError. This includes bytes, bytearray, memoryview, array.array and numpy.array. The output is a subclass of int.

>>> from cslug import ptr
>>> ptr(bytearray([10, 12, 20, 45]))
<Void Pointer 140533125025616>

Pass the output of this function to a C function which expects a pointer.

On calling this function on a buffer, the buffer's reference count is incremented to prevent it from being deleted whilst you're still using it. This reference count is decremented on deletion of the pointer returned by this function. With this in place, you generally shouldn't need to worry about holding onto or releasing memory. There is one exception to this: If you use pointer arithmetic, preducts of that arithmetic are just regular integers and do not carry the reference.

If you are using numpy then you should be aware that this method only accepts C-contiguous buffers. If you understand how contiguity works and have explicitly supported non-contiguous buffers in your C code then you may use nc_ptr instead. Otherwise convert your arrays to contiguous ones using either:

array = np.ascontiguousarray(array)

or:

array = np.asarray(array, order="C")
cslug.nc_ptr(bytes_like)[source]

Retrieve a pointer to a non-contiguous buffer.

Use with caution. If your function assumes a contiguous array but gets a non-contiguous one then you will either get garbage results or memory errors.

class cslug.PointerType(bytes_like, flags)[source]

Bases: int

A raw pointer which inc-refs the buffer it points to.

Please no not instantiate this class directly. Instead use the ptr() function.

cslug.cc(CC=None)[source]

Get the full path of the C compiler.

Parameters

CC – Value to override the CC environment variable.

Returns

Full path of the C compiler.

Return type

str

Raises

The C compiler is chosen by the CC environment variable.

  • If CC is unset then it defaults to gcc.

  • If CC is a name, such as gcc or clang, then it is searched for in PATH (respecting PATHEXT on Windows).

  • If CC is a relative path, such as ./gcc, then it is made absolute.

  • If CC is an absolute path then it is returned as is.

  • If CC is !block then an error is raised. This can be used to test your pre-built package works without a compiler.

Note

The value of CC should never be wrapped in quotes.

cslug.cc_version(CC=None)[source]

Get C compiler's name and version.

Parameters

CC – See cslug.cc.

Returns

A (name, version_info) pair.

Return type

(str, tuple[int])

The name is determined by parsing the output of $CC -v (or %CC% -v on Windows). It can be:

The version_info is in the standard (major, minor, micro) version format.