Skip to content

Commit 2dfaf19

Browse files
committed
Add rewrite fixer to remove Salt's __utils__ usage
Signed-off-by: Pedro Algarvio <palgarvio@vmware.com>
1 parent 2423bef commit 2dfaf19

4 files changed

Lines changed: 235 additions & 1 deletion

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "salt-rewrite"
3-
version = "1.3.3"
3+
version = "2.0.0"
44
description = "A set of Bowler code to rewrite parts of Salt"
55
authors = ["Pedro Algarvio <pedro@algarvio.me>"]
66
license = "Apache-2.0"

src/saltrewrite/salt/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# pylint: disable=missing-module-docstring
22
from saltrewrite.salt import fix_docstrings
3+
from saltrewrite.salt import fix_dunder_utils
34

45
__all__ = [modname for modname in list(globals()) if modname.startswith("fix_")]
56

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
"""
2+
saltrewrite.salt.fix_docstrings
3+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4+
5+
@todo: add description
6+
"""
7+
import logging
8+
9+
from bowler import Query
10+
from bowler import SYMBOL
11+
from bowler import TOKEN
12+
from bowler.types import Leaf
13+
from bowler.types import Node
14+
from fissix.fixer_util import Call
15+
from fissix.fixer_util import Dot
16+
from fissix.fixer_util import touch_import
17+
18+
log = logging.getLogger(__name__)
19+
20+
21+
def rewrite(paths, interactive=False, silent=False):
22+
"""
23+
Rewrite the passed in paths
24+
"""
25+
(
26+
Query(paths)
27+
.select(
28+
"""
29+
(
30+
dunder_call=power<
31+
'__utils__'
32+
trailer< '[' dunder_mod_func=any* ']' >
33+
trailer< '(' function_arguments=any* ')' >
34+
>
35+
36+
)
37+
"""
38+
)
39+
.modify(fix_module_docstrings)
40+
.execute(write=True, interactive=interactive, silent=silent)
41+
)
42+
43+
44+
def fix_module_docstrings(node, capture, filename):
45+
"""
46+
Automaticaly run fixes against docstrings
47+
"""
48+
if "dunder_mod_func" not in capture:
49+
return
50+
dunder_mod_func = capture["dunder_mod_func"][0].value.strip("'").strip('"')
51+
utils_module, utils_module_funcname = dunder_mod_func.split(".")
52+
53+
# Make sure we import the right utils module
54+
touch_import(None, f"salt.utils.{utils_module}", node)
55+
log.info("Dunder Module Func: %r", dunder_mod_func)
56+
57+
# Un-parent the function arguments so we can add them to a new call
58+
for leaf in capture["function_arguments"]:
59+
leaf.parent = None
60+
61+
# Create the new function call
62+
call_node = Call(
63+
Leaf(TOKEN.NAME, utils_module_funcname, prefix=""),
64+
capture["function_arguments"],
65+
)
66+
67+
# Create replacement node
68+
replacement = Node(
69+
SYMBOL.power,
70+
[
71+
Leaf(TOKEN.NAME, "salt", prefix=capture["node"].prefix),
72+
Node(
73+
SYMBOL.trailer,
74+
[
75+
Dot(),
76+
Leaf(TOKEN.NAME, "utils", prefix=""),
77+
Dot(),
78+
Leaf(TOKEN.NAME, utils_module, prefix=""),
79+
Dot(),
80+
call_node,
81+
],
82+
),
83+
],
84+
)
85+
# Replace the whole node with the new function call
86+
node.replace(replacement)
87+
88+
89+
# pylint: enable=missing-function-docstring

tests/salt/test_dunder_utils.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# pylint: disable=missing-module-docstring,missing-function-docstring,too-many-lines
2+
import textwrap
3+
4+
from saltrewrite.salt import fix_dunder_utils
5+
6+
7+
def test_fix_call_one_arg(tempfiles):
8+
code = textwrap.dedent(
9+
"""
10+
import one.two
11+
12+
def one():
13+
one.two.three("four")
14+
__utils__["foo.bar"]("one")
15+
"""
16+
)
17+
expected_code = textwrap.dedent(
18+
"""
19+
import one.two
20+
import salt.utils.foo
21+
22+
def one():
23+
one.two.three("four")
24+
salt.utils.foo.bar("one")
25+
"""
26+
)
27+
fpath = tempfiles.makepyfile(code, prefix="test_")
28+
fix_dunder_utils.rewrite(fpath)
29+
with open(fpath) as rfh:
30+
new_code = rfh.read()
31+
assert new_code == expected_code
32+
33+
34+
def test_fix_call_multiple_args(tempfiles):
35+
code = textwrap.dedent(
36+
"""
37+
import one.two
38+
39+
def one():
40+
one.two.three("four")
41+
__utils__["foo.bar"]("one", True, 1, 2.0)
42+
"""
43+
)
44+
expected_code = textwrap.dedent(
45+
"""
46+
import one.two
47+
import salt.utils.foo
48+
49+
def one():
50+
one.two.three("four")
51+
salt.utils.foo.bar("one", True, 1, 2.0)
52+
"""
53+
)
54+
fpath = tempfiles.makepyfile(code, prefix="test_")
55+
fix_dunder_utils.rewrite(fpath)
56+
with open(fpath) as rfh:
57+
new_code = rfh.read()
58+
assert new_code == expected_code
59+
60+
61+
def test_fix_call_keyword_arguments(tempfiles):
62+
code = textwrap.dedent(
63+
"""
64+
import one.two
65+
66+
def one():
67+
one.two.three("four")
68+
__utils__["foo.bar"](one="one")
69+
"""
70+
)
71+
expected_code = textwrap.dedent(
72+
"""
73+
import one.two
74+
import salt.utils.foo
75+
76+
def one():
77+
one.two.three("four")
78+
salt.utils.foo.bar(one="one")
79+
"""
80+
)
81+
fpath = tempfiles.makepyfile(code, prefix="test_")
82+
fix_dunder_utils.rewrite(fpath)
83+
with open(fpath) as rfh:
84+
new_code = rfh.read()
85+
assert new_code == expected_code
86+
87+
88+
def test_fix_call_multiple_keyword_arguments(tempfiles):
89+
code = textwrap.dedent(
90+
"""
91+
import one.two
92+
93+
def one():
94+
one.two.three("four")
95+
__utils__["foo.bar"](
96+
one="one",
97+
two="two"
98+
)
99+
"""
100+
)
101+
expected_code = textwrap.dedent(
102+
"""
103+
import one.two
104+
import salt.utils.foo
105+
106+
def one():
107+
one.two.three("four")
108+
salt.utils.foo.bar(
109+
one="one",
110+
two="two")
111+
"""
112+
)
113+
fpath = tempfiles.makepyfile(code, prefix="test_")
114+
fix_dunder_utils.rewrite(fpath)
115+
with open(fpath) as rfh:
116+
new_code = rfh.read()
117+
assert new_code == expected_code
118+
119+
120+
def test_fix_call_mixed(tempfiles):
121+
code = textwrap.dedent(
122+
"""
123+
import one.two
124+
125+
def one():
126+
one.two.three("four")
127+
__utils__["foo.bar"]("one", two="two")
128+
"""
129+
)
130+
expected_code = textwrap.dedent(
131+
"""
132+
import one.two
133+
import salt.utils.foo
134+
135+
def one():
136+
one.two.three("four")
137+
salt.utils.foo.bar("one", two="two")
138+
"""
139+
)
140+
fpath = tempfiles.makepyfile(code, prefix="test_")
141+
fix_dunder_utils.rewrite(fpath)
142+
with open(fpath) as rfh:
143+
new_code = rfh.read()
144+
assert new_code == expected_code

0 commit comments

Comments
 (0)