Skip to content

Commit a9cd40a

Browse files
committed
Add --dry option
1 parent f2eb07b commit a9cd40a

File tree

4 files changed

+85
-18
lines changed

4 files changed

+85
-18
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ ignore-names-in-files = ["migrations"]
3131
| Option                                    | Type | Meaning |
3232
|-------------------------------------------|------|----------------------------------------------------------------------|
3333
|`--fix` | - | Automatically remove detected unused code expressions from the code base. |
34+
|`--dry` | - or list | Show changes which would be made in files. Shows changes for provided filenames or shows all changes if no filename is specified. |
3435
|`--exclude` | list | Filenames (or path expressions), which will be completely skipped without being analysed. |
3536
|`--ignore-names` | list | Removes provided list of names from the output. Regexp expressions to match multiple names can also be provided, e.g. `*Mixin` will match all classes ending with `Mixin`. |
3637
|`--ignore-names-in-files` | list | Ignores unused names in files, which filenames match provided path expressions. |

deadcode/actions/parse_arguments.py

+24-6
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ def parse_arguments(args: Optional[List[str]]) -> Args:
3333
action="store_true",
3434
default=False,
3535
)
36-
3736
parser.add_argument(
38-
"-v",
39-
"--verbose",
40-
help="Shows logs useful for debuging",
41-
action="store_true",
42-
default=False,
37+
"--dry",
38+
help="Show changes which would be made in files with --fix option.",
39+
nargs="*",
40+
action="append",
41+
default=[["__all_files__"]],
42+
type=str,
4343
)
4444
parser.add_argument(
4545
"--exclude",
@@ -160,24 +160,34 @@ def parse_arguments(args: Optional[List[str]]) -> Args:
160160
default=[],
161161
type=str,
162162
)
163+
163164
parser.add_argument(
164165
"--no-color",
165166
help="Turn off colors in the output",
166167
action="store_true",
167168
default=False,
168169
)
170+
169171
parser.add_argument(
170172
"--quiet",
171173
help="Does not output anything. Makefile still fails with exit code 1 if unused names are found.",
172174
action="store_true",
173175
default=False,
174176
)
177+
175178
parser.add_argument(
176179
"--count",
177180
help="Provides the count of the detected unused names instead of printing them all out.",
178181
action="store_true",
179182
default=False,
180183
)
184+
parser.add_argument(
185+
"-v",
186+
"--verbose",
187+
help="Shows logs useful for debuging",
188+
action="store_true",
189+
default=False,
190+
)
181191

182192
parsed_args = parser.parse_args(args).__dict__
183193

@@ -190,6 +200,14 @@ def parse_arguments(args: Optional[List[str]]) -> Args:
190200
if key in parsed_args:
191201
parsed_args[key].extend(item)
192202

203+
# Do not fix if dry option is provided:
204+
if len(parsed_args["dry"]) > 1 or "--dry" not in args:
205+
parsed_args["dry"].remove("__all_files__")
206+
207+
# Do not fix if dry option is provided:
208+
if parsed_args["dry"]:
209+
parsed_args["fix"] = False
210+
193211
return Args(**parsed_args)
194212

195213

deadcode/data_types.py

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
@dataclass
1414
class Args:
1515
fix: bool = False
16+
dry: bool = False
1617
verbose: bool = False
1718
paths: Iterable[Pathname] = ()
1819
exclude: Iterable[Pathname] = ()

deadcode/tests/cli_args/test_parsing.py

+59-12
Original file line numberDiff line numberDiff line change
@@ -20,47 +20,94 @@ def setUp(self):
2020
}
2121

2222
def test_calling_with_one_paths_argument(self):
23-
args = parse_arguments(["."])
23+
options = "."
24+
args = parse_arguments(options.split())
2425
self.assertEqual(args.paths, ["."])
2526
self.assertEqual(args.exclude, [])
2627
self.assertEqual(args.ignore_names, [])
2728
self.assertEqual(args.ignore_names_in_files, [])
2829

2930
def test_calling_with_several_paths_argument(self):
30-
args = parse_arguments([".", "tests"])
31+
options = ". tests"
32+
args = parse_arguments(options.split())
3133
self.assertEqual(args.paths, [".", "tests"])
3234
self.assertEqual(args.exclude, [])
3335
self.assertEqual(args.ignore_names, [])
3436
self.assertEqual(args.ignore_names_in_files, [])
3537

3638
def test_calling_with_exclude(self):
37-
args = parse_arguments([".", "--exclude=tests,venv"])
39+
options = ". --exclude=tests,venv"
40+
args = parse_arguments(options.split())
3841
self.assertEqual(args.paths, ["."])
3942
self.assertEqual(args.exclude, ["tests", "venv"])
4043
self.assertEqual(args.ignore_names, [])
4144
self.assertEqual(args.ignore_names_in_files, [])
4245

4346
def test_calling_with_several_exclude_options(self):
44-
args = parse_arguments([".", "--exclude=tests,venv", "--exclude=migrations"])
47+
options = ". --exclude=tests,venv --exclude=migrations"
48+
args = parse_arguments(options.split())
4549
self.assertEqual(args.paths, ["."])
4650
self.assertEqual(args.exclude, ["tests", "venv", "migrations"])
4751
self.assertEqual(args.ignore_names, [])
4852
self.assertEqual(args.ignore_names_in_files, [])
4953

5054
def test_calling_with_no_color_option(self):
51-
args = parse_arguments([".", "--no-color"])
55+
options = ". --no-color"
56+
args = parse_arguments(options.split())
5257
self.assertEqual(args.paths, ["."])
5358
self.assertEqual(args.no_color, True)
5459

5560
def test_ignore_names_and_ignore_files_command_line_argument_parsing(self):
56-
args = parse_arguments(
57-
[
58-
".",
59-
"--ignore-names-in-files=tests,venv",
60-
"--ignore-names=BaseTestCase,lambda_handler",
61-
]
62-
)
61+
options = ". --ignore-names-in-files=tests,venv --ignore-names=BaseTestCase,lambda_handler"
62+
args = parse_arguments(options.split())
6363
self.assertEqual(args.paths, ["."])
6464
self.assertEqual(args.exclude, [])
6565
self.assertEqual(args.ignore_names, ["BaseTestCase", "lambda_handler"])
6666
self.assertEqual(args.ignore_names_in_files, ["tests", "venv"])
67+
68+
def test_verbose_flag_is_on_using_long_name(self):
69+
options = "--verbose ."
70+
args = parse_arguments(options.split())
71+
self.assertEqual(args.verbose, True)
72+
self.assertEqual(args.paths, ["."])
73+
74+
def test_verbose_flag_is_on_using_short_name(self):
75+
options = ". -v"
76+
args = parse_arguments(options.split())
77+
self.assertEqual(args.paths, ["."])
78+
self.assertEqual(args.verbose, True)
79+
80+
def test_calling_with_fix(self):
81+
options = ". --fix"
82+
args = parse_arguments(options.split())
83+
self.assertEqual(args.paths, ["."])
84+
self.assertEqual(args.dry, [])
85+
self.assertEqual(args.fix, True)
86+
87+
def test_calling_with_dry(self):
88+
options = ". --dry --verbose"
89+
args = parse_arguments(options.split())
90+
self.assertEqual(args.paths, ["."])
91+
self.assertEqual(args.dry, ["__all_files__"])
92+
self.assertEqual(args.verbose, True)
93+
self.assertEqual(args.fix, False)
94+
95+
def test_calling_with_fix_and_dry(self):
96+
options = ". --dry --fix"
97+
args = parse_arguments(options.split())
98+
self.assertEqual(args.dry, ["__all_files__"])
99+
self.assertEqual(args.fix, False)
100+
101+
def test_calling_with_single_dry_filename(self):
102+
options = ". --dry foo.py --fix"
103+
args = parse_arguments(options.split())
104+
self.assertEqual(args.paths, ["."])
105+
self.assertEqual(args.dry, ["foo.py"])
106+
self.assertEqual(args.fix, False)
107+
108+
def test_calling_with_two_dry_filenames(self):
109+
options = ". --fix --dry foo.py bar.py"
110+
args = parse_arguments(options.split())
111+
self.assertEqual(args.paths, ["."])
112+
self.assertEqual(args.dry, ["foo.py", "bar.py"])
113+
self.assertEqual(args.fix, False)

0 commit comments

Comments
 (0)