void readXML()
{
Handle(XCAFApp_Application) app = XCAFApp_Application::GetApplication();
XmlXCAFDrivers::DefineFormat(app);
// create and open doc
Handle(TDocStd_Document) doc = new TDocStd_Document("XmlXCAF");
PCDM_ReaderStatus rstatus = app->Open(XML_PATH, doc);
if (rstatus != PCDM_ReaderStatus::PCDM_RS_OK) {
throw std::runtime_error(std::string("Error: cannot open document. Status = ") + std::to_string((int)rstatus));
}
The used method XCAFApp_Application::Open() has peculiar definition to pay attention:
//! Retrieves the document from specified file.
//! In order not to override a version of the document which is already in memory,
//! this method can be made to depend on the value returned by IsInSession.
//! @param[in] thePath file path to open
//! @param[out] theDoc result document
//! @param[in] theRange optional progress indicator
//! @return reading status
PCDM_ReaderStatus Open (const TCollection_ExtendedString& thePath,
Handle(TDocStd_Document)& theDoc,
const Message_ProgressRange& theRange = Message_ProgressRange())
{
return Open (thePath, theDoc, Handle(PCDM_ReaderFilter)(), theRange);
}
Although might think that the method will fill in the document passed in parameter theDoc, it is not (see also @param[out], it is not @param[in,out]). Instead, the function will create a new document and return it to the passed variable reference. So that doc = new TDocStd_Document("XmlXCAF") line before has no effect and you may pass an empty Handle(TDocStd_Document).
This syntax has dramatic consequences when automatically wrapped (via SWIG in this case) into another languages like Python, which follows different paradigm of working with pointers and references compared to C++. Moreover, XCAFApp_Application::Open() has several overloads, which only adds complexity to automatic wrapping.
I have tried your code in pythonOCC 7.9.0 and have struggled that even first assertion doesn't pass:
# ----------- Input data (XML file w/metadata) --------------
INP_FILE = "C:/Users/vnoailles/Downloads/test.xml"
# ------------------------------------------------------------
# Create XDE application and register drivers
app = XCAFApp_Application.GetApplication()
xmlxcafdrivers.DefineFormat(app)
# Initialize document and open file
doc = TDocStd_Document("XmlXCAF")
status = app.Open(INP_FILE,doc)
# Report status
print("Open status:", status, "OK is", PCDM_ReaderStatus.PCDM_RS_OK)
if status != PCDM_ReaderStatus.PCDM_RS_OK:
raise RuntimeError(f"Failed to open XML document, status = {status}")
In fact, status wasn't of type PCDM_ReaderStatus at all, and it looked like function actually returned TDocStd_Document instance.
tmpDoc = TDocStd_Document("XmlXCAF")
doc = app.Open(INP_FILE,tmpDoc)
Probing this document confirmed my assumption that this was indeed expected opened document having 1 free root shape (while dummy document passed via arguments remained empty).
I have looked at the C++ code generated by SWIG for this method, and it is become excessively confusing considering multiple overloads it tries to combine, and indeed in this overload scenario it returns newly created TDocStd_Document instead as return value, which contradicts to automatically generated documentation for the method by pythonOCC building routines.
I cannot say that the behavior is exactly the same on older version of pythonOCC / SWIG, but I suppose that anyway SWIG struggles with wrapping this method and will need some human help here.
I suggest asking this question on pythonOCC site, so maybe they already has some alternative Pythonic methods for opening XCAF documents, and/or will consider improving documentation / wrapping of this API.