@@ -2644,64 +2644,85 @@ class buffer: pass
26442644class rwbuffer : pass
26452645class robuffer : pass
26462646
2647- @add_legacy_c_converter ('s#' , accept = {str , robuffer }, length = True )
2648- @add_legacy_c_converter ('y' , accept = {robuffer })
2649- @add_legacy_c_converter ('y#' , accept = {robuffer }, length = True )
2650- @add_legacy_c_converter ('z' , accept = {str , NoneType })
2651- @add_legacy_c_converter ('z#' , accept = {str , NoneType }, length = True )
2652- # add_legacy_c_converter not supported for es, es#, et, et#
2653- # because of their extra encoding argument
2647+ def str_converter_key (types , encoding , zeroes ):
2648+ return (frozenset (types ), bool (encoding ), bool (zeroes ))
2649+
2650+ str_converter_argument_map = {}
2651+
26542652class str_converter (CConverter ):
26552653 type = 'const char *'
26562654 default_type = (str , Null , NoneType )
26572655 format_unit = 's'
26582656
2659- def converter_init (self , * , encoding = None , accept = {str }, length = False , zeroes = False ):
2660-
2661- self .length = bool (length )
2657+ def converter_init (self , * , accept = {str }, encoding = None , zeroes = False ):
26622658
2663- is_b_or_ba = accept == {bytes , bytearray }
2664- is_b_or_ba_or_none = accept == {bytes , bytearray , NoneType }
2665- is_str = accept == {str }
2666- is_str_or_none = accept == {str , NoneType }
2667- is_robuffer = accept == {robuffer }
2668- is_str_or_robuffer = accept == {str , robuffer }
2669- is_str_or_robuffer_or_none = accept == {str , robuffer , NoneType }
2670-
2671- format_unit = None
2659+ key = str_converter_key (accept , encoding , zeroes )
2660+ format_unit = str_converter_argument_map .get (key )
2661+ if not format_unit :
2662+ fail ("str_converter: illegal combination of arguments" , key )
26722663
2664+ self .format_unit = format_unit
2665+ self .length = bool (zeroes )
26732666 if encoding :
2667+ if self .default not in (Null , None , unspecified ):
2668+ fail ("str_converter: Argument Clinic doesn't support default values for encoded strings" )
26742669 self .encoding = encoding
2670+ self .type = 'char *'
2671+ # sorry, clinic can't support preallocated buffers
2672+ # for es# and et#
2673+ self .c_default = "NULL"
26752674
2676- if is_str and not length and not zeroes :
2677- format_unit = 'es'
2678- elif is_str_or_none and length and zeroes :
2679- format_unit = 'es#'
2680- elif is_b_or_ba and not length and not zeroes :
2681- format_unit = 'et'
2682- elif is_b_or_ba_or_none and length and zeroes :
2683- format_unit = 'et#'
2684-
2685- else :
2686- if zeroes :
2687- fail ("str_converter: illegal combination of arguments (zeroes is only legal with an encoding)" )
2688-
2689- if is_str and not length :
2690- format_unit = 's'
2691- elif is_str_or_none and not length :
2692- format_unit = 'z'
2693- elif is_robuffer and not length :
2694- format_unit = 'y'
2695- elif is_robuffer and length :
2696- format_unit = 'y#'
2697- elif is_str_or_robuffer and length :
2698- format_unit = 's#'
2699- elif is_str_or_robuffer_or_none and length :
2700- format_unit = 'z#'
2675+ def cleanup (self ):
2676+ if self .encoding :
2677+ name = ensure_legal_c_identifier (self .name )
2678+ return "" .join (["if (" , name , ")\n PyMem_FREE(" , name , ");\n " ])
27012679
2702- if not format_unit :
2703- fail ("str_converter: illegal combination of arguments" )
2704- self .format_unit = format_unit
2680+ #
2681+ # This is the fourth or fifth rewrite of registering all the
2682+ # crazy string converter format units. Previous approaches hid
2683+ # bugs--generally mismatches between the semantics of the format
2684+ # unit and the arguments necessary to represent those semantics
2685+ # properly. Hopefully with this approach we'll get it 100% right.
2686+ #
2687+ # The r() function (short for "register") both registers the
2688+ # mapping from arguments to format unit *and* registers the
2689+ # legacy C converter for that format unit.
2690+ #
2691+ def r (format_unit , * , accept , encoding = False , zeroes = False ):
2692+ if not encoding and format_unit != 's' :
2693+ # add the legacy c converters here too.
2694+ #
2695+ # note: add_legacy_c_converter can't work for
2696+ # es, es#, et, or et#
2697+ # because of their extra encoding argument
2698+ #
2699+ # also don't add the converter for 's' because
2700+ # the metaclass for CConverter adds it for us.
2701+ kwargs = {}
2702+ if accept != {str }:
2703+ kwargs ['accept' ] = accept
2704+ if zeroes :
2705+ kwargs ['zeroes' ] = True
2706+ added_f = functools .partial (str_converter , ** kwargs )
2707+ legacy_converters [format_unit ] = added_f
2708+
2709+ d = str_converter_argument_map
2710+ key = str_converter_key (accept , encoding , zeroes )
2711+ if key in d :
2712+ sys .exit ("Duplicate keys specified for str_converter_argument_map!" )
2713+ d [key ] = format_unit
2714+
2715+ r ('es' , encoding = True , accept = {str })
2716+ r ('es#' , encoding = True , zeroes = True , accept = {str })
2717+ r ('et' , encoding = True , accept = {bytes , bytearray , str })
2718+ r ('et#' , encoding = True , zeroes = True , accept = {bytes , bytearray , str })
2719+ r ('s' , accept = {str })
2720+ r ('s#' , zeroes = True , accept = {robuffer , str })
2721+ r ('y' , accept = {robuffer })
2722+ r ('y#' , zeroes = True , accept = {robuffer })
2723+ r ('z' , accept = {str , NoneType })
2724+ r ('z#' , zeroes = True , accept = {robuffer , str , NoneType })
2725+ del r
27052726
27062727
27072728class PyBytesObject_converter (CConverter ):
@@ -2719,17 +2740,17 @@ class unicode_converter(CConverter):
27192740 default_type = (str , Null , NoneType )
27202741 format_unit = 'U'
27212742
2722- @add_legacy_c_converter ('u#' , length = True )
2743+ @add_legacy_c_converter ('u#' , zeroes = True )
27232744@add_legacy_c_converter ('Z' , accept = {str , NoneType })
2724- @add_legacy_c_converter ('Z#' , accept = {str , NoneType }, length = True )
2745+ @add_legacy_c_converter ('Z#' , accept = {str , NoneType }, zeroes = True )
27252746class Py_UNICODE_converter (CConverter ):
27262747 type = 'Py_UNICODE *'
27272748 default_type = (str , Null , NoneType )
27282749 format_unit = 'u'
27292750
2730- def converter_init (self , * , accept = {str }, length = False ):
2751+ def converter_init (self , * , accept = {str }, zeroes = False ):
27312752 format_unit = 'Z' if accept == {str , NoneType } else 'u'
2732- if length :
2753+ if zeroes :
27332754 format_unit += '#'
27342755 self .length = True
27352756 self .format_unit = format_unit
0 commit comments