[checks] Implement Python checks and fix some violations - #9146
[checks] Implement Python checks and fix some violations#9146eisenwave wants to merge 24 commits into
Conversation
| - name: setup Node | ||
| uses: actions/setup-node@v6 | ||
| with: | ||
| node-version: "20" | ||
|
|
||
| - name: install pyright | ||
| run: npm install -g pyright |
There was a problem hiding this comment.
I'm not sure pyright is a good call. There are other alternatives that do not require pulling in node as dependency.
There was a problem hiding this comment.
I just use it because it integrates nicely with VSCode out of the box. What would you recommend instead?
There was a problem hiding this comment.
I (still) tend to go for mypy for my own projects, but if this gives you a better user experience I'm okay with it.
| message: str, | ||
| ) -> None: | ||
| """Report a failure at `self.file_path`.""" | ||
| emit_check_failure( |
There was a problem hiding this comment.
emit_check_failure is only called from within classes deriving from Check. Can you fold it into fail and consistently use this one?
There was a problem hiding this comment.
After the changes in 7af3d56, I wouldn't want to. The difference is that fail is just a convenience function that provides the file path automatically, and I would want there to be a global "emit failure" function at some level, in case we expand the sources of failures in the future.
emit_check_failure is basically the global console.error counterpart, and that has kind of a right to exist even if you always use it via a wrapper. That might change at some point.
There was a problem hiding this comment.
ok. Have you considered a configured global logger for this instead? Python's logging package is very useful.
|
@Tsche thanks a lot for the review. It'll take some time to clean it all up. |
|
All the pre-existing issues are taken care of in separate PRs. Now comes rebasing on the latest motions and addressing all the feedback on the Python script. |
1. Nested if statements get flagged. These are replaced with "and". 2. Mutable default initializers for members are flagged. Initialization is moved to __init__.
Tsche
left a comment
There was a problem hiding this comment.
I took a brief look at this again. Please note that some of the comments apply to multiple locations - I did not repeat them.
|
|
||
| \definition{implementation-defined strict total order over pointers} | ||
| {defns.order.ptr} | ||
| \definition{implementation-defined strict total order over pointers}{defns.order.ptr} |
There was a problem hiding this comment.
this looks like an unrelated change, does it have to be in this patch?
| "reportUnknownParameterType": false, | ||
| "reportUnusedImport": true, | ||
| "reportUnusedVariable": true, | ||
| "pythonVersion": "3.12" |
There was a problem hiding this comment.
Is there any particular reason why this requires Python 3.12 rather than the current version (3.14)?
| from data import * | ||
|
|
||
| # =============================================================================== | ||
| # Terminal color support |
There was a problem hiding this comment.
Please get rid of these section headers
| # Active-set management — controls whether check_line is called. | ||
| active: set[str] = set(all_ids) | ||
| # Per-check next-line skips: check_id → set of line numbers. |
There was a problem hiding this comment.
Please get rid of the LLMy em dashes and other unicode symbols (such as →) in comments. This might cause rendering issues with some editors and environments, I don't see a need to use anything but the ASCII-compatible subset here (even though Python assumes UTF-8 anyway).
| # -- Text checks ------------------------------------------------------------------------------- | ||
| # Such checks run on all files and identify problems like illegal characters, | ||
| # trailing whitespace, etc. | ||
| # ---------------------------------------------------------------------------------------------- | ||
| NonAsciiCheck("text-non-ascii-char"), | ||
| BannedPatternCheck( | ||
| "text-trailing-ws", | ||
| re.compile(r"\s+$"), | ||
| "Line has trailing whitespace; remove the extra spaces.", | ||
| ), | ||
| TrailingEmptyLinesCheck("text-trailing-empty-lines"), |
There was a problem hiding this comment.
Could you push those groups of checks into some CheckGroup object and attach the comment/name to it as strings?
That way you can reuse this for the command line interface (or well, its help text) to group checks and print a short description/name for those groups.
| message: str, | ||
| ) -> None: | ||
| """Report a failure at `self.file_path`.""" | ||
| emit_check_failure( |
There was a problem hiding this comment.
ok. Have you considered a configured global logger for this instead? Python's logging package is very useful.
| help="Specific .tex files or directories to check (default: all .tex files" | ||
| " under the project root).", | ||
| ) | ||
| args = parser.parse_args() |
There was a problem hiding this comment.
Please add a command/argument that just prints all checks and exits. Alternatively you could put this into the help text, but I'd prefer to (at least additionally) have a command.
If you do the check grouping thing I suggested this would allow filtering checks by group etc.
|
|
||
|
|
||
| def main() -> None: | ||
| parser = argparse.ArgumentParser( |
There was a problem hiding this comment.
Could we make it configurable which checks are performed? This could be useful to debug a specific failure.
| exit_code = num_failures > 1 | ||
| sys.exit(exit_code) |
There was a problem hiding this comment.
exit_code seems extraneous. Please fold this into the sys.exit invocation.
|
|
||
| for file_path in tex_files: | ||
| lines = read_file(file_path) | ||
| for idx in range(len(lines) - 1): |
There was a problem hiding this comment.
how about
for idx, line in enumerate(lines[:-1]):





Fixes #8086
I've mentioned the option to improve the checks in Croydon to @tkoeppe, and he seemed generally open to the idea.
In summary, this PR converts all the checks we have in
check-source.sh(plus some additional once adopted fromcheck-output.sh) and turns them into a Python script. The benefits are:sedandgreppiping. The code is relatively simple Python. It still uses a bunch of regular expressions for a lot of checks; the greatest benefits are in the more complex checks.EXPECTCHECKNEXTLINE.NOCHECKBEGINand other directives. This makes it possible for us to add much more aggressive checks that would have previously been infeasible because they generate too much noise in old TeX sources.\tcode{\exposid{...}}in a part ofnumerics.texbecause there are too many violations.\textitin "newer" TeX ranges for example without having to fix every single violation. We could also use it to catch many "bad words" or "bad phrases" that appear too often to be checked right now, but that we don't want perpetuated.\refto a section that is introduced by another paper, you can still get a green build by suppressing the\refcheck (that is, at least for thecheck-sourcechecks).There are a few notable new checks that make things a lot easier in the future:
base-env-balancingchecks for balancing of\beginand\endcommands. It's quite easy to get it wrong, and when you do, it tends to blow up after minutes during the TeX build in bizarre and hard-to-debug ways.base-unknown-commandchecks for use of TeX commands that have never appeared before in the code base. It's very unlikely that this is intentional in most PRs, and very likely that someone just wrote\tocdeinstead of\tcodeor something else stupid. If we need a new TeX command, we can just add it to the list.