Skip to content

Commit 18318f5

Browse files
committed
Handle 'control' mode for treated flag methods
1 parent f76465c commit 18318f5

File tree

2 files changed

+104
-9
lines changed

2 files changed

+104
-9
lines changed

piranha_python/codemods.py

+25-9
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,21 @@ def add_args(arg_parser): # pragma: no cover
3535
type=str,
3636
required=False,
3737
)
38+
arg_parser.add_argument(
39+
"--mode",
40+
dest="mode",
41+
metavar="MODE",
42+
help="Execution mode - can be 'treated' or 'control'",
43+
type=str,
44+
required=False,
45+
)
3846

39-
def __init__(self, context, flag_name, flag_resolution_methods, ignored_module_check_fn_path=None):
47+
def __init__(self, context, flag_name, flag_resolution_methods, ignored_module_check_fn_path=None, mode="treated"):
4048
super().__init__(context)
4149
self.flag_name = flag_name
4250
self.is_in_feature_flag_block = False
4351
self.found_return_stmt_in_ff_block = False
52+
self.running_in_treated_mode = mode == "treated"
4453

4554
if ignored_module_check_fn_path is None:
4655
ignored_module_check_fn_path = self.DEFAULT_TEST_MODULE_CHECK_PATH
@@ -132,18 +141,20 @@ def leave_If(self, original_node, updated_node):
132141
if not self.is_in_feature_flag_block:
133142
return updated_node
134143

135-
if matchers.matches(updated_node.test, self.flag_resolution_matcher):
136-
if self.is_treatment_method:
144+
if self._should_assume_that_flag_is_true():
145+
if matchers.matches(updated_node.test, self.flag_resolution_matcher):
137146
replaced_node = updated_node.body
138-
else:
139-
replaced_node = updated_node.orelse.body
140-
elif matchers.matches(updated_node.test, _inside_not_matcher(self.flag_resolution_matcher)):
141-
if self.is_treatment_method:
147+
elif matchers.matches(updated_node.test, _inside_not_matcher(self.flag_resolution_matcher)):
142148
replaced_node = updated_node.orelse.body
143149
else:
144-
replaced_node = updated_node.body
150+
return updated_node
145151
else:
146-
return updated_node
152+
if matchers.matches(updated_node.test, self.flag_resolution_matcher):
153+
replaced_node = updated_node.orelse.body
154+
elif matchers.matches(updated_node.test, _inside_not_matcher(self.flag_resolution_matcher)):
155+
replaced_node = updated_node.body
156+
else:
157+
return updated_node
147158

148159
return_statements = matchers.findall(replaced_node, matchers.Return())
149160
self.found_return_stmt_in_ff_block = len(return_statements) > 0
@@ -196,6 +207,11 @@ def _updated_tuple_assignment(self, updated_node):
196207
),
197208
)
198209

210+
def _should_assume_that_flag_is_true(self):
211+
return (self.is_treatment_method and self.running_in_treated_mode) or (
212+
not self.is_treatment_method and not self.running_in_treated_mode
213+
)
214+
199215

200216
def _matches_flag_name(flag_name, n):
201217
return matchers.matches(n[0].value, matchers.Name(flag_name))

test/test_codemods.py

+79
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,85 @@ def test_keeps_ELSE_block_body_and_remainder_when_IF_block_contains_return_state
251251
flag_resolution_methods="is_flag_active",
252252
)
253253

254+
# Tests covering "control" mode instead of the default "treated" mode
255+
def test_keeps_ELSE_block_body_when_mode_is_control(self):
256+
self.assertCodemod(
257+
_with_correct_indentation(
258+
"""\
259+
if is_flag_active(%s):
260+
print('Flag is active')
261+
else:
262+
print('This is not related to the feature flag value at all')
263+
264+
print('Completely unrelated statement')
265+
"""
266+
% FEATURE_FLAG_NAME
267+
),
268+
_with_correct_indentation(
269+
"""\
270+
print('This is not related to the feature flag value at all')
271+
272+
print('Completely unrelated statement')
273+
"""
274+
),
275+
flag_name=FEATURE_FLAG_NAME,
276+
flag_resolution_methods="is_flag_active",
277+
mode="control",
278+
)
279+
280+
def test_keeps_only_ELSE_block_body_when_it_contains_return_statement_and_mode_is_control(self):
281+
self.assertCodemod(
282+
_with_correct_indentation(
283+
"""\
284+
if is_flag_active(%s):
285+
print('Flag is active')
286+
else:
287+
print('This is not related to the feature flag value at all')
288+
return 0
289+
290+
print('Completely unrelated statement')
291+
"""
292+
% FEATURE_FLAG_NAME
293+
),
294+
_with_correct_indentation(
295+
"""\
296+
print('This is not related to the feature flag value at all')
297+
return 0
298+
"""
299+
),
300+
flag_name=FEATURE_FLAG_NAME,
301+
flag_resolution_methods="is_flag_active",
302+
mode="control",
303+
)
304+
305+
def test_keeps_ELSE_block_body_and_remainder_when_IF_block_contains_return_statement_but_ELSE_doesnt_and_mode_is_control(
306+
self
307+
):
308+
self.assertCodemod(
309+
_with_correct_indentation(
310+
"""\
311+
if is_flag_active(%s):
312+
print('Flag is active')
313+
return 0
314+
else:
315+
print('This is not related to the feature flag value at all')
316+
317+
print('Completely unrelated statement')
318+
"""
319+
% FEATURE_FLAG_NAME
320+
),
321+
_with_correct_indentation(
322+
"""\
323+
print('This is not related to the feature flag value at all')
324+
325+
print('Completely unrelated statement')
326+
"""
327+
),
328+
flag_name=FEATURE_FLAG_NAME,
329+
flag_resolution_methods="is_flag_active",
330+
mode="control",
331+
)
332+
254333
# Tests covering the presence of docstrings in changed code
255334
def test_keeps_single_lined_docstring_in_module(self):
256335
self.assertCodemod(

0 commit comments

Comments
 (0)