Skip to content

Commit 78ef768

Browse files
committed
replace bool with bool_t in pandas/core/generic
1 parent 64f0844 commit 78ef768

File tree

4 files changed

+131
-0
lines changed

4 files changed

+131
-0
lines changed

.pre-commit-config.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,8 @@ repos:
219219
files: ^pandas/core/
220220
exclude: ^pandas/core/api\.py$
221221
types: [python]
222+
- id: no-bool-in-core-generic
223+
name: Use bool_t instead of bool in pandas/core/generic.py
224+
entry: python scripts/no_bool_in_generic.py
225+
language: python
226+
files: ^pandas/core/generic\.py$

LICENSES/PYUPGRADE_LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2017 Anthony Sottile
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in
11+
all copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

scripts/no_bool_in_generic.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
"""
2+
Check that pandas/core/generic.py doesn't use bool as a type annotation.
3+
4+
There is already the method `bool`, so the alias `bool_t` should be used instead.
5+
6+
This is meant to be run as a pre-commit hook - to run it manually, you can do:
7+
8+
pre-commit run no-bool-in-core-generic --all-files
9+
10+
The function `visit` is adapted from a function by the same name in pyupgrade:
11+
https://github.com/asottile/pyupgrade/blob/5495a248f2165941c5d3b82ac3226ba7ad1fa59d/pyupgrade/_data.py#L70-L113
12+
"""
13+
14+
import argparse
15+
import ast
16+
import collections
17+
from typing import (
18+
Dict,
19+
List,
20+
Optional,
21+
Sequence,
22+
Tuple,
23+
)
24+
25+
26+
def visit(
27+
tree: ast.Module,
28+
) -> Dict[int, List[int]]:
29+
in_annotation = False
30+
nodes: List[Tuple[bool, ast.AST]] = [(in_annotation, tree)]
31+
to_replace = collections.defaultdict(list)
32+
33+
while nodes:
34+
in_annotation, node = nodes.pop()
35+
36+
if isinstance(node, ast.Name) and in_annotation and node.id == "bool":
37+
to_replace[node.lineno].append(node.col_offset)
38+
39+
for name in reversed(node._fields):
40+
value = getattr(node, name)
41+
if name in {"annotation", "returns"}:
42+
next_in_annotation = True
43+
else:
44+
next_in_annotation = in_annotation
45+
if isinstance(value, ast.AST):
46+
nodes.append((next_in_annotation, value))
47+
elif isinstance(value, list):
48+
for value in reversed(value):
49+
if isinstance(value, ast.AST):
50+
nodes.append((next_in_annotation, value))
51+
52+
return to_replace
53+
54+
55+
def replace_bool_with_bool_t(to_replace, content: str) -> str:
56+
new_lines = []
57+
58+
for n, line in enumerate(content.splitlines(), start=1):
59+
if n in to_replace:
60+
for col_offset in reversed(to_replace[n]):
61+
line = line[:col_offset] + "bool_t" + line[col_offset + 4 :]
62+
new_lines.append(line)
63+
return "\n".join(new_lines)
64+
65+
66+
def check_for_bool_in_generic(content: str) -> Tuple[bool, str]:
67+
tree = ast.parse(content)
68+
to_replace = visit(tree)
69+
if not to_replace:
70+
return False, content
71+
return True, replace_bool_with_bool_t(to_replace, content)
72+
73+
74+
def main(argv: Optional[Sequence[str]] = None) -> None:
75+
parser = argparse.ArgumentParser()
76+
parser.add_argument("paths", nargs="*")
77+
args = parser.parse_args(argv)
78+
79+
for path in args.paths:
80+
with open(path, encoding="utf-8") as fd:
81+
content = fd.read()
82+
replace, new_content = check_for_bool_in_generic(content)
83+
if replace:
84+
with open(path, "w", encoding="utf-8") as fd:
85+
fd.write(new_content)
86+
87+
88+
if __name__ == "__main__":
89+
main()
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from scripts.no_bool_in_generic import check_for_bool_in_generic
2+
3+
BAD_FILE = "def foo(a: bool) -> bool:\n return bool(0)"
4+
GOOD_FILE = "def foo(a: bool_t) -> bool_t:\n return bool(0)"
5+
6+
7+
def test_bad_file_with_replace():
8+
content = BAD_FILE
9+
_, result = check_for_bool_in_generic(content)
10+
expected = GOOD_FILE
11+
assert result == expected
12+
13+
14+
def test_good_file_with_replace():
15+
content = GOOD_FILE
16+
_, result = check_for_bool_in_generic(content)
17+
expected = content
18+
assert result == expected

0 commit comments

Comments
 (0)