mirror of
https://github.com/python/cpython.git
synced 2025-12-23 09:19:18 +00:00
bpo-38787: C API for module state access from extension methods (PEP 573) (GH-19936)
Module C state is now accessible from C-defined heap type methods (PEP 573). Patch by Marcel Plch and Petr Viktorin. Co-authored-by: Marcel Plch <mplch@redhat.com> Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
parent
4638c64295
commit
e1becf46b4
19 changed files with 797 additions and 51 deletions
|
|
@ -657,9 +657,14 @@ class CLanguage(Language):
|
|||
if not p.is_optional():
|
||||
min_pos = i
|
||||
|
||||
requires_defining_class = any(
|
||||
isinstance(p.converter, defining_class_converter)
|
||||
for p in parameters)
|
||||
|
||||
meth_o = (len(parameters) == 1 and
|
||||
parameters[0].is_positional_only() and
|
||||
not converters[0].is_optional() and
|
||||
not requires_defining_class and
|
||||
not new_or_init)
|
||||
|
||||
# we have to set these things before we're done:
|
||||
|
|
@ -717,6 +722,11 @@ class CLanguage(Language):
|
|||
{c_basename}({self_type}{self_name}, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||
""")
|
||||
|
||||
parser_prototype_def_class = normalize_snippet("""
|
||||
static PyObject *
|
||||
{c_basename}({self_type}{self_name}, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||
""")
|
||||
|
||||
# parser_body_fields remembers the fields passed in to the
|
||||
# previous call to parser_body. this is used for an awful hack.
|
||||
parser_body_fields = ()
|
||||
|
|
@ -824,7 +834,7 @@ class CLanguage(Language):
|
|||
|
||||
parser_definition = parser_body(parser_prototype, ' {option_group_parsing}')
|
||||
|
||||
elif pos_only == len(parameters):
|
||||
elif not requires_defining_class and pos_only == len(parameters):
|
||||
if not new_or_init:
|
||||
# positional-only, but no option groups
|
||||
# we only need one call to _PyArg_ParseStack
|
||||
|
|
@ -891,7 +901,7 @@ class CLanguage(Language):
|
|||
parser_prototype = parser_prototype_fastcall_keywords
|
||||
argname_fmt = 'args[%d]'
|
||||
declarations = normalize_snippet("""
|
||||
static const char * const _keywords[] = {{{keywords}, NULL}};
|
||||
static const char * const _keywords[] = {{{keywords} NULL}};
|
||||
static _PyArg_Parser _parser = {{NULL, _keywords, "{name}", 0}};
|
||||
PyObject *argsbuf[%s];
|
||||
""" % len(converters))
|
||||
|
|
@ -909,7 +919,7 @@ class CLanguage(Language):
|
|||
parser_prototype = parser_prototype_keyword
|
||||
argname_fmt = 'fastargs[%d]'
|
||||
declarations = normalize_snippet("""
|
||||
static const char * const _keywords[] = {{{keywords}, NULL}};
|
||||
static const char * const _keywords[] = {{{keywords} NULL}};
|
||||
static _PyArg_Parser _parser = {{NULL, _keywords, "{name}", 0}};
|
||||
PyObject *argsbuf[%s];
|
||||
PyObject * const *fastargs;
|
||||
|
|
@ -923,6 +933,9 @@ class CLanguage(Language):
|
|||
goto exit;
|
||||
}}
|
||||
""" % (min_pos, max_pos, min_kw_only), indent=4)]
|
||||
if requires_defining_class:
|
||||
flags = 'METH_METHOD|' + flags
|
||||
parser_prototype = parser_prototype_def_class
|
||||
|
||||
add_label = None
|
||||
for i, p in enumerate(parameters):
|
||||
|
|
@ -983,11 +996,11 @@ class CLanguage(Language):
|
|||
parser_code.append("%s:" % add_label)
|
||||
else:
|
||||
declarations = (
|
||||
'static const char * const _keywords[] = {{{keywords}, NULL}};\n'
|
||||
'static const char * const _keywords[] = {{{keywords} NULL}};\n'
|
||||
'static _PyArg_Parser _parser = {{"{format_units}:{name}", _keywords, 0}};')
|
||||
if not new_or_init:
|
||||
parser_code = [normalize_snippet("""
|
||||
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
|
||||
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser{parse_arguments_comma}
|
||||
{parse_arguments})) {{
|
||||
goto exit;
|
||||
}}
|
||||
|
|
@ -1021,6 +1034,9 @@ class CLanguage(Language):
|
|||
if parses_keywords:
|
||||
assert parses_positional
|
||||
|
||||
if requires_defining_class:
|
||||
raise ValueError("Slot methods cannot access their defining class.")
|
||||
|
||||
if not parses_keywords:
|
||||
fields.insert(0, normalize_snippet("""
|
||||
if ({self_type_check}!_PyArg_NoKeywords("{name}", kwargs)) {{
|
||||
|
|
@ -1297,9 +1313,13 @@ class CLanguage(Language):
|
|||
template_dict['declarations'] = format_escape("\n".join(data.declarations))
|
||||
template_dict['initializers'] = "\n\n".join(data.initializers)
|
||||
template_dict['modifications'] = '\n\n'.join(data.modifications)
|
||||
template_dict['keywords'] = '"' + '", "'.join(data.keywords) + '"'
|
||||
template_dict['keywords'] = ' '.join('"' + k + '",' for k in data.keywords)
|
||||
template_dict['format_units'] = ''.join(data.format_units)
|
||||
template_dict['parse_arguments'] = ', '.join(data.parse_arguments)
|
||||
if data.parse_arguments:
|
||||
template_dict['parse_arguments_comma'] = ',';
|
||||
else:
|
||||
template_dict['parse_arguments_comma'] = '';
|
||||
template_dict['impl_parameters'] = ", ".join(data.impl_parameters)
|
||||
template_dict['impl_arguments'] = ", ".join(data.impl_arguments)
|
||||
template_dict['return_conversion'] = format_escape("".join(data.return_conversion).rstrip())
|
||||
|
|
@ -2730,6 +2750,25 @@ class bool_converter(CConverter):
|
|||
""".format(argname=argname, paramname=self.name)
|
||||
return super().parse_arg(argname, displayname)
|
||||
|
||||
class defining_class_converter(CConverter):
|
||||
"""
|
||||
A special-case converter:
|
||||
this is the default converter used for the defining class.
|
||||
"""
|
||||
type = 'PyTypeObject *'
|
||||
format_unit = ''
|
||||
show_in_signature = False
|
||||
|
||||
def converter_init(self, *, type=None):
|
||||
self.specified_type = type
|
||||
|
||||
def render(self, parameter, data):
|
||||
self._render_self(parameter, data)
|
||||
|
||||
def set_template_dict(self, template_dict):
|
||||
template_dict['defining_class_name'] = self.name
|
||||
|
||||
|
||||
class char_converter(CConverter):
|
||||
type = 'char'
|
||||
default_type = (bytes, bytearray)
|
||||
|
|
@ -4508,6 +4547,19 @@ class DSLParser:
|
|||
else:
|
||||
fail("A 'self' parameter, if specified, must be the very first thing in the parameter block.")
|
||||
|
||||
if isinstance(converter, defining_class_converter):
|
||||
_lp = len(self.function.parameters)
|
||||
if _lp == 1:
|
||||
if (self.parameter_state != self.ps_required):
|
||||
fail("A 'defining_class' parameter cannot be marked optional.")
|
||||
if value is not unspecified:
|
||||
fail("A 'defining_class' parameter cannot have a default value.")
|
||||
if self.group:
|
||||
fail("A 'defining_class' parameter cannot be in an optional group.")
|
||||
else:
|
||||
fail("A 'defining_class' parameter, if specified, must either be the first thing in the parameter block, or come just after 'self'.")
|
||||
|
||||
|
||||
p = Parameter(parameter_name, kind, function=self.function, converter=converter, default=value, group=self.group)
|
||||
|
||||
if parameter_name in self.function.parameters:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue