Skip to content

Commit 3702682

Browse files
authored
Merge pull request #227 from byexamples/Issue-226-Make-Options-slash-Args-File-Easier
Issue 226 make options slash args file easier
2 parents f60b651 + d0e6b31 commit 3702682

9 files changed

Lines changed: 210 additions & 8 deletions

File tree

byexample/cmdline.py

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
from .log_level import str_to_level
66
from .prof import profile
7+
'''
8+
>>> from byexample.cmdline import ByexampleArgumentParser
9+
'''
710

811

912
class _CSV(argparse.Action):
@@ -181,6 +184,88 @@ def _expand_glob_patterns(gpatterns):
181184
return list(set(fnames))
182185

183186

187+
class ByexampleArgumentParser(argparse.ArgumentParser):
188+
def convert_arg_line_to_args(self, arg_line):
189+
''' Return a list with the arguments read from a line.
190+
191+
If in the line there is a flag/argument with one or more
192+
values the flag may be separated from its value(s) with
193+
a space and this method will replace it with an '='.
194+
195+
This is in order to produce a single argument for each
196+
line as it is expected by argparse.ArgumentParser.
197+
198+
>>> parser = ByexampleArgumentParser()
199+
>>> parser.convert_arg_line_to_args('--skip=foo')
200+
['--skip=foo']
201+
202+
>>> parser.convert_arg_line_to_args('--skip foo')
203+
['--skip=foo']
204+
205+
>>> parser.convert_arg_line_to_args('--skip=foo bar')
206+
['--skip=foo bar']
207+
208+
>>> parser.convert_arg_line_to_args('--skip foo bar')
209+
['--skip=foo bar']
210+
211+
>>> parser.convert_arg_line_to_args('--skip=')
212+
['--skip=']
213+
214+
>>> parser.convert_arg_line_to_args('--skip ')
215+
['--skip ']
216+
217+
>>> parser.convert_arg_line_to_args('--skip')
218+
['--skip']
219+
220+
>>> parser.convert_arg_line_to_args('foo')
221+
['foo']
222+
223+
>>> parser.convert_arg_line_to_args('foo bar')
224+
['foo bar']
225+
226+
Empty lines or lines that starts with a # are ignored.
227+
228+
>>> parser.convert_arg_line_to_args(' ')
229+
[]
230+
231+
>>> parser.convert_arg_line_to_args(' # foo ')
232+
[]
233+
'''
234+
arg_line = arg_line.lstrip()
235+
if arg_line and arg_line[0] in self.prefix_chars:
236+
flag, _, value = arg_line.partition(' ')
237+
value = value.lstrip()
238+
if not value:
239+
# the flag is argumentless or it is using '='
240+
# to paste the flag with its argument,
241+
# return the whole line then
242+
#
243+
# Ex:
244+
# -foo
245+
# -bar=32
246+
# -zaz=
247+
return [arg_line]
248+
else:
249+
if '=' in flag:
250+
# the line already has the '=' to paste the
251+
# flag with the argument, leave them as they are
252+
#
253+
# Ex:
254+
# -bar=32 42
255+
return [arg_line]
256+
257+
# Paste the flag with its value (or values) with a '='
258+
return [flag + '=' + value]
259+
260+
if arg_line and arg_line[0] == '#':
261+
return []
262+
263+
if not arg_line:
264+
return []
265+
266+
return [arg_line]
267+
268+
184269
@profile
185270
def parse_args(args=None):
186271
'''Parse the arguments args and return the them.
@@ -192,7 +277,7 @@ def parse_args(args=None):
192277
)
193278

194279
python_version = sys.version.split(' ', 1)[0]
195-
parser = argparse.ArgumentParser(
280+
parser = ByexampleArgumentParser(
196281
fromfile_prefix_chars='@',
197282
add_help=False,
198283
formatter_class=HelpExtraFormatter,

docs/basic/options.md

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,22 +64,104 @@ Other languages and concerns may add their owns.
6464
If the amount of options is a little overwhelming for you, you can
6565
write them down to a file and let ``byexample`` load them for you.
6666

67-
The only convention that you need to follow is to write one option
68-
per line and use ``=`` for the arguments.
67+
The only convention that you need to follow is to write **one** option
68+
per line. If the option receives one argument you can separate them by
69+
`=` or by a space but if there is more than one argument you will have
70+
to write the option and the arguments one by one in its own line.
6971

7072
```shell
7173
$ cat test/ds/options_file
72-
-l=python
73-
--options="+norm-ws"
74+
# Options and their arguments are separated by a = or by a space
75+
-l python
76+
--options=+norm-ws
77+
<...>
78+
# But if the option receives more than one argument, all of them
79+
# must be in its own line
80+
--skip
81+
test/ds/pkg/foo1.py
82+
test/ds/pkg/foo2.py
83+
<...>
84+
# This wouldn't work:
85+
#--skip test/ds/pkg/foo1.py test/ds/pkg/foo2.py
86+
<...>
7487
```
7588

7689
Then load it with ``@`` and the file; you can use multiple files
7790
and combine them with more options from the command line:
7891

7992
```shell
80-
$ byexample @test/ds/options_file test/ds/python-tutorial.v2.md
93+
$ byexample @test/ds/options_file -- test/ds/python-tutorial.v2.md
8194
<...>
8295
File test/ds/python-tutorial.v2.md, 4/4 test ran in <...> seconds
8396
[PASS] Pass: 4 Fail: 0 Skip: 0
8497
```
8598

99+
> **Note:** before `10.5.2` the options in the file required to be followed by an
100+
> `=` like `-l=python`; spaces were not allowed.
101+
102+
## File pattern expansions
103+
104+
Consider the following:
105+
106+
```shell
107+
$ byexample -l python test/ds/pkg/*.py | grep pkg | sort # byexample: +timeout=8
108+
File test/ds/pkg/bar1.py, 1/1 test ran in <...> seconds
109+
File test/ds/pkg/foo1.py, 1/1 test ran in <...> seconds
110+
File test/ds/pkg/foo2.py, 1/1 test ran in <...> seconds
111+
```
112+
113+
Your *shell* usually expands `test/ds/pkg/*.py` into a list of files:
114+
`test/ds/pkg/bar1.py`, `test/ds/pkg/foo1.py` and `test/ds/pkg/foo2.py`
115+
116+
The same happens with the argument list for `--skip`, it is expanded by
117+
your *shell*.
118+
119+
```shell
120+
$ byexample -l python --skip test/ds/pkg/foo* -- test/ds/pkg/*.py | grep pkg | sort # byexample: +timeout=8
121+
File test/ds/pkg/bar1.py, 1/1 test ran in <...> seconds
122+
```
123+
124+
Since `10.0.3`, `byexample` does the same expansion even if you shell does not.
125+
This is in particular useful if the list of files is in a file (where your shell
126+
never ever see).
127+
128+
```shell
129+
$ cat test/ds/pkg/bopts
130+
--skip=test/ds/pkg/foo*.py
131+
--
132+
test/ds/pkg/*.py
133+
134+
$ byexample -l python @test/ds/pkg/bopts | grep pkg | sort # byexample: +timeout=8
135+
File test/ds/pkg/bar1.py, 1/1 test ran in <...> seconds
136+
```
137+
138+
<!--
139+
140+
Extra test checking that options in a file with multiple spaces are
141+
interpreted correctly now that we support separate the flag from its
142+
value with a space (before an '=' was always required)
143+
144+
So the following lines are equivalent
145+
-skip=foo bar
146+
-skip foo bar
147+
148+
$ cat test/ds/pkg/bopts2
149+
<...>skip
150+
test/ds/pkg/foo1.py
151+
test/ds/pkg/foo2.py
152+
<...>
153+
154+
$ byexample -l python @test/ds/pkg/bopts2 | grep pkg | wc -l # byexample: +timeout=8
155+
1
156+
157+
$ cat test/ds/pkg/bopts3
158+
<...>skip=test/ds/pkg/foo1.py test/ds/pkg/foo2.py
159+
<...>
160+
161+
$ byexample -l python @test/ds/pkg/bopts2 | grep pkg | wc -l # byexample: +timeout=8
162+
1
163+
164+
$ byexample -l python @test/ds/pkg/bopts | grep pkg | wc -l # byexample: +timeout=8
165+
1
166+
167+
-->

test/ds/options_file

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,12 @@
1-
-l=python
2-
--options="+norm-ws"
1+
# Options and their arguments are separated by a = or by a space
2+
-l python
3+
--options=+norm-ws
4+
5+
# But if the option receives more than one argument, all of them
6+
# must be in its own line
7+
--skip
8+
test/ds/pkg/foo1.py
9+
test/ds/pkg/foo2.py
10+
11+
# This wouldn't work:
12+
#--skip test/ds/pkg/foo1.py test/ds/pkg/foo2.py

test/ds/pkg/bar1.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
'''
2+
>>> print("bar1")
3+
bar1
4+
'''

test/ds/pkg/bopts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
--skip=test/ds/pkg/foo*.py
2+
--
3+
test/ds/pkg/*.py

test/ds/pkg/bopts2

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
--skip
2+
test/ds/pkg/foo1.py
3+
test/ds/pkg/foo2.py
4+
--
5+
test/ds/pkg/foo1.py
6+
test/ds/pkg/foo2.py
7+
test/ds/pkg/bar1.py

test/ds/pkg/bopts3

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
--skip=test/ds/pkg/foo1.py test/ds/pkg/foo2.py
2+
--
3+
test/ds/pkg/*.py

test/ds/pkg/foo1.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
'''
2+
>>> print("foo1")
3+
foo1
4+
'''

test/ds/pkg/foo2.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
'''
2+
>>> print("foo2")
3+
foo2
4+
'''

0 commit comments

Comments
 (0)