Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add python bindings for UsdValidator, UsdValidatorSuite and UsdValidationRegistry #3242

Conversation

roggiezhang-nv
Copy link
Contributor

@roggiezhang-nv roggiezhang-nv commented Aug 20, 2024

Description of Change(s)

Add python bindings for UsdValidator, UsdValidatorSuite and UsdValidationRegistry.

It is stacked on #3223, #3232, and #3236 for adding Python bindings for validator framework.

Fixes Issue(s)

  • I have verified that all unit tests pass with the proposed changes
  • I have submitted a signed Contributor License Agreement

@roggiezhang-nv roggiezhang-nv changed the base branch from release to dev August 20, 2024 02:46
@roggiezhang-nv
Copy link
Contributor Author

@nvmkuruc for vis.

@roggiezhang-nv roggiezhang-nv force-pushed the add_bindings_for_validator_registry branch from df3bf80 to 3a639cb Compare August 20, 2024 02:51
@roggiezhang-nv roggiezhang-nv changed the title Add python bindings for UsdValidationRegistry Add python bindings for UsdValidator, UsdValidatorSuite and UsdValidationRegistry Aug 20, 2024
@jesschimein
Copy link
Contributor

Filed as internal issue #USD-10006

@jesschimein
Copy link
Contributor

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@roggiezhang-nv roggiezhang-nv force-pushed the add_bindings_for_validator_registry branch from 3a639cb to 96c4413 Compare August 21, 2024 00:37
@jesschimein
Copy link
Contributor

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@tallytalwar
Copy link
Contributor

I haven't started syncing this in yet, so will it be possible for you to follow the naming convention and 80 column line width, etc for commits specific for this PR (not the ones on which this is based)?

Usd._register_test_validators()

def test_query_validators_and_suites(self):
validation_registry = Usd.ValidationRegistry()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable names also need to do follow camelCase convention. Thanks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @roggiezhang-nv.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if it satisfies the format. I'd hope there is a format guideline or style file so we can enforce the same format.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately we do not have a format enforcing (clang format equivalent) thing right now. But most of the coding conventions are documented here: https://openusd.org/release/api/_page__coding__guidelines.html

A change soon will make it more front and center when filing a PR.

@jesschimein
Copy link
Contributor

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Contributor

@tallytalwar tallytalwar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took a pass at this. And I am not able to understand why we will want to restrict python bindings for registering validators. In my initial proposal I also provided an example I think of a usecase for this.

I think since validation registration just require callbacks, python cb can be mapped to the appropriate cpp APIs for the wrapping?

Kindly let me know if there are reasons I am failing to understand here. Thanks

}

// Test only for registering validators as no APIs are exposed to register
// validators from python.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should be the case. We should definitely expose validator registration from python. I will like to hear thoughts on why this restrictions is being placed? Thanks

pxr/usd/usd/wrapValidationRegistry.cpp Show resolved Hide resolved
return_value_policy<TfPySequenceToList>(), (args("schemaTypes")));

// For python testing only
def("_register_test_validators", &_RegisterTestValidators);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we shouldn't be doing this here. Once we have APIs wrapped for registering of various validators, we should have the test code register the validators for its purpose, instead of making this wrapper code handle any test specific wrapping.

pxr/usd/usd/wrapValidationRegistry.cpp Show resolved Hide resolved
@nvmkuruc
Copy link
Collaborator

nvmkuruc commented Aug 22, 2024

Took a pass at this. And I am not able to understand why we will want to restrict python bindings for registering validators. In my initial proposal I also provided an example I think of a usecase for this.

I think since validation registration just require callbacks, python cb can be mapped to the appropriate cpp APIs for the wrapping?

Kindly let me know if there are reasons I am failing to understand here. Thanks

We thought the GIL warrants additional consideration when scheduling and running validators. We're not necessarily opposed to registering validators in python, but didn't want to do it on the initial pass.

@tallytalwar
Copy link
Contributor

We thought the GIL warrants additional consideration when scheduling and running validators. We're not necessarily opposed registering validators in python, but didn't want to do it on the initial pass.

I see in that case, maybe lets remove the test code from the wrapper. I am just starting work on ValidationContext / Running of validators, so can also take a stab at this when this work is all done.

@nvmkuruc
Copy link
Collaborator

We thought the GIL warrants additional consideration when scheduling and running validators. We're not necessarily opposed registering validators in python, but didn't want to do it on the initial pass.

I see in that case, maybe lets remove the test code from the wrapper. I am just starting work on ValidationContext / Running of validators, so can also take a stab at this when this work is all done.

We could consider keeping _registerTest for now just for coverage and then revise once the ValidationContext method is complete.

@roggiezhang-nv
Copy link
Contributor Author

We thought the GIL warrants additional consideration when scheduling and running validators. We're not necessarily opposed registering validators in python, but didn't want to do it on the initial pass.

I see in that case, maybe lets remove the test code from the wrapper. I am just starting work on ValidationContext / Running of validators, so can also take a stab at this when this work is all done.

I think Matt replied all those questions. Let me know if I have anything else to fix.

@tallytalwar
Copy link
Contributor

We thought the GIL warrants additional consideration when scheduling and running validators. We're not necessarily opposed to registering validators in python, but didn't want to do it on the initial pass.

So having a chat with Alex and he mentioned about the use of TfPyFunctionFromPython, which takes care of acquiring/locking and releasing of GIL when appropriate python method is called and I think something along these lines should work:

void
_RegisterPluginPrimValidator(
        const TfToken &validatorName, UsdValidatePrimTaskFn primTaskFn)
{
    UsdValidationRegistry::GetInstance().RegisterPluginValidator(
        validatorName, primTaskFn);
}

void
_RegisterPrimValidator(
        boost::python::object metadata, UsdValidatePrimTaskFn primTaskFn)
{
    const UsdValidatorMetadata validatorMetadata = 
            boost::python::extract<UsdValidatorMetadata>(metadata);
    UsdValidationRegistry::GetInstance().RegisterValidator(
        validatorMetadata, primTaskFn);
}

void wrapUsdValidationRegistry()
{
  // Used with RegisterPluginPrimValidator, RegisterPluginStageValidator,
  // RegisterPluginPrimValidator, RegisterLayerValidator, RegisterStageValidator
  // and RegisterPrimValidator
  TfPyFunctionFromPython<UsdValidationErrorVector(const UsdPrim &)>();

  class_<UsdValidationRegistry, boost::noncopyable>("ValidationRegistry",
                                                    no_init)
      .def("__new__", &_GetRegistrySingleton,
           return_value_policy<reference_existing_object>())
      .staticmethod("__new__")
      .def("__init__", raw_function(_DummyInit))
      .def("RegisterPluginPrimValidator", &_RegisterPluginPrimValidator,
           (arg("validatorName"), arg("primTaskFn") = object()))
      .def("RegisterPrimValidator", &_RegisterPrimValidator,
           (arg("metadata") = object(), arg("primTaskFn") = object()))
      .def("HasValidator", &UsdValidationRegistry::HasValidator,
           (args("validatorName")))
....
}

Let me know if this makes sense?

@roggiezhang-nv
Copy link
Contributor Author

We thought the GIL warrants additional consideration when scheduling and running validators. We're not necessarily opposed to registering validators in python, but didn't want to do it on the initial pass.

So having a chat with Alex and he mentioned about the use of TfPyFunctionFromPython, which takes care of acquiring/locking and releasing of GIL when appropriate python method is called and I think something along these lines should work:

void
_RegisterPluginPrimValidator(
        const TfToken &validatorName, UsdValidatePrimTaskFn primTaskFn)
{
    UsdValidationRegistry::GetInstance().RegisterPluginValidator(
        validatorName, primTaskFn);
}

void
_RegisterPrimValidator(
        boost::python::object metadata, UsdValidatePrimTaskFn primTaskFn)
{
    const UsdValidatorMetadata validatorMetadata = 
            boost::python::extract<UsdValidatorMetadata>(metadata);
    UsdValidationRegistry::GetInstance().RegisterValidator(
        validatorMetadata, primTaskFn);
}

void wrapUsdValidationRegistry()
{
  // Used with RegisterPluginPrimValidator, RegisterPluginStageValidator,
  // RegisterPluginPrimValidator, RegisterLayerValidator, RegisterStageValidator
  // and RegisterPrimValidator
  TfPyFunctionFromPython<UsdValidationErrorVector(const UsdPrim &)>();

  class_<UsdValidationRegistry, boost::noncopyable>("ValidationRegistry",
                                                    no_init)
      .def("__new__", &_GetRegistrySingleton,
           return_value_policy<reference_existing_object>())
      .staticmethod("__new__")
      .def("__init__", raw_function(_DummyInit))
      .def("RegisterPluginPrimValidator", &_RegisterPluginPrimValidator,
           (arg("validatorName"), arg("primTaskFn") = object()))
      .def("RegisterPrimValidator", &_RegisterPrimValidator,
           (arg("metadata") = object(), arg("primTaskFn") = object()))
      .def("HasValidator", &UsdValidationRegistry::HasValidator,
           (args("validatorName")))
....
}

Let me know if this makes sense?

It makes sense for sure. I think Matt concerns are more about how those validators are scheduled and running in a parallel execution environment. I'll let Matt to comment more.

@roggiezhang-nv roggiezhang-nv force-pushed the add_bindings_for_validator_registry branch from 5986aad to bbee580 Compare August 26, 2024 01:34
@roggiezhang-nv
Copy link
Contributor Author

@tallytalwar I rebased this branch to shrink the change history.

@jesschimein
Copy link
Contributor

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@roggiezhang-nv
Copy link
Contributor Author

@tallytalwar @nvmkuruc I moved test cpp code into test plugin.


TF_REGISTRY_FUNCTION(UsdValidationRegistry)
{
fprintf(stderr, "test!!!!!\n");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this still be there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. Removed.

@roggiezhang-nv roggiezhang-nv force-pushed the add_bindings_for_validator_registry branch from 80354ed to 8f0e7fc Compare September 3, 2024 11:33
@jesschimein
Copy link
Contributor

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

def setUpClass(cls) -> None:
Plug.Registry().RegisterPlugins(
os.path.dirname(__file__)
+ "/UsdPlugins/lib/TestUsdValidationRegistryPy/Resources/"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using pathlib or os.path.join to be safe.

const UsdStagePtr &stage) const = &UsdValidator::Validate;
UsdValidationErrorVector (UsdValidator::*_ValidatePrim)(const UsdPrim &prim)
const = &UsdValidator::Validate;
class_<UsdValidator, boost::noncopyable>("Validator", no_init)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be helpful to have a __repr__ implementation.

You could consider returning UsdValidatorRegistry.GetOrLoadValidatorByName(<name>) if you're sure that the validator is in the registry. @tallytalwar for additional thoughts.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that makes sense to me, but to confirm, @nvmkuruc your suggestion will help a developer do:

validator = UsdValidatorRegistry.GetOrLoadValidatorByName("plug:validatorName")
print(validator)
UsdValidator(name='plug:validatorName')

Instead of printing the python object?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__repr__ added. Do we need to add method to stringify validator?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tallytalwar Yup! It's analogous to Sdf.Layer.FindOrOpen.

I don't think stringify has much of a use case.

@jesschimein
Copy link
Contributor

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@roggiezhang-nv roggiezhang-nv force-pushed the add_bindings_for_validator_registry branch 2 times, most recently from 105f6db to 18bc714 Compare September 5, 2024 01:40
@jesschimein
Copy link
Contributor

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@nvmkuruc
Copy link
Collaborator

nvmkuruc commented Sep 9, 2024

@tallytalwar @nvmkuruc I moved test cpp code into test plugin.

Thanks!

@tallytalwar
Copy link
Contributor

Thanks @roggiezhang-nv for addressing notes and cleaning the PR. We just pushed an update to dev branch, which might require rebase/updates to this PR. Can you please update it? Thanks

@roggiezhang-nv roggiezhang-nv force-pushed the add_bindings_for_validator_registry branch from 18bc714 to a83d3ab Compare September 10, 2024 00:29
@roggiezhang-nv
Copy link
Contributor Author

Thanks @roggiezhang-nv for addressing notes and cleaning the PR. We just pushed an update to dev branch, which might require rebase/updates to this PR. Can you please update it? Thanks

Rebased.

@jesschimein
Copy link
Contributor

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@comand
Copy link
Collaborator

comand commented Sep 10, 2024

/AzurePipelines run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@pixar-oss pixar-oss merged commit e6600b8 into PixarAnimationStudios:dev Sep 19, 2024
3 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants