|
8 | 8 | from __future__ import annotations |
9 | 9 |
|
10 | 10 | import os |
| 11 | +import re |
11 | 12 | import subprocess |
12 | 13 | import sys |
13 | 14 | import tempfile |
@@ -76,19 +77,76 @@ def parse_script(input: list[str]) -> list[list[str]]: |
76 | 77 | return steps |
77 | 78 |
|
78 | 79 |
|
| 80 | +def _add_no_pretty_to_dmypy(input: str) -> str: |
| 81 | + """Add --no-pretty to a dmypy run/start command that has no -- separator. |
| 82 | +
|
| 83 | + For dmypy run/start, mypy flags are passed as positional args after --. |
| 84 | + When the command has no --, we need to insert -- before the positional args |
| 85 | + and append --no-pretty. We must keep any dmypy-specific named flags |
| 86 | + (like --export-types, --log-file FILE) before the -- separator. |
| 87 | + """ |
| 88 | + # Match: "dmypy run" or "dmypy start", then the rest of the args |
| 89 | + m = re.match(r"(dmypy (?:run|start))\s*(.*)", input) |
| 90 | + if not m: |
| 91 | + return input |
| 92 | + prefix = m.group(1) |
| 93 | + rest = m.group(2) |
| 94 | + |
| 95 | + # Known dmypy run/start flags that take no value |
| 96 | + no_value_flags = {"--export-types", "--verbose", "-v"} |
| 97 | + # Known dmypy run/start flags that take a value |
| 98 | + value_flags = {"--log-file", "--timeout", "--junit-xml", "--perf-stats-file"} |
| 99 | + |
| 100 | + parts = rest.split() |
| 101 | + dmypy_flags: list[str] = [] |
| 102 | + positional: list[str] = [] |
| 103 | + i = 0 |
| 104 | + while i < len(parts): |
| 105 | + if parts[i] in no_value_flags: |
| 106 | + dmypy_flags.append(parts[i]) |
| 107 | + i += 1 |
| 108 | + elif parts[i] in value_flags: |
| 109 | + dmypy_flags.append(parts[i]) |
| 110 | + if i + 1 < len(parts): |
| 111 | + dmypy_flags.append(parts[i + 1]) |
| 112 | + i += 2 |
| 113 | + elif parts[i].startswith("-") and "=" in parts[i]: |
| 114 | + # Handle --flag=value style for known flags |
| 115 | + flag_name = parts[i].split("=")[0] |
| 116 | + if flag_name in value_flags: |
| 117 | + dmypy_flags.append(parts[i]) |
| 118 | + else: |
| 119 | + positional.append(parts[i]) |
| 120 | + i += 1 |
| 121 | + else: |
| 122 | + positional.append(parts[i]) |
| 123 | + i += 1 |
| 124 | + |
| 125 | + dmypy_part = " ".join(dmypy_flags) |
| 126 | + positional_part = " ".join(positional) |
| 127 | + result = prefix |
| 128 | + if dmypy_part: |
| 129 | + result += " " + dmypy_part |
| 130 | + result += " -- " |
| 131 | + if positional_part: |
| 132 | + result += positional_part + " " |
| 133 | + result += "--no-pretty" |
| 134 | + return result |
| 135 | + |
| 136 | + |
79 | 137 | def run_cmd(input: str) -> tuple[int, str]: |
80 | 138 | if input[1:].startswith("mypy run --") and "--show-error-codes" not in input: |
81 | 139 | input += " --hide-error-codes" |
82 | 140 | if "--pretty" not in input: |
83 | | - if input.startswith("dmypy ") and " -- " in input: |
84 | | - # For dmypy commands, mypy flags come after --, so append at end |
| 141 | + if input.startswith(("dmypy run ", "dmypy start")) and " -- " in input: |
| 142 | + # For dmypy run/start, mypy flags come after --, so append at end |
85 | 143 | input += " --no-pretty" |
86 | 144 | elif input.startswith("mypy ") and " -- " in input: |
87 | 145 | # For mypy commands, options come before --, so insert before -- |
88 | 146 | input = input.replace(" -- ", " --no-pretty -- ", 1) |
89 | 147 | elif input.startswith(("dmypy run ", "dmypy start")): |
90 | | - # dmypy commands without -- need the separator added |
91 | | - input += " -- --no-pretty" |
| 148 | + # dmypy run/start without -- need the separator added |
| 149 | + input = _add_no_pretty_to_dmypy(input) |
92 | 150 | elif input.startswith("mypy "): |
93 | 151 | input += " --no-pretty" |
94 | 152 | if input.startswith("dmypy "): |
|
0 commit comments