Skip to content

Commit 3152970

Browse files
CHANGE: @W-20035005@: Add latest python code updates to flow engine (#382)
1 parent 3658f48 commit 3152970

File tree

25 files changed

+1813
-1915
lines changed

25 files changed

+1813
-1915
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
FlowScanner/build/**/**
22
FlowScanner/**/__pycache__/**
3-
FlowScanner/flowtest.egg-info/**
3+
FlowScanner/*.egg-info/**

packages/code-analyzer-flow-engine/FlowScanner/flow_parser/parse.py

Lines changed: 160 additions & 45 deletions
Large diffs are not rendered by default.

packages/code-analyzer-flow-engine/FlowScanner/flow_scanner/__main__.py

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
import flow_scanner.query_manager
1212
import flow_scanner.util as util
1313
import flow_scanner.version as version
14-
import queries.default_query as default_query
15-
from flow_scanner.query_manager import validate_qry_list, get_all_optional_queries
14+
15+
from flow_scanner.query_manager import validate_qry_list, get_all_queries
1616
from flow_scanner.util import make_id
1717
from public.data_obj import PresetEncoder
1818
from public.parse_utils import quick_validate
@@ -134,18 +134,28 @@ def get_tokens_from_csv_file(file_path: str) -> list[str]:
134134

135135
return get_validated_queries(unsplit(data))
136136

137+
def print_preset_list():
138+
map_ = flow_scanner.query_manager.PRESETS
139+
preset_info = {}
140+
for p in map_:
141+
preset_info[p] = [x[1] for x in map_[p]]
142+
143+
res_json = json.dumps(preset_info, indent=4)
144+
# print to stdout so user can redirect or examine
145+
print(res_json)
137146

138147
def get_validated_queries(data: list[str]) -> list[str]:
139148
cleaned_data = de_kebab_list(clean_str_list(data))
140-
validation = validate_qry_list(cleaned_data)
141-
if validation is True:
142-
return cleaned_data
149+
valid, found, missed, duplicates = validate_qry_list(cleaned_data)
150+
if valid:
151+
return found
143152
else:
144-
if len(validation) == 1:
145-
raise argparse.ArgumentTypeError("Unrecognized query requested: %s" % validation[0])
146-
else:
147-
raise argparse.ArgumentTypeError("Unrecognized queries requested: %s" %
148-
",".join(validation))
153+
for issue, data in [('Duplicate', duplicates), ('Unrecognized', missed)]:
154+
if data is not None and len(data) == 1:
155+
raise argparse.ArgumentTypeError(f"{issue} query requested: %s" % data[0])
156+
else:
157+
raise argparse.ArgumentTypeError(f"{issue} queries requested: %s" %
158+
",".join(data))
149159

150160

151161
def unsplit(msg: str) -> list[str]:
@@ -232,8 +242,8 @@ def parse_args(my_args: list[str], default: str = None) -> argparse.Namespace:
232242
version='%(prog)s ' + version.__version__)
233243
parser.add_argument("-p", "--preset_info", action='store_true',
234244
help="return information on default preset and exit")
235-
parser.add_argument("--optional_query_info", action='store_true',
236-
help="display which optional queries are supported and exit")
245+
parser.add_argument("--query_info", action='store_true',
246+
help="display which queries are supported and exit")
237247

238248
"""
239249
Options for which flows to scan
@@ -255,10 +265,11 @@ def parse_args(my_args: list[str], default: str = None) -> argparse.Namespace:
255265
Option for specifying the workspace path list
256266
"""
257267

258-
parser.add_argument("--workspace", help=("path of file containing csv separated lists of "
259-
"flows in workspace that may be resolved as subflow targets. "
260-
"If empty this defaults to flows target csv file, the specified directory, "
261-
"or contents of flow directory or flows listed in commandline."),
268+
parser.add_argument("--workspace",
269+
help=("path of file containing csv separated lists of "
270+
"flows in workspace that may be resolved as subflow targets. "
271+
"If empty this defaults to flows target csv file, the specified directory, "
272+
"or contents of flow directory or flows listed in commandline."),
262273
type=check_file_exists)
263274

264275
"""
@@ -310,12 +321,12 @@ def parse_args(my_args: list[str], default: str = None) -> argparse.Namespace:
310321
parser.add_argument("--query_path", required=False, help="path of custom query python file")
311322
parser.add_argument("--query_class", required=False, help="name of class to instantiate in query_path")
312323
parser.add_argument("--preset", required=False, help="name of preset to use (consumed by query code)")
313-
parser.add_argument("--optional_queries", required=False,
314-
help="comma separated list of optional queries to execute in addition to the preset.")
315-
parser.add_argument("--optional_queries_path", required=False,
316-
help="path of file containing a comma separated list of optional queries to "
324+
parser.add_argument("--queries", required=False,
325+
help="comma separated list of queries to execute in addition to the preset.")
326+
parser.add_argument("--queries_path", required=False,
327+
help="path of file containing a comma separated list of queries to "
317328
"execute in addition to the preset.", type=check_file_exists)
318-
parser.add_argument("--all_optional", required=False, action='store_true', help=("run all optional queries. "
329+
parser.add_argument("--all_queries", required=False, action='store_true', help=("run all queries. "
319330
"WARNING: this is noisy."))
320331
parser.add_argument("--debug_flow", required=False, help=("For expert use only. Run a debug flow with"
321332
"the supplied parameter."))
@@ -341,24 +352,33 @@ def main(argv: list[str] = None) -> str | None:
341352

342353
args = parse_args(argv, default=default)
343354

355+
if args.preset is not None:
356+
# check preset
357+
if args.preset not in flow_scanner.query_manager.PRESETS:
358+
raise argparse.ArgumentTypeError(f"Invalid preset requested: {args.preset}")
359+
344360
# check if the user wants only a description of the default queries
345361
if args.preset_info is True:
346362
# if user has specified a preset, use that or None
347-
preset_name = args.preset
348-
preset = default_query.build_preset(preset_name)
349-
queries = preset.queries
350-
query_info = [x.to_dict() for x in list(queries)]
351-
sorted_query_info = sorted(query_info, key=lambda x: x['query_id'])
352-
desc = json.dumps(sorted_query_info, indent=4, cls=PresetEncoder)
353-
# print to stdout so user can redirect or examine
354-
print(desc)
355-
363+
preset_name = args.preset or "default"
364+
if preset_name:
365+
preset = flow_scanner.query_manager.build_preset_for_name(preset_name)
366+
if preset is None:
367+
print(f"No preset found with name: {preset_name}")
368+
print_preset_list()
369+
return
370+
queries = preset.queries
371+
query_info = [x.to_dict() for x in list(queries)]
372+
sorted_query_info = sorted(query_info, key=lambda x: x['query_id'])
373+
desc = json.dumps(sorted_query_info, indent=4, cls=PresetEncoder)
374+
# print to stdout so user can redirect or examine
375+
print(desc)
356376
return
357377

358-
# Check if user wants list of optional queries
359-
if args.optional_query_info is True:
360-
desc = flow_scanner.query_manager.get_all_optional_descriptions()
361-
print(desc)
378+
# Check if user wants list of queries
379+
if args.query_info is True:
380+
desc = flow_scanner.query_manager.get_query_descriptions()
381+
print(json.dumps(desc, indent=4, cls=PresetEncoder))
362382
return
363383

364384
# logging
@@ -381,18 +401,18 @@ def main(argv: list[str] = None) -> str | None:
381401

382402

383403
"""
384-
Handle Optional Queries
404+
Handle Queries
385405
"""
386-
if args.all_optional is True:
387-
optional_qry_l = get_all_optional_queries()
406+
if args.all_queries is True:
407+
qry_l = get_all_queries()
388408

389-
elif args.optional_queries_path is not None:
390-
optional_qry_l = get_tokens_from_csv_file(args.optional_queries_path)
409+
elif args.queries_path is not None:
410+
qry_l = get_tokens_from_csv_file(args.queries_path)
391411

392-
elif args.optional_queries is not None:
393-
optional_qry_l = get_validated_queries(unsplit(args.optional_queries))
412+
elif args.queries is not None:
413+
qry_l = get_validated_queries(unsplit(args.queries))
394414
else:
395-
optional_qry_l = None
415+
qry_l = None
396416

397417
"""
398418
Handle chunking
@@ -459,7 +479,7 @@ def main(argv: list[str] = None) -> str | None:
459479
query_module_path=args.query_path,
460480
query_class_name=args.query_class,
461481
query_preset=args.preset,
462-
optional_queries=optional_qry_l,
482+
queries=qry_l,
463483
crawl_dir=args.crawl_dir,
464484
resolver=resolver)
465485

packages/code-analyzer-flow-engine/FlowScanner/flow_scanner/branch_state.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -320,19 +320,20 @@ def load_crawl_step(self, crawler: Crawler, crawl_step: CrawlStep = None) -> Cra
320320
old_history = self.current_crawl_step.visitor.history
321321
new_history = next_cs.visitor.history
322322

323-
if old_history == ():
323+
if old_history == () or old_history == ('*',):
324324
# we are on the first branch, so no backtracking
325325
old_map = self.__influence_map[self.current_crawl_step]
326326

327-
elif len(new_history) >= len(old_history) and new_history[0:len(old_history)] == old_history:
327+
elif len(new_history) == len(old_history) + 1 and new_history[0:len(old_history)] == old_history:
328328
# the new branch is a continuation of old branch so no backtracking
329329
old_map = self.__influence_map[self.current_crawl_step]
330330

331331
else:
332-
# the new history is a different branch, and we need to backtrack
332+
# the new history is a different branch, and we need to either backtrack or jump ahead
333+
# get_last_ancestor returns the last time we visited the element right before the current element
333334
old_cs = crawler.get_last_ancestor(next_cs)
334335
if old_cs is None:
335-
# no predecessor, so we use default
336+
# use default map
336337
old_map = self.__default_map
337338
else:
338339
old_map = self.__influence_map[old_cs]

0 commit comments

Comments
 (0)