mirror of
https://github.com/python/cpython.git
synced 2025-08-04 17:08:35 +00:00
gh-132661: Implement PEP 750 (#132662)
Co-authored-by: Lysandros Nikolaou <lisandrosnik@gmail.com> Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Wingy <git@wingysam.xyz> Co-authored-by: Koudai Aono <koxudaxi@gmail.com> Co-authored-by: Dave Peck <davepeck@gmail.com> Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu> Co-authored-by: Paul Everitt <pauleveritt@me.com> Co-authored-by: sobolevn <mail@sobolevn.me>
This commit is contained in:
parent
5ea9010e89
commit
60202609a2
81 changed files with 7716 additions and 3761 deletions
89
Objects/clinic/interpolationobject.c.h
generated
Normal file
89
Objects/clinic/interpolationobject.c.h
generated
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*[clinic input]
|
||||
preserve
|
||||
[clinic start generated code]*/
|
||||
|
||||
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
|
||||
# include "pycore_gc.h" // PyGC_Head
|
||||
# include "pycore_runtime.h" // _Py_ID()
|
||||
#endif
|
||||
#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
|
||||
|
||||
static PyObject *
|
||||
interpolation_new_impl(PyTypeObject *type, PyObject *value,
|
||||
PyObject *expression, PyObject *conversion,
|
||||
PyObject *format_spec);
|
||||
|
||||
static PyObject *
|
||||
interpolation_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
|
||||
|
||||
#define NUM_KEYWORDS 4
|
||||
static struct {
|
||||
PyGC_Head _this_is_not_used;
|
||||
PyObject_VAR_HEAD
|
||||
Py_hash_t ob_hash;
|
||||
PyObject *ob_item[NUM_KEYWORDS];
|
||||
} _kwtuple = {
|
||||
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
|
||||
.ob_hash = -1,
|
||||
.ob_item = { &_Py_ID(value), &_Py_ID(expression), &_Py_ID(conversion), &_Py_ID(format_spec), },
|
||||
};
|
||||
#undef NUM_KEYWORDS
|
||||
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
|
||||
|
||||
#else // !Py_BUILD_CORE
|
||||
# define KWTUPLE NULL
|
||||
#endif // !Py_BUILD_CORE
|
||||
|
||||
static const char * const _keywords[] = {"value", "expression", "conversion", "format_spec", NULL};
|
||||
static _PyArg_Parser _parser = {
|
||||
.keywords = _keywords,
|
||||
.fname = "Interpolation",
|
||||
.kwtuple = KWTUPLE,
|
||||
};
|
||||
#undef KWTUPLE
|
||||
PyObject *argsbuf[4];
|
||||
PyObject * const *fastargs;
|
||||
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
|
||||
Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 2;
|
||||
PyObject *value;
|
||||
PyObject *expression;
|
||||
PyObject *conversion = Py_None;
|
||||
PyObject *format_spec = &_Py_STR(empty);
|
||||
|
||||
fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser,
|
||||
/*minpos*/ 2, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
|
||||
if (!fastargs) {
|
||||
goto exit;
|
||||
}
|
||||
value = fastargs[0];
|
||||
if (!PyUnicode_Check(fastargs[1])) {
|
||||
_PyArg_BadArgument("Interpolation", "argument 'expression'", "str", fastargs[1]);
|
||||
goto exit;
|
||||
}
|
||||
expression = fastargs[1];
|
||||
if (!noptargs) {
|
||||
goto skip_optional_pos;
|
||||
}
|
||||
if (fastargs[2]) {
|
||||
if (!_conversion_converter(fastargs[2], &conversion)) {
|
||||
goto exit;
|
||||
}
|
||||
if (!--noptargs) {
|
||||
goto skip_optional_pos;
|
||||
}
|
||||
}
|
||||
if (!PyUnicode_Check(fastargs[3])) {
|
||||
_PyArg_BadArgument("Interpolation", "argument 'format_spec'", "str", fastargs[3]);
|
||||
goto exit;
|
||||
}
|
||||
format_spec = fastargs[3];
|
||||
skip_optional_pos:
|
||||
return_value = interpolation_new_impl(type, value, expression, conversion, format_spec);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=599742a5ccd6f060 input=a9049054013a1b77]*/
|
229
Objects/interpolationobject.c
Normal file
229
Objects/interpolationobject.c
Normal file
|
@ -0,0 +1,229 @@
|
|||
/* t-string Interpolation object implementation */
|
||||
|
||||
#include "Python.h"
|
||||
#include "pycore_initconfig.h" // _PyStatus_OK
|
||||
#include "pycore_interpolation.h"
|
||||
#include "pycore_typeobject.h" // _PyType_GetDict
|
||||
|
||||
static int
|
||||
_conversion_converter(PyObject *arg, PyObject **conversion)
|
||||
{
|
||||
if (arg == Py_None) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!PyUnicode_Check(arg)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Interpolation() argument 'conversion' must be str, not %T",
|
||||
arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Py_ssize_t len;
|
||||
const char *conv_str = PyUnicode_AsUTF8AndSize(arg, &len);
|
||||
if (len != 1 || !(conv_str[0] == 'a' || conv_str[0] == 'r' || conv_str[0] == 's')) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Interpolation() argument 'conversion' must be one of 's', 'a' or 'r'");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*conversion = arg;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#include "clinic/interpolationobject.c.h"
|
||||
|
||||
/*[clinic input]
|
||||
class Interpolation "interpolationobject *" "&_PyInterpolation_Type"
|
||||
[clinic start generated code]*/
|
||||
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=161c64a16f9c4544]*/
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
PyObject *value;
|
||||
PyObject *expression;
|
||||
PyObject *conversion;
|
||||
PyObject *format_spec;
|
||||
} interpolationobject;
|
||||
|
||||
#define interpolationobject_CAST(op) \
|
||||
(assert(_PyInterpolation_CheckExact(op)), _Py_CAST(interpolationobject*, (op)))
|
||||
|
||||
/*[clinic input]
|
||||
@classmethod
|
||||
Interpolation.__new__ as interpolation_new
|
||||
|
||||
value: object
|
||||
expression: object(subclass_of='&PyUnicode_Type')
|
||||
conversion: object(converter='_conversion_converter') = None
|
||||
format_spec: object(subclass_of='&PyUnicode_Type', c_default='&_Py_STR(empty)') = ""
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
interpolation_new_impl(PyTypeObject *type, PyObject *value,
|
||||
PyObject *expression, PyObject *conversion,
|
||||
PyObject *format_spec)
|
||||
/*[clinic end generated code: output=6488e288765bc1a9 input=d91711024068528c]*/
|
||||
{
|
||||
interpolationobject *self = PyObject_GC_New(interpolationobject, type);
|
||||
if (!self) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->value = Py_NewRef(value);
|
||||
self->expression = Py_NewRef(expression);
|
||||
self->conversion = Py_NewRef(conversion);
|
||||
self->format_spec = Py_NewRef(format_spec);
|
||||
PyObject_GC_Track(self);
|
||||
return (PyObject *) self;
|
||||
}
|
||||
|
||||
static void
|
||||
interpolation_dealloc(PyObject *op)
|
||||
{
|
||||
PyObject_GC_UnTrack(op);
|
||||
Py_TYPE(op)->tp_clear(op);
|
||||
Py_TYPE(op)->tp_free(op);
|
||||
}
|
||||
|
||||
static int
|
||||
interpolation_clear(PyObject *op)
|
||||
{
|
||||
interpolationobject *self = interpolationobject_CAST(op);
|
||||
Py_CLEAR(self->value);
|
||||
Py_CLEAR(self->expression);
|
||||
Py_CLEAR(self->conversion);
|
||||
Py_CLEAR(self->format_spec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
interpolation_traverse(PyObject *op, visitproc visit, void *arg)
|
||||
{
|
||||
interpolationobject *self = interpolationobject_CAST(op);
|
||||
Py_VISIT(self->value);
|
||||
Py_VISIT(self->expression);
|
||||
Py_VISIT(self->conversion);
|
||||
Py_VISIT(self->format_spec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
interpolation_repr(PyObject *op)
|
||||
{
|
||||
interpolationobject *self = interpolationobject_CAST(op);
|
||||
return PyUnicode_FromFormat("%s(%R, %R, %R, %R)",
|
||||
_PyType_Name(Py_TYPE(self)), self->value, self->expression,
|
||||
self->conversion, self->format_spec);
|
||||
}
|
||||
|
||||
static PyMemberDef interpolation_members[] = {
|
||||
{"value", Py_T_OBJECT_EX, offsetof(interpolationobject, value), Py_READONLY, "Value"},
|
||||
{"expression", Py_T_OBJECT_EX, offsetof(interpolationobject, expression), Py_READONLY, "Expression"},
|
||||
{"conversion", Py_T_OBJECT_EX, offsetof(interpolationobject, conversion), Py_READONLY, "Conversion"},
|
||||
{"format_spec", Py_T_OBJECT_EX, offsetof(interpolationobject, format_spec), Py_READONLY, "Format specifier"},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static PyObject*
|
||||
interpolation_reduce(PyObject *op, PyObject *Py_UNUSED(dummy))
|
||||
{
|
||||
interpolationobject *self = interpolationobject_CAST(op);
|
||||
return Py_BuildValue("(O(OOOO))", (PyObject *)Py_TYPE(op),
|
||||
self->value, self->expression,
|
||||
self->conversion, self->format_spec);
|
||||
}
|
||||
|
||||
static PyMethodDef interpolation_methods[] = {
|
||||
{"__reduce__", interpolation_reduce, METH_NOARGS,
|
||||
PyDoc_STR("__reduce__() -> (cls, state)")},
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
PyTypeObject _PyInterpolation_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "string.templatelib.Interpolation",
|
||||
.tp_doc = PyDoc_STR("Interpolation object"),
|
||||
.tp_basicsize = sizeof(interpolationobject),
|
||||
.tp_itemsize = 0,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
||||
.tp_new = interpolation_new,
|
||||
.tp_alloc = PyType_GenericAlloc,
|
||||
.tp_dealloc = interpolation_dealloc,
|
||||
.tp_clear = interpolation_clear,
|
||||
.tp_free = PyObject_GC_Del,
|
||||
.tp_repr = interpolation_repr,
|
||||
.tp_members = interpolation_members,
|
||||
.tp_methods = interpolation_methods,
|
||||
.tp_traverse = interpolation_traverse,
|
||||
};
|
||||
|
||||
PyStatus
|
||||
_PyInterpolation_InitTypes(PyInterpreterState *interp)
|
||||
{
|
||||
PyObject *tuple = Py_BuildValue("(ssss)", "value", "expression", "conversion", "format_spec");
|
||||
if (!tuple) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
PyObject *dict = _PyType_GetDict(&_PyInterpolation_Type);
|
||||
if (!dict) {
|
||||
Py_DECREF(tuple);
|
||||
goto error;
|
||||
}
|
||||
|
||||
int status = PyDict_SetItemString(dict, "__match_args__", tuple);
|
||||
Py_DECREF(tuple);
|
||||
if (status < 0) {
|
||||
goto error;
|
||||
}
|
||||
return _PyStatus_OK();
|
||||
|
||||
error:
|
||||
return _PyStatus_ERR("Can't initialize interpolation types");
|
||||
}
|
||||
|
||||
PyObject *
|
||||
_PyInterpolation_Build(PyObject *value, PyObject *str, int conversion, PyObject *format_spec)
|
||||
{
|
||||
interpolationobject *interpolation = PyObject_GC_New(interpolationobject, &_PyInterpolation_Type);
|
||||
if (!interpolation) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
interpolation->value = Py_NewRef(value);
|
||||
interpolation->expression = Py_NewRef(str);
|
||||
interpolation->format_spec = Py_NewRef(format_spec);
|
||||
interpolation->conversion = NULL;
|
||||
|
||||
if (conversion == 0) {
|
||||
interpolation->conversion = Py_None;
|
||||
}
|
||||
else {
|
||||
switch (conversion) {
|
||||
case FVC_ASCII:
|
||||
interpolation->conversion = _Py_LATIN1_CHR('a');
|
||||
break;
|
||||
case FVC_REPR:
|
||||
interpolation->conversion = _Py_LATIN1_CHR('r');
|
||||
break;
|
||||
case FVC_STR:
|
||||
interpolation->conversion = _Py_LATIN1_CHR('s');
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_SystemError,
|
||||
"Interpolation() argument 'conversion' must be one of 's', 'a' or 'r'");
|
||||
Py_DECREF(interpolation);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject_GC_Track(interpolation);
|
||||
return (PyObject *) interpolation;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
_PyInterpolation_GetValueRef(PyObject *interpolation)
|
||||
{
|
||||
return Py_NewRef(interpolationobject_CAST(interpolation)->value);
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
#include "pycore_hamt.h" // _PyHamtItems_Type
|
||||
#include "pycore_initconfig.h" // _PyStatus_OK()
|
||||
#include "pycore_instruction_sequence.h" // _PyInstructionSequence_Type
|
||||
#include "pycore_interpolation.h" // _PyInterpolation_Type
|
||||
#include "pycore_list.h" // _PyList_DebugMallocStats()
|
||||
#include "pycore_long.h" // _PyLong_GetZero()
|
||||
#include "pycore_memoryobject.h" // _PyManagedBuffer_Type
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
|
||||
#include "pycore_pystate.h" // _PyThreadState_GET()
|
||||
#include "pycore_symtable.h" // PySTEntry_Type
|
||||
#include "pycore_template.h" // _PyTemplate_Type _PyTemplateIter_Type
|
||||
#include "pycore_tuple.h" // _PyTuple_DebugMallocStats()
|
||||
#include "pycore_typeobject.h" // _PyBufferWrapper_Type
|
||||
#include "pycore_typevarobject.h" // _PyTypeAlias_Type
|
||||
|
@ -2409,6 +2411,7 @@ static PyTypeObject* static_types[] = {
|
|||
&_PyHamt_CollisionNode_Type,
|
||||
&_PyHamt_Type,
|
||||
&_PyInstructionSequence_Type,
|
||||
&_PyInterpolation_Type,
|
||||
&_PyLegacyEventHandler_Type,
|
||||
&_PyLineIterator,
|
||||
&_PyManagedBuffer_Type,
|
||||
|
@ -2418,6 +2421,8 @@ static PyTypeObject* static_types[] = {
|
|||
&_PyNone_Type,
|
||||
&_PyNotImplemented_Type,
|
||||
&_PyPositionsIterator,
|
||||
&_PyTemplate_Type,
|
||||
&_PyTemplateIter_Type,
|
||||
&_PyUnicodeASCIIIter_Type,
|
||||
&_PyUnion_Type,
|
||||
#ifdef _Py_TIER2
|
||||
|
|
483
Objects/templateobject.c
Normal file
483
Objects/templateobject.c
Normal file
|
@ -0,0 +1,483 @@
|
|||
/* t-string Template object implementation */
|
||||
|
||||
#include "Python.h"
|
||||
#include "pycore_interpolation.h" // _PyInterpolation_CheckExact()
|
||||
#include "pycore_runtime.h" // _Py_STR()
|
||||
#include "pycore_template.h"
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
PyObject *stringsiter;
|
||||
PyObject *interpolationsiter;
|
||||
int from_strings;
|
||||
} templateiterobject;
|
||||
|
||||
#define templateiterobject_CAST(op) \
|
||||
(assert(_PyTemplateIter_CheckExact(op)), _Py_CAST(templateiterobject*, (op)))
|
||||
|
||||
static PyObject *
|
||||
templateiter_next(PyObject *op)
|
||||
{
|
||||
templateiterobject *self = templateiterobject_CAST(op);
|
||||
PyObject *item;
|
||||
if (self->from_strings) {
|
||||
item = PyIter_Next(self->stringsiter);
|
||||
self->from_strings = 0;
|
||||
if (PyUnicode_GET_LENGTH(item) == 0) {
|
||||
Py_SETREF(item, PyIter_Next(self->interpolationsiter));
|
||||
self->from_strings = 1;
|
||||
}
|
||||
} else {
|
||||
item = PyIter_Next(self->interpolationsiter);
|
||||
self->from_strings = 1;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
static void
|
||||
templateiter_dealloc(PyObject *op)
|
||||
{
|
||||
PyObject_GC_UnTrack(op);
|
||||
Py_TYPE(op)->tp_clear(op);
|
||||
Py_TYPE(op)->tp_free(op);
|
||||
}
|
||||
|
||||
static int
|
||||
templateiter_clear(PyObject *op)
|
||||
{
|
||||
templateiterobject *self = templateiterobject_CAST(op);
|
||||
Py_CLEAR(self->stringsiter);
|
||||
Py_CLEAR(self->interpolationsiter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
templateiter_traverse(PyObject *op, visitproc visit, void *arg)
|
||||
{
|
||||
templateiterobject *self = templateiterobject_CAST(op);
|
||||
Py_VISIT(self->stringsiter);
|
||||
Py_VISIT(self->interpolationsiter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyTypeObject _PyTemplateIter_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "string.templatelib.TemplateIter",
|
||||
.tp_doc = PyDoc_STR("Template iterator object"),
|
||||
.tp_basicsize = sizeof(templateiterobject),
|
||||
.tp_itemsize = 0,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
||||
.tp_alloc = PyType_GenericAlloc,
|
||||
.tp_dealloc = templateiter_dealloc,
|
||||
.tp_clear = templateiter_clear,
|
||||
.tp_free = PyObject_GC_Del,
|
||||
.tp_traverse = templateiter_traverse,
|
||||
.tp_iter = PyObject_SelfIter,
|
||||
.tp_iternext = templateiter_next,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
PyObject *strings;
|
||||
PyObject *interpolations;
|
||||
} templateobject;
|
||||
|
||||
#define templateobject_CAST(op) \
|
||||
(assert(_PyTemplate_CheckExact(op)), _Py_CAST(templateobject*, (op)))
|
||||
|
||||
static PyObject *
|
||||
template_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
if (kwds != NULL) {
|
||||
PyErr_SetString(PyExc_TypeError, "Template.__new__ only accepts *args arguments");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_ssize_t argslen = PyTuple_GET_SIZE(args);
|
||||
Py_ssize_t stringslen = 0;
|
||||
Py_ssize_t interpolationslen = 0;
|
||||
int last_was_str = 0;
|
||||
|
||||
for (Py_ssize_t i = 0; i < argslen; i++) {
|
||||
PyObject *item = PyTuple_GET_ITEM(args, i);
|
||||
if (PyUnicode_Check(item)) {
|
||||
if (!last_was_str) {
|
||||
stringslen++;
|
||||
}
|
||||
last_was_str = 1;
|
||||
}
|
||||
else if (_PyInterpolation_CheckExact(item)) {
|
||||
if (!last_was_str) {
|
||||
stringslen++;
|
||||
}
|
||||
interpolationslen++;
|
||||
last_was_str = 0;
|
||||
}
|
||||
else {
|
||||
PyErr_Format(
|
||||
PyExc_TypeError,
|
||||
"Template.__new__ *args need to be of type 'str' or 'Interpolation', got %T",
|
||||
item);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (!last_was_str) {
|
||||
stringslen++;
|
||||
}
|
||||
|
||||
PyObject *strings = PyTuple_New(stringslen);
|
||||
if (!strings) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *interpolations = PyTuple_New(interpolationslen);
|
||||
if (!interpolations) {
|
||||
Py_DECREF(strings);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
last_was_str = 0;
|
||||
Py_ssize_t stringsidx = 0, interpolationsidx = 0;
|
||||
for (Py_ssize_t i = 0; i < argslen; i++) {
|
||||
PyObject *item = PyTuple_GET_ITEM(args, i);
|
||||
if (PyUnicode_Check(item)) {
|
||||
if (last_was_str) {
|
||||
PyObject *laststring = PyTuple_GET_ITEM(strings, stringsidx - 1);
|
||||
PyObject *concat = PyUnicode_Concat(laststring, item);
|
||||
Py_DECREF(laststring);
|
||||
if (!concat) {
|
||||
Py_DECREF(strings);
|
||||
Py_DECREF(interpolations);
|
||||
return NULL;
|
||||
}
|
||||
PyTuple_SET_ITEM(strings, stringsidx - 1, concat);
|
||||
}
|
||||
else {
|
||||
PyTuple_SET_ITEM(strings, stringsidx++, Py_NewRef(item));
|
||||
}
|
||||
last_was_str = 1;
|
||||
}
|
||||
else if (_PyInterpolation_CheckExact(item)) {
|
||||
if (!last_was_str) {
|
||||
_Py_DECLARE_STR(empty, "");
|
||||
PyTuple_SET_ITEM(strings, stringsidx++, &_Py_STR(empty));
|
||||
}
|
||||
PyTuple_SET_ITEM(interpolations, interpolationsidx++, Py_NewRef(item));
|
||||
last_was_str = 0;
|
||||
}
|
||||
}
|
||||
if (!last_was_str) {
|
||||
_Py_DECLARE_STR(empty, "");
|
||||
PyTuple_SET_ITEM(strings, stringsidx++, &_Py_STR(empty));
|
||||
}
|
||||
|
||||
PyObject *template = _PyTemplate_Build(strings, interpolations);
|
||||
Py_DECREF(strings);
|
||||
Py_DECREF(interpolations);
|
||||
return template;
|
||||
}
|
||||
|
||||
static void
|
||||
template_dealloc(PyObject *op)
|
||||
{
|
||||
PyObject_GC_UnTrack(op);
|
||||
Py_TYPE(op)->tp_clear(op);
|
||||
Py_TYPE(op)->tp_free(op);
|
||||
}
|
||||
|
||||
static int
|
||||
template_clear(PyObject *op)
|
||||
{
|
||||
templateobject *self = templateobject_CAST(op);
|
||||
Py_CLEAR(self->strings);
|
||||
Py_CLEAR(self->interpolations);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
template_traverse(PyObject *op, visitproc visit, void *arg)
|
||||
{
|
||||
templateobject *self = templateobject_CAST(op);
|
||||
Py_VISIT(self->strings);
|
||||
Py_VISIT(self->interpolations);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
template_repr(PyObject *op)
|
||||
{
|
||||
templateobject *self = templateobject_CAST(op);
|
||||
return PyUnicode_FromFormat("%s(strings=%R, interpolations=%R)",
|
||||
_PyType_Name(Py_TYPE(self)),
|
||||
self->strings,
|
||||
self->interpolations);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
template_iter(PyObject *op)
|
||||
{
|
||||
templateobject *self = templateobject_CAST(op);
|
||||
templateiterobject *iter = PyObject_GC_New(templateiterobject, &_PyTemplateIter_Type);
|
||||
if (iter == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *stringsiter = PyObject_GetIter(self->strings);
|
||||
if (stringsiter == NULL) {
|
||||
Py_DECREF(iter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *interpolationsiter = PyObject_GetIter(self->interpolations);
|
||||
if (interpolationsiter == NULL) {
|
||||
Py_DECREF(iter);
|
||||
Py_DECREF(stringsiter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iter->stringsiter = stringsiter;
|
||||
iter->interpolationsiter = interpolationsiter;
|
||||
iter->from_strings = 1;
|
||||
PyObject_GC_Track(iter);
|
||||
return (PyObject *)iter;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
template_strings_append_str(PyObject *strings, PyObject *str)
|
||||
{
|
||||
Py_ssize_t stringslen = PyTuple_GET_SIZE(strings);
|
||||
PyObject *string = PyTuple_GET_ITEM(strings, stringslen - 1);
|
||||
PyObject *concat = PyUnicode_Concat(string, str);
|
||||
if (concat == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *newstrings = PyTuple_New(stringslen);
|
||||
if (newstrings == NULL) {
|
||||
Py_DECREF(concat);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (Py_ssize_t i = 0; i < stringslen - 1; i++) {
|
||||
PyTuple_SET_ITEM(newstrings, i, Py_NewRef(PyTuple_GET_ITEM(strings, i)));
|
||||
}
|
||||
PyTuple_SET_ITEM(newstrings, stringslen - 1, concat);
|
||||
|
||||
return newstrings;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
template_strings_prepend_str(PyObject *strings, PyObject *str)
|
||||
{
|
||||
Py_ssize_t stringslen = PyTuple_GET_SIZE(strings);
|
||||
PyObject *string = PyTuple_GET_ITEM(strings, 0);
|
||||
PyObject *concat = PyUnicode_Concat(str, string);
|
||||
if (concat == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *newstrings = PyTuple_New(stringslen);
|
||||
if (newstrings == NULL) {
|
||||
Py_DECREF(concat);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyTuple_SET_ITEM(newstrings, 0, concat);
|
||||
for (Py_ssize_t i = 1; i < stringslen; i++) {
|
||||
PyTuple_SET_ITEM(newstrings, i, Py_NewRef(PyTuple_GET_ITEM(strings, i)));
|
||||
}
|
||||
|
||||
return newstrings;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
template_strings_concat(PyObject *left, PyObject *right)
|
||||
{
|
||||
Py_ssize_t left_stringslen = PyTuple_GET_SIZE(left);
|
||||
PyObject *left_laststring = PyTuple_GET_ITEM(left, left_stringslen - 1);
|
||||
Py_ssize_t right_stringslen = PyTuple_GET_SIZE(right);
|
||||
PyObject *right_firststring = PyTuple_GET_ITEM(right, 0);
|
||||
|
||||
PyObject *concat = PyUnicode_Concat(left_laststring, right_firststring);
|
||||
if (concat == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *newstrings = PyTuple_New(left_stringslen + right_stringslen - 1);
|
||||
if (newstrings == NULL) {
|
||||
Py_DECREF(concat);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_ssize_t index = 0;
|
||||
for (Py_ssize_t i = 0; i < left_stringslen - 1; i++) {
|
||||
PyTuple_SET_ITEM(newstrings, index++, Py_NewRef(PyTuple_GET_ITEM(left, i)));
|
||||
}
|
||||
PyTuple_SET_ITEM(newstrings, index++, concat);
|
||||
for (Py_ssize_t i = 1; i < right_stringslen; i++) {
|
||||
PyTuple_SET_ITEM(newstrings, index++, Py_NewRef(PyTuple_GET_ITEM(right, i)));
|
||||
}
|
||||
|
||||
return newstrings;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
template_concat_templates(templateobject *self, templateobject *other)
|
||||
{
|
||||
PyObject *newstrings = template_strings_concat(self->strings, other->strings);
|
||||
if (newstrings == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *newinterpolations = PySequence_Concat(self->interpolations, other->interpolations);
|
||||
if (newinterpolations == NULL) {
|
||||
Py_DECREF(newstrings);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *newtemplate = _PyTemplate_Build(newstrings, newinterpolations);
|
||||
Py_DECREF(newstrings);
|
||||
Py_DECREF(newinterpolations);
|
||||
return newtemplate;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
template_concat_template_str(templateobject *self, PyObject *other)
|
||||
{
|
||||
PyObject *newstrings = template_strings_append_str(self->strings, other);
|
||||
if (newstrings == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *newtemplate = _PyTemplate_Build(newstrings, self->interpolations);
|
||||
Py_DECREF(newstrings);
|
||||
return newtemplate;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
template_concat_str_template(templateobject *self, PyObject *other)
|
||||
{
|
||||
PyObject *newstrings = template_strings_prepend_str(self->strings, other);
|
||||
if (newstrings == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *newtemplate = _PyTemplate_Build(newstrings, self->interpolations);
|
||||
Py_DECREF(newstrings);
|
||||
return newtemplate;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
_PyTemplate_Concat(PyObject *self, PyObject *other)
|
||||
{
|
||||
if (_PyTemplate_CheckExact(self) && _PyTemplate_CheckExact(other)) {
|
||||
return template_concat_templates((templateobject *) self, (templateobject *) other);
|
||||
}
|
||||
else if ((_PyTemplate_CheckExact(self)) && PyUnicode_Check(other)) {
|
||||
return template_concat_template_str((templateobject *) self, other);
|
||||
}
|
||||
else if (PyUnicode_Check(self) && (_PyTemplate_CheckExact(other))) {
|
||||
return template_concat_str_template((templateobject *) other, self);
|
||||
}
|
||||
else {
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
template_values_get(PyObject *op, void *Py_UNUSED(data))
|
||||
{
|
||||
templateobject *self = templateobject_CAST(op);
|
||||
|
||||
Py_ssize_t len = PyTuple_GET_SIZE(self->interpolations);
|
||||
PyObject *values = PyTuple_New(PyTuple_GET_SIZE(self->interpolations));
|
||||
if (values == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (Py_ssize_t i = 0; i < len; i++) {
|
||||
PyObject *item = PyTuple_GET_ITEM(self->interpolations, i);
|
||||
PyTuple_SET_ITEM(values, i, _PyInterpolation_GetValueRef(item));
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
static PyMemberDef template_members[] = {
|
||||
{"strings", Py_T_OBJECT_EX, offsetof(templateobject, strings), Py_READONLY, "Strings"},
|
||||
{"interpolations", Py_T_OBJECT_EX, offsetof(templateobject, interpolations), Py_READONLY, "Interpolations"},
|
||||
{NULL},
|
||||
};
|
||||
|
||||
static PyGetSetDef template_getset[] = {
|
||||
{"values", template_values_get, NULL,
|
||||
PyDoc_STR("Values of interpolations"), NULL},
|
||||
{NULL},
|
||||
};
|
||||
|
||||
static PySequenceMethods template_as_sequence = {
|
||||
.sq_concat = _PyTemplate_Concat,
|
||||
};
|
||||
|
||||
static PyObject*
|
||||
template_reduce(PyObject *op, PyObject *Py_UNUSED(dummy))
|
||||
{
|
||||
PyObject *mod = PyImport_ImportModule("string.templatelib");
|
||||
if (mod == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject *func = PyObject_GetAttrString(mod, "_template_unpickle");
|
||||
Py_DECREF(mod);
|
||||
if (func == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
templateobject *self = templateobject_CAST(op);
|
||||
PyObject *result = Py_BuildValue("O(OO)",
|
||||
func,
|
||||
self->strings,
|
||||
self->interpolations);
|
||||
|
||||
Py_DECREF(func);
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyMethodDef template_methods[] = {
|
||||
{"__reduce__", template_reduce, METH_NOARGS, NULL},
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
PyTypeObject _PyTemplate_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "string.templatelib.Template",
|
||||
.tp_doc = PyDoc_STR("Template object"),
|
||||
.tp_basicsize = sizeof(templateobject),
|
||||
.tp_itemsize = 0,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
||||
.tp_as_sequence = &template_as_sequence,
|
||||
.tp_new = template_new,
|
||||
.tp_alloc = PyType_GenericAlloc,
|
||||
.tp_dealloc = template_dealloc,
|
||||
.tp_clear = template_clear,
|
||||
.tp_free = PyObject_GC_Del,
|
||||
.tp_repr = template_repr,
|
||||
.tp_members = template_members,
|
||||
.tp_methods = template_methods,
|
||||
.tp_getset = template_getset,
|
||||
.tp_iter = template_iter,
|
||||
.tp_traverse = template_traverse,
|
||||
};
|
||||
|
||||
PyObject *
|
||||
_PyTemplate_Build(PyObject *strings, PyObject *interpolations)
|
||||
{
|
||||
templateobject *template = PyObject_GC_New(templateobject, &_PyTemplate_Type);
|
||||
if (template == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template->strings = Py_NewRef(strings);
|
||||
template->interpolations = Py_NewRef(interpolations);
|
||||
PyObject_GC_Track(template);
|
||||
return (PyObject *) template;
|
||||
}
|
|
@ -56,6 +56,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include "pycore_pyhash.h" // _Py_HashSecret_t
|
||||
#include "pycore_pylifecycle.h" // _Py_SetFileSystemEncoding()
|
||||
#include "pycore_pystate.h" // _PyInterpreterState_GET()
|
||||
#include "pycore_template.h" // _PyTemplate_Concat()
|
||||
#include "pycore_tuple.h" // _PyTuple_FromArray()
|
||||
#include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI
|
||||
#include "pycore_unicodeobject.h" // struct _Py_unicode_state
|
||||
|
@ -11628,10 +11629,16 @@ PyUnicode_Concat(PyObject *left, PyObject *right)
|
|||
return NULL;
|
||||
|
||||
if (!PyUnicode_Check(right)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"can only concatenate str (not \"%.200s\") to str",
|
||||
Py_TYPE(right)->tp_name);
|
||||
return NULL;
|
||||
if (_PyTemplate_CheckExact(right)) {
|
||||
// str + tstring is implemented in the tstring type
|
||||
return _PyTemplate_Concat(left, right);
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"can only concatenate str (not \"%.200s\") to str",
|
||||
Py_TYPE(right)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Shortcuts */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue