Get all references and payloads for a UsdPrim

From a given pxr.Usd.Prim I’d like to be able to query the current active references and payloads. It seems that prim.GetReferences() and prim.GetPayloads() ironically do not allow to provide that, but allow to add/remove references (generating new opinions basically) instead of detecting the resolved opinion.

The documentation states to maybe look into the usage of a Usd.PrimCompositionQuery, for example using Usd.PrimCompositionQuery.GetDirectReferences(prim).GetCompositionArcs() but somehow those always kept returning empty lists for me.

The only way I could find was maybe going through:

stack = prim.GetPrimStack()

references = []
payloads = []

for prim_spec in stack:
    for reference in prim_spec.referenceList.GetAppliedItems():
        references.append(reference)
    for payload in prim_spec.payloadList.GetAppliedItems():
        payloads.append(payload)

But as far as I’m aware since that’s the lower level SDF API that wouldn’t actually resolve to the resulting composed references and payloads, but only the resulting apply of that particular Sdf.PrimSpec per Sdf.Layer.

Then I’d also like to have easy access to be able to remove the opinions in a layer as well. As far as I can see I must then manually go through all list change types for payloadList and referenceList.

Something like:

from dataclasses import dataclass

LIST_ATTRS = ['addedItems', 'appendedItems', 'deletedItems', 'explicitItems',
              'orderedItems', 'prependedItems']


@dataclass
class ListProxyItem:
    proxy: object
    value: object

    def update(self, new_value):
        ""Replace value in proxy with a new value""
        index = self.proxy.find(self.value)
        self.proxy[index] = new_value
        self.value = new_value

    def delete(self):
        """Remove self from the proxy's entries"""
        proxy = self.proxy
        proxy[:] = [x for x in proxy if x != self.value]


stack = prim.GetPrimStack()
items = []
for prim_spec in stack:
    for key in LIST_ATTRS:
        proxy = getattr(prim_spec.referencesList, key)
        for reference in proxy:
            item = ListProxyItem(proxy=proxy, value=reference)
            items.append(item)

# So that then I could do e.g.
item = items[0]
item.update(Sdf.Reference(assetPath="new_identfier"))
item.delete()  # removing the entry

Are there better Python patterns or USD Python API usage to go about this?

Hi @BigRoyNL … Usd.PrimCompositionQuery is 100% what you should be using, here. Can you provide, either here or in a GitHub Issue, a simple example scene and python code you use to evaluate it in a scenario that is not returning you any arcs (including especially the prim path), where you expect to be seeing some?

Here’s an example where I would have expected some output:

from pxr import Usd, Sdf

stage = Usd.Stage.CreateInMemory()
prim = stage.DefinePrim("/root", "Xform")
references = prim.GetReferences()
references.AddReference(Sdf.Reference(assetPath="./hello.usd"))
references.AddReference(Sdf.Reference(assetPath="./world.usd"))

arcs = Usd.PrimCompositionQuery.GetDirectReferences(prim).GetCompositionArcs()
print(arcs)
# []

However, I noticed now that this does give me arcs:

arcs = Usd.PrimCompositionQuery(prim).GetCompositionArcs()
print(arcs)
# [<pxr.Usd.CompositionArc object at 0x000001F6AEFB7F60>]

So maybe I misunderstood what direct references are supposed to be. However, that’s just one arc? Where I maybe would have expected two. Additionally I’m not sure how to, from that pxr.Usd.CompositionArc now get the data I’d need.

I must be missing something obvious! :wink:

I surmise one of the referenced files is missing or ill-formed. I made valid ones, and with your code, both rcs and arcs had two elements for the two references.

I see - it’ll only resolve the actually found files since those would’ve only contributed to the final composition. Since I want to expose to the artist the currently set references, even if invalid, I suppose the compositon arc query is not what I’d need and I’ll be required to go through the Prim Stack across all layers via the SDF API?

this is basically what I ended up doing for that other thread you also replied to.
And I also had to resolve local files in their layers with

Sdf.ComputeAssetPathRelativeToLayer( layer, payload_path )