From 780e3ecff53271a7e6ad5e51c6651b700e61b583 Mon Sep 17 00:00:00 2001 From: Jesse Lehrman Date: Fri, 4 Sep 2020 14:58:45 -0400 Subject: [PATCH 1/9] Adds textures and standins to the ass scraper --- conductor/resources/resources.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/conductor/resources/resources.yml b/conductor/resources/resources.yml index 93d5a8ea..f6d8e698 100644 --- a/conductor/resources/resources.yml +++ b/conductor/resources/resources.yml @@ -290,7 +290,12 @@ arnold_dependency_attrs: MayaFile: - filename xgen_procedural: - - data + - data + procedural: + - filename + image: + - filename + # xgen palette file depdency node attrs xgen_dependency_attrs: Palette: From b68f118ab1fcedd75f9446a0f25914abfed235db Mon Sep 17 00:00:00 2001 From: Jesse Lehrman Date: Sun, 6 Sep 2020 14:41:14 -0400 Subject: [PATCH 2/9] Adds a parameter to exclude diving deeper into a given list of paths --- conductor/lib/maya_utils.py | 107 ++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 46 deletions(-) diff --git a/conductor/lib/maya_utils.py b/conductor/lib/maya_utils.py index 9da05ba0..a659ee36 100644 --- a/conductor/lib/maya_utils.py +++ b/conductor/lib/maya_utils.py @@ -472,13 +472,19 @@ def get_render_layers_info(): return render_layers -def collect_dependencies(node_attrs): +def collect_dependencies(node_attrs, leaf_path_list=None): ''' Return a list of filepaths that the current maya scene has dependencies on. This is achieved by inspecting maya's nodes. Use the node_attrs argument to pass in a dictionary + + leaf_path_list: A list of strings. A list of paths to skip for nested dependencies. + Ex: A maya file that itself will be included but not textures, or + other nested references will be. ''' assert isinstance(node_attrs, dict), "node_attrs arg must be a dict. Got %s" % type(node_attrs) + + leaf_path_list = leaf_path_list or [] # TODO: Temporary hack to work around renderman 23.3 bug. # Record the active renderer so that we can restore it after making this cmds.file call @@ -522,36 +528,42 @@ def collect_dependencies(node_attrs): # ---- XGEN SCRAPING ----- # For xgen files, read the .xgen file and parse out the directory where other dependencies may exist if node_type == "xgmPalette": - maya_filepath = cmds.file(query=True, sceneName=True) - palette_filepath = os.path.join(os.path.dirname(maya_filepath), plug_value) - xgen_dependencies = scrape_palette_node(node, palette_filepath) + [palette_filepath] - logger.debug("xgen_dependencies: %s", xgen_dependencies) - dependencies += xgen_dependencies + + if path not in leaf_path_list: + maya_filepath = cmds.file(query=True, sceneName=True) + palette_filepath = os.path.join(os.path.dirname(maya_filepath), plug_value) + xgen_dependencies = scrape_palette_node(node, palette_filepath) + [palette_filepath] + logger.debug("xgen_dependencies: %s", xgen_dependencies) + dependencies += xgen_dependencies + # continue here so that we don't append the path to dependencies later on. # (the path that would have been appended is actually not the correct path). continue # ---- VRAY SCRAPING ----- if node_type == "VRayScene": - vrscene_dependencies = parse_vrscene_file(path) - logger.debug("vrscene dependencies: %s" % vrscene_dependencies) - dependencies += vrscene_dependencies + if path not in leaf_path_list: + vrscene_dependencies = parse_vrscene_file(path) + logger.debug("vrscene dependencies: %s" % vrscene_dependencies) + dependencies += vrscene_dependencies # ---- YETI SCRAPING ----- if node_type == "pgYetiMaya": - yeti_dependencies = scrape_yeti_graph(node) - logger.debug("yeti dependencies: %s" % yeti_dependencies) - dependencies += yeti_dependencies - - # Check whether the node is reading from disk or not. - # If it's not, then we shouldn't include the path as - # a dependency - if not cmds.getAttr('%s.fileMode' % node): - logger.debug("Skipping path because fileMode is disabled") - continue + if path not in leaf_path_list: + yeti_dependencies = scrape_yeti_graph(node) + logger.debug("yeti dependencies: %s" % yeti_dependencies) + dependencies += yeti_dependencies + + # Check whether the node is reading from disk or not. + # If it's not, then we shouldn't include the path as + # a dependency + if not cmds.getAttr('%s.fileMode' % node): + logger.debug("Skipping path because fileMode is disabled") + continue # ---- ARNOLD STANDIN SCRAPING ----- if node_type == "aiStandIn": + # We expect an aiStandin node to point towards and .ass file (or sequence thereof) # Instead of loading/reading the .ass file now, simply append to a list # that we'll process all at one time (*much faster*) @@ -561,8 +573,9 @@ def collect_dependencies(node_attrs): # file in an .ass sequence will have the same file dependencies, so don't bother reading every # ass file. Perhaps dangerous, but we'll cross that bridge later (it's better than reading/loading # potentially thousands of .ass files) - ass_filepath = file_utils.process_upload_filepath(path, strict=True)[0] - ass_filepaths.append(ass_filepath) + if path not in leaf_path_list: + ass_filepath = file_utils.process_upload_filepath(path, strict=True)[0] + ass_filepaths.append(ass_filepath) # ---- RENDERMAN RLF files ----- # If the node type is a RenderManArchive, then it may have an associated .rlf @@ -573,23 +586,24 @@ def collect_dependencies(node_attrs): # will have it's corresponding .rlf file here: # renderman/ribarchives/SpidermanRibArchiveShape/SpidermanRibArchiveShape.job.rlf if node_type == "RenderManArchive" and node_attr == "filename": - archive_dependencies = [] - rlf_dirpath = os.path.splitext(path)[0] - rlf_filename = "%s.job.rlf" % os.path.basename(rlf_dirpath) - rlf_filepath = os.path.join(rlf_dirpath, rlf_filename) - logger.debug("Searching for corresponding rlf file: %s", rlf_filepath) - rlf_filepaths = file_utils.process_upload_filepath(rlf_filepath, strict=False) - if rlf_filepaths: - rlf_filepath = rlf_filepaths[0] # there should only be one - # Parse the rlf file for file dependencies. - # Note that though this is an rlf file, there is embedded rib data within - # that we can parse using this rib parser. - logger.debug("Parsing rlf file: %s", rlf_filepath) - rlf_depedencies = parse_rib_file(rlf_filepath) - archive_dependencies.extend([rlf_filepath] + rlf_depedencies) - - logger.debug('%s dependencies: %s', plug_name, archive_dependencies) - dependencies.extend(archive_dependencies) + if path not in leaf_path_list: + archive_dependencies = [] + rlf_dirpath = os.path.splitext(path)[0] + rlf_filename = "%s.job.rlf" % os.path.basename(rlf_dirpath) + rlf_filepath = os.path.join(rlf_dirpath, rlf_filename) + logger.debug("Searching for corresponding rlf file: %s", rlf_filepath) + rlf_filepaths = file_utils.process_upload_filepath(rlf_filepath, strict=False) + if rlf_filepaths: + rlf_filepath = rlf_filepaths[0] # there should only be one + # Parse the rlf file for file dependencies. + # Note that though this is an rlf file, there is embedded rib data within + # that we can parse using this rib parser. + logger.debug("Parsing rlf file: %s", rlf_filepath) + rlf_depedencies = parse_rib_file(rlf_filepath) + archive_dependencies.extend([rlf_filepath] + rlf_depedencies) + + logger.debug('%s dependencies: %s', plug_name, archive_dependencies) + dependencies.extend(archive_dependencies) # ---- REDSHIFT SCRAPING ----- # The redshiftOptions node populates some cache filepaths by default. However, @@ -601,14 +615,15 @@ def collect_dependencies(node_attrs): # to exist on disk). This is not a perfect assumption, but we can adjust as # needed...perhaps by querying the caching mode. if node_type == "RedshiftOptions" and file_utils.RX_FRAME_REDSHIFT in path: - logger.debug("Resolving path expression: %s", path) - redshift_filepaths = file_utils.process_upload_filepath(path, strict=False) - if redshift_filepaths: - logger.debug("Resolved filepaths: %s", redshift_filepaths) - dependencies.extend(redshift_filepaths) - # continue here so that we don't append the original (unresolved) path as - # file dependency (later on). - continue + if path not in leaf_path_list: + logger.debug("Resolving path expression: %s", path) + redshift_filepaths = file_utils.process_upload_filepath(path, strict=False) + if redshift_filepaths: + logger.debug("Resolved filepaths: %s", redshift_filepaths) + dependencies.extend(redshift_filepaths) + # continue here so that we don't append the original (unresolved) path as + # file dependency (later on). + continue # Append path to list of dependencies dependencies.append(path) From 2f9fdebfd9be3f19e7939a9fcbdea5a6d0bbf81f Mon Sep 17 00:00:00 2001 From: Jesse Lehrman Date: Sun, 6 Sep 2020 14:50:33 -0400 Subject: [PATCH 3/9] Adds a parameter to exclude paths from the depedency scanner --- conductor/lib/maya_utils.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/conductor/lib/maya_utils.py b/conductor/lib/maya_utils.py index a659ee36..3437d2e4 100644 --- a/conductor/lib/maya_utils.py +++ b/conductor/lib/maya_utils.py @@ -472,7 +472,7 @@ def get_render_layers_info(): return render_layers -def collect_dependencies(node_attrs, leaf_path_list=None): +def collect_dependencies(node_attrs, leaf_path_list=None, exclude_path_list=None): ''' Return a list of filepaths that the current maya scene has dependencies on. This is achieved by inspecting maya's nodes. Use the node_attrs argument @@ -481,10 +481,14 @@ def collect_dependencies(node_attrs, leaf_path_list=None): leaf_path_list: A list of strings. A list of paths to skip for nested dependencies. Ex: A maya file that itself will be included but not textures, or other nested references will be. + exclude_path_list: A list of strings. A list of paths to exclude from the + dependency scanner. ''' + assert isinstance(node_attrs, dict), "node_attrs arg must be a dict. Got %s" % type(node_attrs) - leaf_path_list = leaf_path_list or [] + leaf_path_list = leaf_path_list or [] + exclude_path_list = exclude_path_list or [] # TODO: Temporary hack to work around renderman 23.3 bug. # Record the active renderer so that we can restore it after making this cmds.file call @@ -524,7 +528,14 @@ def collect_dependencies(node_attrs, leaf_path_list=None): # it for some reason. Strip this out at the end of the function path = cmds.file(plug_value, expandName=True, query=True, withoutCopyNumber=True) logger.debug("%s: %s", plug_name, path) - + + if path in exclude_path_list: + logger.info("Skipping depedency '{}' - in exclusion list".format(path)) + continue + + if path in leaf_path_list: + logger.info("Skipping nested scanning of '{}' - in leaf list".format(path)) + # ---- XGEN SCRAPING ----- # For xgen files, read the .xgen file and parse out the directory where other dependencies may exist if node_type == "xgmPalette": From aabd0d5d3a10849091564ad89a1164c061e29278 Mon Sep 17 00:00:00 2001 From: Jesse Lehrman Date: Sun, 6 Sep 2020 14:54:45 -0400 Subject: [PATCH 4/9] Fixes logging call that was missing parameters --- conductor/lib/maya_utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/conductor/lib/maya_utils.py b/conductor/lib/maya_utils.py index 3437d2e4..16df5e4b 100644 --- a/conductor/lib/maya_utils.py +++ b/conductor/lib/maya_utils.py @@ -896,8 +896,9 @@ def parse_ocio_config_paths(config_filepath): for path in search_paths: # If the path is relative, resolve it if not os.path.isabs(path): - path = os.path.join(config_dirpath, path) - logging.debug("Resolved relative path '%s' to '%s'", ) + relative_path = os.path.join(config_dirpath, path) + logging.debug("Resolved relative path '%s' to '%s'", path, relative_path) + path = relative_path if not os.path.isdir(path): logger.warning("OCIO search path does not exist: %s", path) From 35156502529290257d452d0aeb2ffad210ec3dd7 Mon Sep 17 00:00:00 2001 From: Jesse Lehrman Date: Sun, 6 Sep 2020 22:45:13 -0400 Subject: [PATCH 5/9] Forces all dependency paths to be normalized --- conductor/lib/maya_utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/conductor/lib/maya_utils.py b/conductor/lib/maya_utils.py index 16df5e4b..2222cabe 100644 --- a/conductor/lib/maya_utils.py +++ b/conductor/lib/maya_utils.py @@ -496,7 +496,7 @@ def collect_dependencies(node_attrs, leaf_path_list=None, exclude_path_list=None # Note that this command will often times return filepaths with an ending "/" on it for some reason. Strip this out at the end of the function dependencies = cmds.file(query=True, list=True, withoutCopyNumber=True) or [] # Strip errant dependences (another part of the renderman bug above). - dependencies = [path for path in dependencies if not path.endswith('_ Date: Sun, 6 Sep 2020 22:48:19 -0400 Subject: [PATCH 6/9] Adds support for glob patterns for leaf/exclude paths --- conductor/lib/file_utils.py | 14 ++++++++++++++ conductor/submitter_maya.py | 11 +++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/conductor/lib/file_utils.py b/conductor/lib/file_utils.py index c3f9941c..8c7ec8b5 100644 --- a/conductor/lib/file_utils.py +++ b/conductor/lib/file_utils.py @@ -487,3 +487,17 @@ def strip_drive_letter(filepath): ''' rx_drive = r'^[a-z]:' return re.sub(rx_drive, "", filepath, flags=re.I) + +def expand_paths(paths): + ''' + Expand a list of paths using glob + ''' + + expanded_paths = [] + + for path in paths: + expanded = [ os.path.normpath(p) for p in glob.glob(path) ] + logger.debug("'{}' expanded to {}".format(path, expanded)) + expanded_paths.extend(expanded) + + return expanded_paths \ No newline at end of file diff --git a/conductor/submitter_maya.py b/conductor/submitter_maya.py index 42f102e6..aee2a50b 100644 --- a/conductor/submitter_maya.py +++ b/conductor/submitter_maya.py @@ -380,8 +380,15 @@ def collectDependencies(self): # Get all of the node types and attributes to query for external filepaths on resources = common.load_resources_file() dependency_attrs = resources.get("maya_dependency_attrs") or {} - - return maya_utils.collect_dependencies(dependency_attrs) + + leaf_path_list = file_utils.expand_paths(os.environ.get('CONDUCTOR_DEPSCAN_LEAF_PATHS', []).split(os.pathsep)) + exclude_path_list = file_utils.expand_paths(os.environ.get('CONDUCTOR_DEPSCAN_EXCLUDE_PATHS', [])) + + logger.debug("Using expanded leaf file list: {}".format(leaf_path_list)) + + return maya_utils.collect_dependencies(dependency_attrs, + leaf_path_list=leaf_path_list, + exclude_path_list=exclude_path_list) def getEnvironment(self): ''' From f9f07acc0534f7eb8add77c9e12a00074c5364a4 Mon Sep 17 00:00:00 2001 From: Jesse Lehrman Date: Sun, 6 Sep 2020 23:17:17 -0400 Subject: [PATCH 7/9] Switches to using external file(s) to contain leaf/exclude patterns --- conductor/lib/file_utils.py | 19 +++++++++++++++++-- conductor/submitter_maya.py | 17 +++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/conductor/lib/file_utils.py b/conductor/lib/file_utils.py index 8c7ec8b5..b99a7ebb 100644 --- a/conductor/lib/file_utils.py +++ b/conductor/lib/file_utils.py @@ -496,8 +496,23 @@ def expand_paths(paths): expanded_paths = [] for path in paths: - expanded = [ os.path.normpath(p) for p in glob.glob(path) ] + + expanded = [ os.path.normpath(p) for p in glob.glob(path.strip()) ] logger.debug("'{}' expanded to {}".format(path, expanded)) expanded_paths.extend(expanded) - return expanded_paths \ No newline at end of file + return expanded_paths + +def expand_paths_from_file(path): + ''' + Opens a file that contains glob-style patterns and returns a list of all + matching files. The file must contain one glob-style pattern per-line. + + path: A string. The path to the file that contains glob patterns + ''' + + with open(path) as fh: + glob_patterns = fh.readlines() + + return expand_paths(glob_patterns) + diff --git a/conductor/submitter_maya.py b/conductor/submitter_maya.py index aee2a50b..b34a7157 100644 --- a/conductor/submitter_maya.py +++ b/conductor/submitter_maya.py @@ -225,6 +225,10 @@ class MayaConductorSubmitter(submitter.ConductorSubmitter): product = "maya-io" def __init__(self, parent=None): + + self.dependency_leaf_glob_file_path = None + self.dependency_exclude_glob_file_path = None + super(MayaConductorSubmitter, self).__init__(parent=parent) self.setMayaWindow() @@ -381,8 +385,17 @@ def collectDependencies(self): resources = common.load_resources_file() dependency_attrs = resources.get("maya_dependency_attrs") or {} - leaf_path_list = file_utils.expand_paths(os.environ.get('CONDUCTOR_DEPSCAN_LEAF_PATHS', []).split(os.pathsep)) - exclude_path_list = file_utils.expand_paths(os.environ.get('CONDUCTOR_DEPSCAN_EXCLUDE_PATHS', [])) + leaf_glob_file_path = os.environ.get('CONDUCTOR_DEPSCAN_LEAF_FILE', self.dependency_leaf_glob_file_path) + exclude_glob_file_path = os.environ.get('CONDUCTOR_DEPSCAN_EXCLUDE_FILE', self.dependency_exclude_glob_file_path) + + leaf_path_list = [] + exclude_path_list = [] + + if leaf_glob_file_path is not None: + leaf_path_list = file_utils.expand_paths_from_file(leaf_glob_file_path) + + if exclude_glob_file_path is not None: + exclude_path_list = file_utils.expand_paths_from_file(exclude_glob_file_path) logger.debug("Using expanded leaf file list: {}".format(leaf_path_list)) From ba476941f9328823b6bcbf31839c452a22b8fa18 Mon Sep 17 00:00:00 2001 From: Jesse Lehrman Date: Wed, 30 Sep 2020 13:22:57 -0400 Subject: [PATCH 8/9] Addresses PR comments --- conductor/lib/maya_utils.py | 30 +++++++++++++++++------------- conductor/submitter_maya.py | 17 +++++++---------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/conductor/lib/maya_utils.py b/conductor/lib/maya_utils.py index 2222cabe..6c8e0bbe 100644 --- a/conductor/lib/maya_utils.py +++ b/conductor/lib/maya_utils.py @@ -472,30 +472,32 @@ def get_render_layers_info(): return render_layers -def collect_dependencies(node_attrs, leaf_path_list=None, exclude_path_list=None): +def collect_dependencies(node_attrs, leaf_path=None, exclude_path=None): ''' Return a list of filepaths that the current maya scene has dependencies on. This is achieved by inspecting maya's nodes. Use the node_attrs argument to pass in a dictionary - leaf_path_list: A list of strings. A list of paths to skip for nested dependencies. + leaf_path: A list of strings. A list of paths to skip for nested dependencies. Ex: A maya file that itself will be included but not textures, or other nested references will be. - exclude_path_list: A list of strings. A list of paths to exclude from the + exclude_path: A list of strings. A list of paths to exclude from the dependency scanner. ''' assert isinstance(node_attrs, dict), "node_attrs arg must be a dict. Got %s" % type(node_attrs) - leaf_path_list = leaf_path_list or [] - exclude_path_list = exclude_path_list or [] + leaf_path = leaf_path or [] + exclude_path = exclude_path or [] # TODO: Temporary hack to work around renderman 23.3 bug. # Record the active renderer so that we can restore it after making this cmds.file call active_renderer = get_active_renderer() # Note that this command will often times return filepaths with an ending "/" on it for some reason. Strip this out at the end of the function dependencies = cmds.file(query=True, list=True, withoutCopyNumber=True) or [] + # Strip errant dependences (another part of the renderman bug above). + # normpath is used as it's possible (in Windows) to have a path with mixed back/forward slashes dependencies = [os.path.normpath(path) for path in dependencies if not path.endswith('_ Date: Thu, 1 Oct 2020 11:14:19 -0400 Subject: [PATCH 9/9] Makes list variables plural --- conductor/lib/maya_utils.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/conductor/lib/maya_utils.py b/conductor/lib/maya_utils.py index 6c8e0bbe..a190591e 100644 --- a/conductor/lib/maya_utils.py +++ b/conductor/lib/maya_utils.py @@ -472,23 +472,23 @@ def get_render_layers_info(): return render_layers -def collect_dependencies(node_attrs, leaf_path=None, exclude_path=None): +def collect_dependencies(node_attrs, leaf_paths=None, exclude_paths=None): ''' Return a list of filepaths that the current maya scene has dependencies on. This is achieved by inspecting maya's nodes. Use the node_attrs argument to pass in a dictionary - leaf_path: A list of strings. A list of paths to skip for nested dependencies. + leaf_paths: A list of strings. A list of paths to skip for nested dependencies. Ex: A maya file that itself will be included but not textures, or other nested references will be. - exclude_path: A list of strings. A list of paths to exclude from the + exclude_paths: A list of strings. A list of paths to exclude from the dependency scanner. ''' assert isinstance(node_attrs, dict), "node_attrs arg must be a dict. Got %s" % type(node_attrs) - leaf_path = leaf_path or [] - exclude_path = exclude_path or [] + leaf_paths = leaf_paths or [] + exclude_paths = exclude_paths or [] # TODO: Temporary hack to work around renderman 23.3 bug. # Record the active renderer so that we can restore it after making this cmds.file call @@ -533,18 +533,18 @@ def collect_dependencies(node_attrs, leaf_path=None, exclude_path=None): path = os.path.normpath(cmds.file(plug_value, expandName=True, query=True, withoutCopyNumber=True)) logger.debug("%s: %s", plug_name, path) - if path in exclude_path: + if path in exclude_paths: logger.info("Skipping depedency '{}' - in exclusion list".format(path)) continue - if path in leaf_path: + if path in leaf_paths: logger.info("Skipping nested scanning of '{}' - in leaf list".format(path)) # ---- XGEN SCRAPING ----- # For xgen files, read the .xgen file and parse out the directory where other dependencies may exist if node_type == "xgmPalette": - if path not in leaf_path: + if path not in leaf_paths: maya_filepath = cmds.file(query=True, sceneName=True) palette_filepath = os.path.join(os.path.dirname(maya_filepath), plug_value) xgen_dependencies = scrape_palette_node(node, palette_filepath) + [palette_filepath] @@ -557,14 +557,14 @@ def collect_dependencies(node_attrs, leaf_path=None, exclude_path=None): # ---- VRAY SCRAPING ----- if node_type == "VRayScene": - if path not in leaf_path: + if path not in leaf_paths: vrscene_dependencies = parse_vrscene_file(path) logger.debug("vrscene dependencies: %s" % vrscene_dependencies) dependencies += vrscene_dependencies # ---- YETI SCRAPING ----- if node_type == "pgYetiMaya": - if path not in leaf_path: + if path not in leaf_paths: yeti_dependencies = scrape_yeti_graph(node) logger.debug("yeti dependencies: %s" % yeti_dependencies) dependencies += yeti_dependencies @@ -588,7 +588,7 @@ def collect_dependencies(node_attrs, leaf_path=None, exclude_path=None): # file in an .ass sequence will have the same file dependencies, so don't bother reading every # ass file. Perhaps dangerous, but we'll cross that bridge later (it's better than reading/loading # potentially thousands of .ass files) - if path not in leaf_path: + if path not in leaf_paths: ass_filepath = file_utils.process_upload_filepath(path, strict=True)[0] ass_filepaths.append(ass_filepath) @@ -601,7 +601,7 @@ def collect_dependencies(node_attrs, leaf_path=None, exclude_path=None): # will have it's corresponding .rlf file here: # renderman/ribarchives/SpidermanRibArchiveShape/SpidermanRibArchiveShape.job.rlf if node_type == "RenderManArchive" and node_attr == "filename": - if path not in leaf_path: + if path not in leaf_paths: archive_dependencies = [] rlf_dirpath = os.path.splitext(path)[0] rlf_filename = "%s.job.rlf" % os.path.basename(rlf_dirpath) @@ -630,7 +630,7 @@ def collect_dependencies(node_attrs, leaf_path=None, exclude_path=None): # to exist on disk). This is not a perfect assumption, but we can adjust as # needed...perhaps by querying the caching mode. if node_type == "RedshiftOptions" and file_utils.RX_FRAME_REDSHIFT in path: - if path not in leaf_path: + if path not in leaf_paths: logger.debug("Resolving path expression: %s", path) redshift_filepaths = file_utils.process_upload_filepath(path, strict=False) if redshift_filepaths: