views:

546

answers:

3

Apparently the answer to my question "Can I restrict nose coverage output to directory (rather than package)?" is no, but I can pass a --coverage-package=PACKAGE option to nose with the package name of each .py file in the directory.

So for example, if the directory contains:

foo.py
bar.py
baz.py

...then I would need to use the command:

nosetests --with-coverage --coverage-package=foo --coverage-package=bar --coverage-package=baz

So my question is, can someone write some shell script code (preferably sh or bash) to take all the filenames in the current directory with a .py extension and generate the above command-line (with the .py extensions removed)? My bash skills are quite limited. (I'm tempted to just do it in Python.)

+11  A: 
nosetests --with-coverage $(for f in *.py; do echo --cover-package="${f%.*}"; done)

The trick is here is using parameter substitution to remove the file extension.

${f%.*}
Ayman Hourieh
This solution will fail for all .py files that contain spaces or glob metacharacters. And while it's obviously uncommon for python script filenames to contain such, there's no point in keeping a broken solution alive when you can just as easily do it right (see my answer to the question).
lhunath
As for why it fails, any unquoted expansions undergo bash word splitting and bash pathname expansion. As such, foo="A * in the sky.mp3"; rm -f $foo will have disastrous results. Because it's often unpredictable how disastrous the results of this bad practice will be, it's best to just **always** properly quote the separate arguments to your commands. In this case, each --cover-package is a separate argument and should be individually quoted.
lhunath
Thanks for the explanation. Quoting "${f%.*}" seems to protect against the problem you are describing. I edited my answer. I tested by creating files called 1.py, 2.py and "3 * 4.pl", and then running rm $(for f in *.pl; do echo "$f"; done). The command deletes the last file only. I prefer to fix my solution as I find a for loop much more readable.
Ayman Hourieh
+2  A: 
nosetests --with-coverage `ls *.py|sed -e 's/^/--cover-package=' -e 's/\.py$//'`
chaos
This doesn't strip trailing '.py' bits - yet.
Jonathan Leffler
Oh. Yeah, missed that requirement. Fixed momentarily.
chaos
+3  A: 

And if you care to do it correct (which means, don't allow wordsplitting to cut your filenames apart or unexpected globbing to expand to random filenames), use an array:

files=(*.py)
packages=("${files[@]/%.py/}")
nosetests --with-coverage "${packages[@]/#/--coverage-package=}"
lhunath
How would I include the required "--cover-package=" part of the options? (And to be correct, you'll also need to include "--with-coverage".)
Daryl Spitzer
Ah, yes, indeed. Let me add that to it.
lhunath
Could you please provide an example in which my answer fails? I used the same globbing pattern as you.
Ayman Hourieh