OpenUSD icon indicating copy to clipboard operation
OpenUSD copied to clipboard

Add python bindings for `UsdValidator`, `UsdValidatorSuite` and `UsdValidationRegistry`

Open roggiezhang-nv opened this issue 1 year ago • 32 comments

Description of Change(s)

Add python bindings for UsdValidator, UsdValidatorSuite and UsdValidationRegistry.

It is stacked on https://github.com/PixarAnimationStudios/OpenUSD/pull/3223, https://github.com/PixarAnimationStudios/OpenUSD/pull/3232, and https://github.com/PixarAnimationStudios/OpenUSD/pull/3236 for adding Python bindings for validator framework.

Fixes Issue(s)

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

roggiezhang-nv avatar Aug 20 '24 02:08 roggiezhang-nv

@nvmkuruc for vis.

roggiezhang-nv avatar Aug 20 '24 02:08 roggiezhang-nv

Filed as internal issue #USD-10006

jesschimein avatar Aug 20 '24 22:08 jesschimein

/AzurePipelines run

jesschimein avatar Aug 20 '24 22:08 jesschimein

Azure Pipelines successfully started running 1 pipeline(s).

azure-pipelines[bot] avatar Aug 20 '24 22:08 azure-pipelines[bot]

/AzurePipelines run

jesschimein avatar Aug 21 '24 17:08 jesschimein

Azure Pipelines successfully started running 1 pipeline(s).

azure-pipelines[bot] avatar Aug 21 '24 17:08 azure-pipelines[bot]

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)?

tallytalwar avatar Aug 21 '24 22:08 tallytalwar

/AzurePipelines run

jesschimein avatar Aug 22 '24 16:08 jesschimein

Azure Pipelines successfully started running 1 pipeline(s).

azure-pipelines[bot] avatar Aug 22 '24 16:08 azure-pipelines[bot]

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.

nvmkuruc avatar Aug 22 '24 23:08 nvmkuruc

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.

tallytalwar avatar Aug 22 '24 23:08 tallytalwar

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.

nvmkuruc avatar Aug 22 '24 23:08 nvmkuruc

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.

roggiezhang-nv avatar Aug 23 '24 01:08 roggiezhang-nv

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?

tallytalwar avatar Aug 23 '24 07:08 tallytalwar

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 avatar Aug 23 '24 08:08 roggiezhang-nv

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

roggiezhang-nv avatar Aug 26 '24 01:08 roggiezhang-nv

/AzurePipelines run

jesschimein avatar Aug 26 '24 16:08 jesschimein

Azure Pipelines successfully started running 1 pipeline(s).

azure-pipelines[bot] avatar Aug 26 '24 16:08 azure-pipelines[bot]

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

roggiezhang-nv avatar Sep 02 '24 08:09 roggiezhang-nv

/AzurePipelines run

jesschimein avatar Sep 03 '24 15:09 jesschimein

Azure Pipelines successfully started running 1 pipeline(s).

azure-pipelines[bot] avatar Sep 03 '24 15:09 azure-pipelines[bot]

/AzurePipelines run

jesschimein avatar Sep 04 '24 17:09 jesschimein

Azure Pipelines successfully started running 1 pipeline(s).

azure-pipelines[bot] avatar Sep 04 '24 17:09 azure-pipelines[bot]

/AzurePipelines run

jesschimein avatar Sep 05 '24 23:09 jesschimein

Azure Pipelines successfully started running 1 pipeline(s).

azure-pipelines[bot] avatar Sep 05 '24 23:09 azure-pipelines[bot]

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

Thanks!

nvmkuruc avatar Sep 09 '24 14:09 nvmkuruc

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

tallytalwar avatar Sep 09 '24 21:09 tallytalwar

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.

roggiezhang-nv avatar Sep 10 '24 00:09 roggiezhang-nv

/AzurePipelines run

jesschimein avatar Sep 10 '24 16:09 jesschimein

Azure Pipelines successfully started running 1 pipeline(s).

azure-pipelines[bot] avatar Sep 10 '24 16:09 azure-pipelines[bot]