ATLAS Offline Software
trfutil.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, shutil
4 
5 from .envutil import find_files_env
6 
7 
8 def linkPresent( filename, findBrokenLink = True ):
9  """Check if the given filename path contains a symlink. This function also checks all it's parent directories.
10  If findBrokenLink is True, the first path component that is a symlink and is broken is returned.
11  If findBrokenLink is False, the first symlink in the path in returned."""
12  if os.path.isabs( filename ):
13  initial_p = ""
14  p = os.path.commonprefix( [ os.getcwd(), filename ] )
15  filename = filename[ len( p ) + 1: ] # name filename a relative path wrt p.
16  else:
17  p = os.getcwd()
18  initial_p = p + os.sep
19  intermediateList = filename.split( os.sep )
20  while intermediateList:
21  p = os.sep.join( [ p, intermediateList.pop(0) ] )
22  if os.path.islink( p ):
23  if findBrokenLink and os.path.exists( p ):
24  continue
25  return p.lstrip( initial_p )
26  return ''
27 
28 def isOnLocalFileSystem( filename ):
29  """Check in the filename at every directory level to determine if any of its parent directories have different mount points."""
30  filename = os.path.realpath( filename )
31  cPrefix = os.path.commonprefix( [ os.getcwd(), filename ] )
32  intermediateDirList = os.path.dirname( filename[ len( cPrefix ): ] ).split( os.sep )
33  cPrefix = cPrefix.rstrip( os.sep )
34  while intermediateDirList:
35  cPrefix = os.sep.join( [ cPrefix, intermediateDirList.pop(0) ] )
36  if os.path.ismount( cPrefix ):
37  return False
38  return True
39 
40 def get_files( listOfFiles, fromWhere='data', doCopy='ifNotLocal', errorIfNotFound=True, keepDir=True, depth=0, sep=os.pathsep ):
41  """Copy or symlink a list of files given from a search path given in an environment variable.
42  <listOfFiles> is either as a python list of strings, or as a comma separated list in one string.
43  Each entry in the list can contain wildcards (a la unix commands)
44  <fromWhere>: name of the environment variable containing the search paths. Available shortcuts: 'data' -> 'DATAPATH'.
45  <doCopy>='Always': copy the file
46  ='Never' : symlink the file
47  ='ifNotLocal': symlink the file if the original is in a subdirectory of the run directory, otherwise copy.
48  <errorIfNotFound>=True: An EnvironmentError exception is raised if any filename in the list is not found.
49  If the filename contains wildcards, it is considered not found if no files are found at all.
50  =False: If a file is not found, only a warning is printed
51  <keepDir>=True : copy the file into a local subdirectory if the requested filename includes its directory part
52  False: copy the file in the current directory with the requested filename while ignoring its directory part
53  If a filename in <listOfFiles> contains an absolute path, the directory part is ignored in the copied filename.
54  """
55  if doCopy not in [ 'Always', 'Never', 'ifNotLocal' ]:
56  print ("doCopy value of %s not recognised. Resetting it to 'ifNotLocal'" % doCopy)
57  doCopy = 'ifNotLocal'
58  fromWhereShorts = { 'data' : 'DATAPATH' }
59  # split a comma separated list of files given in a string into a python list
60  if isinstance( listOfFiles, str ):
61  listOfFiles = listOfFiles.split(',')
62  # origin of files
63  fromPath = fromWhereShorts.get( fromWhere, fromWhere )
64  # copy or symlink
65  if doCopy == 'Always':
66  copy_func = shutil.copyfile
67  elif doCopy == 'Never':
68  copy_func = os.symlink
69  else: # doCopy == ifNotLocal
70  copy_func = None
71  fileDict = {}
72  # Check if srcFileList contains entries from different directories
73  realDirList = []
74  symLinkList = []
75  # First iteration of listOfFiles to create realDirList.
76  # Store created lists for subsequent iteration without having to recreate the lists again.
77  for filename in listOfFiles:
78  filename = os.path.expanduser( os.path.expandvars( os.path.normpath( filename ) ) )
79  srcFileList = find_files_env( filename, fromPath, sep=sep, depth=depth )
80  fileDict[ filename ] = srcFileList
81  if os.path.isabs( filename ):
82  continue
83  srcFilePrefix = os.path.dirname( os.path.commonprefix( srcFileList ) ) #.rstrip( os.sep )
84  dirName = os.path.dirname( filename )
85  # Test if dirName is present at the right end of srcFilePrefix.
86  # If this is not the case, this dirName should not be symlinked but a physical dirName s
87  # hould be created instead (later).
88  try:
89  realDirList.append( symLinkList.pop( symLinkList.index( dirName ) ) )
90  except ValueError:
91  pass
92  else:
93  continue
94  print ("srcFilePrefix = %s, dirName = %s" % ( srcFilePrefix, dirName ))
95  try:
96  srcFilePrefix.rindex( dirName, len( srcFilePrefix ) - len( dirName ) )
97  except ValueError:
98  realDirList.append( dirName )
99  else:
100  symLinkList.append( dirName )
101  del symLinkList
102  # Main iteration
103  for filename, srcFileList in fileDict.items():
104  if not srcFileList:
105  if errorIfNotFound:
106  raise EnvironmentError('Auxiliary file %s not found in %s' % (filename,fromPath) )
107  else:
108  print ("WARNING: auxiliary file %s not found in %s" % (filename,fromPath))
109  parentDirLinked = None
110  for srcFile in srcFileList:
111  # Test if the parent directory of the current entry in srcFileList has been symlinked.
112  # If so, skip this entry. Assumes that the find_files_env() returns a sorted list of files.
113  if os.path.dirname( srcFile ) == parentDirLinked:
114  print ("%s has been symlinked." % parentDirLinked)
115  continue
116  # determine name of destination file
117  if keepDir and not os.path.isabs( filename ):
118  subdir = os.path.dirname( filename ) # e.g. subdir = geomDB
119  targetFile = os.path.abspath( os.path.join( subdir, os.path.basename( srcFile ) ) ) # e.g. targetFile = $CWD/geomDB/actualfile.db
120  if subdir == os.getcwd():
121  subdir = ''
122  else: # keepDir == False or given filename is an absolute path.
123  targetFile = os.path.abspath( os.path.basename( srcFile ) ) # e.g. targetFile = $CWD/actualfile.db
124  subdir = ''
125  # associate copy_func with either shutil.copyfile or os.symlink
126  if doCopy == 'ifNotLocal':
127  if isOnLocalFileSystem( srcFile ):
128  copy_func = os.symlink
129  else: # remote file
130  print ("%s is on a different mount point as $CWD." % srcFile)
131  copy_func = shutil.copyfile
132  realDirRequired = False
133  if copy_func is os.symlink:
134  # redefine targetFile and srcFile to link subdirectory instead.
135  # However, if subdir needs to be a real directory due to multiple distinct contributing
136  # source directories, the reassignment is not done.
137  if subdir:
138  if subdir in realDirList:
139  print ("%s found in realDirList: %s" % ( subdir, realDirList ))
140  realDirRequired = True
141  else:
142 # print ("Reassigning targetFile from %s to %s." % ( targetFile, subdir ))
143  targetFile = subdir
144  srcFile = os.path.dirname( srcFile ).rstrip( os.sep )
145  # set flag to Stop iterating over srcFileList since parent dir has (will be) symlinked.
146  parentDirLinked = srcFile
147  # create the directory to contain the required srcFiles
148  if copy_func is shutil.copyfile or realDirRequired:
149  if subdir and not os.path.isdir( subdir ):
150  brokenLink = linkPresent( subdir, findBrokenLink = True )
151  if brokenLink:
152  try:
153  print ("Attempting to remove broken symlink %s" % brokenLink)
154  os.remove( brokenLink )
155  except OSError as x:
156  raise EnvironmentError( 'Unable to create the directory %s as the broken symlink %s cannot be removed: %s' % ( subdir, brokenLink, x ) )
157 # targetFile =
158  os.makedirs( subdir )
159  # Check existence of targetFile (side effect of an exception when running os.path.samefile).
160  try:
161  isSameFile = os.path.samefile( srcFile, targetFile )
162  except OSError: # good. targetFile does not exist.
163 # print ("%s does not exist. %s" % ( targetFile, x ))
164  if os.path.islink( targetFile ): # broken symlink
165  try:
166  print ("*Attempting to remove %s" % targetFile)
167  os.remove( targetFile )
168  except OSError as x:
169  raise EnvironmentError( 'Unable to remove broken symlink %s: %s' % ( targetFile, x ) )
170  try:
171  copy_func( srcFile, targetFile )
172  except OSError:
173  # try copying instead.
174  shutil.copyfile( srcFile, targetFile )
175  continue
176  # Test if srcFile and targetFile are one and the same
177  # NB: If the requested file is already in $CWD, the boolean flag isSameFile is always going
178  # to be True since a valid, existing file/symlink is given priority. This is so even if the
179  # existing file is a symlink and resides on a different mount point. The responsibility lies
180  # on user to be aware of the potential problems of accessing files over different mount points.
181  if isSameFile:
182  print ("%s is the same as %s. No further action." % ( srcFile, targetFile ))
183  continue # do nothing
184  print ("%s is not the same as %s" % ( srcFile, targetFile ))
185  # This point is reached in only two cases:
186  # 1) An absolute filename was used.
187  # targetFile is not what we want as since the function was called to get a file with an absolute filename.
188  # Attempt to remove the existing file before performing the copy.
189  try:
190  print ("**Attempting to remove %s" % targetFile)
191  os.remove( targetFile ) # remove files and symlinks
192  except Exception: # dst file is a directory
193  for _root, _dirs, _files in os.walk( targetFile , topdown = False ):
194  for name in _files:
195  os.remove( os.path.join( _root, name ) )
196  for name in _dirs:
197  os.rmdir( os.path.join( _root, name ) )
198  os.rmdir( targetFile )
199  try:
200  copy_func( srcFile, targetFile )
201  except OSError:
202  # try copying instead.
203  shutil.copyfile( srcFile, targetFile )
python.trfutil.linkPresent
def linkPresent(filename, findBrokenLink=True)
Definition: trfutil.py:8
python.trfutil.isOnLocalFileSystem
def isOnLocalFileSystem(filename)
Definition: trfutil.py:28
python.envutil.find_files_env
def find_files_env(filename, env_var_name, access=os.R_OK, sep=defaultPathSeps, depth=0)
Definition: envutil.py:190
python.trfutil.get_files
def get_files(listOfFiles, fromWhere='data', doCopy='ifNotLocal', errorIfNotFound=True, keepDir=True, depth=0, sep=os.pathsep)
Definition: trfutil.py:40
Trk::split
@ split
Definition: LayerMaterialProperties.h:38