Conversation
There was a problem hiding this comment.
Pull request overview
This PR syncs the repo with v26-0-0 API/binding changes, primarily standardizing Python wrapper return conventions (status-first tuples), expanding py::list interoperability, and updating the Python engine/session plumbing (wstring notifications, key-in args, session info).
Changes:
- Standardize many Python bindings to return
(status, result)and update corresponding docstrings/tests/samples. - Add additional py::list overloads and Python container conveniences (
__len__, iterators, list conversions) across multiple wrapper types. - Enhance Python engine/session handling (wstring error/output plumbing, key-in argument passing, session info storage, build file restructuring, new samples/tests).
Reviewed changes
Copilot reviewed 115 out of 120 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| PublicAPI/MSPythonCore/ScriptEngineManager.h | Switch notifier messages to wstring; add session APIs; replace namespace macros. |
| MSPythonWrapper/PyMstnPlatform/source/PSolid/psolidcoreapi.cpp | Add py::list overloads for SolidUtil.Modify methods (Blend/Chamfer/Hollow/Offset variants). |
| MSPythonWrapper/PyMstnPlatform/source/Plot/IPrintDescription.cpp | Add py::list overload for GetViewIndependentWorkingFence; minor cleanup. |
| MSPythonWrapper/PyMstnPlatform/source/Plot/IPlotter.cpp | Add py::list overload for FindBestFitForm. |
| MSPythonWrapper/PyMstnPlatform/source/MdlApi/msview.cpp | Add py::list overloads for view list/boolean-array style APIs. |
| MSPythonWrapper/PyMstnPlatform/source/isessionmgr.cpp | Swap tuple return ordering + doc updates for session mgr dialogs. |
| MSPythonWrapper/PyMstnPlatform/source/ImageLib/ImageLibApi.cpp | Reacquire GIL before building Python tuple result. |
| MSPythonWrapper/PyMstnPlatform/source/documentmanager.cpp | Swap tuple return ordering + doc updates for document manager dialogs. |
| MSPythonWrapper/PyECObjects/source/ecschema.cpp | Add __len__ to ECClassContainer iterator wrapper. |
| MSPythonWrapper/PyECObjects/PyECObjects.mke.sourcemap.json | New sourcemap manifest for PyECObjects build. |
| MSPythonWrapper/PyDgnView/testUtilities/PyDgnViewTest.mke.sourcemap.json | New sourcemap manifest for PyDgnView tests. |
| MSPythonWrapper/PyDgnView/source/ParentTypes/iviewmanager.cpp | Add __len__ to IndexedViewSet wrapper. |
| MSPythonWrapper/PyDgnView/PyDgnView.mke.sourcemap.json | New sourcemap manifest for PyDgnView build. |
| MSPythonWrapper/PyDgnPlatform/testUtilities/PyDgnPlatformTest.mke.sourcemap.json | New sourcemap manifest for PyDgnPlatform tests. |
| MSPythonWrapper/PyDgnPlatform/source/xmlfragment.cpp | Add __len__ to XmlFragmentList wrapper. |
| MSPythonWrapper/PyDgnPlatform/source/xdatanodecollection.cpp | Add __len__ to XDataNodeCollection wrapper. |
| MSPythonWrapper/PyDgnPlatform/source/xattributeiter.cpp | Add __len__ to XAttributeCollection wrapper. |
| MSPythonWrapper/PyDgnPlatform/source/workset.cpp | Add __len__ to WorkSetCollection wrapper. |
| MSPythonWrapper/PyDgnPlatform/source/unitdefinition.cpp | Add __len__ to Standard/User unit collections. |
| MSPythonWrapper/PyDgnPlatform/source/texttablehandler.cpp | Add orientation-angle APIs + docstrings for TextTable/TextTableCell. |
| MSPythonWrapper/PyDgnPlatform/source/textblockiterators.cpp | Add __len__ to RunRange iterator wrapper. |
| MSPythonWrapper/PyDgnPlatform/source/sheetsizedefinition.cpp | Add __len__ to SheetSizeCollection wrapper. |
| MSPythonWrapper/PyDgnPlatform/source/scaledefinition.cpp | Add ctor + __len__ to ScaleCollection wrapper. |
| MSPythonWrapper/PyDgnPlatform/source/rastercollection.cpp | Add __len__ to DgnRasterCollection wrapper. |
| MSPythonWrapper/PyDgnPlatform/source/picklist.cpp | Return reference_internal for AddValue/AddPickList; add __len__. |
| MSPythonWrapper/PyDgnPlatform/source/material.cpp | Swap tuple return ordering + doc updates for MaterialManager searches. |
| MSPythonWrapper/PyDgnPlatform/source/linestyle.cpp | Swap tuple return ordering; add __len__ to LsMap; doc fixes. |
| MSPythonWrapper/PyDgnPlatform/source/light.cpp | Add __len__ to LightMapCollection wrapper. |
| MSPythonWrapper/PyDgnPlatform/source/itxnmanager.cpp | Swap tuple return ordering + doc updates for CreateNewModel. |
| MSPythonWrapper/PyDgnPlatform/source/elementtemplateparamshelper.cpp | Add fixed-size unit APIs + updated tuple-style docs. |
| MSPythonWrapper/PyDgnPlatform/source/elementrefbase.cpp | Add Python list-like behaviors to ElementRefVec (iter/del/contains/remove). |
| MSPythonWrapper/PyDgnPlatform/source/ecinstanceholderhandler.cpp | Swap tuple return ordering for CreateECInstanceHolderElement + doc update. |
| MSPythonWrapper/PyDgnPlatform/source/digitalsignaturecellheaderhandler.cpp | Add __len__ to ApplicableSignaturesCollection wrapper. |
| MSPythonWrapper/PyDgnPlatform/source/dgnraster.cpp | Swap tuple return ordering for DgnRaster::Create. |
| MSPythonWrapper/PyDgnPlatform/source/dgnmodelrefcollections.cpp | Add __len__ to reachable model/element collection wrappers. |
| MSPythonWrapper/PyDgnPlatform/source/dgnlinktree.cpp | Add __len__ to DgnLinkUserDataList wrapper. |
| MSPythonWrapper/PyDgnPlatform/source/dgnlinks.cpp | Swap tuple return ordering for DgnModelLink tuple-returning getters. |
| MSPythonWrapper/PyDgnPlatform/source/dgnlinkmanager.cpp | Swap tuple return ordering for CreateLink/CreateLinkSet; doc updates. |
| MSPythonWrapper/PyDgnPlatform/source/dgnitemindex.cpp | Add __len__ to ModelIndex wrapper. |
| MSPythonWrapper/PyDgnPlatform/source/dgnfile.cpp | Add LoadDgnFile doc; add __len__; swap tuple ordering in some methods. |
| MSPythonWrapper/PyBentleyGeom/testUtilities/PyGeomTest.mke.sourcemap.json | New sourcemap manifest for PyBentleyGeom tests. |
| MSPythonWrapper/PyBentleyGeom/source/rotmatrix.cpp | Fix invalid default-arg placement by removing default in binding. |
| MSPythonWrapper/PyBentleyGeom/source/curveprimitive.cpp | Add overloads for CreateSpiral, including maxStrokeLength. |
| MSPythonWrapper/PyBentley/source/ParentTypes/betextfile.cpp | Swap tuple return ordering for BeTextFile.Open + doc update. |
| MSPythonWrapper/PyBentley/source/bestringutilities.cpp | Swap tuple return ordering for ParseUInt64 + doc update. |
| MSPythonWrapper/PyBentley/PyBentley.mke.sourcemap.json | New sourcemap manifest for PyBentley build. |
| MSPythonTests/MstnPlatformTests/Tests/SessionMgr_Test.py | Update tests for status-first tuple returns; update mdlDescr expectations. |
| MSPythonTests/MstnPlatformTests/Tests/PylistTests/Psolidcoreapi_Pylist_Test.py | New tests covering new py::list overloads for PSolid APIs. |
| MSPythonTests/MstnPlatformTests/Tests/PylistTests/Msview_Pylist_test.py | New tests covering new py::list overloads for msview APIs. |
| MSPythonTests/MstnPlatformTests/Tests/PylistTests/IPrintDescription_Pylist_Test.py | New tests for GetViewIndependentWorkingFence py::list overload. |
| MSPythonTests/MstnPlatformTests/Tests/PylistTests/IPlotter_Pylist_Test.py | New tests for FindBestFitForm py::list overload. |
| MSPythonTests/MstnPlatformTests/Tests/PylistTests/Fenceparams_Pylist_test.py | New tests for FenceParams list/array conversions. |
| MSPythonTests/MstnPlatformTests/Tests/Mesh_Test.py | Update tuple unpacking order for getFileFromDirectory usage. |
| MSPythonTests/MstnPlatformTests/Tests/Level_Test.py | Update tuple return indexing order for file/document helpers. |
| MSPythonTests/MstnPlatformTests/Tests/KeyInManager_Test.py | Update callbacks to accept optional unparsed arg string. |
| MSPythonTests/MstnPlatformTests/Tests/DocumentManager_Test.py | Update tuple return indexing order for document manager APIs. |
| MSPythonTests/MstnPlatformTests/Tests/DgnFile_Test.py | Update tuple unpacking order for getFileFromDirectory usage. |
| MSPythonTests/MstnPlatformTests/Tests/ChangeTrackCallback_Test.py | Update CreateFromFileName usage (but currently inconsistent indexing). |
| MSPythonTests/MstnPlatformTests/Tests/ArgSupport_Test.py | New argparse-based key-in argument handling sample/test script. |
| MSPythonTests/MstnPlatformTests/Tests/AddURLLink_Test.py | Update CreateLink tuple return ordering. |
| MSPythonSamples/SystemCallbacks/ChangeTrackCallbacks.py | Update Add/Remove functions to accept optional unparsed arg string. |
| MSPythonSamples/ReportSample/ReportExample.commands.xml | New key-in command table for report example. |
| MSPythonSamples/ReportSample/report_Example.py | New report sample entrypoint + key-in loader. |
| MSPythonSamples/ReportSample/report_example_generate_report.py | New report-definition generation logic. |
| MSPythonSamples/ReportSample/report_example_create_itemtypes.py | New item type library/type creation logic. |
| MSPythonSamples/ReportSample/report_example_common.py | Shared config/utilities for report example workflow. |
| MSPythonSamples/ReportSample/report_example_attach_itemtypes.py | Tool/sample to attach item types to elements. |
| MSPythonSamples/ParametricModeling/VariableAndVariation.py | Update BeTextFile.Open tuple return ordering. |
| MSPythonSamples/ParametricModeling/PSampUtility.py | Update LoadRootModelById tuple return ordering. |
| MSPythonSamples/ParametricModeling/ParametricCell.py | Update LoadRootModelById tuple return ordering. |
| MSPythonSamples/ItemType/Custom/ItemTypeCustomProperty.py | Update key-in entrypoints to accept optional unparsed arg string. |
| MSPythonSamples/ItemType/CRUD/ItemTypeCRUD.py | Update key-in entrypoints to accept optional unparsed arg string. |
| MSPythonSamples/ItemType/Attach/ItemTypeAttachDetach.py | Update key-in entrypoints to accept optional unparsed arg string. |
| MSPythonSamples/GeospatialContext/SHPImportWithDesignLink.py | Update CreateLink tuple return ordering. |
| MSPythonSamples/GeospatialContext/GDBImportAsCell.py | Add FixedSizeUnitType handling via new template helper APIs. |
| MSPythonSamples/GeospatialContext/CreateCustomGCS_StlouisTM96.py | Handle tuple return from InitFromWellKnownText and validate status. |
| MSPythonSamples/GeospatialContext/AddGeographicCoordinateSystemToModel.py | Handle tuple returns from InitFromEPSGCode / InitFromWellKnownText. |
| MSPythonSamples/EC/Schema/ECXSchema.py | Update key-in entrypoints to accept optional unparsed arg string. |
| MSPythonSamples/EC/ExtractProperties/ExtractProperties.py | Update key-in entrypoint to accept optional unparsed arg string. |
| MSPythonSamples/EC/ECChangeEvents/DgnECChangeEvents.py | Update LoadRootModelById tuple return indexing order; key-in entrypoints updated. |
| MSPythonSamples/EC/Dump/ECDumpUtility.py | Update key-in entrypoint to accept optional unparsed arg string. |
| MSPythonSamples/EC/CRUD/ECCrud.py | Update key-in entrypoints to accept optional unparsed arg string. |
| MSPythonSamples/DgnTool/TextExample/TextExample.py | Update key-in entrypoints to accept optional unparsed arg string. |
| MSPythonSamples/DgnTool/PolyfaceTool/PolyfaceTool.py | Update key-in entrypoint to accept optional unparsed arg string. |
| MSPythonSamples/DgnTool/ModifyMultipleElements/ModifyMultipleElements.py | Update key-in entrypoints to accept optional unparsed arg string. |
| MSPythonSamples/DgnTool/ModifyEdgeSamples/ModifyEdgeSamples.py | Update key-in entrypoint to accept optional unparsed arg string. |
| MSPythonSamples/DgnTool/MeshTools/MeshTools.py | Update key-in entrypoints to accept optional unparsed arg string. |
| MSPythonSamples/DgnTool/IncrementText/IncrementText.py | Update key-in entrypoint to accept optional unparsed arg string. |
| MSPythonSamples/DgnTool/ElementLinkExample.py | Update CreateLink tuple return ordering; update key-in entrypoints. |
| MSPythonSamples/DgnElements/UnitConversionsSample.py | Update key-in entrypoint to accept optional unparsed arg string. |
| MSPythonSamples/DgnElements/TagsExamplePlaceTagFromText.py | Improve robustness; auto-create tag set; add additional messaging. |
| MSPythonSamples/DgnElements/TagsExampleCreateTagSet.py | Update messaging; make tagsExample_createTagSet key-in compatible; simplify main. |
| MSPythonSamples/DgnElements/TagsExampleCreateTagSet.commands.xml | Removed old command table XML. |
| MSPythonSamples/DgnElements/Reference/Reference.py | Update key-in entrypoints to accept optional unparsed arg string. |
| MSPythonSamples/DgnElements/MeshStyleSamples.py | Update key-in entrypoints to accept optional unparsed arg string. |
| MSPythonSamples/DgnElements/CreateTextNodes.py | Update tuple indexing for default model load result. |
| MSPythonCore/ScriptEngineManager/source/ScriptEngineManager.cpp | Add PythonSessionInfo + session result accumulation + silence mode. |
| MSPythonCore/ScriptEngineManager/source/MSPythonEngine.cpp | wstring InjectError; safer main dict backup; key-in arg invocation changes. |
| MSPythonCore/ScriptEngineManager/ScriptEngineManager.mki | Removed (build rules moved/reworked). |
| MSPythonCore/MSPythonCore.mke.sourcemap.json | New sourcemap manifest for MSPythonCore build. |
| MSPythonCore/MSPythonCore.mke | New build file replacing top-level MSPythonCore.mke. |
| MSPythonCore.mke | Removed (replaced by MSPythonCore/MSPythonCore.mke). |
| MSPython.mke | Update build invocation path for new MSPythonCore/MSPythonCore.mke. |
| InternalAPI/MSPyCommon.h | Improve pylist conversion casting and add bool conversion specializations. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| virtual void OnException(std::exception& ex) override | ||
| { | ||
| BeConsole::WPrintf(L"%s\n", ex.what()); | ||
| } |
There was a problem hiding this comment.
UstnScriptNotifier::OnException passes std::exception::what() (char*) to BeConsole::WPrintf with a wide format string (L"%s"). For wide printf-family functions, "%s" expects wchar_t*, so this is undefined behavior and can crash or print garbage. Convert the message to a wide string (e.g., WString(ex.what()) / Utf8->WChar) or use the correct format specifier for narrow strings if supported.
| bool isdraw[DgnPlatform::MAX_VIEWS]; | ||
| CONVERT_PYLIST_TO_NEW_CPPARRAY(viewDraw, cppViewDraw, BoolArray, bool); | ||
| size_t arrlen = cppViewDraw.size() > DgnPlatform::MAX_VIEWS ? DgnPlatform::MAX_VIEWS : cppViewDraw.size(); | ||
| for (int i = 0; i < arrlen; i++) | ||
| isdraw[i] = cppViewDraw[i]; | ||
|
|
||
| return mdlView_updateMulti (isdraw, incremental, drawMode, modelRefList, startEndMsg); |
There was a problem hiding this comment.
The local isdraw array is only assigned for indices < arrlen; the remaining elements are left uninitialized but still passed to mdlView_updateMulti, which can read garbage view flags. Initialize the full MAX_VIEWS array (e.g., fill with false) before copying list entries.
| bool isdraw[DgnPlatform::MAX_VIEWS]; | ||
| CONVERT_PYLIST_TO_NEW_CPPARRAY(viewDraw, cppViewDraw, BoolArray, bool); | ||
| size_t arrlen = cppViewDraw.size() > DgnPlatform::MAX_VIEWS ? DgnPlatform::MAX_VIEWS : cppViewDraw.size(); | ||
| for (int i = 0; i < arrlen; i++) | ||
| isdraw[i] = cppViewDraw[i]; | ||
|
|
||
| return mdlView_updateMultiEx(isdraw, incremental, drawMode, modelRefList, startEndMsg, updateViewTitle); |
There was a problem hiding this comment.
The local isdraw array is only assigned for indices < arrlen; the remaining elements are left uninitialized but still passed to mdlView_updateMultiEx. Initialize the full MAX_VIEWS array (e.g., fill with false) before copying list entries.
| bool isdraw[DgnPlatform::MAX_VIEWS]; | ||
| CONVERT_PYLIST_TO_NEW_CPPARRAY(viewList, cppViewList, BoolArray, bool); | ||
| size_t arrlen = cppViewList.size() > DgnPlatform::MAX_VIEWS ? DgnPlatform::MAX_VIEWS : cppViewList.size(); | ||
| for (int i = 0; i < arrlen; i++) | ||
| isdraw[i] = cppViewList[i]; | ||
|
|
||
| return mdlView_setLevelDisplayMaskMulti(modelRef, isdraw, viewLevelMask, doUpdate); |
There was a problem hiding this comment.
The local isdraw array is only assigned for indices < arrlen; the remaining elements are left uninitialized but still passed to mdlView_setLevelDisplayMaskMulti. Initialize the full MAX_VIEWS array (e.g., fill with false) before copying list entries.
| bool isdraw[DgnPlatform::MAX_VIEWS]; | ||
| CONVERT_PYLIST_TO_NEW_CPPARRAY(viewList, cppViewList, BoolArray, bool); | ||
| size_t arrlen = cppViewList.size() > DgnPlatform::MAX_VIEWS ? DgnPlatform::MAX_VIEWS : cppViewList.size(); | ||
| for (int i = 0; i < arrlen; i++) | ||
| isdraw[i] = cppViewList[i]; | ||
|
|
||
| return mdlView_changeLevelDisplayMaskMulti(modelRef, isdraw, levelMask, operation, doUpdate); | ||
| }, "modelRef"_a, "viewList"_a, "levelMask"_a, "operation"_a, "doUpdate"_a, DOC(mdlView, changeLevelDisplayMaskMulti)); |
There was a problem hiding this comment.
The local isdraw array is only assigned for indices < arrlen; the remaining elements are left uninitialized but still passed to mdlView_changeLevelDisplayMaskMulti. Initialize the full MAX_VIEWS array (e.g., fill with false) before copying list entries.
| WString function(funcName); | ||
| WString arg; | ||
| localCtx->getAs(L"PyKeyinArg", arg); | ||
| function.append(L" (PyKeyinArg)"); | ||
|
|
||
| function.append (L" ()"); | ||
| mdlErrno = 0; | ||
| py::exec(py::cast(function.c_str ()) ,globalDict, localDict); |
There was a problem hiding this comment.
localCtx->getAs(L"PyKeyinArg", arg); ignores the return value and the retrieved arg is unused, while the code always invokes the function as funcName(PyKeyinArg). If PyKeyinArg is not present in localDict this will raise a NameError. Consider: (1) ensure PyKeyinArg is defined in localDict (set to "" when missing), and/or (2) only append/call with the argument when it is actually present, and remove the unused arg variable.
| doc = DgnDocument.CreateFromFileName ('ChangeTrack.dgn', dataDir, -101, DgnDocument.FetchMode.eWrite)[1] | ||
| ISessionMgr.GetManager().SwitchToNewFile(doc[1], '', GraphicsFileType.eGRAPHICSFILE_WildCard, True, True) |
There was a problem hiding this comment.
doc is assigned to a single tuple element (CreateFromFileName(...)[1]), but then indexed again (doc[1]) when passed to SwitchToNewFile. This will raise at runtime. Either unpack the tuple into (status, doc) and pass doc, or keep doc as the document object and pass it directly.
| c1.def("FactorRotateScaleRotate", &RotMatrix::FactorRotateScaleRotate, "rotation1"_a, "scalePoint"_a, "rotation2"_a, DOC(Bentley, Geom, RotMatrix, FactorRotateScaleRotate)); | ||
| c1.def("RotateAndSkewFactors", &RotMatrix::RotateAndSkewFactors, "rotation"_a, "skewFactor"_a = 0, "primiaryAxis"_a, "secondaryAxis"_a, DOC(Bentley, Geom, RotMatrix, RotateAndSkewFactors)); | ||
| c1.def("RotateAndSkewFactors", &RotMatrix::RotateAndSkewFactors, "rotation"_a, "skewFactor"_a, "primiaryAxis"_a, "secondaryAxis"_a, DOC(Bentley, Geom, RotMatrix, RotateAndSkewFactors)); | ||
| c1.def("FactorOrthogonalColumns", &RotMatrix::FactorOrthogonalColumns, "matrixB"_a, "maxtrixV"_a, DOC(Bentley, Geom, RotMatrix, FactorOrthogonalColumns)); |
There was a problem hiding this comment.
This binding removed the default value for skewFactor, which is a breaking change for Python callers that previously relied on the optional argument. To preserve the old signature, consider adding an overload/wrapper that calls RotateAndSkewFactors with skewFactor=0 when omitted.
| void ScriptEngineManager::InitPySessionInfo() | ||
| { | ||
| if (nullptr == s_sessionInfo) | ||
| s_sessionInfo = new PythonSessionInfo(); |
There was a problem hiding this comment.
InitPySessionInfo allocates s_sessionInfo with new but it is never freed (ScriptEngineManager::Shutdown only deletes s_EngineManager). Prefer a smart pointer/static value, or ensure s_sessionInfo is deleted during shutdown to avoid a persistent leak (especially in test runners or plugin reload scenarios).
| s_sessionInfo = new PythonSessionInfo(); | |
| { | |
| static PythonSessionInfo sessionInfo; | |
| s_sessionInfo = &sessionInfo; | |
| } |
| keyinXml = os.path.dirname(__file__) + '/ReportExample.commands.xml' | ||
| PythonKeyinManager.GetManager().LoadCommandTableFromXml(WString(__file__), WString(keyinXml)) | ||
|
|
||
| MessageCenter.ShowInfoMessage(f"Loaded key-in commands. /n REPORTSEXAMPLE CREATE ITEMTYPES /nREPORTSEXAMPLE ATTACH ITEMTYPES /n REPORTSEXAMPLE GENERATE REPORT", "", False) | ||
| return True |
There was a problem hiding this comment.
The info message uses "/n" sequences instead of newline escape "\n", so it will display the literal characters rather than line breaks. Replace "/n" with "\n" in the message string.
No description provided.