Skip to content

Commit 1d0b93f

Browse files
larryhastingserlend-aasland
authored andcommitted
including correctly generating code for Clinic blocks inside C
preprocessor conditional blocks. GitHub-Issue-Link: python/cpython#64655
1 parent 96aca64 commit 1d0b93f

File tree

1 file changed

+100
-72
lines changed

1 file changed

+100
-72
lines changed

Doc/howto/clinic.rst

Lines changed: 100 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -561,8 +561,8 @@ in ``Lib/inspect.py``.
561561
to allow full expressions like ``CONSTANT - 1``.)
562562

563563

564-
Renaming the C functions generated by Argument Clinic
565-
-----------------------------------------------------
564+
Renaming the C functions and variables generated by Argument Clinic
565+
-------------------------------------------------------------------
566566

567567
Argument Clinic automatically names the functions it generates for you.
568568
Occasionally this may cause a problem, if the generated name collides with
@@ -584,6 +584,25 @@ The base function would now be named ``pickler_dumper()``,
584584
and the impl function would now be named ``pickler_dumper_impl()``.
585585

586586

587+
Similarly, you may have a problem where you want to give a parameter
588+
a specific Python name, but that name may be inconvenient in C. Argument
589+
Clinic allows you to give a parameter different names in Python and in C,
590+
using the same ``"as"`` syntax::
591+
592+
/*[clinic input]
593+
pickle.Pickler.dump
594+
595+
obj: object
596+
file as file_obj: object
597+
protocol: object = NULL
598+
*
599+
fix_imports: bool = True
600+
601+
Here, the name used in Python (in the signature and the ``keywords``
602+
array) would be ``file``, but the C variable would be named ``file_obj``.
603+
604+
You can use this to rename the ``self`` parameter too!
605+
587606

588607
Converting functions using PyArg_UnpackTuple
589608
--------------------------------------------
@@ -1308,74 +1327,6 @@ them ``__new__`` or ``__init__`` as appropriate. Notes:
13081327
(If your function doesn't support keywords, the parsing function
13091328
generated will throw an exception if it receives any.)
13101329

1311-
The #ifdef trick
1312-
----------------------------------------------
1313-
1314-
If you're converting a function that isn't available on all platforms,
1315-
there's a trick you can use to make life a little easier. The existing
1316-
code probably looks like this::
1317-
1318-
#ifdef HAVE_FUNCTIONNAME
1319-
static module_functionname(...)
1320-
{
1321-
...
1322-
}
1323-
#endif /* HAVE_FUNCTIONNAME */
1324-
1325-
And then in the ``PyMethodDef`` structure at the bottom you'll have::
1326-
1327-
#ifdef HAVE_FUNCTIONNAME
1328-
{'functionname', ... },
1329-
#endif /* HAVE_FUNCTIONNAME */
1330-
1331-
In this scenario, you should change the code to look like the following::
1332-
1333-
#ifdef HAVE_FUNCTIONNAME
1334-
/*[clinic input]
1335-
module.functionname
1336-
...
1337-
[clinic start generated code]*/
1338-
static module_functionname(...)
1339-
{
1340-
...
1341-
}
1342-
#endif /* HAVE_FUNCTIONNAME */
1343-
1344-
Run Argument Clinic on the code in this state, then refresh the file in
1345-
your editor. Now you'll have the generated code, including the #define
1346-
for the ``PyMethodDef``, like so::
1347-
1348-
#ifdef HAVE_FUNCTIONNAME
1349-
/*[clinic input]
1350-
...
1351-
[clinic start generated code]*/
1352-
...
1353-
#define MODULE_FUNCTIONNAME \
1354-
{'functionname', ... },
1355-
...
1356-
/*[clinic end generated code: checksum=...]*/
1357-
static module_functionname(...)
1358-
{
1359-
...
1360-
}
1361-
#endif /* HAVE_FUNCTIONNAME */
1362-
1363-
Change the #endif at the bottom as follows::
1364-
1365-
#else
1366-
#define MODULE_FUNCTIONNAME
1367-
#endif /* HAVE_FUNCTIONNAME */
1368-
1369-
Now you can remove the #ifdefs around the ``PyMethodDef`` structure
1370-
at the end, and replace those three lines with ``MODULE_FUNCTIONNAME``.
1371-
If the function is available, the macro turns into the ``PyMethodDef``
1372-
static value, including the trailing comma; if the function isn't
1373-
available, the macro turns into nothing. Perfect!
1374-
1375-
(This is the preferred approach for optional functions; in the future,
1376-
Argument Clinic may generate the entire ``PyMethodDef`` structure.)
1377-
1378-
13791330
Changing and redirecting Clinic's output
13801331
----------------------------------------
13811332

@@ -1491,8 +1442,9 @@ previous configuration.
14911442
``output preset`` sets Clinic's output to one of several built-in
14921443
preset configurations, as follows:
14931444

1494-
``original``
1495-
Clinic's starting configuration.
1445+
``block``
1446+
Clinic's original starting configuration. Writes everything
1447+
immediately after the input block.
14961448

14971449
Suppress the ``parser_prototype``
14981450
and ``docstring_prototype``, write everything else to ``block``.
@@ -1640,6 +1592,82 @@ it in a Clinic block lets Clinic use its existing checksum functionality to ensu
16401592
the file was not modified by hand before it gets overwritten.
16411593

16421594

1595+
The #ifdef trick
1596+
----------------------------------------------
1597+
1598+
If you're converting a function that isn't available on all platforms,
1599+
there's a trick you can use to make life a little easier. The existing
1600+
code probably looks like this::
1601+
1602+
#ifdef HAVE_FUNCTIONNAME
1603+
static module_functionname(...)
1604+
{
1605+
...
1606+
}
1607+
#endif /* HAVE_FUNCTIONNAME */
1608+
1609+
And then in the ``PyMethodDef`` structure at the bottom the existing code
1610+
will have::
1611+
1612+
#ifdef HAVE_FUNCTIONNAME
1613+
{'functionname', ... },
1614+
#endif /* HAVE_FUNCTIONNAME */
1615+
1616+
In this scenario, you should enclose the body of your impl function inside the ``#ifdef``,
1617+
like so::
1618+
1619+
#ifdef HAVE_FUNCTIONNAME
1620+
/*[clinic input]
1621+
module.functionname
1622+
...
1623+
[clinic start generated code]*/
1624+
static module_functionname(...)
1625+
{
1626+
...
1627+
}
1628+
#endif /* HAVE_FUNCTIONNAME */
1629+
1630+
Then, remove those three lines from the ``PyMethodDef`` structure,
1631+
replacing them with the macro Argument Clinic generated::
1632+
1633+
MODULE_FUNCTIONNAME_METHODDEF
1634+
1635+
(You can find the real name for this macro inside the generated code.
1636+
Or you can calculate it yourself: it's the name of your function as defined
1637+
on the first line of your block, but with periods changed to underscores,
1638+
uppercased, and ``"_METHODDEF"`` added to the end.)
1639+
1640+
Perhaps you're wondering: what if ``HAVE_FUNCTIONNAME`` isn't defined?
1641+
The ``MODULE_FUNCTIONNAME_METHODDEF`` macro won't be defined either!
1642+
1643+
Here's where Argument Clinic gets very clever. It actually detects that the
1644+
Argument Clinic block might be deactivated by the ``#ifdef``. When that
1645+
happens, it generates a little extra code that looks like this::
1646+
1647+
#ifndef MODULE_FUNCTIONNAME_METHODDEF
1648+
#define MODULE_FUNCTIONNAME_METHODDEF
1649+
#endif /* !defined(MODULE_FUNCTIONNAME_METHODDEF) */
1650+
1651+
That means the macro always works. If the function is defined, this turns
1652+
into the correct structure, including the trailing comma. If the function is
1653+
undefined, this turns into nothing.
1654+
1655+
However, this causes one ticklish problem: where should Argument Clinic put this
1656+
extra code when using the "block" output preset? It can't go in the output block,
1657+
because that could be decativated by the ``#ifdef``. (That's the whole point!)
1658+
1659+
In this situation, Argument Clinic writes the extra code to the "buffer" destination.
1660+
This may mean that you get a complaint from Argument Clinic::
1661+
1662+
Warning in file "Modules/posixmodule.c" on line 12357:
1663+
Destination buffer 'buffer' not empty at end of file, emptying.
1664+
1665+
When this happens, just open your file, find the ``dump buffer`` block that
1666+
Argument Clinic added to your file (it'll be at the very bottom), then
1667+
move it above the ``PyMethodDef`` structure where that macro is used.
1668+
1669+
1670+
16431671
Using Argument Clinic in Python files
16441672
-------------------------------------
16451673

0 commit comments

Comments
 (0)