Skip to content

Commit

Permalink
Merge pull request #10 from davidhassell/master
Browse files Browse the repository at this point in the history
Fox for Units.conform behavior varies depending on arguments (#9)
  • Loading branch information
davidhassell committed Jul 2, 2020
2 parents 4e86915 + 815e1b2 commit c6a4805
Show file tree
Hide file tree
Showing 258 changed files with 40,679 additions and 300 deletions.
14 changes: 14 additions & 0 deletions CONTRIBUTING.md
@@ -0,0 +1,14 @@
Thank you for taking the time to consider making a contribution to the
cfunits package.

# General Guidelines

General questions, suggestions for enhancements, and reports of bugs
may be reported in the GitHub issue tracker:
https://github.com/NCAS-CMS/cfunits/issues

Contributions are welcome and should first be raised as issues in the
GitHub issue tracker: https://github.com/NCAS-CMS/cfunits/issues,
prior to submitting a pull request containing the new code made from a
fork of the cfunits GitHub code repository:
https://github.com/NCAS-CMS/cfunits.
12 changes: 12 additions & 0 deletions Changelog.rst
@@ -1,3 +1,15 @@
version 3.2.8
-------------
----

**2020-07-02**

* Fixed bug that caused failure when an `int` or `float` was input to
`cfunits.Units.conform`
(https://github.com/NCAS-CMS/cfunits/issues/9).
* Allowed `list` and `tuple` to be input to `cfunits.Units.conform`
(https://github.com/NCAS-CMS/cfunits/issues/9).

version 3.2.7
-------------
----
Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
@@ -1,6 +1,7 @@
include MANIFEST.in
include Changelog.md
include README.md
include requirements.txt
recursive-include cfunits *
recursive-exclude * *~ *.gz *.pyc
recursive-exclude * *.o *.so .nfs*
Expand Down
4 changes: 2 additions & 2 deletions cfunits/__init__.py
Expand Up @@ -18,8 +18,8 @@
__Conventions__ = 'CF-1.8'
__author__ = 'David Hassell'
__author__ = 'David Hassell'
__date__ = '2020-05-20'
__version__ = '3.2.7'
__date__ = '2020-07-02'
__version__ = '3.2.8'
__cf_version__ = '1.8'

from distutils.version import LooseVersion
Expand Down
47 changes: 47 additions & 0 deletions cfunits/test/run_tests_and_coverage
@@ -0,0 +1,47 @@
#!/bin/bash

# --------------------------------------------------------------------
# Run the full test suite and produce a coverage report.
#
# $ run_test_and_coverage
#
# Or to omit the generation of an html report:
#
# $ run_test_and_coverage --nohtml
# --------------------------------------------------------------------
set -x

library=cfunits

if [[ $1 == "--nohtml" ]] ; then
generate_html="false"
else
generate_html="true"
fi

if ! command -v coverage &> /dev/null
then
echo "Requires the coverage module: install it e.g. via 'pip install coverage'"
exit 3
fi

coverage erase
coverage run --source=.. --omit="*/test/*" run_tests.py
# Capture exit status from unit tests
rc=$?

coverage report

if [[ $generate_html == "true" ]] ; then
dir=${library}_coverage_report
mkdir -p $dir
coverage html --title "$library test suite coverage report" -d $dir

echo "coverage docs: https://coverage.readthedocs.io"
echo "coverage report URL: file://$PWD/$dir/index.html"
fi

# Return exit status from unit tests
exit $rc

#set +x
187 changes: 123 additions & 64 deletions cfunits/test/test_Units.py
Expand Up @@ -2,34 +2,39 @@
import os
import unittest

import numpy

import cfunits
from cfunits import Units


class UnitsTest(unittest.TestCase):
def test_Units___eq__(self):
self.assertTrue(Units('')==Units(''))
self.assertTrue(Units('18')==Units('18'))
self.assertTrue(Units('1')==Units('1'))
self.assertTrue(Units('m')==Units('m'))
self.assertTrue(Units('m')==Units('metres'))
self.assertTrue(Units('m')==Units('meTRES'))

self.assertTrue(
Units('days since 2000-1-1')==Units('d since 2000-1-1 0:0'))
self.assertTrue(
Units('days since 2000-1-1')!=Units('h since 1234-1-1 0:0'))
self.assertEqual(Units(''), Units(''))
self.assertEqual(Units('18'), Units('18'))
self.assertEqual(Units('1'), Units('1'))
self.assertEqual(Units('m'), Units('m'))
self.assertEqual(Units('m'), Units('metres'))
self.assertEqual(Units('m'), Units('meTRES'))

self.assertEqual(Units('days since 2000-1-1'),
Units('d since 2000-1-1 0:0'))
self.assertNotEqual(Units('days since 2000-1-1'),
Units('h since 1234-1-1 0:0'))

self.assertTrue(Units('days since 2000-1-1')==Units('d since 2000-1-1 0:0', calendar='gregorian'))
self.assertTrue(Units('days since 2000-1-1')==Units('d since 2000-1-1 0:0', calendar='standard'))
self.assertEqual(Units('days since 2000-1-1'),
Units('d since 2000-1-1 0:0', calendar='gregorian'))
self.assertEqual(Units('days since 2000-1-1'),
Units('d since 2000-1-1 0:0', calendar='standard'))

self.assertTrue(Units(calendar='noleap')==Units(calendar='noleap'))
self.assertTrue(Units(calendar='noleap')==Units(calendar='365_day'))
self.assertTrue(Units(calendar='nOLEAP')==Units(calendar='365_dAY'))
self.assertEqual(Units(calendar='noleap'), Units(calendar='noleap'))
self.assertEqual(Units(calendar='noleap'), Units(calendar='365_day'))
self.assertEqual(Units(calendar='nOLEAP'), Units(calendar='365_dAY'))

self.assertTrue(Units('days since 2000-1-1', calendar='all_leap')==Units('d since 2000-1-1 0:0', calendar='366_day'))
self.assertTrue(Units('days since 2000-1-1', calendar='all_leap')!=Units('h since 2000-1-1 0:0', calendar='366_day'))

self.assertEqual(Units('days since 2000-1-1', calendar='all_leap'),
Units('d since 2000-1-1 0:0', calendar='366_day'))
self.assertNotEqual(Units('days since 2000-1-1', calendar='all_leap'),
Units('h since 2000-1-1 0:0', calendar='366_day'))

def test_Units_equivalent(self):
self.assertTrue(Units('').equivalent(Units('')))
Expand Down Expand Up @@ -61,92 +66,146 @@ def test_Units_equivalent(self):

u = Units('days since 2000-02-02', calendar='standard')
v = Units('months since 2000-02-02', calendar='standard')
self.assertFalse(u == v)
self.assertNotEqual(u, v)

u = Units('days since 2000-02-02', calendar='standard')
v = Units('months since 2000-02-02', calendar='gregorian')
self.assertFalse(u == v)
self.assertNotEqual(u, v)

def test_Units_conform(self):
self.assertTrue(Units.conform(0.5, Units('km'), Units('m')) == 500)
self.assertTrue(
Units.conform(360, Units('second'), Units('minute')) == 6)
self.assertEqual(Units.conform(0.5, Units('km'), Units('m')), 500)

self.assertEqual(
Units.conform(360, Units('second'), Units('minute')), 6)

x = Units.conform([360], Units('second'), Units('minute'))
self.assertIsInstance(x, numpy.ndarray)
self.assertEqual(x.dtype, numpy.dtype('float64'))
self.assertTrue(numpy.allclose(x, 6))

x = Units.conform((360, 720), Units('second'), Units('minute'))
self.assertIsInstance(x, numpy.ndarray)
self.assertEqual(x.dtype, numpy.dtype('float64'))
self.assertTrue(numpy.allclose(x, [6, 12]))

x = Units.conform([360.0, 720.0], Units('second'), Units('minute'))
self.assertIsInstance(x, numpy.ndarray)
self.assertEqual(x.dtype, numpy.dtype('float64'))
self.assertTrue(numpy.allclose(x, [6, 12]))

x = Units.conform([[360, 720]], Units('second'), Units('minute'))
self.assertIsInstance(x, numpy.ndarray)
self.assertEqual(x.dtype, numpy.dtype('float64'))
self.assertTrue(numpy.allclose(x, [[6, 12]]))

v = numpy.array([360.0, 720.0])
x = Units.conform(v, Units('second'), Units('minute'))
self.assertIsInstance(x, numpy.ndarray)
self.assertEqual(x.dtype, numpy.dtype('float64'))
self.assertTrue(numpy.allclose(x, [6, 12]), x)

v = numpy.array([360, 720])
x = Units.conform(v, Units('second'), Units('minute'), inplace=True)
self.assertIsInstance(x, numpy.ndarray)
self.assertEqual(x.dtype, numpy.dtype('float64'))
self.assertTrue(numpy.allclose(x, [6, 12]))
self.assertTrue(numpy.allclose(x, v))

x = Units.conform(35, Units('degrees_C'), Units('degrees_F'))
self.assertIsInstance(x, float)
self.assertTrue(numpy.allclose(x, 95))

x = Units.conform([35], Units('degrees_C'), Units('degrees_F'))
self.assertIsInstance(x, numpy.ndarray)
self.assertEqual(x.dtype, numpy.dtype('float64'))
self.assertTrue(numpy.allclose(x, 95))

x = Units.conform(35, Units('degrees_C'), Units('degrees_F'),
inplace=True)
self.assertIsInstance(x, float)
self.assertTrue(numpy.allclose(x, 95))

x = Units.conform([35], Units('degrees_C'), Units('degrees_F'),
inplace=True)
self.assertIsInstance(x, numpy.ndarray)
self.assertEqual(x.dtype, numpy.dtype('float64'))
self.assertTrue(numpy.allclose(x, 95))

with self.assertRaises(ValueError):
Units.conform(1, Units('m'), Units('second'))

def test_Units_BINARY_AND_UNARY_OPERATORS(self):
self.assertTrue((Units('m')*2) ==Units('2m'))
self.assertTrue((Units('m')/2) ==Units('0.5m'))
self.assertTrue((Units('m')//2) ==Units('0.5m'))
self.assertTrue((Units('m')+2) ==Units('m @ -2'))
self.assertTrue((Units('m')-2) ==Units('m @ 2'))
self.assertTrue((Units('m')**2) ==Units('m2'))
self.assertTrue((Units('m')**-2) ==Units('m-2'))
self.assertTrue((Units('m2')**0.5)==Units('m'))
self.assertEqual(Units('m') * 2, Units('2m'))
self.assertEqual(Units('m') / 2, Units('0.5m'))
self.assertEqual(Units('m') // 2, Units('0.5m'))
self.assertEqual(Units('m') + 2, Units('m @ -2'))
self.assertEqual(Units('m') - 2, Units('m @ 2'))
self.assertEqual(Units('m') ** 2, Units('m2'))
self.assertEqual(Units('m') ** -2, Units('m-2'))
self.assertEqual(Units('m2') ** 0.5, Units('m'))

u = Units('m')
v = u
u *= 2
self.assertTrue(u==Units('2m'))
self.assertTrue(u!=v)
self.assertEqual(u, Units('2m'))
self.assertNotEqual(u, v)
u = Units('m')
v = u
u /= 2
self.assertTrue(u==Units('0.5m'))
self.assertTrue(u!=v)
self.assertEqual(u, Units('0.5m'))
self.assertNotEqual(u, v)
u = Units('m')
v = u
u //= 2
self.assertTrue(u==Units('0.5m'))
self.assertTrue(u!=v)
self.assertEqual(u, Units('0.5m'))
self.assertNotEqual(u, v)
u = Units('m')
v = u
u += 2
self.assertTrue(u==Units('m @ -2'))
self.assertTrue(u!=v)
self.assertEqual(u, Units('m @ -2'))
self.assertNotEqual(u, v)
u = Units('m')
v = u
u -= 2
self.assertTrue(u==Units('m @ 2'))
self.assertTrue(u!=v)
self.assertEqual(u, Units('m @ 2'))
self.assertNotEqual(u, v)
u = Units('m')
v = u
u **= 2
self.assertTrue(u==Units('m2'))
self.assertTrue(u!=v)
self.assertEqual(u, Units('m2'))
self.assertNotEqual(u, v)

self.assertTrue((2*Units('m')) ==Units('2m'))
self.assertTrue((2/Units('m')) ==Units('2 m-1'))
self.assertTrue((2//Units('m'))==Units('2 m-1'))
self.assertTrue((2+Units('m')) ==Units('m @ -2'))
self.assertTrue((2-Units('m')) ==Units('-1 m @ -2'))
self.assertEqual(2 * Units('m'), Units('2m'))
self.assertEqual(2 / Units('m'), Units('2 m-1'))
self.assertEqual(2 // Units('m'),Units('2 m-1'))
self.assertEqual(2 + Units('m'), Units('m @ -2'))
self.assertEqual(2 - Units('m'), Units('-1 m @ -2'))

self.assertTrue((Units('m')*Units('2m')) ==Units('2 m2'))
self.assertTrue((Units('m')/Units('2m')) ==Units('0.5'))
self.assertTrue((Units('m')//Units('2m'))==Units('0.5'))
self.assertEqual(Units('m') * Units('2m'), Units('2 m2'))
self.assertEqual(Units('m') / Units('2m'), Units('0.5'))
self.assertEqual(Units('m') // Units('2m'), Units('0.5'))

u = Units('m')
v = u
u *= u
self.assertTrue(u==Units('m2'))
self.assertTrue(u!=v)
self.assertEqual(u, Units('m2'))
self.assertNotEqual(u, v)
u = Units('m')
v = u
u /= u
self.assertTrue(u==Units('1'))
self.assertTrue(u!=v)
self.assertEqual(u, Units('1'))
self.assertNotEqual(u, v)
u = Units('m')
v = u
u //= u
self.assertTrue(u==Units('1'))
self.assertTrue(u!=v)
self.assertEqual(u, Units('1'))
self.assertNotEqual(u, v)

self.assertTrue(Units('m').log(10) == Units('lg(re 1 m)'))
self.assertTrue(Units('m').log(2) == Units('lb(re 1 m)'))
self.assertTrue(Units('m').log(math.e)== Units('ln(re 1 m)'))
self.assertTrue(
Units('m').log(1.5) == Units('2.46630346237643 ln(re 1 m)'))

self.assertEqual(Units('m').log(10), Units('lg(re 1 m)'))
self.assertEqual(Units('m').log(2), Units('lb(re 1 m)'))
self.assertEqual(Units('m').log(math.e), Units('ln(re 1 m)'))
self.assertEqual(Units('m').log(1.5),
Units('2.46630346237643 ln(re 1 m)'))

def test_Units_isvalid(self):
self.assertTrue(Units('m').isvalid)
Expand Down

0 comments on commit c6a4805

Please sign in to comment.