ATLAS Offline Software
envutil.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 
3 import os, sys, re, glob
4 from PyJobTransformsCore import fileutil
5 
6 __doc__ = """Environment variables utilities"""
7 
8 #some names of environment variables to avoid typing mistakes
9 LD_LIBRARY_PATH = 'LD_LIBRARY_PATH'
10 
11 # possible wildcards used in unix shell filename completion (and in module glob)
12 filenameWildCards = r'\?|\*|\[.*\]'
13 filenameWildCardsCompiled = re.compile(filenameWildCards)
14 
15 # split paths according to different separators
16 defaultPathSeps = os.pathsep + '|,'
17 
18 # regular expression with allowed endings for shared library names
19 _libraryNameRE = re.compile(r'\.so(\.[0-9]+)*$')
20 
21 
22 def has_wildcards(filename):
23  """Return boolean indicating if the filename contains any unix shell filename wildcards"""
24  if filenameWildCardsCompiled.search(filename):
25  return True
26  else:
27  return False
28 
29 
30 def find_file_split( filename,
31  dirlist = [ os.getcwd() ], # noqa: B008 (getcwd always returns the same)
32  access = os.R_OK,
33  depth = 0 ):
34  """Search for file <filename> with access rights <access> (see os.access()) in directory list <dirlist>.
35  Search into directory tree of each directory in <dirlist> up to depth <depth>. The default directory
36  list is a list containing only the current working directory.
37  No wildcards are allowed in <filename>.
38  <depth> = 0 : only search in directories given in list.
39  <depth> > 0 : descend deeper into the directory tree up to max <depth> levels.
40  <depth> < 0 : ascend upwards into the directory tree up to max -<depth> levels.
41  It returns 2-tuple (dir,file) where:
42  file=<filename> if no wildcards, or the actual (local) match to <filename> if wildcarded.
43  dir=the directory where <file> was found (from <dirlist>, or from a subdir if depth > 0)
44  If no file is found, it returns None."""
45  if not dirlist: return None
46  for dir in dirlist:
47  dir = os.path.abspath( os.path.expandvars( os.path.expanduser(dir) ) )
48  if not os.path.isdir(dir): continue
49  fullfile = os.path.join( dir, filename )
50  # print ("Checking file %s..." % fullfile)
51  if fileutil.access( fullfile, access ):
52  return (dir,filename)
53 
54  if depth == 0:
55  # not found at all
56  return None
57  elif depth > 0:
58  # not found at this level. Go one level down in directory structure
59  for dir in dirlist:
60  dir = os.path.abspath( os.path.expandvars( os.path.expanduser(dir) ) )
61  if not os.path.isdir(dir): continue
62  subdirlist = []
63  for d in fileutil.listdir(dir):
64  fulldir = os.path.join(dir,d)
65  if os.path.isdir(fulldir):
66  subdirlist.append( fulldir )
67  if subdirlist:
68  found = find_file_split( filename, subdirlist, access, depth - 1 )
69  if found: return found
70  elif depth < 0:
71  # not found at this level. Go one level up in directory structure
72  for dir in dirlist:
73  dir = os.path.abspath( os.path.expandvars( os.path.expanduser(dir) ) )
74  if not os.path.isdir(dir): continue
75  updir = os.path.dirname(dir)
76  if updir != dir:
77  found = find_file_split( filename, [ updir ], access, depth + 1 )
78  if found: return found
79 
80 
81  # not found at all
82  return None
83 
84 
85 def find_file( filename,
86  dirlist = [ os.getcwd() ], # noqa: B008 (getcwd always returns the same)
87  access = os.R_OK,
88  depth = 0 ):
89  """Search for file <filename> with access rights <access> (see os.access()) in directory list <dirlist>,
90  Search into directory tree of each directory up to depth <depth>. The default directory list is
91  a list containing only the current working directory.
92  <depth> = 0 : only search in directories given in list.
93  <depth> > 0 : descend deeper into the directory tree up to max <depth> levels.
94  <depth> < 0 : ascend upwards into the directory tree up to max -<depth> levels."""
95  found = find_file_split( filename, dirlist, access, depth )
96  return found and os.path.join( found[0], found[1] )
97 
98 
99 def find_file_updir( filename,
100  dir = os.getcwd(), # noqa: B008 (getcwd always returns the same)
101  access = os.R_OK ):
102  """Find a file in directory <dir> or its higher level dirs."""
103  curdir = os.path.abspath( dir )
104  fullfile = os.path.join( curdir, filename )
105  if fileutil.access( fullfile, access ): return fullfile
106  #go up in directory tree
107  updir = os.path.dirname(curdir)
108  while updir != curdir:
109  curdir = updir
110  fullfile = os.path.join( curdir, filename )
111  if fileutil.access( fullfile, access ): return fullfile
112  updir = os.path.dirname(curdir)
113 
114  return None
115 
116 
117 
118 def find_files_split( filename, dirlist, access, depth ):
119  """Search for all (regular) files that match <filename> with access rights <access> (see os.access())
120  in directory list <dirlist>.
121  Search is done into subdirectories each directory up to depth <depth>.
122  The default value for <dirlist> is the current working directory.
123  If the same file (without the directory name) is found in more than one places, only the first match is kept.
124  <filename> : can contain wildcards as used on the unix command line.
125  <depth> = 0 : only search in directories given in list.
126  <depth> < 0 : treated as = 0
127  <depth> > 0 : descend deeper into the directory tree up to max <depth> levels.
128  It returns a list of 2-tuples (dir,file) where
129  file=<filename> if no wildcards, or the actual (local) match to <filename> if wildcarded.
130  dir=the directory where <file> was found (from <dirlist>, or from a subdir if depth > 0)
131  If none is found, an empty list is returned."""
132 # if dirlist is None:
133 # return []
134  if depth < 0: depth = 0
135  # to speed up search, do a single file search if the filename does not contain wildcards
136  if not has_wildcards(filename):
137  singleFile = find_file_split( filename, dirlist, access, depth )
138  if singleFile:
139  return [ singleFile ]
140  else:
141  return []
142  # filename has wildcards. Find all (first) files that match.
143  filenameList = [] # the list of files to return
144  dirnameList = [] # keep track of files already found
145  for dir in dirlist:
146  dir = os.path.abspath( os.path.expandvars( os.path.expanduser(dir) ) )
147  # protect against non-existing entries
148  if not os.path.isdir(dir): continue
149  olddir = os.getcwd()
150  os.chdir(dir)
151 
152  filelist = glob.glob(filename)
153  for f in filelist:
154 
155  if not os.path.isfile(f) or not fileutil.access(f, access): continue
156  if f not in filenameList:
157 
158  dirnameList.append(dir)
159  filenameList.append(f)
160  else:
161  pass
162 
163  os.chdir(olddir)
164  if depth > 0:
165  # Go one level down in directory structure
166  for dir in dirlist:
167  dir = os.path.abspath( os.path.expandvars( os.path.expanduser(dir) ) )
168  # protect against non-existing entries
169  if not os.path.isdir(dir): continue
170  subdirlist = []
171  for d in fileutil.listdir(dir):
172  fulldir = os.path.join(dir,d)
173  if os.path.isdir(fulldir):
174  subdirlist.append( fulldir )
175  if subdirlist:
176  filelist = find_files_split( filename, subdirlist, access, depth - 1 )
177  for f in filelist:
178  if not f[1] in filenameList:
179  dirnameList.append(f[0])
180  filenameList.append(f[1])
181  return map( lambda arg1,arg2 : (arg1,arg2) , dirnameList, filenameList )
182 
183 
184 def find_files( filename, dirlist, access, depth ):
185  """Search for all (regular) files that match <filename> with access rights <access> (see os.access()) in directory list <dirlist>.
186  Search is done into subdirectories each directory up to depth <depth>.
187  The default value for <dirlist> is the current working directory.
188  If the same file (without the directory name) is found in more than one places, only the first match is kept.
189  <filename> : can contain wildcards as used on the unix command line.
190  <depth> = 0 : only search in directories given in list.
191  <depth> < 0 : treated as = 0
192  <depth> > 0 : descend deeper into the directory tree up to max <depth> levels.
193  It returns a list of filenames with full pathnames. If none is found, an empty list is returned."""
194  return list (map( lambda arg : os.path.join( arg[0], arg[1] ), find_files_split( filename, dirlist, access, depth ) ))
195 
196 
197 def find_file_env( filename, env_var_name, access = os.R_OK, sep = defaultPathSeps, depth = 0 ):
198  """Search for file <filename> with access rights <access> (see os.access()) in directory list
199  given as a <sep> separated list of paths in environment variable <env_var_name>.
200  Search into directory tree of each directory up to depth <depth> (0=don't descend at all)."""
201  env = os.environ.get( env_var_name )
202  envList = [ os.getcwd() ]
203  if not env:
204  return envList
205  envList.extend( re.split( sep, env ) )
206  return find_file( filename, envList, access, depth )
207 
208 
209 def find_files_env( filename, env_var_name, access = os.R_OK, sep = defaultPathSeps, depth = 0 ):
210  """Search for all files that match <filename> with access rights <access> (see os.access()) in directory list
211  given as a <sep> (a regular expression) separated list of paths in environment variable <env_var_name>.
212  <filename> can contain wildcards as used on the unix command line.
213  Search into directory tree of each directory up to depth <depth> (0=don't descend at all)."""
214  env = os.environ.get( env_var_name )
215  envList = [ os.getcwd() ]
216  if not env:
217  return envList
218  envList.extend( re.split( sep, env ) )
219  return find_files( filename, envList, access, depth )
220 
221 
222 def find_libraries( lib ):
223  """Search for libraries in LD_LIBRARY_PATH. Return list of full paths of libraries if found.
224  <lib> can contain wildcards, in which case all files matching the wildcard will be returned.
225  If the same file appears in several paths, the first one found will be taken."""
226  # require extension .so (or .so with version numbers)
227  global _libraryNameRE
228  libsfull = []
229  libname = lib
230  if _libraryNameRE.search(lib): # fully specified ending
231  libsfull = find_files_env( libname, LD_LIBRARY_PATH )
232  else:
233  libsfull = find_files_env( libname, LD_LIBRARY_PATH )
234  # filter results for valid shared library ending (basically to get rid of *.cmtref)
235  libsfull = [ l for l in libsfull if _libraryNameRE.search(l) ]
236  if not libsfull:
237  # add generic ending
238  libname = lib + '.so*'
239  libsfull = find_files_env( libname, LD_LIBRARY_PATH )
240  # filter results for valid shared library ending (basically to get rid of *.cmtref)
241  libsfull = [ l for l in libsfull if _libraryNameRE.search(l) ]
242 
243  # if still not found anything, try with prefix 'lib'
244  if not libsfull and not lib.startswith('lib'):
245  libsfull = find_libraries( 'lib' + lib )
246 
247  return libsfull
248 
249 
250 # list of possible extensions for python module filenames
251 _pyext = [ '.so', '.pyo', '.pyc', '.py' ]
252 
253 def find_python_module_file( modname ):
254  """Search for python file (full path) corresponding to python module <mod> in sys.path
255  (PYTONPATH + some system paths). <mod> should not contain the filename
256  extension (.py etc), and no wildcards. Returns None is module is not found."""
257  # add wildcarded extension
258  filename = modname.replace('.',os.sep) + '.*'
259  # get all python module files
260  all = [ f for f in find_files_split( filename, sys.path ) if os.path.splitext(f[1])[1] in _pyext ]
261  if not all:
262  return None
263  else:
264  # take the first. When 2 appear in the same directory, choose the one with
265  # file extension that appears first in _pyext.
266  found = all[0]
267  if len(all) > 1:
268  foundPrio = _pyext.index( os.path.splitext(found[1])[1] )
269  for df in all[1:]:
270  d,f = df[0],df[1]
271  if d != found[0]: break # done
272  prio = _pyext.index( os.path.splitext(f)[1] )
273  if prio < foundPrio:
274  found = df
275  foundPrio = prio
276 
277  return os.path.join(found[0],found[1])
278 
279 
281  """Search for python module(s) in PYTHONPATH + some system paths.
282  Returns a list of full paths to python module files. If non are found, returns empty list.
283  <mod> can contain wildcards, in which case all files matching the wildcard will be returned.
284  If the same file appears in several paths, the first one found will be taken."""
285  b,e = os.path.splitext(mod)
286  if e in _pyext: # us filename as-is
287  # turn python module syntax into filename syntax
288  filename = b.replace('.',os.sep) + e
289  return find_files( filename, sys.path )
290  elif not has_wildcards( mod ):
291  m = find_python_module_file( mod.replace(os.sep, '.') )
292  if m:
293  return [ m ]
294  else:
295  return []
296  else:
297  # first expand the wildcards
298  filename = mod.replace('.',os.sep) + '.*'
299  modsFound = []
300  for df in find_files_split( filename, sys.path ):
301  f = df[1]
302  b,e = os.path.splitext(f)
303  if e not in _pyext: continue
304  modname = b.replace(os.sep, '.')
305  if modname not in modsFound:
306  modsFound.append( modname )
307  # now find the files for each module
308  found = []
309  for m in modsFound:
310  f = find_python_module_file( m )
311  if f: found.append( f )
312  return found
python.envutil.find_libraries
def find_libraries(lib)
Definition: envutil.py:222
python.envutil.find_file_updir
def find_file_updir(filename, dir=os.getcwd(), access=os.R_OK)
Definition: envutil.py:99
python.envutil.find_file_env
def find_file_env(filename, env_var_name, access=os.R_OK, sep=defaultPathSeps, depth=0)
Definition: envutil.py:197
python.envutil.find_file
def find_file(filename, dirlist=[os.getcwd()], access=os.R_OK, depth=0)
Definition: envutil.py:85
python.envutil.find_python_modules
def find_python_modules(mod)
Definition: envutil.py:280
python.envutil.find_files_split
def find_files_split(filename, dirlist, access, depth)
Definition: envutil.py:118
python.envutil.find_python_module_file
def find_python_module_file(modname)
Definition: envutil.py:253
python.envutil.find_files_env
def find_files_env(filename, env_var_name, access=os.R_OK, sep=defaultPathSeps, depth=0)
Definition: envutil.py:209
python.envutil.has_wildcards
def has_wildcards(filename)
Definition: envutil.py:22
python.envutil.find_file_split
def find_file_split(filename, dirlist=[os.getcwd()], access=os.R_OK, depth=0)
Definition: envutil.py:30
python.envutil.find_files
def find_files(filename, dirlist, access, depth)
Definition: envutil.py:184