Ah, I had missed that, thanks. It doesn't look like you can use assemblyContext to get the component that contains an occurrence though.
e.g.
parent_occurrence = root().occurrences.addNewComponent(adsk.core.Matrix3D.create())
mybox = box(2, 2, 3, name="mybox") # creates a new component under root and adds a box body created from TemporaryBrepManager
mybox.moveToComponent(parent_occurrence)
print(mybox.assemblyContext)
----
None
But in any case, I think I use occurrence.createForAssemblyOccurrence whenever I create a new component, and return that occurrence instead of returning the "raw" occurrence that doesn't have an assembly context. That way I'll only be dealing with occurrences with an assemblyContext, so I can properly traverse up the tree when needed. Although, I guess I can't use that trick for occurrences contained by the root component. But I can just treat assemblyContext = None as a special case, and know that the container is root in that case.
parent_occurrence = root().occurrences.addNewComponent(adsk.core.Matrix3D.create())
mybox = box(2, 2, 3, name="mybox") # creates a new component under root and adds a box body created from TemporaryBrepManager
mybox.moveToComponent(parent_occurrence)
mybox = mybox.createForAssemblyContext(parent_occurrence)
print(mybox.assemblyContext)
----
<adsk.fusion.Occurrence; proxy of <Swig Object of type 'std::vector< adsk::core::Ptr< adsk::fusion::Occurrence > >::value_type *' at 0x0000022E0FA854B0> >
I'd still be interested to know if there's a good way to find the containing component of the "native" occurrence, but I think the above workaround should be workable at least.