summaryrefslogtreecommitdiffstats
path: root/Python/compile.c
diff options
context:
space:
mode:
authorInada Naoki <songofacandy@gmail.com>2023-07-15 10:33:32 (GMT)
committerGitHub <noreply@github.com>2023-07-15 10:33:32 (GMT)
commit2566b74b26bcce24199427acea392aed644f4b17 (patch)
tree91d2ae486db81b01c8451f26382f2eedddaf2ba1 /Python/compile.c
parentbbf62979851283b601b2dac0073ab331ebeb3be9 (diff)
downloadcpython-2566b74b26bcce24199427acea392aed644f4b17.zip
cpython-2566b74b26bcce24199427acea392aed644f4b17.tar.gz
cpython-2566b74b26bcce24199427acea392aed644f4b17.tar.bz2
gh-81283: compiler: remove indent from docstring (#106411)
Co-authored-by: Éric <merwok@netwok.org>
Diffstat (limited to 'Python/compile.c')
-rw-r--r--Python/compile.c99
1 files changed, 98 insertions, 1 deletions
diff --git a/Python/compile.c b/Python/compile.c
index 9e86e06..b80f7c0 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -1704,10 +1704,16 @@ compiler_body(struct compiler *c, location loc, asdl_stmt_seq *stmts)
if (c->c_optimize < 2) {
docstring = _PyAST_GetDocString(stmts);
if (docstring) {
+ PyObject *cleandoc = _PyCompile_CleanDoc(docstring);
+ if (cleandoc == NULL) {
+ return ERROR;
+ }
i = 1;
st = (stmt_ty)asdl_seq_GET(stmts, 0);
assert(st->kind == Expr_kind);
- VISIT(c, expr, st->v.Expr.value);
+ location loc = LOC(st->v.Expr.value);
+ ADDOP_LOAD_CONST(c, loc, cleandoc);
+ Py_DECREF(cleandoc);
RETURN_IF_ERROR(compiler_nameop(c, NO_LOCATION, &_Py_ID(__doc__), Store));
}
}
@@ -2252,11 +2258,19 @@ compiler_function_body(struct compiler *c, stmt_ty s, int is_async, Py_ssize_t f
/* if not -OO mode, add docstring */
if (c->c_optimize < 2) {
docstring = _PyAST_GetDocString(body);
+ if (docstring) {
+ docstring = _PyCompile_CleanDoc(docstring);
+ if (docstring == NULL) {
+ compiler_exit_scope(c);
+ return ERROR;
+ }
+ }
}
if (compiler_add_const(c->c_const_cache, c->u, docstring ? docstring : Py_None) < 0) {
compiler_exit_scope(c);
return ERROR;
}
+ Py_XDECREF(docstring);
c->u->u_metadata.u_argcount = asdl_seq_LEN(args->args);
c->u->u_metadata.u_posonlyargcount = asdl_seq_LEN(args->posonlyargs);
@@ -7967,6 +7981,89 @@ error:
return NULL;
}
+// C implementation of inspect.cleandoc()
+//
+// Difference from inspect.cleandoc():
+// - Do not remove leading and trailing blank lines to keep lineno.
+PyObject *
+_PyCompile_CleanDoc(PyObject *doc)
+{
+ doc = PyObject_CallMethod(doc, "expandtabs", NULL);
+ if (doc == NULL) {
+ return NULL;
+ }
+
+ Py_ssize_t doc_size;
+ const char *doc_utf8 = PyUnicode_AsUTF8AndSize(doc, &doc_size);
+ if (doc_utf8 == NULL) {
+ Py_DECREF(doc);
+ return NULL;
+ }
+ const char *p = doc_utf8;
+ const char *pend = p + doc_size;
+
+ // First pass: find minimum indentation of any non-blank lines
+ // after first line.
+ while (p < pend && *p++ != '\n') {
+ }
+
+ Py_ssize_t margin = PY_SSIZE_T_MAX;
+ while (p < pend) {
+ const char *s = p;
+ while (*p == ' ') p++;
+ if (p < pend && *p != '\n') {
+ margin = Py_MIN(margin, p - s);
+ }
+ while (p < pend && *p++ != '\n') {
+ }
+ }
+ if (margin == PY_SSIZE_T_MAX) {
+ margin = 0;
+ }
+
+ // Second pass: write cleandoc into buff.
+
+ // copy first line without leading spaces.
+ p = doc_utf8;
+ while (*p == ' ') {
+ p++;
+ }
+ if (p == doc_utf8 && margin == 0 ) {
+ // doc is already clean.
+ return doc;
+ }
+
+ char *buff = PyMem_Malloc(doc_size);
+ char *w = buff;
+
+ while (p < pend) {
+ int ch = *w++ = *p++;
+ if (ch == '\n') {
+ break;
+ }
+ }
+
+ // copy subsequent lines without margin.
+ while (p < pend) {
+ for (Py_ssize_t i = 0; i < margin; i++, p++) {
+ if (*p != ' ') {
+ assert(*p == '\n' || *p == '\0');
+ break;
+ }
+ }
+ while (p < pend) {
+ int ch = *w++ = *p++;
+ if (ch == '\n') {
+ break;
+ }
+ }
+ }
+
+ Py_DECREF(doc);
+ return PyUnicode_FromStringAndSize(buff, w - buff);
+}
+
+
PyObject *
_PyCompile_CodeGen(PyObject *ast, PyObject *filename, PyCompilerFlags *pflags,
int optimize, int compile_mode)