Skip to content

Commit

Permalink
pythongh-112205: Support @Getter annotation from AC
Browse files Browse the repository at this point in the history
  • Loading branch information
corona10 committed Nov 25, 2023
1 parent 4ec849b commit 72d9018
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 53 deletions.
81 changes: 33 additions & 48 deletions Modules/_io/bufferedio.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include "Python.h"
#include "pycore_bytesobject.h" // _PyBytes_Join()
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION()
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
#include "pycore_pyerrors.h" // _Py_FatalErrorFormat()
#include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing()
Expand Down Expand Up @@ -518,25 +517,20 @@ buffered_closed(buffered *self)
return closed;
}

/*[clinic input]
@critical_section
@getter
_io._Buffered.closed
[clinic start generated code]*/

static PyObject *
buffered_closed_get_impl(buffered *self, void *context)
_io__Buffered_closed_get_impl(buffered *self)
/*[clinic end generated code: output=f08ce57290703a1a input=18eddefdfe4a3d2f]*/
{
CHECK_INITIALIZED(self)
return PyObject_GetAttr(self->raw, &_Py_ID(closed));
}

static PyObject *
buffered_closed_get(buffered *self, void *context)
{
PyObject *return_value = NULL;

Py_BEGIN_CRITICAL_SECTION(self);
return_value = buffered_closed_get_impl(self, context);
Py_END_CRITICAL_SECTION();

return return_value;
}

/*[clinic input]
@critical_section
_io._Buffered.close
Expand Down Expand Up @@ -662,44 +656,35 @@ _io__Buffered_writable_impl(buffered *self)
return PyObject_CallMethodNoArgs(self->raw, &_Py_ID(writable));
}


/*[clinic input]
@critical_section
@getter
_io._Buffered.name
[clinic start generated code]*/

static PyObject *
buffered_name_get_impl(buffered *self, void *context)
_io__Buffered_name_get_impl(buffered *self)
/*[clinic end generated code: output=d2adf384051d3d10 input=6b84a0e6126f545e]*/
{
CHECK_INITIALIZED(self)
return PyObject_GetAttr(self->raw, &_Py_ID(name));
}

static PyObject *
buffered_name_get(buffered *self, void *context)
{
PyObject *return_value = NULL;

Py_BEGIN_CRITICAL_SECTION(self);
return_value = buffered_name_get_impl(self, context);
Py_END_CRITICAL_SECTION();

return return_value;
}
/*[clinic input]
@critical_section
@getter
_io._Buffered.mode
[clinic start generated code]*/

static PyObject *
buffered_mode_get_impl(buffered *self, void *context)
_io__Buffered_mode_get_impl(buffered *self)
/*[clinic end generated code: output=0feb205748892fa4 input=0762d5e28542fd8c]*/
{
CHECK_INITIALIZED(self)
return PyObject_GetAttr(self->raw, &_Py_ID(mode));
}

static PyObject *
buffered_mode_get(buffered *self, void *context)
{
PyObject *return_value = NULL;

Py_BEGIN_CRITICAL_SECTION(self);
return_value = buffered_mode_get_impl(self, context);
Py_END_CRITICAL_SECTION();

return return_value;
}

/* Lower-level APIs */

/*[clinic input]
Expand Down Expand Up @@ -2541,9 +2526,9 @@ static PyMemberDef bufferedreader_members[] = {
};

static PyGetSetDef bufferedreader_getset[] = {
{"closed", (getter)buffered_closed_get, NULL, NULL},
{"name", (getter)buffered_name_get, NULL, NULL},
{"mode", (getter)buffered_mode_get, NULL, NULL},
_IO__BUFFERED_CLOSED_GET_GETTERDEF
_IO__BUFFERED_NAME_GET_GETTERDEF
_IO__BUFFERED_MODE_GET_GETTERDEF
{NULL}
};

Expand Down Expand Up @@ -2601,9 +2586,9 @@ static PyMemberDef bufferedwriter_members[] = {
};

static PyGetSetDef bufferedwriter_getset[] = {
{"closed", (getter)buffered_closed_get, NULL, NULL},
{"name", (getter)buffered_name_get, NULL, NULL},
{"mode", (getter)buffered_mode_get, NULL, NULL},
_IO__BUFFERED_CLOSED_GET_GETTERDEF
_IO__BUFFERED_NAME_GET_GETTERDEF
_IO__BUFFERED_MODE_GET_GETTERDEF
{NULL}
};

Expand Down Expand Up @@ -2719,9 +2704,9 @@ static PyMemberDef bufferedrandom_members[] = {
};

static PyGetSetDef bufferedrandom_getset[] = {
{"closed", (getter)buffered_closed_get, NULL, NULL},
{"name", (getter)buffered_name_get, NULL, NULL},
{"mode", (getter)buffered_mode_get, NULL, NULL},
_IO__BUFFERED_CLOSED_GET_GETTERDEF
_IO__BUFFERED_NAME_GET_GETTERDEF
_IO__BUFFERED_MODE_GET_GETTERDEF
{NULL}
};

Expand Down
56 changes: 55 additions & 1 deletion Modules/_io/clinic/bufferedio.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 40 additions & 4 deletions Tools/clinic/clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,10 @@ class CLanguage(Language):
static PyObject *
{c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored))
""")
PARSER_PROTOTYPE_GETTER: Final[str] = normalize_snippet("""
static PyObject *
{c_basename}({self_type}{self_name}, void *context)
""")
METH_O_PROTOTYPE: Final[str] = normalize_snippet("""
static PyObject *
{c_basename}({impl_parameters})
Expand All @@ -865,6 +869,10 @@ class CLanguage(Language):
#define {methoddef_name} \
{{"{name}", {methoddef_cast}{c_basename}{methoddef_cast_end}, {methoddef_flags}, {c_basename}__doc__}},
""")
GETTERDEF_PROTOTYPE_DEFINE: Final[str] = normalize_snippet(r"""
#define {methoddef_name} \
{{"{name}", {methoddef_cast}{c_basename}{methoddef_cast_end}, NULL, NULL}},
""")
METHODDEF_PROTOTYPE_IFNDEF: Final[str] = normalize_snippet("""
#ifndef {methoddef_name}
#define {methoddef_name}
Expand Down Expand Up @@ -1161,6 +1169,9 @@ def output_templates(
methoddef_define = self.METHODDEF_PROTOTYPE_DEFINE
if new_or_init and not f.docstring:
docstring_prototype = docstring_definition = ''
elif f.getter:
methoddef_define = self.GETTERDEF_PROTOTYPE_DEFINE
docstring_prototype = docstring_definition = ''
else:
docstring_prototype = self.DOCSTRING_PROTOTYPE_VAR
docstring_definition = self.DOCSTRING_PROTOTYPE_STRVAR
Expand Down Expand Up @@ -1217,7 +1228,11 @@ def parser_body(
parsearg: str | None
if not parameters:
parser_code: list[str] | None
if not requires_defining_class:
if f.getter:
flags = "NULL"
parser_prototype = self.PARSER_PROTOTYPE_GETTER
parser_code = []
elif not requires_defining_class:
# no parameters, METH_NOARGS
flags = "METH_NOARGS"
parser_prototype = self.PARSER_PROTOTYPE_NOARGS
Expand Down Expand Up @@ -1670,6 +1685,8 @@ def parser_body(
methoddef_cast_end = ""
if flags in ('METH_NOARGS', 'METH_O', 'METH_VARARGS'):
methoddef_cast = "(PyCFunction)"
elif f.getter:
methoddef_cast = "(getter)"
elif limited_capi:
methoddef_cast = "(PyCFunction)(void(*)(void))"
else:
Expand Down Expand Up @@ -1928,7 +1945,10 @@ def render_function(
template_dict = {'full_name': full_name}
template_dict['name'] = f.displayname
template_dict['c_basename'] = f.c_basename
template_dict['methoddef_name'] = f.c_basename.upper() + "_METHODDEF"
if f.getter:
template_dict['methoddef_name'] = f.c_basename.upper() + "_GETTERDEF"
else:
template_dict['methoddef_name'] = f.c_basename.upper() + "_METHODDEF"

template_dict['docstring'] = self.docstring_for_c_string(f)

Expand Down Expand Up @@ -2932,6 +2952,7 @@ class FunctionKind(enum.Enum):
CLASS_METHOD = enum.auto()
METHOD_INIT = enum.auto()
METHOD_NEW = enum.auto()
GETTER = enum.auto()

@functools.cached_property
def new_or_init(self) -> bool:
Expand All @@ -2947,6 +2968,7 @@ def __repr__(self) -> str:
CLASS_METHOD: Final = FunctionKind.CLASS_METHOD
METHOD_INIT: Final = FunctionKind.METHOD_INIT
METHOD_NEW: Final = FunctionKind.METHOD_NEW
GETTER: Final = FunctionKind.GETTER

ParamDict = dict[str, "Parameter"]
ReturnConverterType = Callable[..., "CReturnConverter"]
Expand Down Expand Up @@ -2983,6 +3005,7 @@ class Function:
docstring_only: bool = False
critical_section: bool = False
target_critical_section: list[str] = dc.field(default_factory=list)
getter: bool = False

def __post_init__(self) -> None:
self.parent = self.cls or self.module
Expand Down Expand Up @@ -3032,6 +3055,8 @@ def methoddef_flags(self) -> str | None:
flags.append('METH_CLASS')
case FunctionKind.STATIC_METHOD:
flags.append('METH_STATIC')
case FunctionKind.GETTER:
pass
case _ as kind:
assert kind is FunctionKind.CALLABLE, f"unknown kind: {kind!r}"
if self.coexist:
Expand Down Expand Up @@ -4678,7 +4703,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
def correct_name_for_self(
f: Function
) -> tuple[str, str]:
if f.kind in (CALLABLE, METHOD_INIT):
if f.kind in (CALLABLE, METHOD_INIT, GETTER):
if f.cls:
return "PyObject *", "self"
return "PyObject *", "module"
Expand Down Expand Up @@ -5140,6 +5165,7 @@ class DSLParser:
preserve_output: bool
critical_section: bool
target_critical_section: list[str]
getter: bool
from_version_re = re.compile(r'([*/]) +\[from +(.+)\]')

def __init__(self, clinic: Clinic) -> None:
Expand Down Expand Up @@ -5176,6 +5202,7 @@ def reset(self) -> None:
self.preserve_output = False
self.critical_section = False
self.target_critical_section = []
self.getter = False

def directive_version(self, required: str) -> None:
global version
Expand Down Expand Up @@ -5310,6 +5337,9 @@ def at_critical_section(self, *args: str) -> None:
self.target_critical_section.extend(args)
self.critical_section = True

def at_getter(self) -> None:
self.getter = True

def at_staticmethod(self) -> None:
if self.kind is not CALLABLE:
fail("Can't set @staticmethod, function is not a normal callable")
Expand Down Expand Up @@ -5427,6 +5457,9 @@ def update_function_kind(self, fullname: str) -> None:
if (self.kind is not CALLABLE) or (not cls):
fail("'__init__' must be a normal method, not a class or static method!")
self.kind = METHOD_INIT
elif self.getter and cls:
self.kind = GETTER


def state_modulename_name(self, line: str) -> None:
# looking for declaration, which establishes the leftmost column
Expand Down Expand Up @@ -5500,6 +5533,8 @@ def state_modulename_name(self, line: str) -> None:
line, _, returns = line.partition('->')
returns = returns.strip()
full_name, c_basename = self.parse_function_names(line)
if self.getter:
c_basename += '_get'

return_converter = None
if returns:
Expand Down Expand Up @@ -5534,7 +5569,8 @@ def state_modulename_name(self, line: str) -> None:
self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename,
return_converter=return_converter, kind=self.kind, coexist=self.coexist,
critical_section=self.critical_section,
target_critical_section=self.target_critical_section)
target_critical_section=self.target_critical_section,
getter=self.getter)
self.block.signatures.append(self.function)

# insert a self converter automatically
Expand Down

0 comments on commit 72d9018

Please sign in to comment.