Merge branch 'docstring-indentation'

* docstring-indentation:
  Improve python code indentation warning / error messages
  Function comment header formatting corrections
  Improve Python docstring indentation handling
This commit is contained in:
William S Fulton 2015-07-30 20:17:49 +01:00
commit 00a9b8d3af
9 changed files with 410 additions and 78 deletions

View file

@ -12,6 +12,7 @@
* ----------------------------------------------------------------------------- */
#include "swigmod.h"
#include <limits.h>
#include "cparse.h"
#include <ctype.h>
#include <errno.h>
@ -1299,7 +1300,7 @@ public:
/* ------------------------------------------------------------
* import_name_string()
* ------------------------------------------------------------ */
* ------------------------------------------------------------ */
static String *import_name_string(const String *mainpkg, const String *mainmod, const String *pkg, const String *mod, const String *sym) {
if (!relativeimport) {
@ -1345,9 +1346,10 @@ public:
/* ------------------------------------------------------------
* funcCall()
* Emit shadow code to call a function in the extension
* module. Using proper argument and calling style for
* given node n.
*
* Emit shadow code to call a function in the extension
* module. Using proper argument and calling style for
* given node n.
* ------------------------------------------------------------ */
String *funcCall(String *name, String *parms) {
String *str = NewString("");
@ -1356,12 +1358,15 @@ public:
return str;
}
/* ------------------------------------------------------------
* pythoncode() - Output python code into the shadow file
* indent_pythoncode()
*
* Format (indent) Python code.
* Remove leading whitespace from 'code' and re-indent using
* the indentation string in 'indent'.
* ------------------------------------------------------------ */
String *pythoncode(String *code, const_String_or_char_ptr indent, String * file, int line) {
String *indent_pythoncode(const String *code, const_String_or_char_ptr indent, String *file, int line, const char *directive_name) {
String *out = NewString("");
String *temp;
char *t;
@ -1383,7 +1388,7 @@ public:
// Line number within the pythoncode.
int py_line = 0;
String * initial = 0;
String *initial = 0;
Iterator si;
/* Get the initial indentation. Skip lines which only contain whitespace
@ -1401,7 +1406,7 @@ public:
const char *c = Char(si.item);
int i;
for (i = 0; isspace((unsigned char)c[i]); i++) {
// Scan forward until we find a non-space (which may be a nul byte).
// Scan forward until we find a non-space (which may be a null byte).
}
char ch = c[i];
if (ch && ch != '#') {
@ -1423,7 +1428,7 @@ public:
int i;
for (i = 0; isspace((unsigned char)c[i]); i++) {
// Scan forward until we find a non-space (which may be a nul byte).
// Scan forward until we find a non-space (which may be a null byte).
}
char ch = c[i];
if (!ch) {
@ -1448,7 +1453,7 @@ public:
if (i < Len(initial)) {
// There's non-whitespace in the initial prefix of this line.
Swig_error(file, line, "Line indented less than expected (line %d of pythoncode)\n", py_line);
Swig_error(file, line, "Line indented less than expected (line %d of %s) as no line should be indented less than the indentation in line 1\n", py_line, directive_name);
Printv(out, indent, c, "\n", NIL);
} else {
if (memcmp(c, Char(initial), Len(initial)) == 0) {
@ -1457,7 +1462,7 @@ public:
continue;
}
Swig_warning(WARN_PYTHON_INDENT_MISMATCH,
file, line, "Whitespace prefix doesn't match (line %d of pythoncode)\n", py_line);
file, line, "Whitespace indentation is inconsistent compared to earlier lines (line %d of %s)\n", py_line, directive_name);
// To avoid gratuitously breaking interface files which worked with
// SWIG <= 3.0.5, we remove a prefix of the same number of bytes for
// lines which start with different whitespace to the line we got
@ -1469,6 +1474,72 @@ public:
return out;
}
/* ------------------------------------------------------------
* indent_docstring()
*
* Format (indent) a Python docstring.
* Remove leading whitespace from 'code' and re-indent using
* the indentation string in 'indent'.
* ------------------------------------------------------------ */
String *indent_docstring(const String *code, const_String_or_char_ptr indent) {
String *out = NewString("");
String *temp;
char *t;
if (!indent)
indent = "";
temp = NewString(code);
t = Char(temp);
if (*t == '{') {
Delitem(temp, 0);
Delitem(temp, DOH_END);
}
/* Split the input text into lines */
List *clist = SplitLines(temp);
Delete(temp);
Iterator si;
int truncate_characters_count = INT_MAX;
for (si = First(clist); si.item; si = Next(si)) {
const char *c = Char(si.item);
int i;
for (i = 0; isspace((unsigned char)c[i]); i++) {
// Scan forward until we find a non-space (which may be a null byte).
}
char ch = c[i];
if (ch) {
// Found a line which isn't just whitespace
if (i < truncate_characters_count)
truncate_characters_count = i;
}
}
if (truncate_characters_count == INT_MAX)
truncate_characters_count = 0;
for (si = First(clist); si.item; si = Next(si)) {
const char *c = Char(si.item);
int i;
for (i = 0; isspace((unsigned char)c[i]); i++) {
// Scan forward until we find a non-space (which may be a null byte).
}
char ch = c[i];
if (!ch) {
// Line is just whitespace - emit an empty line.
Putc('\n', out);
continue;
}
Printv(out, indent, c + truncate_characters_count, "\n", NIL);
}
Delete(clist);
return out;
}
/* ------------------------------------------------------------
* autodoc level declarations
@ -1504,8 +1575,9 @@ public:
/* ------------------------------------------------------------
* have_docstring()
* Check if there is a docstring directive and it has text,
* or there is an autodoc flag set
*
* Check if there is a docstring directive and it has text,
* or there is an autodoc flag set
* ------------------------------------------------------------ */
bool have_docstring(Node *n) {
@ -1515,9 +1587,10 @@ public:
/* ------------------------------------------------------------
* docstring()
* Get the docstring text, stripping off {} if neccessary,
* and enclose in triple double quotes. If autodoc is also
* set then it will build a combined docstring.
*
* Get the docstring text, stripping off {} if neccessary,
* and enclose in triple double quotes. If autodoc is also
* set then it will build a combined docstring.
* ------------------------------------------------------------ */
String *docstring(Node *n, autodoc_t ad_type, const String *indent, bool use_triple = true) {
@ -1552,21 +1625,21 @@ public:
if (have_auto && have_ds) { // Both autodoc and docstring are present
doc = NewString("");
Printv(doc, triple_double, "\n",
pythoncode(autodoc, indent, Getfile(n), Getline(n)), "\n",
pythoncode(str, indent, Getfile(n), Getline(n)), indent, triple_double, NIL);
indent_docstring(autodoc, indent), "\n",
indent_docstring(str, indent), indent, triple_double, NIL);
} else if (!have_auto && have_ds) { // only docstring
if (Strchr(str, '\n') == 0) {
doc = NewStringf("%s%s%s", triple_double, str, triple_double);
} else {
doc = NewString("");
Printv(doc, triple_double, "\n", pythoncode(str, indent, Getfile(n), Getline(n)), indent, triple_double, NIL);
Printv(doc, triple_double, "\n", indent_docstring(str, indent), indent, triple_double, NIL);
}
} else if (have_auto && !have_ds) { // only autodoc
if (Strchr(autodoc, '\n') == 0) {
doc = NewStringf("%s%s%s", triple_double, autodoc, triple_double);
} else {
doc = NewString("");
Printv(doc, triple_double, "\n", pythoncode(autodoc, indent, Getfile(n), Getline(n)), indent, triple_double, NIL);
Printv(doc, triple_double, "\n", indent_docstring(autodoc, indent), indent, triple_double, NIL);
}
} else
doc = NewString("");
@ -1580,8 +1653,9 @@ public:
/* ------------------------------------------------------------
* cdocstring()
* Get the docstring text as it would appear in C-language
* source code.
*
* Get the docstring text as it would appear in C-language
* source code.
* ------------------------------------------------------------ */
String *cdocstring(Node *n, autodoc_t ad_type)
@ -1610,7 +1684,8 @@ public:
/* -----------------------------------------------------------------------------
* addMissingParameterNames()
* For functions that have not had nameless parameters set in the Language class.
*
* For functions that have not had nameless parameters set in the Language class.
*
* Inputs:
* plist - entire parameter list
@ -1635,8 +1710,9 @@ public:
/* ------------------------------------------------------------
* make_autodocParmList()
* Generate the documentation for the function parameters
* Parameters:
*
* Generate the documentation for the function parameters
* Parameters:
* func_annotation: Function annotation support
* ------------------------------------------------------------ */
@ -1743,12 +1819,13 @@ public:
/* ------------------------------------------------------------
* make_autodoc()
* Build a docstring for the node, using parameter and other
* info in the parse tree. If the value of the autodoc
* attribute is "0" then do not include parameter types, if
* it is "1" (the default) then do. If it has some other
* value then assume it is supplied by the extension writer
* and use it directly.
*
* Build a docstring for the node, using parameter and other
* info in the parse tree. If the value of the autodoc
* attribute is "0" then do not include parameter types, if
* it is "1" (the default) then do. If it has some other
* value then assume it is supplied by the extension writer
* and use it directly.
* ------------------------------------------------------------ */
String *make_autodoc(Node *n, autodoc_t ad_type) {
@ -1887,9 +1964,10 @@ public:
}
/* ------------------------------------------------------------
* convertDoubleValue()
* Check if the given string looks like a decimal floating point constant
* and return it if it does, otherwise return NIL.
* convertDoubleValue()
*
* Check if the given string looks like a decimal floating point constant
* and return it if it does, otherwise return NIL.
* ------------------------------------------------------------ */
String *convertDoubleValue(String *v) {
const char *const s = Char(v);
@ -1931,8 +2009,9 @@ public:
/* ------------------------------------------------------------
* convertValue()
* Check if string v can be a Python value literal or a
* constant. Return NIL if it isn't.
*
* Check if string v can be a Python value literal or a
* constant. Return NIL if it isn't.
* ------------------------------------------------------------ */
String *convertValue(String *v, SwigType *type) {
const char *const s = Char(v);
@ -2054,12 +2133,13 @@ public:
/* ------------------------------------------------------------
* is_representable_as_pyargs()
* Check if the function parameters default argument values
* can be represented in Python.
*
* If this method returns false, the parameters will be translated
* to a generic "*args" which allows us to deal with default values
* at C++ code level where they can always be handled.
* Check if the function parameters default argument values
* can be represented in Python.
*
* If this method returns false, the parameters will be translated
* to a generic "*args" which allows us to deal with default values
* at C++ code level where they can always be handled.
* ------------------------------------------------------------ */
bool is_representable_as_pyargs(Node *n) {
ParmList *plist = CopyParmList(Getattr(n, "parms"));
@ -2101,9 +2181,10 @@ public:
/* ------------------------------------------------------------
* is_real_overloaded()
* Check if the function is overloaded, but not just have some
* siblings generated due to the original function have
* default arguments.
*
* Check if the function is overloaded, but not just have some
* siblings generated due to the original function have
* default arguments.
* ------------------------------------------------------------ */
bool is_real_overloaded(Node *n) {
Node *h = Getattr(n, "sym:overloaded");
@ -2127,8 +2208,9 @@ public:
/* ------------------------------------------------------------
* make_pyParmList()
* Generate parameter list for Python functions or methods,
* reuse make_autodocParmList() to do so.
*
* Generate parameter list for Python functions or methods,
* reuse make_autodocParmList() to do so.
* ------------------------------------------------------------ */
String *make_pyParmList(Node *n, bool in_class, bool is_calling, int kw) {
/* Get the original function for a defaultargs copy,
@ -2174,7 +2256,8 @@ public:
/* ------------------------------------------------------------
* have_pythonprepend()
* Check if there is a %pythonprepend directive and it has text
*
* Check if there is a %pythonprepend directive and it has text
* ------------------------------------------------------------ */
bool have_pythonprepend(Node *n) {
@ -2184,7 +2267,8 @@ public:
/* ------------------------------------------------------------
* pythonprepend()
* Get the %pythonprepend code, stripping off {} if neccessary
*
* Get the %pythonprepend code, stripping off {} if neccessary
* ------------------------------------------------------------ */
String *pythonprepend(Node *n) {
@ -2199,7 +2283,8 @@ public:
/* ------------------------------------------------------------
* have_pythonappend()
* Check if there is a %pythonappend directive and it has text
*
* Check if there is a %pythonappend directive and it has text
* ------------------------------------------------------------ */
bool have_pythonappend(Node *n) {
@ -2211,7 +2296,8 @@ public:
/* ------------------------------------------------------------
* pythonappend()
* Get the %pythonappend code, stripping off {} if neccessary
*
* Get the %pythonappend code, stripping off {} if neccessary
* ------------------------------------------------------------ */
String *pythonappend(Node *n) {
@ -2229,7 +2315,8 @@ public:
/* ------------------------------------------------------------
* have_addtofunc()
* Check if there is a %addtofunc directive and it has text
*
* Check if there is a %addtofunc directive and it has text
* ------------------------------------------------------------ */
bool have_addtofunc(Node *n) {
@ -2239,8 +2326,9 @@ public:
/* ------------------------------------------------------------
* returnTypeAnnotation()
* Helper function for constructing the function annotation
* of the returning type, return a empty string for Python 2.x
*
* Helper function for constructing the function annotation
* of the returning type, return a empty string for Python 2.x
* ------------------------------------------------------------ */
String *returnTypeAnnotation(Node *n) {
String *ret = 0;
@ -2273,9 +2361,10 @@ public:
/* ------------------------------------------------------------
* emitFunctionShadowHelper()
* Refactoring some common code out of functionWrapper and
* dispatchFunction that writes the proxy code for non-member
* functions.
*
* Refactoring some common code out of functionWrapper and
* dispatchFunction that writes the proxy code for non-member
* functions.
* ------------------------------------------------------------ */
void emitFunctionShadowHelper(Node *n, File *f_dest, String *name, int kw) {
@ -2286,10 +2375,10 @@ public:
if (have_docstring(n))
Printv(f_dest, tab4, docstring(n, AUTODOC_FUNC, tab4), "\n", NIL);
if (have_pythonprepend(n))
Printv(f_dest, pythoncode(pythonprepend(n), tab4, Getfile(n), Getline(n)), "\n", NIL);
Printv(f_dest, indent_pythoncode(pythonprepend(n), tab4, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
if (have_pythonappend(n)) {
Printv(f_dest, tab4 "val = ", funcCall(name, callParms), "\n", NIL);
Printv(f_dest, pythoncode(pythonappend(n), tab4, Getfile(n), Getline(n)), "\n", NIL);
Printv(f_dest, indent_pythoncode(pythonappend(n), tab4, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n", NIL);
Printv(f_dest, tab4 "return val\n", NIL);
} else {
Printv(f_dest, tab4 "return ", funcCall(name, callParms), "\n", NIL);
@ -2304,7 +2393,8 @@ public:
/* ------------------------------------------------------------
* check_kwargs()
* check if using kwargs is allowed for this Node
*
* check if using kwargs is allowed for this Node
* ------------------------------------------------------------ */
int check_kwargs(Node *n) const {
@ -4498,7 +4588,7 @@ public:
have_repr = 1;
}
if (Getattr(n, "feature:shadow")) {
String *pycode = pythoncode(Getattr(n, "feature:shadow"), tab4, Getfile(n), Getline(n));
String *pycode = indent_pythoncode(Getattr(n, "feature:shadow"), tab4, Getfile(n), Getline(n), "%feature(\"shadow\")");
String *pyaction = NewStringf("%s.%s", module, fullname);
Replaceall(pycode, "$action", pyaction);
Delete(pyaction);
@ -4520,12 +4610,12 @@ public:
Printv(f_shadow, tab8, docstring(n, AUTODOC_METHOD, tab8), "\n", NIL);
if (have_pythonprepend(n)) {
fproxy = 0;
Printv(f_shadow, pythoncode(pythonprepend(n), tab8, Getfile(n), Getline(n)), "\n", NIL);
Printv(f_shadow, indent_pythoncode(pythonprepend(n), tab8, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
}
if (have_pythonappend(n)) {
fproxy = 0;
Printv(f_shadow, tab8, "val = ", funcCall(fullname, callParms), "\n", NIL);
Printv(f_shadow, pythoncode(pythonappend(n), tab8, Getfile(n), Getline(n)), "\n", NIL);
Printv(f_shadow, indent_pythoncode(pythonappend(n), tab8, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n", NIL);
Printv(f_shadow, tab8, "return val\n\n", NIL);
} else {
Printv(f_shadow, tab8, "return ", funcCall(fullname, callParms), "\n\n", NIL);
@ -4605,10 +4695,10 @@ public:
if (have_docstring(n))
Printv(f_shadow, tab8, docstring(n, AUTODOC_STATICFUNC, tab8), "\n", NIL);
if (have_pythonprepend(n))
Printv(f_shadow, pythoncode(pythonprepend(n), tab8, Getfile(n), Getline(n)), "\n", NIL);
Printv(f_shadow, indent_pythoncode(pythonprepend(n), tab8, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
if (have_pythonappend(n)) {
Printv(f_shadow, tab8, "val = ", funcCall(Swig_name_member(NSPACE_TODO, class_name, symname), callParms), "\n", NIL);
Printv(f_shadow, pythoncode(pythonappend(n), tab8, Getfile(n), Getline(n)), "\n", NIL);
Printv(f_shadow, indent_pythoncode(pythonappend(n), tab8, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n", NIL);
Printv(f_shadow, tab8, "return val\n\n", NIL);
} else {
Printv(f_shadow, tab8, "return ", funcCall(Swig_name_member(NSPACE_TODO, class_name, symname), callParms), "\n\n", NIL);
@ -4693,7 +4783,7 @@ public:
if (!have_constructor && handled_as_init) {
if (!builtin) {
if (Getattr(n, "feature:shadow")) {
String *pycode = pythoncode(Getattr(n, "feature:shadow"), tab4, Getfile(n), Getline(n));
String *pycode = indent_pythoncode(Getattr(n, "feature:shadow"), tab4, Getfile(n), Getline(n), "%feature(\"shadow\")");
String *pyaction = NewStringf("%s.%s", module, Swig_name_construct(NSPACE_TODO, symname));
Replaceall(pycode, "$action", pyaction);
Delete(pyaction);
@ -4722,7 +4812,7 @@ public:
if (have_docstring(n))
Printv(f_shadow, tab8, docstring(n, AUTODOC_CTOR, tab8), "\n", NIL);
if (have_pythonprepend(n))
Printv(f_shadow, pythoncode(pythonprepend(n), tab8, Getfile(n), Getline(n)), "\n", NIL);
Printv(f_shadow, indent_pythoncode(pythonprepend(n), tab8, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
Printv(f_shadow, pass_self, NIL);
if (fastinit) {
Printv(f_shadow, tab8, module, ".", class_name, "_swiginit(self, ", funcCall(Swig_name_construct(NSPACE_TODO, symname), callParms), ")\n", NIL);
@ -4732,7 +4822,7 @@ public:
tab8, "try:\n", tab8, tab4, "self.this.append(this)\n", tab8, "except:\n", tab8, tab4, "self.this = this\n", NIL);
}
if (have_pythonappend(n))
Printv(f_shadow, pythoncode(pythonappend(n), tab8, Getfile(n), Getline(n)), "\n\n", NIL);
Printv(f_shadow, indent_pythoncode(pythonappend(n), tab8, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n\n", NIL);
Delete(pass_self);
}
have_constructor = 1;
@ -4741,7 +4831,7 @@ public:
/* Hmmm. We seem to be creating a different constructor. We're just going to create a
function for it. */
if (Getattr(n, "feature:shadow")) {
String *pycode = pythoncode(Getattr(n, "feature:shadow"), "", Getfile(n), Getline(n));
String *pycode = indent_pythoncode(Getattr(n, "feature:shadow"), "", Getfile(n), Getline(n), "%feature(\"shadow\")");
String *pyaction = NewStringf("%s.%s", module, Swig_name_construct(NSPACE_TODO, symname));
Replaceall(pycode, "$action", pyaction);
Delete(pyaction);
@ -4755,7 +4845,7 @@ public:
if (have_docstring(n))
Printv(f_shadow_stubs, tab4, docstring(n, AUTODOC_CTOR, tab4), "\n", NIL);
if (have_pythonprepend(n))
Printv(f_shadow_stubs, pythoncode(pythonprepend(n), tab4, Getfile(n), Getline(n)), "\n", NIL);
Printv(f_shadow_stubs, indent_pythoncode(pythonprepend(n), tab4, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
String *subfunc = NULL;
/*
if (builtin)
@ -4768,7 +4858,7 @@ public:
Printv(f_shadow_stubs, tab4, "val.thisown = 1\n", NIL);
#endif
if (have_pythonappend(n))
Printv(f_shadow_stubs, pythoncode(pythonappend(n), tab4, Getfile(n), Getline(n)), "\n", NIL);
Printv(f_shadow_stubs, indent_pythoncode(pythonappend(n), tab4, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n", NIL);
Printv(f_shadow_stubs, tab4, "return val\n", NIL);
Delete(subfunc);
}
@ -4805,7 +4895,7 @@ public:
if (shadow) {
if (Getattr(n, "feature:shadow")) {
String *pycode = pythoncode(Getattr(n, "feature:shadow"), tab4, Getfile(n), Getline(n));
String *pycode = indent_pythoncode(Getattr(n, "feature:shadow"), tab4, Getfile(n), Getline(n), "%feature(\"shadow\")");
String *pyaction = NewStringf("%s.%s", module, Swig_name_destroy(NSPACE_TODO, symname));
Replaceall(pycode, "$action", pyaction);
Delete(pyaction);
@ -4823,7 +4913,7 @@ public:
if (have_docstring(n))
Printv(f_shadow, tab8, docstring(n, AUTODOC_DTOR, tab8), "\n", NIL);
if (have_pythonprepend(n))
Printv(f_shadow, pythoncode(pythonprepend(n), tab8, Getfile(n), Getline(n)), "\n", NIL);
Printv(f_shadow, indent_pythoncode(pythonprepend(n), tab8, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
#ifdef USE_THISOWN
Printv(f_shadow, tab8, "try:\n", NIL);
Printv(f_shadow, tab8, tab4, "if self.thisown:", module, ".", Swig_name_destroy(NSPACE_TODO, symname), "(self)\n", NIL);
@ -4831,7 +4921,7 @@ public:
#else
#endif
if (have_pythonappend(n))
Printv(f_shadow, pythoncode(pythonappend(n), tab8, Getfile(n), Getline(n)), "\n", NIL);
Printv(f_shadow, indent_pythoncode(pythonappend(n), tab8, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n", NIL);
Printv(f_shadow, tab8, "pass\n", NIL);
Printv(f_shadow, "\n", NIL);
}
@ -5011,12 +5101,12 @@ public:
if (!ImportMode && (Cmp(section, "python") == 0 || Cmp(section, "shadow") == 0)) {
if (shadow) {
String *pycode = pythoncode(code, shadow_indent, Getfile(n), Getline(n));
String *pycode = indent_pythoncode(code, shadow_indent, Getfile(n), Getline(n), "%pythoncode or %insert(\"python\") block");
Printv(f_shadow, pycode, NIL);
Delete(pycode);
}
} else if (!ImportMode && (Cmp(section, "pythonbegin") == 0)) {
String *pycode = pythoncode(code, "", Getfile(n), Getline(n));
String *pycode = indent_pythoncode(code, "", Getfile(n), Getline(n), "%pythonbegin or %insert(\"pythonbegin\") block");
Printv(f_shadow_begin, pycode, NIL);
Delete(pycode);
} else {