Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
FSL
fslpy
Commits
5851e4c0
Commit
5851e4c0
authored
May 13, 2022
by
Paul McCarthy
🚵
Browse files
Merge branch 'bf/wrapper-singlechar-args' into 'master'
RF: New options to applyArgStyle See merge request fsl/fslpy!335
parents
281160db
169e2a66
Pipeline
#14141
canceled with stages
in 4 seconds
Changes
6
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
CHANGELOG.rst
View file @
5851e4c0
...
...
@@ -3,6 +3,21 @@ order.
3.9.1 (Friday 13th May 2022)
----------------------------
Changed
^^^^^^^
* Adjusted the :func:`.applyArgStyle` function so that it allows separate
specification of the style to use for single-character arguments. This
fixes some usage issues with commands such as FSL ``fast``, which have
regular ``--=`` arguments, but also single-character arguments which
expect multiple positional values (!335).
3.9.0 (Tuesday 12th April 2022)
-------------------------------
...
...
fsl/utils/run.py
View file @
5851e4c0
...
...
@@ -252,7 +252,8 @@ def _dryrun(submit, returnStdout, returnStderr, returnExitcode, *args):
results
=
[]
stderr
=
''
stdout
=
' '
.
join
(
args
)
join
=
getattr
(
shlex
,
'join'
,
' '
.
join
)
stdout
=
join
(
args
)
if
returnStdout
:
results
.
append
(
stdout
)
if
returnStderr
:
results
.
append
(
stderr
)
...
...
fsl/wrappers/fast.py
View file @
5851e4c0
...
...
@@ -51,10 +51,8 @@ def fast(imgs, out='fast', **kwargs):
}
cmd
=
[
'fast'
,
'--out=%s'
%
out
]
cmd
+=
wutils
.
applyArgStyle
(
'--='
,
valmap
=
valmap
,
cmd
+=
wutils
.
applyArgStyle
(
valmap
=
valmap
,
argmap
=
argmap
,
singlechar_args
=
True
,
**
kwargs
)
cmd
+=
imgs
...
...
fsl/wrappers/wrapperutils.py
View file @
5851e4c0
...
...
@@ -243,18 +243,24 @@ generated command line arguments.
"""
def
applyArgStyle
(
style
,
def
applyArgStyle
(
style
=
None
,
valsep
=
None
,
argmap
=
None
,
valmap
=
None
,
singlechar_args
=
False
,
charstyle
=
None
,
charsep
=
None
,
**
kwargs
):
"""Turns the given ``kwargs`` into command line options. This function
is intended to be used to automatically generate command line options
from arguments passed into a Python function.
The ``style`` and ``valsep`` arguments control how key-value pairs
are converted into command-line options:
The default settings will generate arguments that match typical UNIX
conventions, e.g. ``-a val``, ``--arg=val``, ``-a val1 val2``,
``--arg=val1,val2``.
The ``style`` and ``valsep`` (and ``charstyle`` and ``charsep``) arguments
control how key-value pairs are converted into command-line options:
========= ========== ===========================
...
...
@@ -275,86 +281,117 @@ def applyArgStyle(style,
========= ========== ===========================
:arg style: Controls how the ``kwargs`` are converted into command-line
options - must be one of ``'-'``, ``'--'``, ``'-='``, or
``'--='``.
:arg style: Controls how the ``kwargs`` are converted into command-line
options - must be one of ``'-'``, ``'--'``, ``'-='``, or
``'--='`` (the default).
:arg valsep: Controls how the values passed to command-line options
which expect multiple arguments are delimited - must be
one of ``' '``, ``','`` or ``'"'``. Defaults to ``' '``
if ``'=' not in style``, ``','`` otherwise.
:arg argmap: Dictionary of ``{kwarg-name : cli-name}`` mappings. This be
used if you want to use different argument names in your
Python function for the command-line options.
:arg valsep: Controls how the values passed to command-line options
which expect multiple arguments are delimited - must be
one of ``' '``, ``','`` or ``'"'``. Defaults to ``' '``
if ``'=' not in style``, ``','`` otherwise.
:arg valmap: Dictionary of ``{cli-name : value}`` mappings. This can be
used to define specific semantics for some command-line
options. Acceptable values for ``value`` are as follows
:arg argmap: Dictionary of ``{kwarg-name : cli-name}`` mappings. This c
an
be used if you want to use different argument names in your
Python function for the command-line options
.
- :data:`SHOW_IF_TRUE` - if the argument is present,
an
d
``True`` in ``kwargs``, the command line option
will be added (without any arguments)
.
:arg valmap: Dictionary of ``{cli-name : value}`` mappings. This can be
used to define specific semantics for som
e command
-
line
options. Acceptable values for ``value`` are as follows
- :data:`HIDE_IF_TRUE` - if the argument is present, and
``False`` in ``kwargs``, th
e command
line
option
will be added (without any arguments).
- :data:`SHOW_IF_TRUE` - i
f the argument is present
, and
``True``
in ``kwargs``,
the
command
line option
will be
added
(
with
out any
argument
s)
.
- Any other constant value. I
f the argument is present
in ``kwargs``,
its
command
-
line option
will be
added
,
with
the constant value as its
argument.
- :data:`HIDE_IF_TRUE` - if the argument is present, and
``False`` in ``kwargs``, the command line option
will be added (without any arguments).
The argument for any options not specified in the
``valmap`` will be converted into strings.
- Any other constant value. If the argument is present
in ``kwargs``, its command-line option will be
added, with the constant value as its argument.
:arg charstyle: Separate style specification for single-character
arguments. If ``style == '--='``, defaults to ``'-'``,
matching UNIX conventions. Otherwise defaults to the
value of ``style``.
The argument for any options not specified in the ``valmap``
will be converted into strings.
:arg charsep: Controls how the values passed to command-line options
which expect multiple arguments are delimited - must be
one of ``' '``, ``','`` or ``'"'``. Defaults to ``' '``
if ``'=' not in style``, ``','`` otherwise.
:arg singlechar_args: If True, single character arguments always take a
single hyphen prefix (e.g. -h) regardless of the
style.
:arg singlechar_args: If ``True``, equivalent to ``charstyle='-'``.
:arg kwargs: Arguments to be converted into command-line options.
:returns: A list containing the generated command-line options.
"""
if
style
is
None
:
style
=
'--='
if
charstyle
is
None
:
if
singlechar_args
:
charstyle
=
'-'
elif
style
==
'--='
:
charstyle
=
'-'
else
:
charstyle
=
style
if
valsep
is
None
:
if
'='
in
style
:
valsep
=
','
else
:
valsep
=
' '
if
charsep
is
None
:
if
'='
in
charstyle
:
charsep
=
','
else
:
charsep
=
' '
if
style
not
in
(
'-'
,
'--'
,
'-='
,
'--='
):
raise
ValueError
(
'Invalid style: {}'
.
format
(
style
))
raise
ValueError
(
f
'Invalid style:
{
style
}
'
)
if
charstyle
not
in
(
'-'
,
'--'
,
'-='
,
'--='
):
raise
ValueError
(
f
'Invalid charstyle:
{
charstyle
}
'
)
if
valsep
not
in
(
' '
,
','
,
'"'
):
raise
ValueError
(
'Invalid valsep: {}'
.
format
(
valsep
))
raise
ValueError
(
f
'Invalid valsep:
{
valsep
}
'
)
if
charsep
not
in
(
' '
,
','
,
'"'
):
raise
ValueError
(
f
'Invalid charsep:
{
charsep
}
'
)
#
we don't handle the case where '=' in
#
style, and valsep == ' ', because no
#
sane CLI app would do this. Right?
#
It makes no sense to combine argument+value
#
with an equals sign, but not have the value
#
quoted (e.g "--arg=val1 val2 val3").
if
'='
in
style
and
valsep
==
' '
:
raise
ValueError
(
'Incompatible style and valsep: s={} v={}'
.
format
(
style
,
valsep
))
raise
ValueError
(
f
'Incompatible style
{
style
}
'
'and valsep ({valsep})'
)
if
'='
in
charstyle
and
charsep
==
' '
:
raise
ValueError
(
f
'Incompatible style
{
charstyle
}
'
'and valsep ({charsep})'
)
if
argmap
is
None
:
argmap
=
{}
if
valmap
is
None
:
valmap
=
{}
def
fmtarg
(
arg
):
if
style
in
(
'-'
,
'-='
)
or
(
singlechar_args
and
len
(
arg
)
==
1
):
arg
=
'-{}'
.
format
(
arg
)
elif
style
in
(
'--'
,
'--='
):
arg
=
'--{}'
.
format
(
arg
)
return
arg
# always returns a sequence
def
fmtval
(
val
):
# Format the argument.
def
fmtarg
(
arg
,
style
):
if
style
in
(
'--'
,
'--='
):
return
f
'--
{
arg
}
'
else
:
return
f
'-
{
arg
}
'
# Formt the argument value. Always returns
# a sequence. We don't add quotes around
# values - instead we just ensure that
# arguments+values are grouped correctly in
# the final result (the same as what
# shlex.split would generate for a properly
# quoted string).
def
fmtval
(
val
,
sep
):
if
isinstance
(
val
,
abc
.
Sequence
)
and
(
not
isinstance
(
val
,
str
)):
val
=
[
str
(
v
)
for
v
in
val
]
if
val
sep
==
' '
:
return
val
elif
val
sep
==
'"'
:
return
[
' '
.
join
(
val
)]
else
:
return
[
val
sep
.
join
(
val
)]
if
sep
==
' '
:
return
val
elif
sep
==
'"'
:
return
[
' '
.
join
(
val
)]
else
:
return
[
sep
.
join
(
val
)]
else
:
return
[
str
(
val
)]
# val is assumed to be a sequence
def
fmtargval
(
arg
,
val
):
# Format the argument and value together.
# val is assumed to be a sequence.
def
fmtargval
(
arg
,
val
,
style
):
# if '=' in style, val will
# always be a single string
if
'='
in
style
:
return
[
'{}={}'
.
format
(
arg
,
val
[
0
])]
...
...
@@ -366,16 +403,19 @@ def applyArgStyle(style,
if
v
is
None
:
continue
if
len
(
k
)
==
1
:
sty
,
sep
=
charstyle
,
charsep
else
:
sty
,
sep
=
style
,
valsep
k
=
argmap
.
get
(
k
,
k
)
mapv
=
valmap
.
get
(
k
,
fmtval
(
v
))
k
=
fmtarg
(
k
)
mapv
=
valmap
.
get
(
k
,
fmtval
(
v
,
sep
))
k
=
fmtarg
(
k
,
sty
)
if
mapv
in
(
SHOW_IF_TRUE
,
HIDE_IF_TRUE
):
if
(
mapv
is
SHOW_IF_TRUE
and
v
)
or
\
(
mapv
is
HIDE_IF_TRUE
and
not
v
):
args
.
append
(
k
)
else
:
args
.
extend
(
fmtargval
(
k
,
mapv
))
args
.
extend
(
fmtargval
(
k
,
mapv
,
sty
))
return
args
...
...
tests/test_wrappers/test_wrappers.py
View file @
5851e4c0
...
...
@@ -27,6 +27,10 @@ def checkResult(cmd, base, args, stripdir=None):
Pre python 3.7, we couldn't control the order in which command
line args were generated, so we needed to test all possible orderings.
But for Python >= 3.7, the order in which kwargs are passed will
be the same as the order in which they are rendered, so this function
is not required.
:arg cmd: Generated command
:arg base: Beginning of expected command
:arg args: Sequence of expected arguments
...
...
@@ -366,6 +370,12 @@ def test_fast():
expected
=
[
cmd
,
'--out=myseg'
,
'--class=3'
,
'--verbose'
,
'in1'
,
'in2'
,
'in3'
]
assert
result
.
stdout
[
0
]
==
' '
.
join
(
expected
)
result
=
fw
.
fast
((
'in1'
,
'in2'
,
'in3'
),
'myseg'
,
n_classes
=
3
,
a
=
'reg.mat'
,
A
=
(
'csf'
,
'gm'
,
'wm'
),
Prior
=
True
)
expected
=
[
cmd
,
'--out=myseg'
,
'--class=3'
,
'-a'
,
'reg.mat'
,
'-A'
,
'csf'
,
'gm'
,
'wm'
,
'--Prior'
,
'in1'
,
'in2'
,
'in3'
]
assert
result
.
stdout
[
0
]
==
' '
.
join
(
expected
)
def
test_fsl_anat
():
with
asrt
.
disabled
(),
\
...
...
tests/test_wrappers/test_wrapperutils.py
View file @
5851e4c0
...
...
@@ -30,11 +30,24 @@ from .. import mockFSLDIR, cleardir, checkdir, testdir, touch
from
..test_run
import
mock_fsl_sub
def
test_applyArgStyle_default
():
kwargs
=
{
'arg1'
:
'val'
,
'arg2'
:
[
'val1'
,
'val2'
],
'a'
:
'val'
,
'b'
:
[
'val1'
,
'val2'
],
}
exp
=
[
'--arg1=val'
,
'--arg2=val1,val2'
,
'-a'
,
'val'
,
'-b'
,
'val1'
,
'val2'
]
assert
wutils
.
applyArgStyle
(
**
kwargs
)
==
exp
def
test_applyArgStyle
():
kwargs
=
{
'name'
:
'val'
,
'name2'
:
[
'val1'
,
'val2'
],
'name3'
:
'val1 val2'
,
}
# these combinations of style+valsep should
...
...
@@ -51,28 +64,70 @@ def test_applyArgStyle():
wutils
.
applyArgStyle
(
'-'
,
valsep
=
'b'
,
**
kwargs
)
# style, valsep, expected_result.
# Order of arguments is not guaranteed
tests
=
[
(
'-'
,
' '
,
[
'
-name
val'
,
'-name2
val1 val2'
]),
(
'-'
,
'"'
,
[
'
-name
val'
,
'-name
2 "
val1 val2
"
'
]),
(
'-'
,
','
,
[
'
-name
val'
,
'-name2
val1
,
val2'
]),
(
'-'
,
' '
,
[
'-name
'
,
'
val'
,
'-name2
'
,
'val1'
,
'val2'
,
'-name3'
,
'
val1 val2'
]),
(
'-'
,
'"'
,
[
'
-name'
,
'val'
,
'-name2'
,
'val1
val
2
'
,
'-name
3'
,
'
val1 val2'
]),
(
'-'
,
','
,
[
'-name
'
,
'
val'
,
'-name2
'
,
'val1,val2'
,
'-name3'
,
'
val1
val2'
]),
(
'--'
,
' '
,
[
'--name
val'
,
'--name2
val1 val2'
]),
(
'--'
,
'"'
,
[
'--name
val'
,
'--name2
"
val1 val2
"
'
]),
(
'--'
,
','
,
[
'--name
val'
,
'--name2
val1
,
val2'
]),
(
'--'
,
' '
,
[
'--name
'
,
'
val'
,
'--name2
'
,
'val1'
,
'val2'
,
'--name3'
,
'
val1 val2'
]),
(
'--'
,
'"'
,
[
'--name
'
,
'
val'
,
'--name2
'
,
'
val1 val2
'
,
'--name3'
,
'val1 val2
'
]),
(
'--'
,
','
,
[
'--name
'
,
'
val'
,
'--name2
'
,
'val1,val2'
,
'--name3'
,
'
val1
val2'
]),
(
'-='
,
'"'
,
[
'
-name=val'
,
'-name
2="
val1 val2
"
'
]),
(
'-='
,
','
,
[
'
-name=val'
,
'-name2=val1,val2'
]),
(
'-='
,
'"'
,
[
'
-name=val'
,
'
-name
2
=val
1 val2
'
,
'-name
3=
val1 val2'
]),
(
'-='
,
','
,
[
'-name=val'
,
'-name2=val1,
val2'
,
'-name3=val1
val2'
]),
(
'--='
,
'"'
,
[
'--name=val'
,
'--name2=
"
val1 val2
"
'
]),
(
'--='
,
','
,
[
'--name=val'
,
'--name2=val1,val2'
]),
(
'--='
,
'"'
,
[
'--name=val'
,
'--name2=
val1 val2'
,
'--name3=
val1 val2'
]),
(
'--='
,
','
,
[
'--name=val'
,
'--name2=val1,
val2'
,
'--name3=val1
val2'
]),
]
for
style
,
valsep
,
exp
in
tests
:
exp
=
[
shlex
.
split
(
e
)
for
e
in
exp
]
result
=
wutils
.
applyArgStyle
(
style
,
valsep
=
valsep
,
**
kwargs
)
assert
result
==
exp
def
test_applyArgStyle_charstyle
():
kwargs
=
{
'n'
:
'val'
,
'm'
:
[
'val1'
,
'val2'
],
'o'
:
'val1 val2'
,
}
# these combinations of charstyle+
# charsep should raise an error
with
pytest
.
raises
(
ValueError
):
wutils
.
applyArgStyle
(
charstyle
=
'--='
,
charsep
=
' '
,
**
kwargs
)
with
pytest
.
raises
(
ValueError
):
wutils
.
applyArgStyle
(
charstyle
=
'-='
,
charsep
=
' '
,
**
kwargs
)
# unsupported chrastyle/charsep
with
pytest
.
raises
(
ValueError
):
wutils
.
applyArgStyle
(
charstyle
=
'?'
,
**
kwargs
)
with
pytest
.
raises
(
ValueError
):
wutils
.
applyArgStyle
(
'-'
,
charsep
=
'b'
,
**
kwargs
)
# style, valsep, charstyle, charsep, expected_result.
# Order of arguments is not guaranteed
tests
=
[
(
'-'
,
' '
,
[
'-n'
,
'val'
,
'-m'
,
'val1'
,
'val2'
,
'-o'
,
'val1 val2'
]),
(
'-'
,
'"'
,
[
'-n'
,
'val'
,
'-m'
,
'val1 val2'
,
'-o'
,
'val1 val2'
]),
(
'-'
,
','
,
[
'-n'
,
'val'
,
'-m'
,
'val1,val2'
,
'-o'
,
'val1 val2'
]),
(
'--'
,
' '
,
[
'--n'
,
'val'
,
'--m'
,
'val1'
,
'val2'
,
'--o'
,
'val1 val2'
]),
(
'--'
,
'"'
,
[
'--n'
,
'val'
,
'--m'
,
'val1 val2'
,
'--o'
,
'val1 val2'
]),
(
'--'
,
','
,
[
'--n'
,
'val'
,
'--m'
,
'val1,val2'
,
'--o'
,
'val1 val2'
]),
(
'-='
,
'"'
,
[
'-n=val'
,
'-m=val1 val2'
,
'-o=val1 val2'
]),
(
'-='
,
','
,
[
'-n=val'
,
'-m=val1,val2'
,
'-o=val1 val2'
]),
(
'--='
,
'"'
,
[
'--n=val'
,
'--m=val1 val2'
,
'--o=val1 val2'
]),
(
'--='
,
','
,
[
'--n=val'
,
'--m=val1,val2'
,
'--o=val1 val2'
]),
]
for
style
,
valsep
,
exp
in
tests
:
result
=
wutils
.
applyArgStyle
(
charstyle
=
style
,
charsep
=
valsep
,
**
kwargs
)
assert
result
in
(
exp
[
0
]
+
exp
[
1
],
exp
[
1
]
+
exp
[
0
])
assert
result
==
exp
def
test_applyArgStyle_argmap
():
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment