Skip to content

cleanup_explicit

Cleanup files when publishing is done.

ExplicitCleanUp

Bases: ContextPlugin

Cleans up the files and folder defined to be deleted.

plugin is looking for 2 keys into context data: - cleanupFullPaths - full paths that should be removed not matter if is path to file or to directory - cleanupEmptyDirs - full paths to directories that should be removed only if do not contain any file in it but will be removed if contain sub-folders

Source code in client/ayon_core/plugins/publish/cleanup_explicit.py
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
class ExplicitCleanUp(pyblish.api.ContextPlugin):
    """Cleans up the files and folder defined to be deleted.

    plugin is looking for 2 keys into context data:
    - `cleanupFullPaths` - full paths that should be removed not matter if
        is path to file or to directory
    - `cleanupEmptyDirs` - full paths to directories that should be removed
        only if do not contain any file in it but will be removed if contain
        sub-folders
    """

    order = pyblish.api.IntegratorOrder + 10
    label = "Explicit Clean Up"
    optional = True
    active = True

    def process(self, context):
        cleanup_full_paths = context.data.get("cleanupFullPaths")
        cleanup_empty_dirs = context.data.get("cleanupEmptyDirs")

        self._remove_full_paths(cleanup_full_paths)
        self._remove_empty_dirs(cleanup_empty_dirs)

    def _remove_full_paths(self, full_paths):
        """Remove files and folders from disc.

        Folders are removed with whole content.
        """
        if not full_paths:
            self.log.debug("No full paths to cleanup were collected.")
            return

        # Separate paths into files and directories
        filepaths = set()
        dirpaths = set()
        for path in full_paths:
            # Skip empty items
            if not path:
                continue
            # Normalize path
            normalized = os.path.normpath(path)
            # Check if path exists
            if not os.path.exists(normalized):
                continue

            if os.path.isfile(normalized):
                filepaths.add(normalized)
            else:
                dirpaths.add(normalized)

        # Store failed paths with exception
        failed = []
        # Store removed filepaths for logging
        succeeded_files = set()
        # Remove file by file
        for filepath in filepaths:
            try:
                os.remove(filepath)
                succeeded_files.add(filepath)
            except Exception as exc:
                failed.append((filepath, exc))

        if succeeded_files:
            self.log.info(
                "Removed files:\n{}".format("\n".join(sorted(succeeded_files)))
            )

        # Delete folders with its content
        succeeded = set()
        for dirpath in dirpaths:
            # Check if directory still exists
            #   - it is possible that directory was already deleted with
            #       different dirpath to delete
            if os.path.exists(dirpath):
                try:
                    shutil.rmtree(dirpath)
                    succeeded.add(dirpath)
                except Exception:
                    failed.append(dirpath)

        if succeeded:
            self.log.info(
                "Removed directories:\n{}".format(
                    "\n".join(sorted(succeeded))
                )
            )

        # Prepare lines for report of failed removals
        lines = []
        for filepath, exc in failed:
            lines.append("{}: {}".format(filepath, str(exc)))

        if lines:
            self.log.warning(
                "Failed to remove filepaths:\n{}".format(
                    "\n".join(sorted(lines))
                )
            )

    def _remove_empty_dirs(self, empty_dirpaths):
        """Remove directories if do not contain any files."""
        if not empty_dirpaths:
            self.log.debug("No empty dirs to cleanup were collected.")
            return

        # First filtering of directories and making sure those are
        #   existing directories
        filtered_dirpaths = set()
        for path in empty_dirpaths:
            if (
                path
                and os.path.exists(path)
                and os.path.isdir(path)
            ):
                filtered_dirpaths.add(os.path.normpath(path))

        to_delete_dirpaths = set()
        to_skip_dirpaths = set()
        # Check if contain any files (or it's subfolders contain files)
        for dirpath in filtered_dirpaths:
            valid = True
            for _, _, filenames in os.walk(dirpath):
                if filenames:
                    valid = False
                    break

            if valid:
                to_delete_dirpaths.add(dirpath)
            else:
                to_skip_dirpaths.add(dirpath)

        if to_skip_dirpaths:
            self.log.debug(
                "Skipped directories because they contain files:\n{}".format(
                    "\n".join(sorted(to_skip_dirpaths))
                )
            )

        # Remove empty directies
        for dirpath in to_delete_dirpaths:
            if os.path.exists(dirpath):
                shutil.rmtree(dirpath)

        if to_delete_dirpaths:
            self.log.debug(
                "Deleted empty directories:\n{}".format(
                    "\n".join(sorted(to_delete_dirpaths))
                )
            )