Skip to content

Commit 7f90cba

Browse files
committed
Issue python#23935: Argument Clinic's understanding of format units
accepting bytes, bytearrays, and buffers is now consistent with both the documentation and the implementation.
1 parent 3b81248 commit 7f90cba

4 files changed

Lines changed: 59 additions & 66 deletions

File tree

‎Misc/NEWS‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ Tests
187187
Tools/Demos
188188
-----------
189189

190+
- Issue #23935: Argument Clinic's understanding of format units
191+
accepting bytes, bytearrays, and buffers is now consistent with
192+
both the documentation and the implementation.
193+
190194
- Issue #23944: Argument Clinic now wraps long impl prototypes at column 78.
191195

192196
- Issue #20586: Argument Clinic now ensures that functions without docstrings

‎Modules/_dbmmodule.c‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ dbm.dbm.get
272272
273273
self: dbmobject
274274
275-
key: str(length=True)
275+
key: str(types={'str', 'robuffer'}, length=True)
276276
default: object = None
277277
/
278278
@@ -282,7 +282,7 @@ Return the value for key if present, otherwise default.
282282
static PyObject *
283283
dbm_dbm_get_impl(dbmobject *dp, const char *key, Py_ssize_clean_t key_length,
284284
PyObject *default_value)
285-
/*[clinic end generated code: output=4f5c0e523eaf1251 input=aecf5efd2f2b1a3b]*/
285+
/*[clinic end generated code: output=4f5c0e523eaf1251 input=f81478bc211895ef]*/
286286
{
287287
datum dbm_key, val;
288288

‎Modules/arraymodule.c‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,7 +1600,7 @@ frombytes(arrayobject *self, Py_buffer *buffer)
16001600
/*[clinic input]
16011601
array.array.fromstring
16021602
1603-
buffer: Py_buffer(types='str bytes bytearray buffer')
1603+
buffer: Py_buffer(types={'str', 'buffer'})
16041604
/
16051605
16061606
Appends items from the string, interpreting it as an array of machine values, as if it had been read from a file using the fromfile() method).
@@ -1610,7 +1610,7 @@ This method is deprecated. Use frombytes instead.
16101610

16111611
static PyObject *
16121612
array_array_fromstring_impl(arrayobject *self, Py_buffer *buffer)
1613-
/*[clinic end generated code: output=31c4baa779df84ce input=1302d94c97696b84]*/
1613+
/*[clinic end generated code: output=31c4baa779df84ce input=fdde1a56cbe2b05b]*/
16141614
{
16151615
if (PyErr_WarnEx(PyExc_DeprecationWarning,
16161616
"fromstring() is deprecated. Use frombytes() instead.", 2) != 0)
@@ -1929,7 +1929,7 @@ make_array(PyTypeObject *arraytype, char typecode, PyObject *items)
19291929
array._array_reconstructor
19301930
19311931
arraytype: object(type="PyTypeObject *")
1932-
typecode: int(types='str')
1932+
typecode: int(types={'str'})
19331933
mformat_code: int(type="enum machine_format_code")
19341934
items: object
19351935
/
@@ -1942,7 +1942,7 @@ array__array_reconstructor_impl(PyModuleDef *module, PyTypeObject *arraytype,
19421942
int typecode,
19431943
enum machine_format_code mformat_code,
19441944
PyObject *items)
1945-
/*[clinic end generated code: output=6ecbf0e8e4d92ab9 input=f72492708c0a1d50]*/
1945+
/*[clinic end generated code: output=6ecbf0e8e4d92ab9 input=a9ae223306d7b262]*/
19461946
{
19471947
PyObject *converted_items;
19481948
PyObject *result;

‎Tools/clinic/clinic.py‎

Lines changed: 49 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2493,12 +2493,12 @@ def converter_init(self):
24932493

24942494
class char_converter(CConverter):
24952495
type = 'char'
2496-
default_type = str
2496+
default_type = (bytes, bytearray)
24972497
format_unit = 'c'
24982498
c_ignored_default = "'\0'"
24992499

25002500
def converter_init(self):
2501-
if isinstance(self.default, str) and (len(self.default) != 1):
2501+
if isinstance(self.default, self.default_type) and (len(self.default) != 1):
25022502
fail("char_converter: illegal default value " + repr(self.default))
25032503

25042504

@@ -2531,18 +2531,18 @@ def converter_init(self, *, bitwise=False):
25312531
if not bitwise:
25322532
fail("Unsigned shorts must be bitwise (for now).")
25332533

2534-
@add_legacy_c_converter('C', types='str')
2534+
@add_legacy_c_converter('C', types={'str'})
25352535
class int_converter(CConverter):
25362536
type = 'int'
25372537
default_type = int
25382538
format_unit = 'i'
25392539
c_ignored_default = "0"
25402540

2541-
def converter_init(self, *, types='int', type=None):
2542-
if types == 'str':
2541+
def converter_init(self, *, types={'int'}, type=None):
2542+
if types == {'str'}:
25432543
self.format_unit = 'C'
2544-
elif types != 'int':
2545-
fail("int_converter: illegal 'types' argument")
2544+
elif types != {'int'}:
2545+
fail("int_converter: illegal 'types' argument " + repr(types))
25462546
if type != None:
25472547
self.type = type
25482548

@@ -2633,63 +2633,64 @@ def converter_init(self, *, converter=None, type=None, subclass_of=None):
26332633
self.type = type
26342634

26352635

2636-
@add_legacy_c_converter('s#', length=True)
2637-
@add_legacy_c_converter('y', types="bytes")
2638-
@add_legacy_c_converter('y#', types="bytes", length=True)
2636+
#
2637+
# We define three string conventions for buffer types in the 'types' argument:
2638+
# 'buffer' : any object supporting the buffer interface
2639+
# 'rwbuffer': any object supporting the buffer interface, but must be writeable
2640+
# 'robuffer': any object supporting the buffer interface, but must not be writeable
2641+
#
2642+
2643+
@add_legacy_c_converter('s#', types={"str", "robuffer"}, length=True)
2644+
@add_legacy_c_converter('y', types={"robuffer"})
2645+
@add_legacy_c_converter('y#', types={"robuffer"}, length=True)
26392646
@add_legacy_c_converter('z', nullable=True)
2640-
@add_legacy_c_converter('z#', nullable=True, length=True)
2647+
@add_legacy_c_converter('z#', types={"str", "robuffer"}, nullable=True, length=True)
2648+
# add_legacy_c_converter not supported for es, es#, et, et#
2649+
# because of their extra encoding argument
26412650
class str_converter(CConverter):
26422651
type = 'const char *'
26432652
default_type = (str, Null, NoneType)
26442653
format_unit = 's'
26452654

2646-
def converter_init(self, *, encoding=None, types="str",
2655+
def converter_init(self, *, encoding=None, types={"str"},
26472656
length=False, nullable=False, zeroes=False):
26482657

2649-
types = set(types.strip().split())
2650-
bytes_type = {"bytes"}
2651-
str_type = {"str"}
2652-
all_3_type = {"bytearray"} | bytes_type | str_type
2653-
is_bytes = types == bytes_type
2654-
is_str = types == str_type
2655-
is_all_3 = types == all_3_type
2656-
26572658
self.length = bool(length)
2659+
2660+
is_b_or_ba = types == {"bytes", "bytearray"}
2661+
is_str = types == {"str"}
2662+
is_robuffer = types == {"robuffer"}
2663+
is_str_or_robuffer = types == {"str", "robuffer"}
2664+
26582665
format_unit = None
26592666

26602667
if encoding:
26612668
self.encoding = encoding
26622669

2663-
if is_str and not (length or zeroes or nullable):
2670+
if is_str and not length and not zeroes and not nullable:
26642671
format_unit = 'es'
2665-
elif is_all_3 and not (length or zeroes or nullable):
2666-
format_unit = 'et'
2667-
elif is_str and length and zeroes and not nullable:
2672+
elif is_str and length and zeroes and nullable:
26682673
format_unit = 'es#'
2669-
elif is_all_3 and length and not (nullable or zeroes):
2674+
elif is_b_or_ba and not length and not zeroes and not nullable:
2675+
format_unit = 'et'
2676+
elif is_b_or_ba and length and zeroes and nullable:
26702677
format_unit = 'et#'
26712678

2672-
if format_unit.endswith('#'):
2673-
fail("Sorry: code using format unit ", repr(format_unit), "probably doesn't work properly yet.\nGive Larry your test case and he'll it.")
2674-
# TODO set pointer to NULL
2675-
# TODO add cleanup for buffer
2676-
pass
2677-
26782679
else:
26792680
if zeroes:
26802681
fail("str_converter: illegal combination of arguments (zeroes is only legal with an encoding)")
26812682

2682-
if is_bytes and not (nullable or length):
2683+
if is_str and not length and not nullable:
2684+
format_unit = 's'
2685+
elif is_str and not length and nullable:
2686+
format_unit = 'z'
2687+
elif is_robuffer and not length and not nullable:
26832688
format_unit = 'y'
2684-
elif is_bytes and length and not nullable:
2689+
elif is_robuffer and length and not nullable:
26852690
format_unit = 'y#'
2686-
elif is_str and not (nullable or length):
2687-
format_unit = 's'
2688-
elif is_str and length and not nullable:
2691+
elif is_str_or_robuffer and length and not nullable:
26892692
format_unit = 's#'
2690-
elif is_str and nullable and not length:
2691-
format_unit = 'z'
2692-
elif is_str and nullable and length:
2693+
elif is_str_or_robuffer and length and nullable:
26932694
format_unit = 'z#'
26942695

26952696
if not format_unit:
@@ -2700,10 +2701,12 @@ def converter_init(self, *, encoding=None, types="str",
27002701
class PyBytesObject_converter(CConverter):
27012702
type = 'PyBytesObject *'
27022703
format_unit = 'S'
2704+
# types = {'bytes'}
27032705

27042706
class PyByteArrayObject_converter(CConverter):
27052707
type = 'PyByteArrayObject *'
27062708
format_unit = 'Y'
2709+
# types = {'bytearray'}
27072710

27082711
class unicode_converter(CConverter):
27092712
type = 'PyObject *'
@@ -2725,43 +2728,29 @@ def converter_init(self, *, nullable=False, length=False):
27252728
self.length = True
27262729
self.format_unit = format_unit
27272730

2728-
#
2729-
# We define three string conventions for buffer types in the 'types' argument:
2730-
# 'buffer' : any object supporting the buffer interface
2731-
# 'rwbuffer': any object supporting the buffer interface, but must be writeable
2732-
# 'robuffer': any object supporting the buffer interface, but must not be writeable
2733-
#
2734-
@add_legacy_c_converter('s*', types='str bytes bytearray buffer')
2735-
@add_legacy_c_converter('z*', types='str bytes bytearray buffer', nullable=True)
2736-
@add_legacy_c_converter('w*', types='bytearray rwbuffer')
2731+
@add_legacy_c_converter('s*', types={'str', 'buffer'})
2732+
@add_legacy_c_converter('z*', types={'str', 'buffer'}, nullable=True)
2733+
@add_legacy_c_converter('w*', types={'rwbuffer'})
27372734
class Py_buffer_converter(CConverter):
27382735
type = 'Py_buffer'
27392736
format_unit = 'y*'
27402737
impl_by_reference = True
27412738
c_ignored_default = "{NULL, NULL}"
27422739

2743-
def converter_init(self, *, types='bytes bytearray buffer', nullable=False):
2740+
def converter_init(self, *, types={'buffer'}, nullable=False):
27442741
if self.default not in (unspecified, None):
27452742
fail("The only legal default value for Py_buffer is None.")
27462743
self.c_default = self.c_ignored_default
2747-
types = set(types.strip().split())
2748-
bytes_type = {'bytes'}
2749-
bytearray_type = {'bytearray'}
2750-
buffer_type = {'buffer'}
2751-
rwbuffer_type = {'rwbuffer'}
2752-
robuffer_type = {'robuffer'}
2753-
str_type = {'str'}
2754-
bytes_bytearray_buffer_type = bytes_type | bytearray_type | buffer_type
27552744

27562745
format_unit = None
2757-
if types == (str_type | bytes_bytearray_buffer_type):
2746+
if types == {'str', 'buffer'}:
27582747
format_unit = 's*' if not nullable else 'z*'
27592748
else:
27602749
if nullable:
27612750
fail('Py_buffer_converter: illegal combination of arguments (nullable=True)')
2762-
elif types == (bytes_bytearray_buffer_type):
2751+
elif types == {'buffer'}:
27632752
format_unit = 'y*'
2764-
elif types == (bytearray_type | rwbuffer_type):
2753+
elif types == {'rwbuffer'}:
27652754
format_unit = 'w*'
27662755
if not format_unit:
27672756
fail("Py_buffer_converter: illegal combination of arguments")

0 commit comments

Comments
 (0)