From 1c48d7147584dc3bcb5b8ee9190802fd0b701fe6 Mon Sep 17 00:00:00 2001 From: Dominik Gresch Date: Wed, 18 Nov 2020 09:59:17 +0100 Subject: [PATCH] `SinglefileData`: add support for `pathlib.Path` for `file` argument (#3614) --- aiida/orm/nodes/data/singlefile.py | 3 +- tests/orm/data/test_singlefile.py | 312 ++++++++++++++++------------- 2 files changed, 180 insertions(+), 135 deletions(-) diff --git a/aiida/orm/nodes/data/singlefile.py b/aiida/orm/nodes/data/singlefile.py index 17c03663ed..eecc0484d3 100644 --- a/aiida/orm/nodes/data/singlefile.py +++ b/aiida/orm/nodes/data/singlefile.py @@ -11,6 +11,7 @@ import inspect import os import warnings +import pathlib from aiida.common import exceptions from aiida.common.warnings import AiidaDeprecationWarning @@ -102,7 +103,7 @@ def set_file(self, file, filename=None): """ # pylint: disable=redefined-builtin - if isinstance(file, str): + if isinstance(file, (str, pathlib.Path)): is_filelike = False key = os.path.basename(file) diff --git a/tests/orm/data/test_singlefile.py b/tests/orm/data/test_singlefile.py index 0815749f22..d4cfac3edc 100644 --- a/tests/orm/data/test_singlefile.py +++ b/tests/orm/data/test_singlefile.py @@ -10,151 +10,195 @@ """Tests for the `SinglefileData` class.""" import os -import tempfile import io +import tempfile +import pathlib -from aiida.backends.testbase import AiidaTestCase -from aiida.orm import SinglefileData, load_node - - -class TestSinglefileData(AiidaTestCase): - """Tests for the `SinglefileData` class.""" - - def test_reload_singlefile_data(self): - """Test writing and reloading a `SinglefileData` instance.""" - content_original = 'some text ABCDE' - - with tempfile.NamedTemporaryFile(mode='w+') as handle: - filepath = handle.name - basename = os.path.basename(filepath) - handle.write(content_original) - handle.flush() - node = SinglefileData(file=filepath) - - uuid = node.uuid - - with node.open() as handle: - content_written = handle.read() - - self.assertEqual(node.list_object_names(), [basename]) - self.assertEqual(content_written, content_original) - - node.store() - - with node.open() as handle: - content_stored = handle.read() - - self.assertEqual(content_stored, content_original) - self.assertEqual(node.list_object_names(), [basename]) - - node_loaded = load_node(uuid) - self.assertTrue(isinstance(node_loaded, SinglefileData)) - - with node.open() as handle: - content_loaded = handle.read() - - self.assertEqual(content_loaded, content_original) - self.assertEqual(node_loaded.list_object_names(), [basename]) - - with node_loaded.open() as handle: - self.assertEqual(handle.read(), content_original) - - def test_construct_from_filelike(self): - """Test constructing an instance from filelike instead of filepath.""" - content_original = 'some testing text\nwith a newline' - - with tempfile.NamedTemporaryFile(mode='wb+') as handle: - basename = os.path.basename(handle.name) - handle.write(content_original.encode('utf-8')) - handle.flush() - handle.seek(0) - node = SinglefileData(file=handle) - - with node.open() as handle: - content_stored = handle.read() - - self.assertEqual(content_stored, content_original) - self.assertEqual(node.list_object_names(), [basename]) - - node.store() - - with node.open() as handle: - content_stored = handle.read() - - self.assertEqual(content_stored, content_original) - self.assertEqual(node.list_object_names(), [basename]) - - def test_construct_from_string(self): - """Test constructing an instance from a string.""" - content_original = 'some testing text\nwith a newline' - - with io.BytesIO(content_original.encode('utf-8')) as handle: - node = SinglefileData(file=handle) - - with node.open() as handle: - content_stored = handle.read() - - self.assertEqual(content_stored, content_original) - self.assertEqual(node.list_object_names(), [SinglefileData.DEFAULT_FILENAME]) - - node.store() - - with node.open() as handle: - content_stored = handle.read() - - self.assertEqual(content_stored, content_original) - self.assertEqual(node.list_object_names(), [SinglefileData.DEFAULT_FILENAME]) - - def test_construct_with_filename(self): - """Test constructing an instance, providing a filename.""" - content_original = 'some testing text\nwith a newline' - filename = 'myfile.txt' +import pytest - # test creating from string - with io.BytesIO(content_original.encode('utf-8')) as handle: - node = SinglefileData(file=handle, filename=filename) +from aiida.orm import SinglefileData, load_node - with node.open() as handle: - content_stored = handle.read() - self.assertEqual(content_stored, content_original) - self.assertEqual(node.list_object_names(), [filename]) +@pytest.fixture +def check_singlefile_content(): + """Fixture to check the content of a SinglefileData. - # test creating from file - with tempfile.NamedTemporaryFile(mode='wb+') as handle: - handle.write(content_original.encode('utf-8')) - handle.flush() - handle.seek(0) - node = SinglefileData(file=handle, filename=filename) + Checks the content of a SinglefileData node against the given + reference content and filename. + """ - with node.open() as handle: - content_stored = handle.read() + def inner(node, content_reference, filename, open_mode='r'): + with node.open(mode=open_mode) as handle: + assert handle.read() == content_reference - self.assertEqual(content_stored, content_original) - self.assertEqual(node.list_object_names(), [filename]) + assert node.list_object_names() == [filename] - def test_binary_file(self): - """Test that the constructor accepts binary files.""" - byte_array = [120, 3, 255, 0, 100] - content_binary = bytearray(byte_array) + return inner - with tempfile.NamedTemporaryFile(mode='wb+') as handle: - basename = os.path.basename(handle.name) - handle.write(bytearray(content_binary)) - handle.flush() - handle.seek(0) - node = SinglefileData(handle.name) - with node.open(mode='rb') as handle: - content_stored = handle.read() +@pytest.fixture +def check_singlefile_content_with_store(check_singlefile_content): # pylint: disable=redefined-outer-name + """Fixture to check the content of a SinglefileData before and after .store(). - self.assertEqual(content_stored, content_binary) - self.assertEqual(node.list_object_names(), [basename]) + Checks the content of a SinglefileData node against the given reference + content and filename twice, before and after calling .store(). + """ + def inner(node, content_reference, filename, open_mode='r'): + check_singlefile_content( + node=node, + content_reference=content_reference, + filename=filename, + open_mode=open_mode, + ) node.store() - - with node.open(mode='rb') as handle: - content_stored = handle.read() - - self.assertEqual(content_stored, content_binary) - self.assertEqual(node.list_object_names(), [basename]) + check_singlefile_content( + node=node, + content_reference=content_reference, + filename=filename, + open_mode=open_mode, + ) + + return inner + + +def test_reload_singlefile_data( + clear_database_before_test, # pylint: disable=unused-argument + check_singlefile_content_with_store, # pylint: disable=redefined-outer-name + check_singlefile_content # pylint: disable=redefined-outer-name +): + """Test writing and reloading a `SinglefileData` instance.""" + content_original = 'some text ABCDE' + + with tempfile.NamedTemporaryFile(mode='w+') as handle: + filepath = handle.name + basename = os.path.basename(filepath) + handle.write(content_original) + handle.flush() + node = SinglefileData(file=filepath) + + check_singlefile_content_with_store( + node=node, + content_reference=content_original, + filename=basename, + ) + + node_loaded = load_node(node.uuid) + assert isinstance(node_loaded, SinglefileData) + + check_singlefile_content( + node=node, + content_reference=content_original, + filename=basename, + ) + check_singlefile_content( + node=node_loaded, + content_reference=content_original, + filename=basename, + ) + + +def test_construct_from_filelike( + clear_database_before_test, # pylint: disable=unused-argument + check_singlefile_content_with_store # pylint: disable=redefined-outer-name +): + """Test constructing an instance from filelike instead of filepath.""" + content_original = 'some testing text\nwith a newline' + + with tempfile.NamedTemporaryFile(mode='wb+') as handle: + basename = os.path.basename(handle.name) + handle.write(content_original.encode('utf-8')) + handle.flush() + handle.seek(0) + node = SinglefileData(file=handle) + + check_singlefile_content_with_store( + node=node, + content_reference=content_original, + filename=basename, + ) + + +def test_construct_from_string( + clear_database_before_test, # pylint: disable=unused-argument + check_singlefile_content_with_store # pylint: disable=redefined-outer-name +): + """Test constructing an instance from a string.""" + content_original = 'some testing text\nwith a newline' + + with io.BytesIO(content_original.encode('utf-8')) as handle: + node = SinglefileData(file=handle) + + check_singlefile_content_with_store( + node=node, + content_reference=content_original, + filename=SinglefileData.DEFAULT_FILENAME, + ) + + +def test_construct_with_path( + clear_database_before_test, # pylint: disable=unused-argument + check_singlefile_content_with_store # pylint: disable=redefined-outer-name +): + """Test constructing an instance from a pathlib.Path.""" + content_original = 'please report to the ministry of silly walks' + + with tempfile.NamedTemporaryFile(mode='w+') as handle: + filepath = pathlib.Path(handle.name).resolve() + filename = filepath.name + handle.write(content_original) + handle.flush() + node = SinglefileData(file=filepath) + + check_singlefile_content_with_store( + node=node, + content_reference=content_original, + filename=filename, + ) + + +def test_construct_with_filename( + clear_database_before_test, # pylint: disable=unused-argument + check_singlefile_content # pylint: disable=redefined-outer-name +): + """Test constructing an instance, providing a filename.""" + content_original = 'some testing text\nwith a newline' + filename = 'myfile.txt' + + # test creating from string + with io.BytesIO(content_original.encode('utf-8')) as handle: + node = SinglefileData(file=handle, filename=filename) + + check_singlefile_content(node=node, content_reference=content_original, filename=filename) + + # test creating from file + with tempfile.NamedTemporaryFile(mode='wb+') as handle: + handle.write(content_original.encode('utf-8')) + handle.flush() + handle.seek(0) + node = SinglefileData(file=handle, filename=filename) + + check_singlefile_content(node=node, content_reference=content_original, filename=filename) + + +def test_binary_file( + clear_database_before_test, # pylint: disable=unused-argument + check_singlefile_content_with_store # pylint: disable=redefined-outer-name +): + """Test that the constructor accepts binary files.""" + byte_array = [120, 3, 255, 0, 100] + content_binary = bytearray(byte_array) + + with tempfile.NamedTemporaryFile(mode='wb+') as handle: + basename = os.path.basename(handle.name) + handle.write(bytearray(content_binary)) + handle.flush() + handle.seek(0) + node = SinglefileData(handle.name) + + check_singlefile_content_with_store( + node=node, + content_reference=content_binary, + filename=basename, + open_mode='rb', + )