-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathcli.py
More file actions
174 lines (147 loc) · 5.22 KB
/
cli.py
File metadata and controls
174 lines (147 loc) · 5.22 KB
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import argparse
import sys
from codemodder import __version__
from codemodder.code_directory import DEFAULT_INCLUDED_PATHS, DEFAULT_EXCLUDED_PATHS
from codemodder.logging import OutputFormat, logger
class ArgumentParser(argparse.ArgumentParser):
def error(self, message):
"""If there is an argument parsing error, print the `--help` message,
log the error, and exit with status code `3`."""
self.print_help(sys.stderr)
logger.error("CLI error: %s", message)
sys.exit(3)
def build_list_action(codemod_registry):
class ListAction(argparse.Action):
""" """
def _print_codemods(self):
for codemod_id in sorted(codemod_registry.ids):
print(codemod_id)
def __call__(self, parser, *args, **kwargs):
"""
Print codemod(s) metadata in the following format:
pixee:python/secure-random
pixee:python/url-sandbox
...
and exit gracefully.
"""
self._print_codemods()
parser.exit()
return ListAction
class CsvListAction(argparse.Action):
"""
argparse Action to convert "a,b,c" into ["a", "b", "c"]
"""
def __call__(self, parser, namespace, values, option_string=None):
# Conversion to dict removes duplicates while preserving order
items = list(dict.fromkeys(values.split(",")).keys())
self.validate_items(items)
setattr(namespace, self.dest, items)
def validate_items(self, items):
"""Basic Action does not validate the items"""
def build_codemod_validator(codemod_registry):
names = codemod_registry.names
ids = codemod_registry.ids
class ValidatedCodmods(CsvListAction):
"""
argparse Action to convert "codemod1,codemod2,codemod3" into a list
representation and validate against existing codemods
"""
def validate_items(self, items):
potential_names = ids + names
unrecognized_codemods = [
name for name in items if name not in potential_names
]
if unrecognized_codemods:
args = {
"values": unrecognized_codemods,
"choices": ", ".join(map(repr, names)),
}
msg = "invalid choice(s): %(values)r (choose from %(choices)s)"
raise argparse.ArgumentError(self, msg % args)
return ValidatedCodmods
def parse_args(argv, codemod_registry):
"""
Parse CLI arguments according to:
https://www.notion.so/pixee/Codemodder-CLI-Arguments
"""
parser = ArgumentParser(description="Run codemods and change code.")
parser.add_argument("directory", type=str, help="path to find files")
parser.add_argument(
"--output",
type=str,
help="name of output file to produce",
default="stdout",
required=True,
)
codemod_validator = build_codemod_validator(codemod_registry)
codemod_args_group = parser.add_mutually_exclusive_group()
codemod_args_group.add_argument(
"--codemod-exclude",
action=codemod_validator,
help="Comma-separated set of codemod ID(s) to exclude",
)
codemod_args_group.add_argument(
"--codemod-include",
action=codemod_validator,
help="Comma-separated set of codemod ID(s) to include",
)
parser.add_argument("--version", action="version", version=__version__)
parser.add_argument(
"--list",
action=build_list_action(codemod_registry),
nargs=0,
help="Print codemod(s) metadata",
)
parser.add_argument(
"--output-format",
type=str,
help="the format for the data output file",
default="codetf",
choices=["codetf", "diff"],
)
parser.add_argument(
"--dry-run",
action=argparse.BooleanOptionalAction,
help="do everything except make changes to files",
)
parser.add_argument(
"--verbose",
action=argparse.BooleanOptionalAction,
help="print more to stdout",
)
parser.add_argument(
"--log-format",
type=OutputFormat,
default=OutputFormat.HUMAN,
choices=[OutputFormat.HUMAN, OutputFormat.JSON],
help="the format for the log output",
)
parser.add_argument(
"--project-name",
help="optional descriptive name for the project used in log output",
)
parser.add_argument(
"--path-exclude",
action=CsvListAction,
default=DEFAULT_EXCLUDED_PATHS,
help="Comma-separated set of UNIX glob patterns to exclude",
)
parser.add_argument(
"--path-include",
action=CsvListAction,
default=DEFAULT_INCLUDED_PATHS,
help="Comma-separated set of UNIX glob patterns to include",
)
parser.add_argument(
"--max-workers",
type=int,
default=1,
help="maximum number of workers (threads) to use for processing files in parallel",
)
# At this time we don't do anything with the sarif arg.
parser.add_argument(
"--sarif",
action=CsvListAction,
help="Comma-separated set of path(s) to SARIF file(s) to feed to the codemods",
)
return parser.parse_args(argv)