diff options
Diffstat (limited to 'Tools/clinic/clinic.py')
-rwxr-xr-x | Tools/clinic/clinic.py | 142 |
1 files changed, 90 insertions, 52 deletions
diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 5626dd9..4f0615f 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -26,8 +26,12 @@ import sys import tempfile import textwrap import traceback +import types import uuid +from types import * +NoneType = type(None) + # TODO: # # soon: @@ -2526,18 +2530,18 @@ class unsigned_short_converter(CConverter): if not bitwise: fail("Unsigned shorts must be bitwise (for now).") -@add_legacy_c_converter('C', types={'str'}) +@add_legacy_c_converter('C', accept={str}) class int_converter(CConverter): type = 'int' default_type = int format_unit = 'i' c_ignored_default = "0" - def converter_init(self, *, types={'int'}, type=None): - if types == {'str'}: + def converter_init(self, *, accept={int}, type=None): + if accept == {str}: self.format_unit = 'C' - elif types != {'int'}: - fail("int_converter: illegal 'types' argument " + repr(types)) + elif accept != {int}: + fail("int_converter: illegal 'accept' argument " + repr(accept)) if type != None: self.type = type @@ -2629,17 +2633,22 @@ class object_converter(CConverter): # -# We define three string conventions for buffer types in the 'types' argument: -# 'buffer' : any object supporting the buffer interface -# 'rwbuffer': any object supporting the buffer interface, but must be writeable -# 'robuffer': any object supporting the buffer interface, but must not be writeable +# We define three conventions for buffer types in the 'accept' argument: +# +# buffer : any object supporting the buffer interface +# rwbuffer: any object supporting the buffer interface, but must be writeable +# robuffer: any object supporting the buffer interface, but must not be writeable # -@add_legacy_c_converter('s#', types={"str", "robuffer"}, length=True) -@add_legacy_c_converter('y', types={"robuffer"}) -@add_legacy_c_converter('y#', types={"robuffer"}, length=True) -@add_legacy_c_converter('z', nullable=True) -@add_legacy_c_converter('z#', types={"str", "robuffer"}, nullable=True, length=True) +class buffer: pass +class rwbuffer: pass +class robuffer: pass + +@add_legacy_c_converter('s#', accept={str, robuffer}, length=True) +@add_legacy_c_converter('y', accept={robuffer}) +@add_legacy_c_converter('y#', accept={robuffer}, length=True) +@add_legacy_c_converter('z', accept={str, NoneType}) +@add_legacy_c_converter('z#', accept={str, NoneType}, length=True) # add_legacy_c_converter not supported for es, es#, et, et# # because of their extra encoding argument class str_converter(CConverter): @@ -2647,45 +2656,47 @@ class str_converter(CConverter): default_type = (str, Null, NoneType) format_unit = 's' - def converter_init(self, *, encoding=None, types={"str"}, - length=False, nullable=False, zeroes=False): + def converter_init(self, *, encoding=None, accept={str}, length=False, zeroes=False): self.length = bool(length) - is_b_or_ba = types == {"bytes", "bytearray"} - is_str = types == {"str"} - is_robuffer = types == {"robuffer"} - is_str_or_robuffer = types == {"str", "robuffer"} + is_b_or_ba = accept == {bytes, bytearray} + is_b_or_ba_or_none = accept == {bytes, bytearray, NoneType} + is_str = accept == {str} + is_str_or_none = accept == {str, NoneType} + is_robuffer = accept == {robuffer} + is_str_or_robuffer = accept == {str, robuffer} + is_str_or_robuffer_or_none = accept == {str, robuffer, NoneType} format_unit = None if encoding: self.encoding = encoding - if is_str and not length and not zeroes and not nullable: + if is_str and not length and not zeroes: format_unit = 'es' - elif is_str and length and zeroes and nullable: + elif is_str_or_none and length and zeroes: format_unit = 'es#' - elif is_b_or_ba and not length and not zeroes and not nullable: + elif is_b_or_ba and not length and not zeroes: format_unit = 'et' - elif is_b_or_ba and length and zeroes and nullable: + elif is_b_or_ba_or_none and length and zeroes: format_unit = 'et#' else: if zeroes: fail("str_converter: illegal combination of arguments (zeroes is only legal with an encoding)") - if is_str and not length and not nullable: + if is_str and not length: format_unit = 's' - elif is_str and not length and nullable: + elif is_str_or_none and not length: format_unit = 'z' - elif is_robuffer and not length and not nullable: + elif is_robuffer and not length: format_unit = 'y' - elif is_robuffer and length and not nullable: + elif is_robuffer and length: format_unit = 'y#' - elif is_str_or_robuffer and length and not nullable: + elif is_str_or_robuffer and length: format_unit = 's#' - elif is_str_or_robuffer and length and nullable: + elif is_str_or_robuffer_or_none and length: format_unit = 'z#' if not format_unit: @@ -2696,12 +2707,12 @@ class str_converter(CConverter): class PyBytesObject_converter(CConverter): type = 'PyBytesObject *' format_unit = 'S' - # types = {'bytes'} + # accept = {bytes} class PyByteArrayObject_converter(CConverter): type = 'PyByteArrayObject *' format_unit = 'Y' - # types = {'bytearray'} + # accept = {bytearray} class unicode_converter(CConverter): type = 'PyObject *' @@ -2709,45 +2720,44 @@ class unicode_converter(CConverter): format_unit = 'U' @add_legacy_c_converter('u#', length=True) -@add_legacy_c_converter('Z', nullable=True) -@add_legacy_c_converter('Z#', nullable=True, length=True) +@add_legacy_c_converter('Z', accept={str, NoneType}) +@add_legacy_c_converter('Z#', accept={str, NoneType}, length=True) class Py_UNICODE_converter(CConverter): type = 'Py_UNICODE *' default_type = (str, Null, NoneType) format_unit = 'u' - def converter_init(self, *, nullable=False, length=False): - format_unit = 'Z' if nullable else 'u' + def converter_init(self, *, accept={str}, length=False): + format_unit = 'Z' if accept=={str, NoneType} else 'u' if length: format_unit += '#' self.length = True self.format_unit = format_unit -@add_legacy_c_converter('s*', types={'str', 'buffer'}) -@add_legacy_c_converter('z*', types={'str', 'buffer'}, nullable=True) -@add_legacy_c_converter('w*', types={'rwbuffer'}) +@add_legacy_c_converter('s*', accept={str, buffer}) +@add_legacy_c_converter('z*', accept={str, buffer, NoneType}) +@add_legacy_c_converter('w*', accept={rwbuffer}) class Py_buffer_converter(CConverter): type = 'Py_buffer' format_unit = 'y*' impl_by_reference = True c_ignored_default = "{NULL, NULL}" - def converter_init(self, *, types={'buffer'}, nullable=False): + def converter_init(self, *, accept={buffer}): if self.default not in (unspecified, None): fail("The only legal default value for Py_buffer is None.") + self.c_default = self.c_ignored_default - format_unit = None - if types == {'str', 'buffer'}: - format_unit = 's*' if not nullable else 'z*' + if accept == {str, buffer, NoneType}: + format_unit = 'z*' + elif accept == {str, buffer}: + format_unit = 's*' + elif accept == {buffer}: + format_unit = 'y*' + elif accept == {rwbuffer}: + format_unit = 'w*' else: - if nullable: - fail('Py_buffer_converter: illegal combination of arguments (nullable=True)') - elif types == {'buffer'}: - format_unit = 'y*' - elif types == {'rwbuffer'}: - format_unit = 'w*' - if not format_unit: fail("Py_buffer_converter: illegal combination of arguments") self.format_unit = format_unit @@ -3030,6 +3040,24 @@ class DecodeFSDefault_return_converter(CReturnConverter): 'return_value = PyUnicode_DecodeFSDefault(_return_value);\n') +def eval_ast_expr(node, globals, *, filename='-'): + """ + Takes an ast.Expr node. Compiles and evaluates it. + Returns the result of the expression. + + globals represents the globals dict the expression + should see. (There's no equivalent for "locals" here.) + """ + + if isinstance(node, ast.Expr): + node = node.value + + node = ast.Expression(node) + co = compile(node, filename, 'eval') + fn = types.FunctionType(co, globals) + return fn() + + class IndentStack: def __init__(self): self.indents = [] @@ -3617,7 +3645,7 @@ class DSLParser: except SyntaxError: try: # the last = was probably inside a function call, like - # i: int(nullable=True) + # c: int(accept={str}) # so assume there was no actual default value. default = None ast_input = "def x({}): pass".format(line) @@ -3628,6 +3656,14 @@ class DSLParser: fail("Function " + self.function.name + " has an invalid parameter declaration:\n\t" + line) function_args = module.body[0].args + + if len(function_args.args) > 1: + fail("Function " + self.function.name + " has an invalid parameter declaration (comma?):\n\t" + line) + if function_args.defaults or function_args.kw_defaults: + fail("Function " + self.function.name + " has an invalid parameter declaration (default value?):\n\t" + line) + if function_args.vararg or function_args.kwarg: + fail("Function " + self.function.name + " has an invalid parameter declaration (*args? **kwargs?):\n\t" + line) + parameter = function_args.args[0] parameter_name = parameter.arg @@ -3790,7 +3826,9 @@ class DSLParser: fail("Annotations must be either a name, a function call, or a string.") name = annotation.func.id - kwargs = {node.arg: ast.literal_eval(node.value) for node in annotation.keywords} + symbols = globals() + + kwargs = {node.arg: eval_ast_expr(node.value, symbols) for node in annotation.keywords} return name, False, kwargs def parse_special_symbol(self, symbol): |