Skip to content

Commit 88e8219

Browse files
committed
Improved the sysargv filter. Wrote some extra tests.
1 parent 27c89a4 commit 88e8219

File tree

2 files changed

+99
-68
lines changed

2 files changed

+99
-68
lines changed

cylc/flow/option_parsers.py

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -858,39 +858,66 @@ def cleanup_sysargv(
858858
for x in compound_script_opts
859859
}
860860

861-
# Filter out non-cylc-play options:
862-
# The set of options which we want to weed out:
861+
# Get a list of unwanted args:
862+
unwanted_compound: List[str] = []
863+
unwanted_simple: List[str] = []
863864
for unwanted_dest in (set(options.__dict__)) - set(script_opts_by_dest):
864-
865-
# The possible ways this could be written - if the above
866-
# were "workflow_name" this could be '-n' or '--workflow-name':
867865
for unwanted_arg in compound_opts_by_dest[unwanted_dest].args:
866+
if (
867+
compound_opts_by_dest[unwanted_dest].kwargs.get('action', None)
868+
in ['store_true', 'store_false']
869+
):
870+
unwanted_simple.append(unwanted_arg)
871+
else:
872+
unwanted_compound.append(unwanted_arg)
868873

869-
# Check for args which are standalone or space separated
870-
# `--workflow-name foo`:
871-
if unwanted_arg in sys.argv:
872-
index = sys.argv.index(unwanted_arg)
873-
sys.argv.pop(index)
874-
if (
875-
compound_opts_by_dest[unwanted_dest].kwargs['action']
876-
not in ['store_true', 'store_false']
877-
):
878-
sys.argv.pop(index)
879-
880-
# Check for `--workflow-name=foo`:
881-
elif unwanted_arg in [a.split('=')[0] for a in sys.argv]:
882-
for cli_arg in sys.argv:
883-
if cli_arg.startswith(unwanted_arg):
884-
sys.argv.remove(cli_arg)
874+
new_args = filter_sysargv(sys.argv, unwanted_simple, unwanted_compound)
885875

886876
# replace compound script name:
887-
sys.argv[1] = script_name
877+
new_args[1] = script_name
888878

889879
# replace source path with workflow ID.
890880
if str(source) in sys.argv:
891-
sys.argv.remove(str(source))
881+
new_args.remove(str(source))
892882
if workflow_id not in sys.argv:
893-
sys.argv.append(workflow_id)
883+
new_args.append(workflow_id)
884+
885+
sys.argv = new_args
886+
887+
888+
def filter_sysargv(
889+
sysargs, unwanted_simple: List, unwanted_compound: List
890+
) -> List:
891+
"""Create a copy of sys.argv without unwanted arguments:
892+
893+
Cases:
894+
>>> this = filter_sysargv
895+
>>> this(['--foo', 'expects-a-value', '--bar'], [], ['--foo'])
896+
['--bar']
897+
>>> this(['--foo=expects-a-value', '--bar'], [], ['--foo'])
898+
['--bar']
899+
>>> this(['--foo', '--bar'], ['--foo'], [])
900+
['--bar']
901+
"""
902+
pop_next: bool = False
903+
new_args: List = []
904+
for this_arg in sysargs:
905+
parts = this_arg.split('=', 1)
906+
if pop_next:
907+
pop_next = False
908+
continue
909+
elif parts[0] in unwanted_compound:
910+
# Case --foo=value or --foo value
911+
if len(parts) == 1:
912+
# --foo value
913+
pop_next = True
914+
continue
915+
elif parts[0] in unwanted_simple:
916+
# Case --foo does not expect a value:
917+
continue
918+
else:
919+
new_args.append(this_arg)
920+
return new_args
894921

895922

896923
def log_subcommand(*args):

tests/unit/test_option_parsers.py

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import cylc.flow.flags
2727
from cylc.flow.option_parsers import (
2828
CylcOptionParser as COP, Options, combine_options, combine_options_pair,
29-
OptionSettings, cleanup_sysargv
29+
OptionSettings, cleanup_sysargv, filter_sysargv
3030
)
3131

3232

@@ -321,20 +321,6 @@ def test_combine_options(inputs, expect):
321321
@pytest.mark.parametrize(
322322
'argv_before, kwargs, expect',
323323
[
324-
param(
325-
'vip myworkflow --foo something'.split(),
326-
{
327-
'script_name': 'play',
328-
'workflow_id': 'myworkflow',
329-
'compound_script_opts': [
330-
OptionSettings(['--foo', '-f']),
331-
],
332-
'script_opts': [
333-
OptionSettings(['--foo', '-f'])]
334-
},
335-
'play myworkflow --foo something'.split(),
336-
id='no opts to remove'
337-
),
338324
param(
339325
'vip myworkflow -f something -b something_else --baz'.split(),
340326
{
@@ -397,35 +383,6 @@ def test_combine_options(inputs, expect):
397383
'play --foo something myworkflow'.split(),
398384
id='no path given'
399385
),
400-
param(
401-
'vip --bar=something'.split(),
402-
{
403-
'script_name': 'play',
404-
'workflow_id': 'myworkflow',
405-
'compound_script_opts': [
406-
OptionSettings(['--bar', '-b'])],
407-
'script_opts': [],
408-
'source': './myworkflow',
409-
},
410-
'play myworkflow'.split(),
411-
id='removes --key=value'
412-
),
413-
param(
414-
# Test for https://github.com/cylc/cylc-flow/issues/5905
415-
'vip ./myworkflow --no-run-name --workflow-name=hi'.split(),
416-
{
417-
'script_name': 'play',
418-
'workflow_id': 'myworkflow',
419-
'compound_script_opts': [
420-
OptionSettings(['--no-run-name'], action='store_true'),
421-
OptionSettings(['--workflow-name'], action='store')
422-
],
423-
'script_opts': [],
424-
'source': './myworkflow',
425-
},
426-
'play myworkflow'.split(),
427-
id='equals-bug'
428-
),
429386
]
430387
)
431388
def test_cleanup_sysargv(monkeypatch, argv_before, kwargs, expect):
@@ -448,6 +405,53 @@ def test_cleanup_sysargv(monkeypatch, argv_before, kwargs, expect):
448405
assert sys.argv == dummy_cylc_path + expect
449406

450407

408+
@pytest.mark.parametrize(
409+
'sysargs, simple, compound, expect', (
410+
param(
411+
# Test for https://github.com/cylc/cylc-flow/issues/5905
412+
'--no-run-name --workflow-name=name'.split(),
413+
['--no-run-name'],
414+
['--workflow-name'],
415+
[],
416+
id='--workflow-name=name'
417+
),
418+
param(
419+
'--foo something'.split(),
420+
[], [], '--foo something'.split(),
421+
id='no-opts-removed'
422+
),
423+
param(
424+
[], ['--foo'], ['--bar'], [],
425+
id='Null-check'
426+
),
427+
param(
428+
'''--keep1 --keep2 42 --keep3=Hi
429+
--throw1 --throw2 84 --throw3=There
430+
'''.split(),
431+
['--throw1'],
432+
'--throw2 --throw3'.split(),
433+
'--keep1 --keep2 42 --keep3=Hi'.split(),
434+
id='complex'
435+
),
436+
param(
437+
"--foo 'foo=42' --bar='foo=94'".split(),
438+
[], ['--foo'],
439+
['--bar=\'foo=94\''],
440+
id='--bar=\'foo=94\''
441+
)
442+
)
443+
)
444+
def test_filter_sysargv(
445+
sysargs, simple, compound, expect
446+
):
447+
"""It returns the subset of sys.argv that we ask for.
448+
449+
n.b. The three most basic cases for this function are stored in
450+
its own docstring.
451+
"""
452+
assert filter_sysargv(sysargs, simple, compound) == expect
453+
454+
451455
class TestOptionSettings():
452456
@staticmethod
453457
def test_init():

0 commit comments

Comments
 (0)