11import os
2- from fnmatch import fnmatch
3-
4- import fs
5- import fs .errors
6-
7- from galaxy .exceptions import (
8- AuthenticationRequired ,
9- MessageException ,
2+ import posixpath
3+ from typing import (
4+ Optional ,
5+ Union ,
106)
7+
118from galaxy .files .models import (
12- AnyRemoteEntry ,
13- BaseFileSourceConfiguration ,
14- BaseFileSourceTemplateConfiguration ,
159 FilesSourceRuntimeContext ,
1610)
11+ from galaxy .files .sources ._fsspec import (
12+ CacheOptionsDictType ,
13+ FsspecBaseFileSourceConfiguration ,
14+ FsspecBaseFileSourceTemplateConfiguration ,
15+ FsspecFilesSource ,
16+ )
1717from galaxy .util .config_templates import TemplateExpansion
18- from ._pyfilesystem2 import PyFilesystem2FilesSource
1918
2019try :
21- from fs_irods import iRODSFS
2220 from irods .session import iRODSSession
21+ from mangofs import IRODSFileSystem
2322except ImportError :
24- iRODSFS = None
2523 iRODSSession = None
26-
27-
28- class IrodsFileSourceTemplateConfiguration (BaseFileSourceTemplateConfiguration ):
29- host : str | TemplateExpansion
30- port : int | TemplateExpansion = 1247
31- username : str | TemplateExpansion
32- password : str | TemplateExpansion
33- zone : str | TemplateExpansion
34- root : str | TemplateExpansion | None = None
35- timeout : int | TemplateExpansion = 30
36- refresh_time : int | TemplateExpansion = 300
37- client_server_negotiation : str | TemplateExpansion | None = None
38- client_server_policy : str | TemplateExpansion | None = None
39- encryption_algorithm : str | TemplateExpansion | None = None
40- encryption_key_size : int | TemplateExpansion | None = None
41- encryption_num_hash_rounds : int | TemplateExpansion | None = None
42- encryption_salt_size : int | TemplateExpansion | None = None
43- ssl_verify_server : str | TemplateExpansion | None = None
44- ssl_ca_certificate_file : str | TemplateExpansion | None = None
45- resource : str | TemplateExpansion | None = None
46-
47-
48- class IrodsFileSourceConfiguration (BaseFileSourceConfiguration ):
24+ IRODSFileSystem = None
25+
26+
27+ class IrodsFsspecFileSourceTemplateConfiguration (FsspecBaseFileSourceTemplateConfiguration ):
28+ host : Union [str , TemplateExpansion ]
29+ port : Union [int , TemplateExpansion ] = 1247
30+ username : Union [str , TemplateExpansion ]
31+ password : Union [str , TemplateExpansion ]
32+ zone : Union [str , TemplateExpansion ]
33+ root : Optional [Union [str , TemplateExpansion ]] = None
34+ timeout : Union [int , TemplateExpansion ] = 30
35+ refresh_time : Union [int , TemplateExpansion ] = 300
36+ client_server_negotiation : Optional [Union [str , TemplateExpansion ]] = None
37+ client_server_policy : Optional [Union [str , TemplateExpansion ]] = None
38+ encryption_algorithm : Optional [Union [str , TemplateExpansion ]] = None
39+ encryption_key_size : Optional [Union [int , TemplateExpansion ]] = None
40+ encryption_num_hash_rounds : Optional [Union [int , TemplateExpansion ]] = None
41+ encryption_salt_size : Optional [Union [int , TemplateExpansion ]] = None
42+ ssl_verify_server : Optional [Union [str , TemplateExpansion ]] = None
43+ ssl_ca_certificate_file : Optional [Union [str , TemplateExpansion ]] = None
44+ resource : Optional [Union [str , TemplateExpansion ]] = None
45+
46+
47+ class IrodsFsspecFileSourceConfiguration (FsspecBaseFileSourceConfiguration ):
4948 host : str
5049 port : int = 1247
5150 username : str
@@ -65,70 +64,28 @@ class IrodsFileSourceConfiguration(BaseFileSourceConfiguration):
6564 resource : str | None = None
6665
6766
68- class IrodsFilesSource (PyFilesystem2FilesSource [IrodsFileSourceTemplateConfiguration , IrodsFileSourceConfiguration ]):
67+ class IrodsFsspecFilesSource (
68+ FsspecFilesSource [IrodsFsspecFileSourceTemplateConfiguration , IrodsFsspecFileSourceConfiguration ]
69+ ):
6970 plugin_type = "irods"
70- required_module = iRODSFS
71- required_package = "fs-irods"
72-
73- template_config_class = IrodsFileSourceTemplateConfiguration
74- resolved_config_class = IrodsFileSourceConfiguration
75-
76- def _iter_directory_entries (self , fs_handle , parent_path : str , normalized_query : str | None = None ):
77- for raw_name in fs_handle .listdir (parent_path ):
78- name = os .path .basename (str (raw_name ).rstrip ("/" ))
79- if not name :
80- continue
81- if normalized_query and not fnmatch (name .lower (), f"*{ normalized_query } *" ):
82- continue
83- entry_path = fs .path .join (parent_path , name )
84- info = fs_handle .getinfo (entry_path , namespaces = ["details" ])
85- yield entry_path , info
86-
87- def _list_non_recursive (
88- self ,
89- fs_handle ,
90- path : str ,
91- limit : int | None = None ,
92- offset : int | None = None ,
93- query : str | None = None ,
94- ) -> tuple [list [AnyRemoteEntry ], int ]:
95- normalized_query = query .lower () if query else None
96- entries = []
97- for _ , info in self ._iter_directory_entries (fs_handle , path , normalized_query ):
98- entries .append (self ._resource_info_to_dict (path , info ))
99- count = len (entries )
100- if (page := self ._to_page (limit , offset )) is not None :
101- entries = entries [page [0 ] : page [1 ]]
102- return entries , count
103-
104- def _list (
71+ required_module = IRODSFileSystem
72+ required_package = "mangofs"
73+
74+ template_config_class = IrodsFsspecFileSourceTemplateConfiguration
75+ resolved_config_class = IrodsFsspecFileSourceConfiguration
76+
77+ def _open_fs (
10578 self ,
106- context : FilesSourceRuntimeContext [IrodsFileSourceConfiguration ],
107- path = "/" ,
108- recursive = False ,
109- write_intent : bool = False ,
110- limit : int | None = None ,
111- offset : int | None = None ,
112- query : str | None = None ,
113- sort_by : str | None = None ,
114- ) -> tuple [list [AnyRemoteEntry ], int ]:
115- try :
116- with self ._open_fs (context ) as fs_handle :
117- if recursive :
118- raise MessageException ("Recursive listing is not supported for iRODS file sources." )
119- return self ._list_non_recursive (fs_handle , path , limit , offset , query )
120- except fs .errors .PermissionDenied as e :
121- raise AuthenticationRequired (
122- f"Permission Denied. Reason: { e } . Please check your credentials in your preferences for { self .label } ."
123- ) from e
124- except fs .errors .FSError as e :
125- raise MessageException (f"Problem listing file source path { path } . Reason: { e } " ) from e
126-
127- def _open_fs (self , context : FilesSourceRuntimeContext [IrodsFileSourceConfiguration ]):
128- if iRODSFS is None or iRODSSession is None :
79+ context : FilesSourceRuntimeContext [IrodsFsspecFileSourceConfiguration ],
80+ cache_options : CacheOptionsDictType ,
81+ ):
82+ if IRODSFileSystem is None or iRODSSession is None :
12983 raise self .required_package_exception
13084
131- config = context .config
85+ session = self ._open_session (context .config )
86+ return IRODSFileSystem (session = session , ** cache_options )
87+
88+ def _open_session (self , config : IrodsFsspecFileSourceConfiguration ):
13289 session_kwargs = {
13390 "host" : config .host ,
13491 "port" : config .port ,
@@ -152,8 +109,33 @@ def _open_fs(self, context: FilesSourceRuntimeContext[IrodsFileSourceConfigurati
152109 session .connection_timeout = config .timeout
153110 if config .resource :
154111 session .default_resource = config .resource
155-
156- return iRODSFS (session = session , root = config .root )
157-
158-
159- __all__ = ("IrodsFilesSource" ,)
112+ return session
113+
114+ def _to_filesystem_path (self , path : str , config : IrodsFsspecFileSourceConfiguration ) -> str :
115+ root = self ._normalized_root (config )
116+ path = path or "/"
117+ if path .startswith ("irods://" ):
118+ return path
119+ if path == "/" :
120+ return root
121+ return posixpath .join (root , path .lstrip ("/" ))
122+
123+ def _adapt_entry_path (self , filesystem_path : str , config : IrodsFsspecFileSourceConfiguration ) -> str :
124+ root = self ._normalized_root (config )
125+ filesystem_path = filesystem_path or "/"
126+ if filesystem_path == root :
127+ return "/"
128+ root_prefix = root .rstrip ("/" ) + "/"
129+ if filesystem_path .startswith (root_prefix ):
130+ return "/" + filesystem_path [len (root_prefix ) :]
131+ return filesystem_path if filesystem_path .startswith ("/" ) else f"/{ filesystem_path } "
132+
133+ @staticmethod
134+ def _normalized_root (config : IrodsFsspecFileSourceConfiguration ) -> str :
135+ if not config .root :
136+ return "/"
137+ root = os .path .normpath (config .root )
138+ return root if root .startswith ("/" ) else f"/{ root } "
139+
140+
141+ __all__ = ("IrodsFsspecFilesSource" ,)
0 commit comments