Source code for aiida.backends.tests.common.test_extendeddicts

# -*- coding: utf-8 -*-
###########################################################################
# Copyright (c), The AiiDA team. All rights reserved.                     #
# This file is part of the AiiDA code.                                    #
#                                                                         #
# The code is hosted on GitHub at https://github.com/aiidateam/aiida_core #
# For further information on the license, see the LICENSE.txt file        #
# For further information please visit http://www.aiida.net               #
###########################################################################
"""Tests for the extended dictionary classes."""
# pylint: disable=pointless-statement,attribute-defined-outside-init
from __future__ import division
from __future__ import print_function
from __future__ import absolute_import

import copy
import pickle
import unittest

from six import integer_types

from aiida.common import json
from aiida.common import exceptions
from aiida.common import extendeddicts


[docs]class TestFFADExample(extendeddicts.FixedFieldsAttributeDict): """ An example class that accepts only the 'alpha', 'beta' and 'gamma' keys/attributes. """ _valid_fields = ('alpha', 'beta', 'gamma')
[docs]class TestDFADExample(extendeddicts.DefaultFieldsAttributeDict): """ An example class that has 'alpha', 'beta' and 'gamma' as default keys. """ _default_fields = ('alpha', 'beta', 'gamma')
[docs] @staticmethod def validate_alpha(value): """Validate a value.""" # Ok if unset if value is None: return if not isinstance(value, integer_types): raise TypeError('expecting integer') if value < 0: raise ValueError('expecting a positive or zero value')
[docs]class TestAttributeDictAccess(unittest.TestCase): """ Try to access the dictionary elements in various ways, copying (shallow and deep), check raised exceptions. """
[docs] def test_access_dict_to_attr(self): """Test dictionary to attribute.""" dictionary = extendeddicts.AttributeDict() dictionary['test'] = 'abc' self.assertEqual(dictionary.test, 'abc')
[docs] def test_access_attr_to_dict(self): """Test attribute to dictionary.""" dictionary = extendeddicts.AttributeDict() dictionary.test = 'def' self.assertEqual(dictionary['test'], 'def')
[docs] def test_access_nonexisting_asattr(self): """Test non-existing attribute.""" dictionary = extendeddicts.AttributeDict() with self.assertRaises(AttributeError): dictionary.test
[docs] def test_access_nonexisting_askey(self): """Test non-existing attribute as key.""" dictionary = extendeddicts.AttributeDict() with self.assertRaises(KeyError): dictionary['test']
[docs] def test_del_nonexisting_askey(self): """Test deleting non-existing attribute as key.""" dictionary = extendeddicts.AttributeDict() with self.assertRaises(KeyError): del dictionary['test']
[docs] def test_del_nonexisting_asattr(self): """Test deleting non-existing attribute.""" dictionary = extendeddicts.AttributeDict() with self.assertRaises(AttributeError): del dictionary.test
[docs] def test_copy(self): """Test copying.""" dictionary_01 = extendeddicts.AttributeDict() dictionary_01.alpha = 'a' dictionary_02 = dictionary_01.copy() dictionary_02.alpha = 'b' self.assertEqual(dictionary_01.alpha, 'a') self.assertEqual(dictionary_02.alpha, 'b')
[docs] def test_delete_after_copy(self): """Test deleting after copying.""" dictionary_01 = extendeddicts.AttributeDict() dictionary_01.alpha = 'a' dictionary_01.beta = 'b' dictionary_02 = dictionary_01.copy() del dictionary_01.alpha del dictionary_01['beta'] with self.assertRaises(AttributeError): _ = dictionary_01.alpha with self.assertRaises(KeyError): _ = dictionary_01['beta'] self.assertEqual(dictionary_02['alpha'], 'a') self.assertEqual(dictionary_02.beta, 'b') self.assertEqual(set(dictionary_01.keys()), set({})) self.assertEqual(set(dictionary_02.keys()), set({'alpha', 'beta'}))
[docs] def test_shallowcopy1(self): """Test shallow copying.""" dictionary_01 = extendeddicts.AttributeDict() dictionary_01.alpha = [1, 2, 3] dictionary_01.beta = 3 dictionary_02 = dictionary_01.copy() dictionary_02.alpha[0] = 4 dictionary_02.beta = 5 self.assertEqual(dictionary_01.alpha, [4, 2, 3]) # copy does a shallow copy self.assertEqual(dictionary_02.alpha, [4, 2, 3]) self.assertEqual(dictionary_01.beta, 3) self.assertEqual(dictionary_02.beta, 5)
[docs] def test_shallowcopy2(self): """Test shallow copying.""" dictionary_01 = extendeddicts.AttributeDict() dictionary_01.alpha = {'a': 'b', 'c': 'd'} # dictionary_02 = copy.deepcopy(dictionary_01) dictionary_02 = dictionary_01.copy() # doesn't work like this, would work as dictionary_02['x']['a'] # i think that it is because deepcopy on dict actually creates a # copy only if the data is changed; but for a nested dict, # dictionary_02.alpha returns a dict wrapped in our class and this looses all the # information on what should be updated when changed. dictionary_02.alpha['a'] = 'ggg' self.assertEqual(dictionary_01.alpha['a'], 'ggg') # copy does a shallow copy self.assertEqual(dictionary_02.alpha['a'], 'ggg')
[docs] def test_deepcopy1(self): """Test deep copying.""" dictionary_01 = extendeddicts.AttributeDict() dictionary_01.alpha = [1, 2, 3] dictionary_02 = copy.deepcopy(dictionary_01) dictionary_02.alpha[0] = 4 self.assertEqual(dictionary_01.alpha, [1, 2, 3]) self.assertEqual(dictionary_02.alpha, [4, 2, 3])
[docs] def test_shallowcopy3(self): """Test shallow copying.""" dictionary_01 = extendeddicts.AttributeDict() dictionary_01.alpha = {'a': 'b', 'c': 'd'} dictionary_02 = copy.deepcopy(dictionary_01) dictionary_02.alpha['a'] = 'ggg' self.assertEqual(dictionary_01.alpha['a'], 'b') # copy does a shallow copy self.assertEqual(dictionary_02.alpha['a'], 'ggg')
[docs]class TestAttributeDictNested(unittest.TestCase): """ Test the functionality of nested AttributeDict classes. """
[docs] def test_nested(self): """Test nested dictionary.""" dictionary_01 = extendeddicts.AttributeDict({'x': 1, 'y': 2}) dictionary_02 = extendeddicts.AttributeDict({'z': 3, 'w': 4}) dictionary_01.nested = dictionary_02 self.assertEqual(dictionary_01.nested.z, 3) self.assertEqual(dictionary_01['nested'].w, 4) self.assertEqual(dictionary_01.nested['w'], 4) dictionary_02['w'] = 5 self.assertEqual(dictionary_01['nested'].w, 5) self.assertEqual(dictionary_01.nested['w'], 5)
[docs] def test_comparison(self): """Test dictionary comparison.""" dictionary_01 = extendeddicts.AttributeDict({'x': 1, 'y': 2, 'z': extendeddicts.AttributeDict({'w': 3})}) dictionary_02 = extendeddicts.AttributeDict({'x': 1, 'y': 2, 'z': extendeddicts.AttributeDict({'w': 3})}) # They compare to the same value but they are different objects self.assertFalse(dictionary_01 is dictionary_02) self.assertEqual(dictionary_01, dictionary_02) dictionary_02.z.w = 4 self.assertNotEqual(dictionary_01, dictionary_02)
[docs] def test_nested_deepcopy(self): """Test nested deepcopy.""" dictionary_01 = extendeddicts.AttributeDict({'x': 1, 'y': 2}) dictionary_02 = extendeddicts.AttributeDict({'z': 3, 'w': 4}) dictionary_01.nested = dictionary_02 dictionary_01copy = copy.deepcopy(dictionary_01) self.assertEqual(dictionary_01copy.nested.z, 3) self.assertEqual(dictionary_01copy['nested'].w, 4) self.assertEqual(dictionary_01copy.nested['w'], 4) dictionary_02['w'] = 5 self.assertEqual(dictionary_01copy['nested'].w, 4) # Nothing has changed self.assertEqual(dictionary_01copy.nested['w'], 4) # Nothing has changed self.assertEqual(dictionary_01copy.nested.w, 4) # Nothing has changed self.assertEqual(dictionary_01['nested'].w, 5) # The old one is updated self.assertEqual(dictionary_01.nested['w'], 5) # The old one is updated self.assertEqual(dictionary_01.nested.w, 5) # The old one is updated dictionary_01copy.nested.w = 6 self.assertEqual(dictionary_01copy.nested.w, 6) self.assertEqual(dictionary_01.nested.w, 5) self.assertEqual(dictionary_02.w, 5)
[docs]class TestAttributeDictSerialize(unittest.TestCase): """ Test serialization/deserialization (with json, pickle, ...) """
[docs] def test_json(self): """Test loading and dumping from json.""" dictionary_01 = extendeddicts.AttributeDict({'x': 1, 'y': 2}) dictionary_02 = json.loads(json.dumps(dictionary_01)) # Note that here I am comparing a dictionary (dictionary_02) with a # extendeddicts.AttributeDict (dictionary_02) and they still compare to equal self.assertEqual(dictionary_01, dictionary_02)
[docs] def test_json_recursive(self): """Test loading and dumping from json recursively.""" dictionary_01 = extendeddicts.AttributeDict({'x': 1, 'y': 2, 'z': extendeddicts.AttributeDict({'w': 4})}) dictionary_02 = json.loads(json.dumps(dictionary_01)) # Note that here I am comparing a dictionary (dictionary_02) with a (recursive) # extendeddicts.AttributeDict (dictionary_02) and they still compare to equal self.assertEqual(dictionary_01, dictionary_02)
[docs] def test_pickle(self): """Test pickling.""" dictionary_01 = extendeddicts.AttributeDict({'x': 1, 'y': 2}) dictionary_02 = pickle.loads(pickle.dumps(dictionary_01)) self.assertEqual(dictionary_01, dictionary_02)
[docs] def test_pickle_recursive(self): """Test pickling recursively.""" dictionary_01 = extendeddicts.AttributeDict({'x': 1, 'y': 2, 'z': extendeddicts.AttributeDict({'w': 4})}) dictionary_02 = pickle.loads(pickle.dumps(dictionary_01)) self.assertEqual(dictionary_01, dictionary_02)
[docs]class TestFFAD(unittest.TestCase): """Test for the fixed fields attribute dictionary."""
[docs] def test_insertion(self): """Test insertion.""" dictionary = TestFFADExample() dictionary['alpha'] = 1 dictionary.beta = 2 # Not a valid key. with self.assertRaises(KeyError): dictionary['delta'] = 2 with self.assertRaises(AttributeError): dictionary.epsilon = 5
[docs] def test_insertion_on_init(self): """Test insertion in constructor.""" TestFFADExample({'alpha': 1, 'beta': 2}) with self.assertRaises(KeyError): # 'delta' is not a valid key TestFFADExample({'alpha': 1, 'delta': 2})
[docs] def test_pickle(self): """Note: pickle works here because self._valid_fields is defined at the class level!""" dictionary_01 = TestFFADExample({'alpha': 1, 'beta': 2}) dictionary_02 = pickle.loads(pickle.dumps(dictionary_01)) dictionary_02.gamma = 3 with self.assertRaises(KeyError): dictionary_02['delta'] = 2
[docs] def test_class_attribute(self): """ I test that the get_valid_fields() is working as a class method, so I don't need to instantiate the class to get the list. """ self.assertEqual(set(TestFFADExample.get_valid_fields()), set(['alpha', 'beta', 'gamma']))
[docs]class TestDFAD(unittest.TestCase): """Test for the default fields attribute dictionary."""
[docs] def test_insertion_and_retrieval(self): """Test insertion and retrieval.""" dictionary = TestDFADExample() dictionary['alpha'] = 1 dictionary.beta = 2 dictionary['delta'] = 3 dictionary.epsilon = 4 self.assertEqual(dictionary.alpha, 1) self.assertEqual(dictionary.beta, 2) self.assertEqual(dictionary['delta'], 3) self.assertEqual(dictionary['epsilon'], 4)
[docs] def test_keylist_method(self): """Test keylist retrieval.""" dictionary = TestDFADExample() dictionary['alpha'] = 1 dictionary.beta = 2 dictionary['delta'] = 3 dictionary.epsilon = 4 self.assertEqual(set(dictionary.get_default_fields()), set(['alpha', 'beta', 'gamma'])) self.assertEqual(set(dictionary.keys()), set(['alpha', 'beta', 'delta', 'epsilon'])) self.assertEqual(set(dictionary.defaultkeys()), set(['alpha', 'beta'])) self.assertEqual(set(dictionary.extrakeys()), set(['delta', 'epsilon'])) self.assertIsNone(dictionary.gamma)
[docs] def test_class_attribute(self): """ I test that the get_default_fields() is working as a class method, so I don't need to instantiate the class to get the list. """ self.assertEqual(set(TestDFADExample.get_default_fields()), set(['alpha', 'beta', 'gamma']))
[docs] def test_validation(self): """Test validation.""" dictionary = TestDFADExample() # Should be ok to have an empty 'alpha' attribute dictionary.validate() dictionary.alpha = 4 dictionary.beta = 'text' # This should be fine dictionary.validate() dictionary.alpha = 'string' # dictionary.alpha must be a positive integer with self.assertRaises(exceptions.ValidationError): dictionary.validate() dictionary.alpha = -3 # alpha must be a positive integer with self.assertRaises(exceptions.ValidationError): dictionary.validate()