Document lifecycle
- Resolve the active document explicitly. If a task may run in an empty process, create a document with
FreeCAD.newDocument(...). - Close temporary documents with
FreeCAD.closeDocument(doc.Name)orFreeCAD.closeDocument("Name")in cleanup paths. - Use internal
Namefor programmatic lookup and durable object references. UseLabelfor human-facing naming and expression label references only when the expression syntax explicitly requires it. - Use
doc.getObject(name_or_id)when the exact internal name or numeric id is known. - Use
doc.findObjects(Type="...", Name="...", Label="...")for filtered discovery. - Use
doc.getObjectsByLabel(label)only when label-based ambiguity is acceptable or expected. - Use
doc.supportedTypes()before constructing optional or workbench-dependent TypeIds.Part::Featurecreation should be guarded this way and falls back toPart.show(shape, name)when needed. - Prefer
doc.Objects,doc.RootObjects, anddoc.TopologicalSortedObjectsfor inventory or dependency-sensitive traversal.
Transactions
- Wrap coherent document mutation in a transaction:
- Use transaction labels that identify the modeling intent, not implementation trivia.
- Treat document transactions as potentially application-scoped. The
Document.pyistub documentsopenTransaction(name)as routing throughFreeCAD.setActiveTransaction(name), and multi-document changes can share one internal transaction id. - Commit only after all properties and links in that logical edit are assigned.
- Abort on exceptions before retrying with a different strategy.
- Use
doc.hasPendingTransactionwhen recovery logic needs to know whether abort/commit is valid. doc.undo(),doc.redo(),doc.clearUndos(), anddoc.clearDocument()exist, but agents should not use them as primary modeling operations. Use them for explicit rollback workflows or tests.- Use
FreeCAD.getActiveTransaction()andFreeCAD.closeActiveTransaction(abort=False, id=0)when coordinating application-level transaction state directly.
Recompute discipline
- Recompute after creating geometry, setting expressions, changing spreadsheet cells, adding TechDraw views, or changing parameter objects.
doc.recompute()returns an object count. Capture it when the task needs to know whether a mutation actually propagated.doc.recompute([obj1, obj2])is supported. Use targeted recompute for parameter propagation tests or narrow dependency updates, then validate downstream state.doc.recompute(objects, force, checkCycle)acceptsforceandcheckCyclebooleans at the binding layer. Use cycle checking when generating or modifying expression graphs.- Use object state as a dependency signal. Check
"Touched" in obj.Stateand"Up-to-date" in dimension.Statewhen validating state. obj.recompute(recursive=False)recomputes a feature and returns a boolean. Prefer document recompute for normal modeling because document dependencies are broader.obj.isValid()is available for object validity checks; shape-specific validation still requires shape inspection.
Expressions
- Use
obj.setExpression(path, expr)withexpras a string. - Use
obj.setExpression(path, None)orobj.clearExpression(path)to remove an expression. Do not overwrite with an empty string unless the target API explicitly documents that behavior. - Use
obj.getExpression(path)when checking current bindings. - Expression changes create dependency edges. Validate expected propagation by recomputing and checking touched state or driven property values.
- Use
hiddenref(...)only when the dependency is intentionally hidden from normal object link tracking.hiddenref(VarSet.Length)removes ordinaryInList,OutList, and property edge visibility while still allowing expression evaluation. This is a specialized dependency tool, not a default reference form. - Use
setPropertyStatus(name, "Output")for computed output properties andsetPropertyStatus(name, ["Input"])for input properties that should participate in fine-grained dependency behavior.
Parameter objects
- Use
App::VarSetfor compact named parameters when a full spreadsheet is unnecessary.
- Use explicit property types such as
App::PropertyLength,App::PropertyInteger,App::PropertyBool,App::PropertyString, andApp::PropertyEnumeration. - Put parameter properties in a stable group such as
"Params"so later introspection can distinguish design inputs from incidental object properties. - Use
FreeCAD.Units.Quantity("32.0 mm")or string quantities like"20 mm"for dimensional values. Do not rely on unitless floats for dimensions unless the target property semantics are known.
Property introspection
- Query
obj.PropertiesListbefore assigning uncommon properties. - Use
obj.getPropertyByName(name, checkOwner)when direct attribute access could cross a link boundary.checkOwner=1requires local ownership;checkOwner=2returns owner plus value. - Query
obj.getTypeIdOfProperty(name)before coercing values for dynamic or unfamiliar properties. - Query
obj.getGroupOfProperty(name)to distinguish design inputs, outputs, and implementation groups. - Query
obj.getDocumentationOfProperty(name)when an agent needs the property intent. - Respect property status. The binding exposes status concepts including
Output,Input,NoRecompute,NoPersist,ReadOnly, andHidden. - Use negative status values or text prefixed with
-only when intentionally clearing property status. - For Python feature objects, add properties before assigning values, set status before depending on the property, and implement
execute(obj)to update shape/output properties.
Sketch and PartDesign
- For PartDesign profiles, create a
PartDesign::Body, then create the sketch withbody.newObject("Sketcher::SketchObject", "Sketch"). - Attach sketches to origin planes when constructing base profiles:
- Use
Part.LineSegment(FreeCAD.Vector(...), FreeCAD.Vector(...))and other Part geometry constructors for sketch geometry. - Batch-add geometry and constraints where possible:
- Set sketch dimensions through constraints and datum values, not by leaving underconstrained raw geometry:
- Use
sketch.setGeometryIds(...)when the model expects stable geometry ids. - For pads, assign
pad.Profile = sketch, set length with an explicit unit, and setpad.ReferenceAxiswhen the source pattern requires an axis. - Recompute the document after constructing the profile before measuring
body.Shapeor downstream feature output. - Validate parametric sketch updates by changing the driving parameter, recomputing, and checking dimensions through
body.Shape.BoundBox.
Shape validation
- Validate created or modified solids by checking the object shape, not just object existence.
- Use
obj.Shape.BoundBoxfor dimensional assertions and screen-space selection setup. - Use shape validity/null checks when available for the shape type. Pair them with recompute and measurable shape assertions.
- Use
Shape.Volume,BoundBox.XLength,BoundBox.YLength, andBoundBox.ZLengthfor coarse geometry sanity checks after recompute. - For boolean or generated Part shapes, assign the resulting shape to a
Part::Featureonly after confirming the target TypeId is available. - Do not treat a successful
addObjectcall as proof that recompute produced valid geometry.
Spreadsheet practices
- Create sheets with
doc.addObject("Spreadsheet::Sheet", "Spreadsheet"). - Set cells with
sheet.set(cell, value_or_formula), then recompute before reading calculated attributes or dependent geometry. - Use aliases for stable parametric references:
- Alias names must be unique, must not be cell addresses, and must not collide with units or reserved words. Duplicate and invalid aliases are expected to fail.
- Clear an alias with
sheet.setAlias("A1", "");sheet.getAlias("A1")should then returnNone. - Renaming an alias updates formulas and expression-engine bindings. After rename, inspect
sheet.getContents(cell)orobj.ExpressionEngineif correctness depends on the rewritten reference. - Use label-qualified references such as
<<Spreadsheet>>.Lengthwhen the expression needs object label semantics. If the label changes, verify the expression engine rewrites to the new label form. - Insert/remove rows and columns can move aliases and formulas. Recompute and inspect aliases after structural edits.
- Wrap alias creation in transactions when undo/reuse behavior matters.
- Cross-document expressions require saved documents. The tests save, reopen, then validate cross-document links. Do not assume unsaved document names are durable references.
- Spreadsheet formulas support units, ranges, aggregate functions, conditionals, vector/matrix/rotation/placement constructors, and aliases. Validate units because range aggregation with mixed units returns error strings.
- Use
.cells.Bind...and.cells.BindHiddenRef...expression paths only when binding spreadsheet cell regions intentionally. Clear withsetExpression(path, None).
Draft API
- The Draft public programming interface is designed so its creation/manipulation functions are usable without requiring the graphical user interface.
- Use Draft for 2D construction objects, cleanup, transforms, wires, arrays, SVG/DXF extraction, and object type utilities.
- Prefer public constructors and functions exported by
Draft.py, includingmake_line,make_wire,make_circle,make_rectangle,make_polygon,array,move,rotate,scale,offset,mirror,upgrade,downgrade,join_wires,split,fuse,cut,extrude,heal,draftify,shapify,get_type,get_objects_of_type,get_svg, andget_dxf. - Guard GUI-dependent helpers with
FreeCAD.GuiUp. Selection, style, active view, and viewprovider helpers are not headless-safe by default. - Recompute after Draft object creation or transform operations before measuring geometry or exporting downstream artifacts.
BIM and architectural API
- Guard active-document assumptions. Source constructors abort or return when no active document exists for active-document-only APIs.
- Creation functions typically return the created object. Capture that return value and continue mutating that object directly.
- FeaturePython patterns create an object, assign a proxy, optionally assign a viewprovider only when
FreeCAD.GuiUp, set domain properties, recompute, and return the object. - Normalize single-object/list inputs before adding them to container objects.
makeAxisSystemcoerces one axis to a list; building-part helpers accept both one object and a sequence. - Set domain metadata explicitly, for example
IfcTypeandCompositionType, after object creation. - Do not assume BIM object creation recomputes in every helper. Some helpers recompute immediately; others return after assigning properties. Recompute before validation.
TechDraw
- Create a page and template before views:
- Add views to the page with
page.addView(view)before final validation. - Use list form for sources where the API expects it:
- For Draft views, use
TechDraw::DrawViewDraft, setSource, setDirection, add to the page, then recompute. - For dimensions, create
TechDraw::DrawViewDimension, add it to the page, setType, setReferences2D, then recompute. - Verify drawing state with
"Up-to-date" in dimension.Stateor equivalent view/page state checks before reporting a drawing as current. - Rendering or page inspection should happen after recompute, not immediately after object creation.
GUI boundaries
- Always check
FreeCAD.GuiUpbefore importing GUI modules, assigning viewproviders, updating views, or using selection/viewport APIs. - Headless agent snippets should avoid GUI-only state. If the task needs rendered confirmation through GUI APIs, only run that path after
FreeCAD.GuiUpis true. - When GUI selection tests create geometry, they recompute, refresh the view, and only then inspect projected bounds. Follow the same order for viewport-dependent workflows.
Python feature objects
- Use
Part::FeaturePythonorApp::FeaturePythonwhen the object needs a Python proxy. - Proxy setup pattern:
- Keep output properties marked as output so expression dependencies can distinguish generated values from design inputs.
- If
onChangedeagerly recomputes an output, still validate via document recompute because other dependent objects may not update until the document graph runs.
File and cross-document workflows
- Use
saveAs(...), close/reopen, then validate when testing persistence of expressions, spreadsheet formulas, strings, or cross-document links. - Do not depend on temporary unsaved document identity for cross-file references.
- When an expression includes another document, verify after reopening both documents because persistence behavior is part of the contract being tested.
Agent direct-use policy
- Use the FreeCAD-compatible Python surface directly for compiled-binding introspection, uncommon TypeIds, dynamic properties, expression graph inspection, and workbench APIs.
- Before assigning an unfamiliar property, introspect
PropertiesListand metadata. - Before using an unfamiliar TypeId, check
doc.supportedTypes(). - After any mutation path, recompute and validate the exact changed objects.