-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#456] auto-close data objects that go out of scope
Previously, open data-object write handles (those with modes including 'w' or 'a') could go out of scope at the wrong time relative to the session object which managed their connection. This could result in pending write updates to the data object being lost, and/or the replica ending up stale. Now, we can opt-in to use "managed" write handles to ensure f.close() will ultimately be called for any write handle f persisting to the end of the Python interpreter's lifetime. Those that exit scope prior to exit time are also guaranteed (as much as Python allows) the same managed clean-up via their "__del__" method. remove unneeded quotes; unset script executable status
- Loading branch information
Showing
7 changed files
with
196 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
from __future__ import print_function | ||
import ast | ||
import copy | ||
import io | ||
import logging | ||
import re | ||
import sys | ||
import types | ||
|
||
logger = logging.Logger(__name__) | ||
|
||
class iRODSConfiguration(object): | ||
__slots__ = () | ||
|
||
def getter(category, setting): | ||
return lambda:getattr(globals()[category], setting) | ||
|
||
# ############################################################################# | ||
# | ||
# Classes for building client configuration categories | ||
# (irods.client_configuration.data_objects is one such category): | ||
|
||
class DataObjects(iRODSConfiguration): | ||
__slots__ = ('auto_close',) | ||
|
||
def __init__(self): | ||
|
||
# Setting it in the constructor lets the attribute be a | ||
# configurable one and allows a default value of False. | ||
# | ||
# Running following code will opt in to the the auto-closing | ||
# behavior for any data objects opened subsequently. | ||
# | ||
# >>> import irods.client_configuration as config | ||
# >>> irods.client_configuration.data_objects.auto_close = True | ||
|
||
self.auto_close = False | ||
|
||
# ############################################################################# | ||
# | ||
# Instantiations of client-configuration categories: | ||
|
||
# The usage "irods.client_configuration.data_objects" reflects the commonly used | ||
# manager name (session.data_objects) and is thus understood to influence the | ||
# behavior of data objects. | ||
# | ||
# By design, valid configurable targets (e.g. auto_close) are limited to the names | ||
# listed in the __slots__ member of the category class. | ||
|
||
data_objects = DataObjects() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
46 changes: 46 additions & 0 deletions
46
irods/test/modules/test_auto_close_of_data_objects__issue_456.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# This helper module can double as a Python script, allowing us to run the below | ||
# test() method either within the current process or in a child process. The | ||
# method in question can thus be called by the following unit tests so that we may assert | ||
# proper data object auto-closing functionality under these respective scenarios: | ||
# | ||
# irods.test.data_obj_test.TestDataObjOps.test_data_objects_auto_close_on_function_exit__issue_456 | ||
# irods.test.data_obj_test.TestDataObjOps.test_data_objects_auto_close_on_process_exit__issue_456 | ||
|
||
from __future__ import print_function | ||
import contextlib | ||
try: | ||
import irods.client_configuration as config | ||
except ImportError: | ||
pass | ||
from datetime import datetime | ||
import os | ||
from irods.test import helpers | ||
|
||
@contextlib.contextmanager | ||
def auto_close_data_objects(value): | ||
if 'config' not in globals(): | ||
yield | ||
return | ||
ORIGINAL_VALUE = config.data_objects.auto_close | ||
try: | ||
config.data_objects.auto_close = value | ||
yield | ||
finally: | ||
config.data_objects.auto_close = ORIGINAL_VALUE | ||
|
||
def test(return_locals = True): | ||
with auto_close_data_objects(True): | ||
expected_content = 'content' | ||
ses = helpers.make_session() | ||
name = '/{0.zone}/home/{0.username}/{1}-object.dat'.format(ses, helpers.unique_name(os.getpid(), datetime.now())) | ||
f = ses.data_objects.open(name,'w') | ||
f.write(expected_content.encode('utf8')) | ||
L=locals() | ||
# By default, ses and f will be automatically exported to calling frame (with L being returned), | ||
# but by specifying a list/tuple of keys we can export only those specific locals by name. | ||
return L if not isinstance(return_locals,(tuple,list)) \ | ||
else [ L[k] for k in return_locals ] | ||
|
||
if __name__ == '__main__': | ||
test_output = test() | ||
print("{name} {expected_content}".format(**test_output)) |