.. _toml_options: Using a TOML configuration file ******************************** A TOML configuration file can optionally be provided to ``clair-c2py`` to customize the generated Python bindings. It has the same name as the module, with a ``.toml`` extension. If this configuration file is absent, clair uses default options. Generating a template TOML file -------------------------------- The command .. code-block:: bash clair-c2py --gen-default-config my_module.cpp generates a ``my_module.toml`` file with default configurations. You can then edit this file to customize the behavior, and provide it with your sources. A basic documentation of each option is provided as comments in the generated file. TOML Configuration Options -------------------------- .. list-table:: :widths: 30 70 :header-rows: 1 * - Option - Description * - ``package_name`` - Name of the package (optional). Module name will be ``package_name.module_filename``. * - ``documentation`` - The documentation string of the module. * - ``namespaces`` - Space-separated list of namespaces (e.g. ``"A A::B"``). If non-empty, only elements declared in exactly these namespaces are wrapped. * - ``match_names`` - **Regex** pattern to match element names. Only elements whose fully qualified name matches are wrapped. * - ``reject_names`` - **Regex** pattern to reject element names. Elements whose fully qualified name matches are excluded. * - ``match_files`` - **Regex** pattern to filter by source file. Only elements declared in matching files are wrapped. * - ``wrap_no_arg_methods_as_properties`` - If ``true``, methods with no arguments are wrapped as read-only properties. Default: ``false``. * - ``exclude_system_headers`` - If ``true`` (default), elements declared in system headers (``-isystem``) are ignored. .. warning:: | Beware of **regular expressions** (Regex), they are not simple strings. | For example, ``N::*`` does not match ``N::f``, while ``N::.*`` does ! | Also note that the qualified name starts with ``::``. So to match the start of a name, use ``^::N::f``. Example ------- We consider a simple module ``my_module.cpp`` which wraps some functions and classes from two files and two namespaces. .. literalinclude:: ../../examples/toml/foo.hpp :language: cpp .. literalinclude:: ../../examples/toml/bar.hpp :language: cpp .. literalinclude:: ../../examples/toml/my_module.cpp :language: cpp The initial TOML file looks as follows: .. literalinclude:: ../../examples/toml/initial.toml :language: toml After generating the Python bindings and compiling, we obtain: .. code-block:: python >>> import my_module >>> dir(my_module) ['A', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f1', 'f2', 'g1', 'g2', 'h1', 'h2'] >>> print(my_module.__doc__) This is an awesome module generated by c2py/clair. Filtering by namespace ====================== | We can restrict the wrapping to only functions declared in specific namespaces using the ``namespaces`` option. | For example, to only wrap functions in the ``foo`` namespace, we modify the TOML file as follows: .. code-block:: toml # List of namespaces as a space separated string e.g. "A A::B" # If non-empty, only the elements declared in EXACTLY these namespaces are wrapped. # NB e.g. in previous example A::detail will be ignored, whatever match_names. namespaces = "foo" Now only the names in the ``foo`` namespace are available in Python: .. code-block:: python >>> import my_module >>> dir(my_module) ['A', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f1', 'f2', 'g1', 'g2'] Filtering by name ================= | A more fine-grained filtering can be achieved using the ``match_names`` and ``reject_names`` options. | For example, to further restrict the wrapping to only functions whose name starts with ``f``, we modify the TOML file as follows: .. code-block:: toml # Match/reject elements according to a regex (LLVM regex) # If match_names/match_names are both set, the algorithm is # for each element X of qualified name Qname: # if match_names and not match_names matches Qname : skip X # if reject_names and reject_names matches Qname : skip X # else keep X. # match_names = "foo::f.*" # reject_names = "" Now only the functions ``f1`` and ``f2`` are available in Python: .. code-block:: python >>> import my_module >>> dir(my_module) ['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f1', 'f2'] To only wrap the function ``f1``, we can for example reject ``foo::f2`` explicitly: .. code-block:: toml # Match/reject elements according to a regex (LLVM regex) # If match_names/match_names are both set, the algorithm is # for each element X of qualified name Qname: # if match_names and not match_names matches Qname : skip X # if reject_names and reject_names matches Qname : skip X # else keep X. # match_names = "foo::f.*" reject_names = "foo::f2" This results in only ``f1`` being available in Python: .. code-block:: python >>> import my_module >>> dir(my_module) ['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f1'] Filter by file name =================== Finally, we can restrict the wrapping to only elements declared in files matching a given regex using the ``match_files`` option. Let's start again from our initial TOML file without any filtering. To only wrap functions declared in files whose name contains ``foo``, we modify the TOML file as follows: .. code-block:: toml # Only keep elements declared in files matching the pattern match_files = ".*foo.*" # String must be a regex We obtain: .. code-block:: python >>> import my_module >>> dir(my_module) ['A', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f1', 'f2', 'g1', 'g2'] Wrap no argument methods as properties ====================================== | If ``wrap_no_arg_methods_as_properties`` is set to ``true``, all methods of wrapped classes without argument are wrapped as Python properties. | | For example, our class ``A`` defined in ``foo.hpp`` has the getter method ``A::x()`` with no argument. | By default, this method is wrapped as a normal method in Python: .. code-block:: python >>> import my_module >>> a = my_module.A() >>> a.x() 0 >>> a.set_x(5) >>> a.x() 5 >>> a.x To wrap it as a property instead, we modify the TOML file as follows: .. code-block:: toml # If true, all methods with no argument are wrapped as properties wrap_no_arg_methods_as_properties = true Now the method ``A::x()`` is wrapped as a read-only property in Python: .. code-block:: python >>> import my_module >>> a = my_module.A() >>> a.x 0 >>> a.set_x(5) >>> a.x 5