Commit 6bc09154 authored by Larry Hastings's avatar Larry Hastings

Issue #20141: Improved Argument Clinic's support for the PyArg_Parse "O!"

format unit.
parent 1e22aea6
...@@ -640,7 +640,7 @@ on the right is the text you'd replace it with. ...@@ -640,7 +640,7 @@ on the right is the text you'd replace it with.
``'K'`` ``unsigned_PY_LONG_LONG`` ``'K'`` ``unsigned_PY_LONG_LONG``
``'L'`` ``PY_LONG_LONG`` ``'L'`` ``PY_LONG_LONG``
``'n'`` ``Py_ssize_t`` ``'n'`` ``Py_ssize_t``
``'O!'`` ``object(type='name_of_Python_type')`` ``'O!'`` ``object(subclass_of='&PySomething_Type')``
``'O&'`` ``object(converter='name_of_c_function')`` ``'O&'`` ``object(converter='name_of_c_function')``
``'O'`` ``object`` ``'O'`` ``object``
``'p'`` ``bool`` ``'p'`` ``bool``
...@@ -693,20 +693,22 @@ conversion functions, or types, or strings specifying an encoding. ...@@ -693,20 +693,22 @@ conversion functions, or types, or strings specifying an encoding.
(But "legacy converters" don't support arguments. That's why we (But "legacy converters" don't support arguments. That's why we
skipped them for your first function.) The argument you specified skipped them for your first function.) The argument you specified
to the format unit is now an argument to the converter; this to the format unit is now an argument to the converter; this
argument is either ``converter`` (for ``O&``), ``type`` (for ``O!``), argument is either ``converter`` (for ``O&``), ``subclass_of`` (for ``O!``),
or ``encoding`` (for all the format units that start with ``e``). or ``encoding`` (for all the format units that start with ``e``).
Note that ``object()`` must explicitly support each Python type you specify When using ``subclass_of``, you may also want to use the other
for the ``type`` argument. Currently it only supports ``str``. It should be custom argument for ``object()``: ``type``, which lets you set the type
easy to add more, just edit ``Tools/clinic/clinic.py``, search for ``O!`` in actually used for the parameter. For example, if you want to ensure
the text, and add more entries to the dict mapping types to strings just above it. that the object is a subclass of ``PyUnicode_Type``, you probably want
to use the converter ``object(type='PyUnicodeObject *', subclass_of='&PyUnicode_Type')``.
Note also that this approach takes away some possible flexibility for the format One possible problem with using Argument Clinic: it takes away some possible
units starting with ``e``. It used to be possible to decide at runtime what flexibility for the format units starting with ``e``. When writing a
``PyArg_Parse`` call by hand, you could theoretically decide at runtime what
encoding string to pass in to :c:func:`PyArg_ParseTuple`. But now this string must encoding string to pass in to :c:func:`PyArg_ParseTuple`. But now this string must
be hard-coded at compile-time. This limitation is deliberate; it made supporting be hard-coded at Argument-Clinic-preprocessing-time. This limitation is deliberate;
this format unit much easier, and may allow for future compile-time optimizations. it made supporting this format unit much easier, and may allow for future optimizations.
This restriction does not seem unreasonable; CPython itself always passes in static This restriction doesn't seem unreasonable; CPython itself always passes in static
hard-coded encoding strings for parameters whose format units start with ``e``. hard-coded encoding strings for parameters whose format units start with ``e``.
...@@ -796,7 +798,8 @@ block, and ensure that its converter is an instance of ...@@ -796,7 +798,8 @@ block, and ensure that its converter is an instance of
``self_converter`` or a subclass thereof. ``self_converter`` or a subclass thereof.
What's the point? This lets you automatically cast ``self`` What's the point? This lets you automatically cast ``self``
from ``PyObject *`` to a custom type. from ``PyObject *`` to a custom type, just like ``object()``
does with its ``type`` parameter.
How do you specify the custom type you want to cast ``self`` to? How do you specify the custom type you want to cast ``self`` to?
If you only have one or two functions with the same type for ``self``, If you only have one or two functions with the same type for ``self``,
......
...@@ -21,6 +21,9 @@ Library ...@@ -21,6 +21,9 @@ Library
Tools/Demos Tools/Demos
----------- -----------
- Issue #20141: Improved Argument Clinic's support for the PyArg_Parse "O!"
format unit.
- Issue #20144: Argument Clinic now supports simple symbolic constants - Issue #20144: Argument Clinic now supports simple symbolic constants
as parameter default values. as parameter default values.
......
...@@ -117,7 +117,7 @@ static Py_UCS4 getuchar(PyUnicodeObject *obj) ...@@ -117,7 +117,7 @@ static Py_UCS4 getuchar(PyUnicodeObject *obj)
unicodedata.UCD.decimal unicodedata.UCD.decimal
unichr: object(type='str') unichr: object(type='PyUnicodeObject *', subclass_of='&PyUnicode_Type')
default: object=NULL default: object=NULL
/ /
...@@ -140,13 +140,13 @@ PyDoc_STRVAR(unicodedata_UCD_decimal__doc__, ...@@ -140,13 +140,13 @@ PyDoc_STRVAR(unicodedata_UCD_decimal__doc__,
{"decimal", (PyCFunction)unicodedata_UCD_decimal, METH_VARARGS, unicodedata_UCD_decimal__doc__}, {"decimal", (PyCFunction)unicodedata_UCD_decimal, METH_VARARGS, unicodedata_UCD_decimal__doc__},
static PyObject * static PyObject *
unicodedata_UCD_decimal_impl(PyObject *self, PyObject *unichr, PyObject *default_value); unicodedata_UCD_decimal_impl(PyObject *self, PyUnicodeObject *unichr, PyObject *default_value);
static PyObject * static PyObject *
unicodedata_UCD_decimal(PyObject *self, PyObject *args) unicodedata_UCD_decimal(PyObject *self, PyObject *args)
{ {
PyObject *return_value = NULL; PyObject *return_value = NULL;
PyObject *unichr; PyUnicodeObject *unichr;
PyObject *default_value = NULL; PyObject *default_value = NULL;
if (!PyArg_ParseTuple(args, if (!PyArg_ParseTuple(args,
...@@ -160,8 +160,8 @@ exit: ...@@ -160,8 +160,8 @@ exit:
} }
static PyObject * static PyObject *
unicodedata_UCD_decimal_impl(PyObject *self, PyObject *unichr, PyObject *default_value) unicodedata_UCD_decimal_impl(PyObject *self, PyUnicodeObject *unichr, PyObject *default_value)
/*[clinic checksum: 9576fa55f4ea0be82968af39dc9d0283e634beeb]*/ /*[clinic checksum: 73edde0e9cd5913ea174c4fa81504369761b7426]*/
{ {
PyUnicodeObject *v = (PyUnicodeObject *)unichr; PyUnicodeObject *v = (PyUnicodeObject *)unichr;
int have_old = 0; int have_old = 0;
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -1358,6 +1358,12 @@ class CConverter(metaclass=CConverterAutoRegister): ...@@ -1358,6 +1358,12 @@ class CConverter(metaclass=CConverterAutoRegister):
# by format units starting with 'e'. # by format units starting with 'e'.
encoding = None encoding = None
# Should this object be required to be a subclass of a specific type?
# If not None, should be a string representing a pointer to a
# PyTypeObject (e.g. "&PyUnicode_Type").
# Only used by the 'O!' format unit (and the "object" converter).
subclass_of = None
# Do we want an adjacent '_length' variable for this variable? # Do we want an adjacent '_length' variable for this variable?
# Only used by format units ending with '#'. # Only used by format units ending with '#'.
length = False length = False
...@@ -1446,7 +1452,9 @@ class CConverter(metaclass=CConverterAutoRegister): ...@@ -1446,7 +1452,9 @@ class CConverter(metaclass=CConverterAutoRegister):
list.append(self.converter) list.append(self.converter)
if self.encoding: if self.encoding:
list.append(self.encoding) list.append(c_repr(self.encoding))
elif self.subclass_of:
list.append(self.subclass_of)
legal_name = ensure_legal_c_identifier(self.name) legal_name = ensure_legal_c_identifier(self.name)
s = ("&" if self.parse_by_reference else "") + legal_name s = ("&" if self.parse_by_reference else "") + legal_name
...@@ -1627,20 +1635,12 @@ class object_converter(CConverter): ...@@ -1627,20 +1635,12 @@ class object_converter(CConverter):
type = 'PyObject *' type = 'PyObject *'
format_unit = 'O' format_unit = 'O'
def converter_init(self, *, type=None): def converter_init(self, *, type=None, subclass_of=None):
if type: if subclass_of:
assert isinstance(type, str)
assert type.isidentifier()
try:
type = eval(type)
# need more of these!
type = {
str: '&PyUnicode_Type',
}[type]
except NameError:
type = type
self.format_unit = 'O!' self.format_unit = 'O!'
self.encoding = type self.subclass_of = subclass_of
if type is not None:
self.type = type
@add_legacy_c_converter('s#', length=True) @add_legacy_c_converter('s#', length=True)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment