10C Inc 3 + 4: I/O and text — the app's whole OCP surface is bound

138/138 symbols across 53 modules. New: IFSelect, Interface, XSControl,
IGESData, STEPControl, IGESControl, RWStl, Graphic3d, NCollection, StdPrs.

**The full app suite passes against this wheel: 1797 passed, 1 skipped — the
same result as the stock wheel**, run from the parity venv.

Getting there needed six methods that no static analysis could have found:
`inventory --check` only sees symbols reached through an import, so a method
called on an instance is invisible to it. The suite found them in one pass, and
one of them (gp_Vec.Reverse, which every outward-normal probe calls) accounted
for 311 of the 255 failing tests on its own. The others: gp_Trsf.SetMirror over
a plane and a point, Geom_Surface.D0, BRep_Builder.MakeFace from a
triangulation, MakePipeShell.SetMode with a fixed binormal, and MakeFace from a
surface plus tolerance.

Open question **S5 is settled: no**. The wheel does not need OCCT's share/
resources. test_inc3_io.py asserts no CSF_* variable is set and then round-trips
STEP and IGES, reading the declared units back off both — which is exactly the
resource-less container the question was about.

The XSTEP readers keep the GIL, amending the blanket "file readers and writers"
line in design.md's GIL policy. STEP and IGES traffic in process-global
Interface_Static state, the IGES reader is documented as not thread-safe, and
the app already serialises imports behind a lock — so holding it costs nothing
and removes a class of question. RWStl, which touches no global state, releases.

XSControl_Reader and IGESData are registered although the app imports neither:
they are the reader base both concrete readers inherit their transfer surface
from, and the model-to-global-section chain the IGES unit probe walks.
This commit is contained in:
2026-08-10 20:31:15 +02:00
parent fc49130232
commit a01b1de26c
20 changed files with 723 additions and 1 deletions
+87
View File
@@ -0,0 +1,87 @@
/*
OCP.STEPControl — STEP read and write.
The reader inherits its transfer surface from XSControl_Reader; what is
added here is the unit handling the app needs (FileUnits reports what the
file declares, SetSystemLengthUnit rescales the transfer) and ReadStream,
which takes a Python file-like.
ReadStream reuses the slurp bridge from occt_stream.h rather than wrapping
a streambuf that calls back into Python — same reasoning as BinTools, and
the same one extra copy.
The GIL stays held throughout, per the note in mod_XSControl.cpp: XSTEP is
built on process-global state and the app serialises imports anyway.
This is also where the S5 question lands (design.md): whether the wheel has
to ship OCCT's share/ resources for STEP to resolve units in a container
with no CSF_* environment. tests/test_inc3_io.py answers it.
*/
#include "../common/occt_module.h"
#include "../common/occt_policies.h"
#include "../common/occt_stream.h"
#include <STEPControl_Controller.hxx>
#include <STEPControl_Reader.hxx>
#include <STEPControl_StepModelType.hxx>
#include <STEPControl_Writer.hxx>
#include <TColStd_SequenceOfAsciiString.hxx>
#include <TopoDS_Shape.hxx>
#include <XSControl_Reader.hxx>
#include <sstream>
void register_STEPControl(nb::module_ &root) {
nb::module_ m = ocp_submodule(root, "STEPControl");
nb::enum_<STEPControl_StepModelType>(m, "STEPControl_StepModelType",
nb::is_arithmetic())
.value("STEPControl_AsIs", STEPControl_AsIs)
.value("STEPControl_ManifoldSolidBrep", STEPControl_ManifoldSolidBrep)
.value("STEPControl_BrepWithVoids", STEPControl_BrepWithVoids)
.value("STEPControl_FacetedBrep", STEPControl_FacetedBrep)
.value("STEPControl_FacetedBrepAndBrepWithVoids",
STEPControl_FacetedBrepAndBrepWithVoids)
.value("STEPControl_ShellBasedSurfaceModel",
STEPControl_ShellBasedSurfaceModel)
.value("STEPControl_GeometricCurveSet", STEPControl_GeometricCurveSet)
.value("STEPControl_Hybrid", STEPControl_Hybrid)
.export_values();
nb::class_<STEPControl_Controller> controller(m, "STEPControl_Controller");
OCP_DEF_S(
controller, "Init", []() { return STEPControl_Controller::Init(); });
nb::class_<STEPControl_Reader, XSControl_Reader>(m, "STEPControl_Reader")
.def(nb::init<>())
.def(
"ReadStream",
[](STEPControl_Reader &self, const char *name, nb::handle stream) {
std::istringstream buffer(ocp_slurp(stream));
return self.ReadStream(name, buffer);
},
"theName"_a, "theIStream"_a)
.def("FileUnits", &STEPControl_Reader::FileUnits, "theLengthUnits"_a,
"theAngleUnits"_a, "theSolidAngleUnits"_a)
.def("SetSystemLengthUnit", &STEPControl_Reader::SetSystemLengthUnit,
"theLengthUnit"_a)
.def("SystemLengthUnit", &STEPControl_Reader::SystemLengthUnit);
nb::class_<STEPControl_Writer>(m, "STEPControl_Writer")
.def(nb::init<>())
.def(
"Transfer",
[](STEPControl_Writer &self, const TopoDS_Shape &sh,
const STEPControl_StepModelType mode,
const Standard_Boolean compgraph) {
return self.Transfer(sh, mode, compgraph);
},
"sh"_a, "mode"_a, "compgraph"_a = Standard_True)
.def(
"Write",
[](STEPControl_Writer &self, const Standard_CString filename) {
return self.Write(filename);
},
"filename"_a);
}