Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#18] close iRODSFS file handles before interpreter exit. #19

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion fs_irods/iRODSFS.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import datetime
from io import BufferedRandom
import io
import logging
import os

from multiprocessing import RLock
from typing import Text
from weakref import WeakKeyDictionary
from fs.base import FS
from fs.info import Info
from fs.permissions import Permissions
Expand All @@ -18,6 +20,24 @@

from fs_irods.utils import can_create

fses = WeakKeyDictionary()
_logger = logging.getLogger(__name__)

# Close out dangling file handles.
def finalize():
for fs in list(fses):
fs._finalize_files()

try:
# (see python-irodsclient issue #614)
from irods.at_client_exit import (
register as register_cleanup_function,
BEFORE_PRC)
register_cleanup_function(BEFORE_PRC, finalize)
except ImportError:
_logger.info("Content written to iRODSFS file handles may not be automatically saved at process exit [#18]."
" Recommend upgrading to >=v3.0.0 of the Python iRODS Client.")

_utc=datetime.timezone(datetime.timedelta(0))

class iRODSFS(FS):
Expand All @@ -27,8 +47,9 @@ def __init__(self, session: iRODSSession) -> None:
self._host = session.host
self._port = session.port
self._zone = session.zone

self._session = session
self.files = WeakKeyDictionary()
fses[self] = None

def wrap(self, path: str) -> str:
if path.startswith(f"/{self._zone}"):
Expand Down Expand Up @@ -112,6 +133,27 @@ def makedir(self, path: str, permissions: Permissions|None = None, recreate: boo
with self._lock:
self._session.collections.create(self.wrap(path), recurse=False)

# Allow Python iRODS Client to preemptively close handles to data object (aka "file") handles opened via
# iRODSFS, if this is happening at interpreter exit, so it can ensure shutdown happen in the proper order.
def _finalize_files(self):
self._files_finalized = 1
l = list(self.files)
while l:
f = l.pop()
if not f.closed:
f.close()

def __del__(self):
if not getattr(self,'_files_finalized',None):
self._finalize_files()

# Store weak references to open file handles that maintain a hard reference to the iRODSFS object.
# In this way, the iRODSFS can only be destructed once these file handles are gone.
def open(self,*a,**kw):
fd = super().open(*a,**kw)
self.files[fd] = self
return fd

def openbin(self, path: str, mode:str = "r", buffering: int = -1, **options) -> BufferedRandom:
"""Open a binary file-like object on the filesystem.
Args:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ license = "MIT"
[tool.poetry.dependencies]
python = "^3.8"
fs = "^2.4.16"
python-irodsclient = "^1.1.9"
python-irodsclient = ">=1.1.9"


[tool.poetry.group.dev.dependencies]
Expand Down
Loading