Skip to content

validate_resources

ValidateResources

Bases: InstancePlugin

Validates mapped resources.

These are external files to the current application, for example these could be textures, image planes, cache files or other linked media.

A single resource entry MUST contain source and files: { "source": "/path/to/file..exr", "files": ['/path/to/file.1001.exr', '/path/to/file.1002.exr'] }

It may contain additional metadata like attribute or node so other publishing plug-ins can detect where the resource was used. The color_space data is also frequently used (e.g. in Maya and Houdini)

This validates
  • The resources are existing files.
  • The resources have correctly collected the data.
  • The resources must be unique to the source filepath so that multiple source filepaths do not write to the same publish filepath.
Source code in client/ayon_core/plugins/publish/validate_resources.py
 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
class ValidateResources(pyblish.api.InstancePlugin):
    """Validates mapped resources.

    These are external files to the current application, for example
    these could be textures, image planes, cache files or other linked
    media.

    A single resource entry MUST contain `source` and `files`:
        {
            "source": "/path/to/file.<UDIM>.exr",
            "files": ['/path/to/file.1001.exr', '/path/to/file.1002.exr']
        }

    It may contain additional metadata like `attribute` or `node` so other
    publishing plug-ins can detect where the resource was used. The
    `color_space` data is also frequently used (e.g. in Maya and Houdini)

    This validates:
        - The resources are existing files.
        - The resources have correctly collected the data.
        - The resources must be unique to the source filepath so that multiple
          source filepaths do not write to the same publish filepath.

    """

    order = ValidateContentsOrder
    label = "Resources"

    def process(self, instance):

        resources = instance.data.get("resources", [])
        if not resources:
            self.log.debug("No resources to validate..")
            return

        # Validate the `resources` data structure is valid
        invalid_data = False
        for resource in resources:
            # Required data
            if "source" not in resource:
                invalid_data = True
                self.log.error("Missing 'source' in resource: %s", resource)
            if "files" not in resource or not resource["files"]:
                invalid_data = True
                self.log.error("Missing 'files' in resource: %s", resource)
            if not all(os.path.exists(f) for f in resource.get("files", [])):
                invalid_data = True
                self.log.error(
                    "Resource contains files that do not exist "
                    "on disk: %s", resource
                )

        # Ensure unique resource names
        basenames = defaultdict(set)
        for resource in resources:
            files = resource.get("files", [])
            for filename in files:

                # Use normalized paths in comparison and ignore case
                # sensitivity
                filename = os.path.normpath(filename).lower()

                basename = os.path.splitext(os.path.basename(filename))[0]
                basenames[basename].add(filename)

        invalid_resources = list()
        for basename, sources in basenames.items():
            if len(sources) > 1:
                invalid_resources.extend(sources)
                self.log.error(
                    "Non-unique resource filename: {0}\n- {1}".format(
                        basename,
                        "\n- ".join(sources)
                    )
                )

        if invalid_data or invalid_resources:
            raise PublishValidationError(
                "Invalid resources in instance.",
                description=self.get_description()
            )

    def get_description(self):
        return inspect.cleandoc(
            """### Invalid resources

            Used resources, like textures, must exist on disk and must have
            unique filenames.

            #### Filenames must be unique

            In most cases this will invalidate  due to using the same filenames
            from different folders, and as such the file to be transferred is
            unique but has the same filename. Either rename the source files or
            make sure to use the same source file if they are intended to
            be the same file.

            """
        )