Skip to content

Commit 37f3fd0

Browse files
committed
based regex
1 parent 6b73ef2 commit 37f3fd0

32 files changed

+854
-477
lines changed

.mypy/baseline.json

Lines changed: 36 additions & 386 deletions
Large diffs are not rendered by default.

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- Type-guards narrow in the negative (#553)
1111
- Conditional types for asymmetric type-guards (#553)
1212
- Static conditions report an error (#553)
13+
- Regex groups are special-cased (#531)
1314
### Enhancements
1415
- Show 'narrowed from' in `reveal_type` (#550)
1516
- `--color-output` is enabled by default (#531)

docs/source/based_features.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,3 +331,18 @@ Checked Argument Names
331331
class B(A):
332332
@override
333333
def f(self, b: int): ... # error: Signature of "f" incompatible with supertype "A"
334+
335+
336+
Regex Checks
337+
------------
338+
339+
.. code-block:: python
340+
341+
re.compile("as(df") # error: missing ), unterminated subpattern at position 0 [regex]
342+
343+
if m := re.search("(a)?(b)", s):
344+
reveal_type(m.groups()) # Revealed type is "(str | None, str)"
345+
346+
if m := re.search("(?P<foo>a)", s):
347+
reveal_type(m.group("foo")
348+
reveal_type(m.group("bar") # error: no such group: 'bar' [regex]

docs/source/error_code_list3.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,17 @@ Check that typeguard definitions are valid [typeguard-subtype]
8080
def guard(x: str) -> x is int: # A type-guard's type must be assignable to its parameter's type. (guard has type "int", parameter has type "str") [typeguard-subtype]
8181
...
8282
83+
.. _code-regex:
84+
85+
Check for regex issues [regex]
86+
------------------------------
87+
88+
.. code-block:: python
89+
90+
re.compile("(foo).*)") # error: unbalanced parenthesis at position 7 [regex]
91+
m = re.match("(?P<name>\w+)", s)
92+
assert m
93+
m.group("nane") # error: no such group: 'nane' [regex]
8394
8495
.. _code-bad-cast:
8596

misc/cherry-pick-typeshed.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,11 @@
1414
import sys
1515
import tempfile
1616

17-
from mypy.util import safe
18-
1917

2018
def parse_commit_title(diff: str) -> str:
2119
m = re.search("\n ([^ ].*)", diff)
2220
assert m is not None, "Could not parse diff"
23-
return safe(m.group(1))
21+
return m.group(1)
2422

2523

2624
def main() -> None:

misc/find_type.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@
3131
import sys
3232
import tempfile
3333

34-
from mypy.util import safe
35-
3634
REVEAL_TYPE_START = "reveal_type("
3735
REVEAL_TYPE_END = ")"
3836

@@ -53,11 +51,7 @@ def run_mypy(mypy_and_args: list[str], filename: str, tmp_name: str) -> str:
5351

5452
def get_revealed_type(line: str, relevant_file: str, relevant_line: int) -> str | None:
5553
m = re.match(r'(.+?):(\d+): note: Revealed type is "(.*)"$', line)
56-
if (
57-
m
58-
and int(safe(m.group(2))) == relevant_line
59-
and os.path.samefile(relevant_file, safe(m.group(1)))
60-
):
54+
if m and int(m.group(2)) == relevant_line and os.path.samefile(relevant_file, m.group(1)):
6155
return m.group(3)
6256
else:
6357
return None

misc/incremental_checker.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@
4747
from typing import Any, Dict, Final
4848
from typing_extensions import TypeAlias as _TypeAlias
4949

50-
from mypy.util import safe
51-
5250
CACHE_PATH: Final = ".incremental_checker_cache.json"
5351
MYPY_REPO_URL: Final = "https://github.com/python/mypy.git"
5452
MYPY_TARGET_FILE: Final = "mypy"
@@ -171,7 +169,7 @@ def filter_daemon_stats(output: str) -> tuple[str, dict[str, Any]]:
171169
for line in lines:
172170
m = re.match(r"(\w+)\s+:\s+(.*)", line)
173171
if m:
174-
key, value = [safe(g) for g in m.groups()]
172+
key, value = [g for g in m.groups()]
175173
stats[key] = value
176174
else:
177175
output_lines.append(line)

mypy/config_parser.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from io import StringIO
1010

1111
from mypy.errorcodes import error_codes
12-
from mypy.util import safe
1312

1413
if sys.version_info >= (3, 11):
1514
import tomllib
@@ -29,6 +28,7 @@
2928
TextIO,
3029
Tuple,
3130
Union,
31+
cast,
3232
)
3333
from typing_extensions import TypeAlias as _TypeAlias
3434

@@ -45,7 +45,7 @@ def parse_version(v: str | float) -> tuple[int, int]:
4545
m = re.match(r"\A(\d)\.(\d+)\Z", str(v))
4646
if not m:
4747
raise argparse.ArgumentTypeError(f"Invalid python version '{v}' (expected format: 'x.y')")
48-
major, minor = int(safe(m.group(1))), int(safe(m.group(2)))
48+
major, minor = int(m.group(1)), int(m.group(2))
4949
if major == 2 and minor == 7:
5050
pass # Error raised elsewhere
5151
elif major == 3:
@@ -68,7 +68,7 @@ def parse_version(v: str | float) -> tuple[int, int]:
6868
def try_split(v: str | Sequence[str], split_regex: str = "[,]") -> list[str]:
6969
"""Split and trim a str or list of str into a list of str"""
7070
if isinstance(v, str):
71-
return [p.strip() for p in re.split(split_regex, v)]
71+
return [p.strip() for p in cast(List[str], re.split(split_regex, v))]
7272

7373
return [p.strip() for p in v]
7474

mypy/copytype.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ def visit_deleted_type(self, t: DeletedType) -> ProperType:
6666

6767
def visit_instance(self, t: Instance) -> ProperType:
6868
dup = Instance(t.type, t.args, last_known_value=t.last_known_value)
69+
dup.metadata = t.metadata.copy()
6970
dup.invalid = t.invalid
7071
return self.copy_common(t, dup)
7172

mypy/errorcodes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@ def __hash__(self) -> int:
277277
BAD_CAST: Final[ErrorCode] = ErrorCode(
278278
"bad-cast", "cast of non-overlapping types", "General", default_enabled=False
279279
)
280+
281+
REGEX: Final = ErrorCode("regex", "Regex related errors", "General")
280282
REVEAL: Final = ErrorCode("reveal", "Reveal types at check time", "General")
281283

282284
# Syntax errors are often blocking.

0 commit comments

Comments
 (0)