Skip to content

Commit

Permalink
Docs: Argument Clinic: Improve 'How to write a custom converter'
Browse files Browse the repository at this point in the history
- Move the CConverter class reference to the reference
- Omit unneccesary wording and sentences
- Don't mention implementation details (no digression, explanation)
  • Loading branch information
erlend-aasland committed Jul 26, 2023
1 parent 507d8bc commit 6e2d0f5
Showing 1 changed file with 83 additions and 81 deletions.
164 changes: 83 additions & 81 deletions Doc/howto/clinic.rst
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,66 @@ The CLI supports the following options:
The list of files to process.


.. module:: clinic

.. class:: CConverter

The base class for all converters.
See :ref:`clinic-howto-custom-converter` for how to subclass this class.

.. attribute:: type

The C type to use for this variable.
:attr:`!type` should be a Python string specifying the type,
e.g. ``'int'``.
If this is a pointer type, the type string should end with ``' *'``.

.. attribute:: default

The Python default value for this parameter, as a Python value.
Or the magic value ``unspecified`` if there is no default.

.. attribute:: py_default

:attr:`!default` as it should appear in Python code,
as a string.
Or ``None`` if there is no default.

.. attribute:: c_default

:attr:`!default` as it should appear in C code,
as a string.
Or ``None`` if there is no default.

.. attribute:: c_ignored_default

The default value used to initialize the C variable when
there is no default, but not specifying a default may
result in an "uninitialized variable" warning. This can
easily happen when using option groups—although
properly written code will never actually use this value,
the variable does get passed in to the impl, and the
C compiler will complain about the "use" of the
uninitialized value. This value should always be a
non-empty string.

.. attribute:: converter

The name of the C converter function, as a string.

.. attribute:: impl_by_reference

A boolean value. If true,
Argument Clinic will add a ``&`` in front of the name of
the variable when passing it into the impl function.

.. attribute:: parse_by_reference

A boolean value. If true,
Argument Clinic will add a ``&`` in front of the name of
the variable when passing it into :c:func:`PyArg_ParseTuple`.


.. _clinic-tutorial:

Tutorial
Expand Down Expand Up @@ -1393,87 +1453,29 @@ state. Example from the ``setattro`` slot method in
See also :pep:`573`.


.. _clinic-howto-custom-converter:

How to write a custom converter
-------------------------------

As we hinted at in the previous section... you can write your own converters!
A converter is simply a Python class that inherits from :py:class:`!CConverter`.
The main purpose of a custom converter is if you have a parameter using
the ``O&`` format unit—parsing this parameter means calling
A converter is a Python class that inherits from :py:class:`CConverter`.
The main purpose of a custom converter, is for parameters parsed with
the ``O&`` format unit --- parsing such a parameter means calling
a :c:func:`PyArg_ParseTuple` "converter function".

Your converter class should be named ``*something*_converter``.
If the name follows this convention, then your converter class
will be automatically registered with Argument Clinic; its name
will be the name of your class with the ``_converter`` suffix
stripped off. (This is accomplished with a metaclass.)

You shouldn't subclass :py:meth:`!CConverter.__init__`. Instead, you should
write a :py:meth:`!converter_init` function. :py:meth:`!converter_init`
always accepts a *self* parameter; after that, all additional
parameters *must* be keyword-only. Any arguments passed in to
the converter in Argument Clinic will be passed along to your
:py:meth:`!converter_init`.

There are some additional members of :py:class:`!CConverter` you may wish
to specify in your subclass. Here's the current list:

.. module:: clinic

.. class:: CConverter

.. attribute:: type

The C type to use for this variable.
:attr:`!type` should be a Python string specifying the type,
e.g. ``'int'``.
If this is a pointer type, the type string should end with ``' *'``.

.. attribute:: default

The Python default value for this parameter, as a Python value.
Or the magic value ``unspecified`` if there is no default.

.. attribute:: py_default

:attr:`!default` as it should appear in Python code,
as a string.
Or ``None`` if there is no default.

.. attribute:: c_default

:attr:`!default` as it should appear in C code,
as a string.
Or ``None`` if there is no default.

.. attribute:: c_ignored_default

The default value used to initialize the C variable when
there is no default, but not specifying a default may
result in an "uninitialized variable" warning. This can
easily happen when using option groups—although
properly written code will never actually use this value,
the variable does get passed in to the impl, and the
C compiler will complain about the "use" of the
uninitialized value. This value should always be a
non-empty string.

.. attribute:: converter

The name of the C converter function, as a string.

.. attribute:: impl_by_reference

A boolean value. If true,
Argument Clinic will add a ``&`` in front of the name of
the variable when passing it into the impl function.

.. attribute:: parse_by_reference

A boolean value. If true,
Argument Clinic will add a ``&`` in front of the name of
the variable when passing it into :c:func:`PyArg_ParseTuple`.
Your converter class should be named :samp:`{ConverterName}_converter`.
By following this convention, your converter class will be automatically
registered with Argument Clinic, with its *converter name* being be the name of
your converter class with the ``_converter`` suffix stripped off.

Instead of subclassing :py:meth:`!CConverter.__init__`,
write a :py:meth:`!converter_init` method.
:py:meth:`!converter_init` always accepts a *self* parameter.
After *self*, all additional parameters **must** be keyword-only.
Any arguments passed to the converter in Argument Clinic
will be passed along to your :py:meth:`!converter_init` method.
See :py:class:`CConverter` for a list of members you may wish to specify in
your subclass.

Here's the simplest example of a custom converter, from :source:`Modules/zlibmodule.c`::

Expand All @@ -1486,16 +1488,16 @@ Here's the simplest example of a custom converter, from :source:`Modules/zlibmod
[python start generated code]*/
/*[python end generated code: output=da39a3ee5e6b4b0d input=35521e4e733823c7]*/

This block adds a converter to Argument Clinic named ``ssize_t``. Parameters
declared as ``ssize_t`` will be declared as type :c:type:`Py_ssize_t`, and will
be parsed by the ``'O&'`` format unit, which will call the
``ssize_t_converter`` converter function. ``ssize_t`` variables
automatically support default values.
This block adds a converter named ``ssize_t`` to Argument Clinic.
Parameters declared as ``ssize_t`` will be declared with type :c:type:`Py_ssize_t`,
and will be parsed by the ``'O&'`` format unit,
which will call the ``ssize_t_converter`` converter C function.
``ssize_t`` variables automatically support default values.

More sophisticated custom converters can insert custom C code to
handle initialization and cleanup.
You can see more examples of custom converters in the CPython
source tree; grep the C files for the string :py:class:`!CConverter`.
source tree; grep the C files for the string ``CConverter``.


How to write a custom return converter
Expand Down

0 comments on commit 6e2d0f5

Please sign in to comment.