Skip to content

Commit d1743eb

Browse files
d-w-moorekorydraughn
authored andcommitted
[#681,#714] when opening data object results in error, do not leave a local file.
1 parent 974760a commit d1743eb

3 files changed

Lines changed: 109 additions & 19 deletions

File tree

irods/helpers/__init__.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import contextlib
22
import sys
33
from irods import env_filename_from_keyword_args
4+
import irods.exception as ex
45
from irods.message import ET, XML_Parser_Type, IRODS_VERSION
6+
from irods.path import iRODSPath
57
from irods.session import iRODSSession
68

7-
8-
__all__ = ["make_session", "home_collection", "xml_mode"]
9+
__all__ = [
10+
"make_session",
11+
"home_collection",
12+
"xml_mode",
13+
"get_collection",
14+
"get_data_object",
15+
]
916

1017

1118
class StopTestsException(Exception):
@@ -104,3 +111,33 @@ def temporarily_assign_attribute(
104111
setattr(target, attr, save)
105112
else:
106113
delattr(target, attr)
114+
115+
116+
def get_data_object(sess, logical_path):
117+
"""Get a reference to the data object (as an iRODSDataObject) at the given path, if one is found.
118+
Else, return None.
119+
120+
Parameters:
121+
sess: an authenticated session object.
122+
logical_path: the full logical path where the data object is to be found. Can be in unnormalized form.
123+
"""
124+
try:
125+
# Check for a data object at the normalized path.
126+
return sess.data_objects.get(iRODSPath(logical_path))
127+
except ex.DataObjectDoesNotExist:
128+
return None
129+
130+
131+
def get_collection(sess, logical_path):
132+
"""Get a reference to the collection (as an iRODSCollection) at the given path, if one is found.
133+
Else, return None.
134+
135+
Parameters:
136+
sess: an authenticated session object.
137+
logical_path: the full logical path where the collection is to be found. Can be in unnormalized form.
138+
"""
139+
try:
140+
# Path normalization is internal to this call.
141+
return sess.collections.get(logical_path)
142+
except ex.CollectionDoesNotExist:
143+
return None

irods/manager/data_object_manager.py

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -237,24 +237,23 @@ def _download(self, obj, local_path, num_threads, updatables=(), **options):
237237
raise ex.OVERWRITE_WITHOUT_FORCE_FLAG
238238

239239
data_open_returned_values_ = {}
240-
with open(local_file, "wb") as f:
241-
with self.open(
242-
obj, "r", returned_values=data_open_returned_values_, **options
243-
) as o:
244-
if self.should_parallelize_transfer(
245-
num_threads, o, open_options=options.items()
240+
with self.open(
241+
obj, "r", returned_values=data_open_returned_values_, **options
242+
) as o:
243+
if self.should_parallelize_transfer(
244+
num_threads, o, open_options=options.items()
245+
):
246+
if not self.parallel_get(
247+
(obj, o),
248+
local_file,
249+
num_threads=num_threads,
250+
target_resource_name=options.get(kw.RESC_NAME_KW, ""),
251+
data_open_returned_values=data_open_returned_values_,
252+
updatables=updatables,
246253
):
247-
f.close()
248-
if not self.parallel_get(
249-
(obj, o),
250-
local_file,
251-
num_threads=num_threads,
252-
target_resource_name=options.get(kw.RESC_NAME_KW, ""),
253-
data_open_returned_values=data_open_returned_values_,
254-
updatables=updatables,
255-
):
256-
raise RuntimeError("parallel get failed")
257-
else:
254+
raise RuntimeError("parallel get failed")
255+
else:
256+
with open(local_file, "wb") as f:
258257
for chunk in chunks(o, self.READ_BUFFER_SIZE):
259258
f.write(chunk)
260259
do_progress_updates(updatables, len(chunk))

irods/test/data_obj_test.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2955,6 +2955,60 @@ def test_replica_truncate__issue_534(self):
29552955
if data_objs.exists(data_path):
29562956
data_objs.unlink(data_path, force=True)
29572957

2958+
def test_get_collection_returns_a_reference_exclusively_to_an_existing_collection__issue_734(
2959+
self,
2960+
):
2961+
from irods.helpers import get_collection, get_data_object
2962+
2963+
sess = self.sess
2964+
logical_path = "{}/{}".format(
2965+
self.coll_path, unique_name(my_function_name(), datetime.now())
2966+
)
2967+
sess.collections.create(logical_path)
2968+
2969+
self.assertIs(get_data_object(sess, logical_path), None)
2970+
self.assertTrue(get_collection(sess, logical_path))
2971+
2972+
def test_get_data_object_returns_a_reference_exclusively_to_an_existing_data_object__issue_734(
2973+
self,
2974+
):
2975+
from irods.helpers import get_collection, get_data_object
2976+
2977+
sess = self.sess
2978+
logical_path = "{}/{}".format(
2979+
self.coll_path, unique_name(my_function_name(), datetime.now())
2980+
)
2981+
sess.data_objects.create(logical_path)
2982+
2983+
self.assertIs(get_collection(sess, logical_path), None)
2984+
self.assertTrue(get_data_object(sess, logical_path))
2985+
2986+
def test_data_object_download_error_leaves_no_file_relic__issue_681(self):
2987+
from irods.helpers import get_collection, get_data_object
2988+
2989+
sess = self.sess
2990+
2991+
# Generate a test path.
2992+
data_path = "{}/{}".format(
2993+
self.coll_path, unique_name(my_function_name(), datetime.now())
2994+
)
2995+
2996+
# Test that neither a data object nor a collection exists at the data_path.
2997+
self.assertIs(None, get_data_object(sess, data_path))
2998+
self.assertIs(None, get_collection(sess, data_path))
2999+
3000+
# Make sure that no directory or file exists at the download target path in the local filesystem.
3001+
with NamedTemporaryFile(delete=True) as f:
3002+
local_path = f.name
3003+
self.assertFalse(os.path.exists(local_path))
3004+
3005+
# Attempt downloading the nonexisting object, expecting an error.
3006+
with self.assertRaises((ex.DataObjectDoesNotExist, ex.OBJ_PATH_DOES_NOT_EXIST)):
3007+
sess.data_objects.get(data_path, local_path)
3008+
3009+
# Assert no relic left at local_path.
3010+
self.assertFalse(os.path.exists(local_path))
3011+
29583012

29593013
if __name__ == "__main__":
29603014
# let the tests find the parent irods lib

0 commit comments

Comments
 (0)