-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
/
Copy pathvalidate_string_concatenation.py
executable file
·129 lines (102 loc) · 3.41 KB
/
validate_string_concatenation.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/usr/bin/env python
"""
GH #30454
Check where there is a string that needs to be concatenated.
This is necessary after black formating,
where for example black transforms this:
>>> foo = (
... "bar "
... "baz"
... )
into this:
>>> foo = ("bar " "baz")
Black is not considering this as an
issue (see issue https://github.com/psf/black/issues/1051),
so we are checking it here.
"""
import argparse
import os
import sys
import token
import tokenize
from typing import Generator, List, Tuple
FILE_EXTENSIONS_TO_CHECK = (".py", ".pyx", ".pyx.ini", ".pxd")
def main(source_path: str, output_format: str) -> bool:
"""
Main entry point of the script.
Parameters
----------
source_path : str
Source path representing path to a file/directory.
output_format : str
Output format of the script.
Returns
-------
bool
True if found any strings that needs to be concatenated.
Raises
------
ValueError
If the `source_path` is not pointing to existing file/directory.
"""
if not os.path.exists(source_path):
raise ValueError(
"Please enter a valid path, pointing to a valid file/directory."
)
is_failed: bool = False
msg = "String unnecessarily split in two by black. Please merge them manually."
if os.path.isfile(source_path):
for source_path, line_number in strings_to_concatenate(source_path):
is_failed = True
print(
output_format.format(
source_path=source_path, line_number=line_number, msg=msg
)
)
for subdir, _, files in os.walk(source_path):
for file_name in files:
if any(
file_name.endswith(extension) for extension in FILE_EXTENSIONS_TO_CHECK
):
for source_path, line_number in strings_to_concatenate(
os.path.join(subdir, file_name)
):
is_failed = True
print(
output_format.format(
source_path=source_path, line_number=line_number, msg=msg
)
)
return is_failed
def strings_to_concatenate(source_path: str) -> Generator[Tuple[str, int], None, None]:
"""
Yielding the strings that needs to be concatenated in a given file.
Parameters
----------
source_path : str
File path pointing to a single file.
Yields
------
source_path : str
Source file path.
line_number : int
Line number of unconcatenated string.
"""
with open(source_path, "r") as file_name:
tokens: List = list(tokenize.generate_tokens(file_name.readline))
for current_token, next_token in zip(tokens, tokens[1:]):
if current_token[0] == next_token[0] == token.STRING:
yield source_path, current_token[2][0]
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Validate concatenated strings")
parser.add_argument(
"path", nargs="?", default=".", help="Source path of file/directory to check."
)
parser.add_argument(
"--format",
"-f",
default="{source_path}:{line_number}:{msg}",
help="Output format of the unconcatenated strings.",
)
args = parser.parse_args()
sys.exit(main(source_path=args.path, output_format=args.format))