Skip to content

Commit

Permalink
clean_value work (#2129)
Browse files Browse the repository at this point in the history
* common/test_hashing: add tests for truncation of subnormals, infs and nans

fixes: #2123

* o/i/g/node.clean_value: make inf/nan raise an exception

part of #2124
  • Loading branch information
dev-zero authored and giovannipizzi committed Oct 26, 2018
1 parent cc90894 commit 0f8721a
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 6 deletions.
19 changes: 18 additions & 1 deletion aiida/common/test_hashing.py
Expand Up @@ -17,6 +17,7 @@
import itertools
import collections
import uuid
import math
from datetime import datetime

import numpy as np
Expand All @@ -27,7 +28,7 @@
except ImportError:
import unittest

from aiida.common.hashing import make_hash, create_unusable_pass, is_password_usable
from aiida.common.hashing import make_hash, create_unusable_pass, is_password_usable, truncate_float64
from aiida.common.folders import SandboxFolder


Expand All @@ -46,6 +47,22 @@ def test_is_usable(self):
self.assertFalse(is_password_usable('random string without hash identification'))


class TruncationTest(unittest.TestCase):
"""
Tests for the truncate_* methods
"""

def test_nan(self):
self.assertTrue(math.isnan(truncate_float64(np.nan)))

def test_inf(self):
self.assertTrue(math.isinf(truncate_float64(np.inf)))
self.assertTrue(math.isinf(truncate_float64(-np.inf)))

def test_subnormal(self):
self.assertTrue(np.isclose(truncate_float64(1.0e-308), 1.0e-308, atol=1.0e-309))


class MakeHashTest(unittest.TestCase):
"""
Tests for the make_hash function.
Expand Down
20 changes: 15 additions & 5 deletions aiida/orm/implementation/general/node.py
Expand Up @@ -16,6 +16,8 @@
import logging
import importlib
import collections
import numbers
import math

import six

Expand Down Expand Up @@ -51,13 +53,20 @@ def clean_value(value):
# Must be imported in here to avoid recursive imports
from aiida.orm.data import BaseType

def clean_builtin(val):
if isinstance(val, numbers.Real) and (math.isnan(val) or math.isinf(val)):
# see https://www.postgresql.org/docs/current/static/datatype-json.html#JSON-TYPE-MAPPING-TABLE
raise ValidationError("nan and inf/-inf can not be serialized to the database")

return val

if isinstance(value, BaseType):
return value.value
elif isinstance(value, dict):
return clean_builtin(value.value)

if isinstance(value, dict):
# Check dictionary before iterables
return {k: clean_value(v) for k, v in value.items()}
elif (isinstance(value, collections.Iterable) and
not isinstance(value, six.string_types)):
if (isinstance(value, collections.Iterable) and not isinstance(value, six.string_types)):
# list, tuple, ... but not a string
# This should also properly take care of dealing with the
# basedatatypes.List object
Expand All @@ -67,7 +76,8 @@ def clean_value(value):
# itself - it's not super robust, but relies on duck typing
# (e.g. if there is something that behaves like an integer
# but is not an integer, I still accept it)
return value

return clean_builtin(value)


class _AbstractNodeMeta(ABCMeta):
Expand Down

0 comments on commit 0f8721a

Please sign in to comment.