summaryrefslogtreecommitdiffstats
path: root/Tools/clinic
diff options
context:
space:
mode:
authorLarry Hastings <larry@hastings.org>2015-05-04 13:59:46 (GMT)
committerLarry Hastings <larry@hastings.org>2015-05-04 13:59:46 (GMT)
commitdbfdc380df615fe7e85107ff3954b8fff3ce7741 (patch)
tree25dd738cb50b260d6822ab76f39697c69e9a44c4 /Tools/clinic
parentcb985563739f00dcccb03189c95deba604155777 (diff)
downloadcpython-dbfdc380df615fe7e85107ff3954b8fff3ce7741.zip
cpython-dbfdc380df615fe7e85107ff3954b8fff3ce7741.tar.gz
cpython-dbfdc380df615fe7e85107ff3954b8fff3ce7741.tar.bz2
Issue #24001: Argument Clinic converters now use accept={type}
instead of types={'type'} to specify the types the converter accepts.
Diffstat (limited to 'Tools/clinic')
-rwxr-xr-xTools/clinic/clinic.py142
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):