Python 3 and testing.

95 views
Skip to first unread message

Edward d'Auvergne

unread,
Dec 4, 2014, 4:24:51 AM12/4/14
to nmrglue...@googlegroups.com
Hi Jonathan,

I have created a fork of nmrglue (https://github.com/edward-dauvergne/nmrglue) and am playing around with Python 3 support for using nmrglue with the software relax (http://www.nmr-relax.com).  I was wondering how the tests are run in nmrglue?  And is there a reason why the unit test framework is not being used to run all the tests together as a cohesive test suite?

Cheers,

Edward

Jonathan Helmus

unread,
Dec 4, 2014, 12:45:05 PM12/4/14
to nmrglue...@googlegroups.com
--
You received this message because you are subscribed to the Google Groups "nmrglue-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nmrglue-discu...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Edward,

    nmrglue uses nose (https://nose.readthedocs.org/en/latest/) as a unit test framework in the same manner as a many Scientific Python packages.  When everything is installed correctly the test suite can be run from the source directory using the command nosetests. 

    nmrglue has two types of tests, the first are tests which use experimental and simulated NMR data which must be downloaded and processing using external tools such as NMRPipe, Sparky, RNMRTK, etc.  Details on how to prepare this data can be found in the documentation in the section "Test and Example Data" [1].  These tests are in the root "tests" directory.  NMRPipe is also required when running these tests to create reference output.  Details on running these tests can be found at the "Testing" section of the documentation [2].  Phasing out these tests is planned in the future once the second type of tests have more complete coverage.

    The second type of tests are unit tests for which all data files are included in the source directory and no external programs are required.  Currently only tests of this nature have been written for the nmrglue.pipe module but additions are always welcome.  These tests are stored in the nmrglue/fileio/tests directory with the corresponding data stored in a data directory under that path.  These tests will be run if nosetests is run from the root folder but can also be run using the command nosetests -exe nmrglue from any location if nmrglue is installed.  These tests are the only ones currently being run by Travis-CI [3] when commit are added to the primary repository.

I have verified that with the current master (d9f6099) all tests of both types pass on Python 2.6 2.7, 3.3 and 3.4.

Cheers,

    - Jonathan Helmus

[1] http://nmrglue.readthedocs.org/en/latest/devel/index.html#test-and-example-data
[2] http://nmrglue.readthedocs.org/en/latest/devel/index.html#testing
[3] https://travis-ci.org/jjhelmus/nmrglue

Edward d'Auvergne

unread,
Dec 5, 2014, 3:35:30 AM12/5/14
to nmrglue...@googlegroups.com
Hi Jonathan,

To run the full set of tests, do you run 'nosetests' in the base directory?  When I do this, it complains about many missing files ('data/simpson_1d/1d_rawbin.fid' for example).  In the fork I made, there were a few essential changes for Python 3.  For example passing the output of the dictionary keys() function and the range() builtin function through the list() builtin function to convert the Python 3 generator objects these create into true list objects.  There was also the fatal has_keys() dictionary method call and xrange() which don't exist in Python 3, but these are in the 'doc' directory I guess they are not so important.  I used the 2to3 conversion script to find all of these.  Have a look and see if there is anything of interest (https://github.com/edward-dauvergne/nmrglue.git).  As for running nosetests in the nmrglue subdirectory, this passes for me for Python 2.6, 2.7, 3.1, 3.2, 3.3, and 3.4.

Cheers,

Edward

Jonathan Helmus

unread,
Dec 5, 2014, 11:15:02 AM12/5/14
to nmrglue...@googlegroups.com
Edward,

    Yes, you should be able to run nosetests in the base directory.  Before doing this you will need to download, process and set the data directory so that the tests in the "tests" directory know about these files.  There is not a method for running these tests without this data.  All of this is documented in the Developement Guide section titled "Test and Example Data" [1].

I'll check into the keys and range iterators, from my look through all the instances of these functions were fine as generators and did not need to be lists.  I didn't spend time converting the Python files in the docs directory to be Python 3 compatible.  For the time being the documentation will be build using Python 2.7 and getting this to work in Python 3 is a low priority.

    I'm not seeing any new commits on your GitHub repo, did you do a "git push"?

    The tests in the nmrglue directory are minimal, it is good to hear they are passing but getting the other tests to pass is more significant.
On 12/05/2014 02:35 AM, Edward d'Auvergne wrote:
Hi Jonathan,

To run the full set of tests, do you run 'nosetests' in the base directory?  When I do this, it complains about many missing files ('data/simpson_1d/1d_rawbin.fid' for example).  In the fork I made, there were a few essential changes for Python 3.  For example passing the output of the dictionary keys() function and the range() builtin function through the list() builtin function to convert the Python 3 generator objects these create into true list objects.  There was also the fatal has_keys() dictionary method call and xrange() which don't exist in Python 3, but these are in the 'doc' directory I guess they are not so important.  I used the 2to3 conversion script to find all of these.  Have a look and see if there is anything of interest (https://github.com/edward-dauvergne/nmrglue.git).  As for running nosetests in the nmrglue subdirectory, this passes for me for Python 2.6, 2.7, 3.1, 3.2, 3.3, and 3.4.

Cheers,

Edward



On Thursday, 4 December 2014 10:24:51 UTC+1, Edward d'Auvergne wrote:
Hi Jonathan,

I have created a fork of nmrglue (https://github.com/edward-dauvergne/nmrglue) and am playing around with Python 3 support for using nmrglue with the software relax (http://www.nmr-relax.com).  I was wondering how the tests are run in nmrglue?  And is there a reason why the unit test framework is not being used to run all the tests together as a cohesive test suite?

Cheers,

Edward
--

Edward d'Auvergne

unread,
Dec 5, 2014, 12:43:57 PM12/5/14
to nmrglue...@googlegroups.com
Hi,

The git push didn't work as I didn't check and it was asking for the
user name and password. As for the test data, I downloaded the file
linked on the docs page
(http://nmrglue.googlecode.com/files/all_none_test_example_data.zip,
159 Mb) and unpacked it in the 'examples' directory, then ran the
'make_links.sh' script. This didn't seem to fix the problem, as that
file does not contain the ones that are missing. I can't find
'LiCl_ref1.tnt' anywhere, for example.

Regards,

Edward
> You received this message because you are subscribed to a topic in the
> Google Groups "nmrglue-discuss" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/nmrglue-discuss/-DGErzCS6WM/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to

Jonathan Helmus

unread,
Dec 5, 2014, 1:08:30 PM12/5/14
to nmrglue...@googlegroups.com
As described in the documentation the linked file is for the examples
not the tests. You want the test data. I realized that only the test
data for version 0.4-dev is available on the Google code download site
and Google no longer allows new uploads. I've uploaded the current test
data to my Google drive:

https://drive.google.com/folderview?id=0BzCZObue1SeERk52WmJfSVdVZWc&usp=sharing

Download the test_data_v0.5-dev.zip file, unpack it into a directory (I
suggest data under the root nmrglue source), verify that NMRPipe,
Sparky, RNMRTK and SIMPSON are installed and available, and run the
make_fill_test_data_set.com script.

Cheers,

- Jonathan Helmus

Edward d'Auvergne

unread,
Dec 5, 2014, 1:46:36 PM12/5/14
to nmrglue...@googlegroups.com
Cheers,

This looks much better :) I just installed SIMPSON, and I already
have NMRPipe and Sparky. But I'll skip RNMRTK, as there is no direct
download, and just ignore those missing file failures. For the tests,
the data looks like it must be unpacked and set up in the 'data'
directory. It seems like this is a hardcoded path. Anyway, I'm
testing now. Using Python 2.7, at the end I see:

"""
Ran 165 tests in 177.902s

FAILED (errors=21, failures=10)
"""

The errors are from the missing RNMRTK files. I'm not sure about the
failures though. With nosetests-3.4, I see:

"""
Ran 165 tests in 158.442s

FAILED (errors=21, failures=10)
"""

Anyway, despite the tests performing identically, the warning messages
were not exactly the same. The following is a significant message:

"""
/data/relax/nmrglue/edward-nmrglue/nmrglue/fileio/bruker.py:1538:
UserWarning: Unable to correctly parse line:##$LOCKED= True
warn("Unable to correctly parse line:" + line)
/data/relax/nmrglue/edward-nmrglue/nmrglue/fileio/bruker.py:1538:
UserWarning: Unable to correctly parse line:##$LOCSHFT= False
warn("Unable to correctly parse line:" + line)
/data/relax/nmrglue/edward-nmrglue/nmrglue/fileio/bruker.py:1538:
UserWarning: Unable to correctly parse line:##$PROSOL= False
warn("Unable to correctly parse line:" + line)
F/data/relax/nmrglue/edward-nmrglue/nmrglue/fileio/bruker.py:1538:
UserWarning: Unable to correctly parse line:##$LOCKED= False
warn("Unable to correctly parse line:" + line)
"""

Looking at the code, I can see that you are not decoding the Python 3
byte array. In relax (http://www.nmr-relax.com), I simply use the
lines:

# Decode Python 3 byte arrays.
if hasattr(line, 'decode'):
line = line.decode()

This is essential whenever you read IO from a file, STDIN, etc. to
convert it to a unicode string. It allows for both Python 2 and 3
support. Without it, you will never match with checks such as:

line = f.readline().rstrip() # read a line
if line == '': # end of file found
break

if line[:6] == "##END=":
#print("End of file")
break
elif line[:2] == "$$":
dic["_comments"].append(line)
elif line[:2] == "##" and line[2] != "$":
dic["_coreheader"].append(line)
elif line[:3] == "##$":
try:
key, value = parse_jcamp_line(line, f)
dic[key] = value
except:
warn("Unable to correctly parse line:" + line)
else:
warn("Extraneous line:" + line)

Cheers,

Edward


P. S. Have you though of setting up an account for one of the other
open source infrastructure frameworks - Gnu Savannah
(https://savannah.nongnu.org/), Gna! (http://gna.org/), SourceForge
(http://sourceforge.net/), etc. - and using the key pieces of open
source infrastructure missing from GitHub and Google forums? It could
even just be used for file uploads. I've heard others doing this to
fill in the missing parts. Though maybe GitHub have improved their
services since then, I haven't been following.

Edward d'Auvergne

unread,
Dec 5, 2014, 2:06:28 PM12/5/14
to nmrglue...@googlegroups.com
Hi,

I may have broken something in my fork for nmrglue with the Python 3
automated 2to3 fixes. In https://github.com/jjhelmus/nmrglue/, I see:

"""
Ran 165 tests in 176.212s

FAILED (errors=24, failures=1)
"""

This is probably the Pthon 'idioms' change with the command:

$ 2to3 -j 4 -w -f idioms .

This has made changes such as:

- if type(v) == str:
+ if isinstance(v, str):

However in Python 3 this should be compared to the 'unicode' object as
string objects should no longer exist. I'm not sure how "type(v) ==
str" is functioning in the main repository with Python 3.

Regards,

Edward



On 5 December 2014 at 19:46, Edward d'Auvergne

Jonathan Helmus

unread,
Dec 8, 2014, 12:58:11 PM12/8/14
to nmrglue...@googlegroups.com
The idiomatic change that is made by 2to3 is not an equivalent statement:

type(v) == int

is False when v is a boolean where as

isinstance(v, int)

is True when v is a boolean since bool is a subclass of int.

This causes the behaviour of the write_jcamp function to change when
2to3 is used to covert the bruker.py module.

- Jonathan Helmus

Edward d'Auvergne

unread,
Dec 10, 2014, 4:21:22 AM12/10/14
to nmrglue...@googlegroups.com
On 8 December 2014 at 18:57, Jonathan Helmus <jjhe...@gmail.com> wrote:
> The idiomatic change that is made by 2to3 is not an equivalent statement:
>
> type(v) == int
>
> is False when v is a boolean where as
>
> isinstance(v, int)
>
> is True when v is a boolean since bool is a subclass of int.
>
> This causes the behaviour of the write_jcamp function to change when 2to3 is
> used to covert the bruker.py module.
>
> - Jonathan Helmus
>

Hi,

I was close to that point, but I didn't realise this code was
differentiating between numbers and Boolean types, as I assumed that
"type(True) == int" would be True. Fortunately I made this false
assumption in relax and therefore such 2to3 changes had zero impact on
the code. Or in some places I test for Booleans before integers.
Anyway, I have used the 2to3 script to change most of the idioms (or
sometimes called anti-idioms) in relax, as that will prepare the code
base for the future Python feature depreciations.

There are still a few Python 3 issues in nmrglue which are not
triggered in the test suite and normal operation. For example the
StandardError exception class used twice in fileio/sparky.py when
non-Sparky files are read. But this has been replaced by Exception in
Python 3:

[edward@localhost ~]$ python2.5
Python 2.5.6 (r256, Dec 4 2014, 15:01:21)
[GCC 4.8.2] on linux3
Type "help", "copyright", "credits" or "license" for more information.
>>> StandardError
<type 'exceptions.StandardError'>
>>> Exception
<type 'exceptions.Exception'>
>>>
[edward@localhost ~]$ python3.4
Python 3.4.1 (default, Jul 2 2014, 11:13:57)
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> StandardError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'StandardError' is not defined
>>> Exception
<class 'Exception'>
>>>

For some reason this is not triggered when importing nmrglue (or
running the test suite). As for my fork, I'm only using this for
experimenting and for watching the delta produced when running:

$ 2to3 -j 4 -w -f apply -f basestring -f buffer -f callable -f dict -f
except -f exec -f execfile -f exitfunc -f filter -f funcattrs -f
future -f getcwdu -f has_key -f idioms -f import -f imports -f
imports2 -f input -f intern -f isinstance -f itertools -f
itertools_imports -f long -f map -f metaclass -f methodattrs -f ne -f
next -f nonzero -f numliterals -f operator -f paren -f raise -f
raw_input -f reduce -f renames -f repr -f set_literal -f standarderror
-f sys_exc -f throw -f tuple_params -f types -f unicode -f urllib -f
ws_comma -f xrange -f xreadlines -f zip .

Half the delta is rubbish or deadly for both Python 2 and 3 compatible
code, while others are quite informative.

Cheers,

Edward
Reply all
Reply to author
Forward
0 new messages