diff --git a/contrib/check-code.py b/contrib/check-code.py --- a/contrib/check-code.py +++ b/contrib/check-code.py @@ -40,6 +40,8 @@ try: except ImportError: re2 = None +import testparseutil + def compilere(pat, multiline=False): if multiline: pat = '(?m)' + pat @@ -402,6 +404,15 @@ pypats = [ ] + commonpypats[1] ] +# patterns to check *.py for embedded ones in test script +embeddedpypats = [ + [ + ] + commonpypats[0], + # warnings + [ + ] + commonpypats[1] +] + # common filters to convert *.py commonpyfilters = [ (r"""(?msx)(?P\#.*?$)| @@ -426,6 +437,10 @@ pynfpats = [ [], ] +# filters to convert *.py for embedded ones in test script +embeddedpyfilters = [ +] + commonpyfilters + # extension non-filter patterns pyextnfpats = [ [(r'^"""\n?[A-Z]', "don't capitalize docstring title")], @@ -560,6 +575,15 @@ checks = [ allfilesfilters, allfilespats), ] +# (desc, +# func to pick up embedded code fragments, +# list of patterns to convert target files +# list of patterns to detect errors/warnings) +embeddedchecks = [ + ('embedded python', + testparseutil.pyembedded, embeddedpyfilters, embeddedpypats) +] + def _preparepats(): def preparefailandwarn(failandwarn): for pats in failandwarn: @@ -580,7 +604,7 @@ def _preparepats(): for i, flt in enumerate(filters): filters[i] = re.compile(flt[0]), flt[1] - for cs in (checks,): + for cs in (checks, embeddedchecks): for c in cs: failandwarn = c[-1] preparefailandwarn(failandwarn) @@ -674,6 +698,30 @@ def checkfile(f, logfunc=_defaultlogger. if fc: result = False + if f.endswith('.t') and "no-" "check-code" not in pre: + if debug: + print("Checking embedded code in %s" % (f)) + + prelines = pre.splitlines() + embeddederros = [] + for name, embedded, filters, pats in embeddedchecks: + # "reset curmax at each repetition" treats maxerr as "max + # nubmer of errors in an actual file per entry of + # (embedded)checks" + curmaxerr = maxerr + + for found in embedded(f, prelines, embeddederros): + filename, starts, ends, code = found + fc = _checkfiledata(name, f, code, filters, pats, context, + logfunc, curmaxerr, warnings, blame, debug, + lineno, offset=starts - 1) + if fc: + result = False + if curmaxerr: + if fc >= curmaxerr: + break + curmaxerr -= fc + return result def _checkfiledata(name, f, filedata, filters, pats, context, diff --git a/tests/test-contrib-check-code.t b/tests/test-contrib-check-code.t --- a/tests/test-contrib-check-code.t +++ b/tests/test-contrib-check-code.t @@ -379,3 +379,51 @@ should break rules depending on result o > class empty(object): omit superfluous pass [1] + +Check code fragments embedded in test script + + $ cat > embedded-code.t < code fragment in doctest style + > >>> x = (1,2) + > ... + > ... x = (1,2) + > + > code fragment in heredoc style + > $ python < > x = (1,2) + > > EOF + > + > code fragment in file heredoc style + > $ python > file.py < > x = (1,2) + > > EOF + > NO_CHECK_EOF + $ "$check_code" embedded-code.t + embedded-code.t:2: + > x = (1,2) + missing whitespace after , + embedded-code.t:4: + > x = (1,2) + missing whitespace after , + embedded-code.t:8: + > x = (1,2) + missing whitespace after , + embedded-code.t:13: + > x = (1,2) + missing whitespace after , + [1] + +"max warnings per file" is shared by all embedded code fragments + + $ "$check_code" --per-file=3 embedded-code.t + embedded-code.t:2: + > x = (1,2) + missing whitespace after , + embedded-code.t:4: + > x = (1,2) + missing whitespace after , + embedded-code.t:8: + > x = (1,2) + missing whitespace after , + (too many errors, giving up) + [1]