I recently noticed a recurring pattern in Python develop toolchains that seems worth discussing:
Python tooling has accumulated a growing number of tiny, tool-specific DSLs hidden inside comments:
# pylint: ...
# mypy: ...
# pyright: ...
# noqa
# ruff: ...
# fmt: ...
# isort: ...
and many others.
Even Python itself has special comment-based syntax, such as type comments and # type: ignore[...], which are treated specially by the parser and AST.
These constructs are no longer really ordinary comments: they carry structured, machine-readable semantics. Yet every tool has to invent its own mini grammar and parsing rules for essentially the same purpose.
I wonder whether we could approach this in a unified way:
Use a clear, dedicated syntax marker for all tool directives, instead of pseudo comment. May be some unoccupied operator in python syntax like “!” (exclaimation operator) is helpful? for example, to declare lint rules:
Instead of letting every tool invent its own tiny DSL, let the directive payload use normal Python expression syntax. Just like the example that I took above.
Type comments have already gone through a process of standardization, eventually becomes type annotations. I wonder if these different comment DSLs invented by various tools could someday go through a similar process and become something standardized.
You’ll have to come up with the actual semantics of the ! operator. Does it produce something that is attached to the code in some way, similar to how docstrings become __doc__ attributes?
If so, then you have the problem of how to do that in all the needed ways. How will you associated one of these markers with a statement, for example?
If you don’t attach anything to the code, then isn’t this just another comment syntax? What have we gained?
Alternatively, specify that such special comments start with, say, #!. We already have that with the shebang line, although the shebang line must be the first line. If that’s a problem, then maybe #@, which kind of resembles the decorator syntax.
What is the advantage of insisting the comments all start with #!? The tools need to find the comments themselves anyway.
BTW, for people interested in handling these comment pragmas: coverage.py uses user-defineable regexes, which have proven even more powerful than expected: Coverage.py regex pragmas | Ned Batchelder
I think it should be handled like type_ignore in current python parser: to produce real AST nodes (ast.type_ignore). In this way, it can be post-proceeded by regular ast tools.
This fits nicely with my second suggestion: tool directives should use normal Python expression syntax. That way, the AST for the expression (ast.expr) can be embedded directly in the new AST node.
I’m not particularly attached to ! (or any specific marker). I only mentioned it because ! is currently mostly unused syntax space in Python. Any other clear and intuitive syntax would work just as well.
For a language-level solution, though, I think this eventually needs AST support. Regexes may be enough for an individual tool, but if this grows into a standardized syntax, they would become much harder to maintain.
I think it’s neat that they’re comments, not python code. I also think it’s neat that # type: ignore ends up in the AST.
I don’t see any reason why we couldn’t formalise that the pattern # {rule-catagory}: {instruction} should result in a specific AST node? That way almost all of these examples you’ve collected can be kept as-is. Only # noqa should be adjusted to # noqa: all or something in that direction, and # type: ignore[...] could be adjusted to # type-ignore: ..., but that wouldn’t be strictly necessary and therefore probably wouldn’t happen.
One complication is you’d also catch # TODO: ... and # TODO(peter, 2026-09-15): ..., but is that really a problem? The general pattern is that specific linters specify rule-catagory patterns they care about (for example, mypy cares about #type: ignore[...] and # mypy: ...), so linters that don’t care about TODO can specify that they don’t, and linters that want to can also have the freedom to specify that they do.
Let’s try to think about what kind of expressions there linter-only expressions there are.
All the ones I am familiar with belong to the categories:
rule-catagory: ignore this rule for this line
rule-catagory: begin ignoring this rule
rule-catagory: stop ignoring this rule
Where one complication is that some “begin ignoring this rule” instructions are only allowed to appear at the start of a file and don’t have a corresponding “stop ignoring this rule” tag.
But there’s probably important other use-cases?
Another related question here is whether we could get (for example) mypy and ty to agree on type: ignore[...] catagories.
Pylint also has a disable-next= directive affecting the following line which I find very useful. You don’t have to make a long line even longer. Often I wish other tools had such feature too.
And I’m not sure if all utilities have the same notion of “line”. There can be a logical line spanning multiple source code lines.
I’ve long thought that we should have something “better” or at least more consistent for these pragmas as they’re commonly called. I just have never really been able to come up with any concrete proposal.
Some tools at least honor pragmas on the preceding line, but many don’t, which can lead to long lines for right-hanging comments. The other problem is combining pragmas from multiple tools (say a type checker and an auto-formatter).
Thank you very much for the reply ! — I really appreciate hearing this from a Steering Council member
The two issues you mentioned are actually very close to what I had in mind. These issues are hard to coordinate when every tool invents its own little comment DSL. A unified and expressive syntax — and Python’s own expression syntax already gives us exactly that — seems like the most natural way forward.
I don’t think comment is the final place that tool directives should go to. For example, the typing standard library has already introduced tool hints that are pure python code, like @no_type_check. It is a normal decorator.
Yes, exactly. The biggest problem isn’t whether these tool directives can be neatly categorized, but that they all use different syntaxes, which creates a lot of context-switching overhead for developers and also makes IDE completion much harder.
where name could probably be limited to valid python identifiers. value can be more complex. Maybe accept anything to the end of the line.
As a thought experiment, that could be enough for a very basic specification. Multiple pragmas for a line and multi-line pragmas could be considered lated.
There’s a major issue that both patterns can also occur as regular comments. We have no way to structurally identify current pragmas. This means
we only know whether a comment is a pragma if we know that a tool has defined it. E.g. # fmt: skip is only special because ruff has defined it. This means there cannot be a be syntax highlighting or tool-agnostic parser, which makes it a bad spec
or a spec has to be breaking with the status quo, for example through introducing special comments like #! name: vlaue
Both are IMHO at the border to prohibitively bad design choices.