Authoring rules#
usd-validation-nvidia is rule-based: each check is a class that inspects a
Usd.Stage (or its layers, prims, or package contents) and reports
Issue objects. This page shows how to write, register,
and distribute your own rules.
The rule interface#
A rule subclasses BaseRuleChecker and overrides one or
more Check* callbacks. The engine invokes them as it walks an asset, so a rule only
implements the callbacks it needs:
Callback |
Invoked with |
|---|---|
|
The composed |
|
Each |
|
Each |
|
The stage together with its layer and asset dependencies. |
|
Asset paths that did not resolve. |
|
Diagnostics emitted while opening/composing the stage. |
|
Each |
|
Each format-specific dependency (a |
|
Hook to clear any per-asset state the rule caches between runs. |
Report findings from inside a callback with the helper methods, in increasing severity:
self._AddInfo(message, ...)- informational only.self._AddWarning(message, ...)- a non-blocking concern.self._AddFailedCheck(message, ...)- a normative failure.self._AddError(message, ...)- the rule itself could not run.
Each accepts an at= location (a prim, property, layer, or other
Identifier), an optional code, and an optional
requirement. _AddFailedCheck, _AddError, and _AddWarning also take one or
more suggestions used for auto-fixing (see below); _AddInfo does not.
Registering a rule#
Decorate the class with register_rule() and a category
name. Registered rules become part of every default ValidationEngine:
from pxr import UsdGeom
from usd_validation_nvidia import BaseRuleChecker, register_rule
@register_rule("MyStudio")
class VisibilityAttributeChecker(BaseRuleChecker):
"""Meshes should author a 'visibility' attribute."""
def CheckPrim(self, prim):
if not prim.IsA(UsdGeom.Mesh):
return
if not prim.GetAttribute("visibility").IsValid():
self._AddWarning(
message=f"Mesh <{prim.GetPath()}> does not author 'visibility'.",
at=prim,
)
Run it like any other rule; an engine created with no arguments discovers all registered rules:
from usd_validation_nvidia import ValidationEngine
engine = ValidationEngine()
results = engine.validate("asset.usda")
register_rule also accepts skip= (register conditionally, for example in tests) and
overwrite= (replace an existing rule in a category). Categories group related rules:
the CLI’s --category flag and register_rule use them. To turn individual rules on or
off on an engine, use enable_rule() / disable_rule().
Providing an auto-fix#
Attach a Suggestion to an issue to make it fixable.
IssueFixer applies the suggestion to the stage and
writes the result back:
from usd_validation_nvidia import IssueFixer
fixer = IssueFixer("asset.usda")
fixer.fix(results.issues())
fixer.save()
See Python API for the Suggestion, IssueFixer, and Issue signatures.
Wrapping a native USD validator#
OpenUSD ships its own UsdValidation validators. Rather than reimplement one, subclass
UsdValidatorAdapter and name the validator to expose it
as a rule:
from usd_validation_nvidia import register_rule, UsdValidatorAdapter
@register_rule("Basic", skip="usdUtilsValidators:UsdzPackageValidator" not in UsdValidatorAdapter)
class UsdzPackageValidator(UsdValidatorAdapter):
@classmethod
def validator_name(cls):
return "usdUtilsValidators:UsdzPackageValidator"
Distributing rules as a plugin#
Rules register when their module is imported. To ship rules in your own package so they
load automatically, expose a plugin through the usd_validation_nvidia entry-point group;
the plugin manager discovers it at startup:
# pyproject.toml of your rule package
[project.entry-points."usd_validation_nvidia"]
my_studio_rules = "my_studio_rules:Plugin"
The referenced object implements PluginProtocol: an
on_startup() hook, where you import or register your rule modules, and an
on_shutdown() hook. DefaultPlugin is the built-in
reference implementation. The legacy omni.asset_validator group is also scanned for
backward compatibility, but new plugins should use usd_validation_nvidia.