@@ -262,14 +262,14 @@ def getROOTclass(classPath):
262262################################################################################
263263# this is not really specific to ROOT, but we often have ROOT file lists
264264def expandFileList (
265- fileListPaths : "path (or paths) of the file list (or single file)" ,
265+ dataPaths : "path (or paths) of the file list (or single file)" ,
266266 comment : "(default: '#') character used to introduce a comment" = '#' ,
267- fileListSuffixes : "suffix of entries to recursively add file lists" = [] ,
268- fileSuffixes : "suffix of entries never to be treated as file lists" = [ '.root' ] ,
267+ fileListSuffixes : "suffix of entries to recursively add file lists" = () ,
268+ fileSuffixes : "suffix of entries never to be treated as file lists" = ( '.root' , ) ,
269269 ) -> "a list of file names" :
270270 """Returns a list of file names as found in the specified file lists.
271271
272- The `fileListPaths ` paths are read as text files; in them, each line
272+ The `dataPaths ` paths are read as text files; in them, each line
273273 represents a full file path.
274274 Empty lines and lines starting with a comment character are ignored.
275275 Also if blanks and a comment character are found, the content of the line
@@ -281,13 +281,16 @@ def expandFileList(
281281 If file suffixes are specified, a line ending with any of those suffixes
282282 will be considered a file, and not expanded. That takes priority over the file
283283 list suffix.
284+ Only absolute paths are supported in file lists. The current behaviour,
285+ which does not reject relative paths and assumes them relative to the current
286+ directory, is not guaranteed and may change in the future (ideally, it will).
284287
285- If any of the file lists in `fileListPaths ` can't be read, an exception is
288+ If any of the file lists in `dataPaths ` can't be read, an exception is
286289 raised.
287290
288- NOTE: because of the internal implementation, a `fileListPaths ` that is a
291+ NOTE: because of the internal implementation, a `dataPaths ` that is a
289292 collection of file names that are all one character long may be mistaken
290- for a string. The general solution is: do not name your file not file lists
293+ for a string. The general solution is: do not name your file nor file lists
291294 with a single-character name. It's most often a bad idea anyway.
292295 """
293296
@@ -297,48 +300,65 @@ def expandFileList(
297300 )
298301
299302 # ----------------------------------------------------------------------------
300- # Path or list ?
303+ # Path or collection of paths ?
301304 #
302- # This looks very silly, but distinguishing a string and a list is not trivial
303- # (even less so in Python); and we need to support both std::vector and list,
304- # both str and std::string.
305+ # This looks very silly, but distinguishing a string and a collection of
306+ # strings is not trivial (even less so in Python); and we need to support both
307+ # std::vector and list, both str and std::string.
305308 # The elements of all these types are of Python type str.
306309 # So it is hereby decided that if they all are of length 1, then it's a string
307- # otherwise it is a list . Yes, this fails in an obvious corner case.
308- # Just don't call your file lists "a", "b" etc. Nor your files .
310+ # otherwise it is a collection . Yes, this fails in an obvious corner case.
311+ # Just don't call your files or file lists "a", "b" etc.
309312 # Nevertheless, some special cases are singled out and specifically addressed.
310313
311- isClearlyList = isinstance (fileListPaths , (ROOT .std .vector [ROOT .std .string ], list , set ))
312- isClearlyPath = isinstance (fileListPaths , (ROOT .std .string , str ))
314+ isClearlyColl = isinstance (dataPaths , (ROOT .std .vector [ROOT .std .string ], list , tuple , set ))
315+ isClearlyPath = isinstance (dataPaths , (ROOT .std .string , str ))
313316 # we expand the argument to support single-pass generators; what happens?
314317 # str -> list[str] (one character each element)
315318 # std::string -> [str ->] list[str] (one character each element)
316319 # Python iterable -> list (usually?)
317320 # Python generator -> list
318- fileListList = list (fileListPaths )
319- if isClearlyList or (any (len (p ) > 1 for p in fileListList ) and not isClearlyPath ):
321+ dataPathList = list (dataPaths )
322+ if isClearlyColl or (any (len (p ) > 1 for p in dataPathList ) and not isClearlyPath ):
320323 Logger .debug ("Input is a list of %d paths and will be expanded as such." ,
321- len (fileListList ))
322- return sum ([ expandAgain (path ) for path in fileListList ], [])
324+ len (dataPathList ))
325+ return sum ([ expandAgain (path ) for path in dataPathList ], [])
323326 # if the argument is a list
324327
325- # at this point we believe the original argument was a string (not a list,
326- # not a generator) and we broke it into pieces into `fileListList `.
327- # But `fileListPath ` is still whole, so we will use it.
328+ # at this point we believe the original argument was a string (not a
329+ # collection nor generator) and we broke it into pieces into `dataPaths `.
330+ # But `dataPaths ` is still whole, so we will use it.
328331
329332 # ----------------------------------------------------------------------------
330333 # Expand, at last
331334 #
332- # from here on, it's only one file or file list
333- fileListPath = fileListPaths
335+ # from here on, it's only a single path (file or file list)
336+ #
337+ # NOTE on relative paths: in principle we can expand relative paths prepending
338+ # the base path of the parent file list (if any; otherwise we can either
339+ # append the current directory, or leave them relative).
340+ # One complication is that paths may not be easy to identify as relative;
341+ # for example, a XRootD URL is considered by `os.path.isabs()` as relative.
342+ # Here `pathlib` module may help. Another complication is if a base path
343+ # includes symbolic links. Imagine an `output` directory structure with
344+ # `output/data/` and `output/lists/`, where the items of the file lists in
345+ # the latter all include a `../data` path. While that list works well when
346+ # expanded using its real path (`output/lists/../data/...`), when such list
347+ # is linked somewhere else, the simple expansion of those files to
348+ # `<current dir>/../data/...` will be broken. In addition, the use of
349+ # file-system-accessing functions as `os.path.realpath()` may still break
350+ # since those functions won't work paths like XRootD URL.
351+ # All of this can be worked around, with enough motivation.
352+ #
353+ dataPath = dataPaths
334354 hasSuffix = lambda s , suffixes : any (s .endswith (sx ) for sx in suffixes )
335355
336- if hasSuffix (fileListPath , fileSuffixes ):
337- Logger .debug ("'%s' was for sure a file, not a file list." , fileListPath )
338- return [ fileListPath ]
356+ if hasSuffix (dataPath , fileSuffixes ):
357+ Logger .debug ("'%s' was for sure a file, not a file list." , dataPath )
358+ return [ dataPath ]
339359
340- Logger .debug ("Processing file list '%s'" , fileListPath )
341- with open (fileListPath , 'r' ) as fileList :
360+ Logger .debug ("Processing file list '%s'" , dataPath )
361+ with open (dataPath , 'r' ) as fileList :
342362
343363 l = []
344364 for iLine , line in enumerate (fileList ):
0 commit comments