Entry points

Build a drawing

build_drawing(step_file, out=None, title=None, number='DWG-001', tolerance=None, drawn_by='', scale=None, page=None, auto_dims=True, detail_view=True, pmi=None, repair=True, assembly=None, model=None, decorations=None, requested=None, authored=None, trace=None, material='', date='', revision='A', company='', frame=False, projection=None, zones=False, scale_policy='fallback', reproducible=True, framed_recognition=False, text_position='inline', text_orientation='aligned', _post_build=None, _required_tables=(), _views=None, _include_iso=True, _view_constraints=None, _document_input=None, *, _analysis_base=None, _analysis_sink=None, projection_symbol=True, source=None, approved_by='', document_type='', sheet='', margin_left=None, margin_right=None, margin_top=None, margin_bottom=None, title_block_width=None, leader_region='auto', annotation_layout='demand-guided', _replayed_scale=None)

Build a drawing, protecting required annotations under an explicit scale.

scale_policy applies only when scale is supplied. "fallback" (the safe default) retries smaller preferred ISO 5455 scales and returns the largest one with no required placement drop. "strict" raises :class:ScaleIncompatibilityError instead. "permissive" explicitly opts into the historical best-effort result and warns when it is degraded. Every returned drawing exposes the JSON-friendly decision through :attr:Drawing.scale_decision; :attr:Drawing.scale is the effective scale.

Pass framed_recognition=True to opt an automatic build into the provider-owned local recognition frame. Raw remains the default. Other arguments and return semantics are unchanged from the one-pass builder.

Third-angle layout and its matching projection symbol are the default. projection_symbol=False suppresses only the symbol. projection='first' places plan below front and side to its left, keeping the physical viewing directions unchanged.

text_position="inline"|"above" and text_orientation="aligned"|"horizontal" independently select dimension typography. Defaults preserve existing appearance.

leader_region="auto"|"interior"|"exterior" controls the candidate regions for feature-leader labels without specifying page coordinates. "auto" preserves the normal shared solve, "exterior" restores the historical exterior-only inventory, and "interior" requires interior candidates where the feature family has proved them. Explicit per-feature side= constraints remain exterior.

annotation_layout="compare" evaluates an alternative on the settled sheet and scale, retaining the existing layout unless finished-drawing semantic parity and layout quality prove a strict gain. The default "demand-guided" selects a profile before rendering in one build, without a comparison. "estimated-strips" uses the original feature-estimated reservations. The original spellings "best", "candidate-preview", and "baseline" remain accepted aliases.

Build and export in one call

make_drawing(step_file, out=None, title=None, number='DWG-001', tolerance=None, drawn_by='', scale=None, page=None, auto_dims=True, detail_view=True, pmi=None, assembly=None, material='', date='', revision='A', company='', frame=False, projection=None, zones=False, scale_policy='fallback', reproducible=True, framed_recognition=False, text_position='inline', text_orientation='aligned', *, projection_symbol=True, source=None, approved_by='', document_type='', sheet='', margin_left=None, margin_right=None, margin_top=None, margin_bottom=None, title_block_width=None, leader_region='auto', annotation_layout='demand-guided')

Generate a technical drawing from a STEP file or build123d object.

Parameters:

Name Type Description Default
step_file str | Path | Shape

Path to a STEP/STP file, or a build123d Shape (e.g. a Part, Solid, or Compound) to draw directly.

required
out str | None

Output path stem (default: input filename stem, or "drawing" when a build123d object is passed).

None
title str | None

Part title for the title block (default: stem uppercased).

None
number str

Drawing number (e.g. "DWG-042").

'DWG-001'
tolerance str | None

General tolerance string (e.g. "ISO 2768-m"). None (the default) states none: the title block says the tolerance is unspecified rather than inventing a manufacturing requirement the source never carried (#1157). "" requests a blank cell.

None
drawn_by str

Designer name for the title block.

''
scale float | None

Drawing-scale override (e.g. 5 for 5:1, 0.5 for 1:2). Default: chosen automatically by :func:choose_scale.

None
scale_policy Literal['strict', 'fallback', 'permissive']

required-annotation policy for an explicit scale. "fallback" retries smaller preferred scales, "strict" raises when the request loses a required outcome, and "permissive" explicitly returns the degraded result.

'fallback'
page str | tuple | None

Page-size override — an ISO name ("A3"), "WIDTHxHEIGHT" in mm, or a (width, height) tuple. Default: chosen automatically by :func:choose_scale.

None
auto_dims bool

pass False to skip the automatic dimensions, centrelines, and leaders (#74) — views, scale, page, and title block only.

True
detail_view bool

automatically add an enlarged view for crowded prismatic step dimensions. Default True; pass False to disable that recovery.

True
reproducible bool

make repeated exports from the returned drawing byte-identical on the same Draftwright version. On by default; pass False to skip canonical DXF ordering.

True
framed_recognition bool

opt an automatic build into the provider-owned local recognition frame. Raw remains the default rollout path.

False
leader_region Literal['auto', 'interior', 'exterior']

feature-leader label region policy. "auto" keeps the normal solver, "exterior" restores exterior-only compatibility, and "interior" requires interior placement where that feature family supports it.

'auto'
annotation_layout Literal['estimated-strips', 'demand-guided', 'compare', 'baseline', 'candidate-preview', 'best']

"compare" compares finished layouts on the same sheet and scale and selects a candidate only when required annotations and quality are preserved. The default "demand-guided" chooses before rendering in one build; it does not establish per-drawing parity to the original layout. "estimated-strips" uses the original planning policy. The former names remain aliases.

'demand-guided'

Returns:

Type Description
tuple[str, str]

Tuple of (svg_path, dxf_path) for the generated files.

This is a thin wrapper: make_drawing(...) is build_drawing(...).export(formats=("svg", "dxf")), unpacked to a tuple. To add or remove annotations or add section/auxiliary views before export, call :func:build_drawing and use the returned :class:Drawing.

Inspect a STEP file without drawing it

inspect_step(path)

Return the recognition evidence for the STEP file at path.

The source is resolved once and read once; recognition consumes a private copy of those exact hashed bytes, so replacing a mutable or symlinked source mid-inspection cannot make the document describe two different files.

Exactly one aggregate recognition run happens, and its evidence, model and conversion-time ownership are reused as-is. No drawing build, view projection, annotation placement, render, export or physical lint path runs.

It does, however, share the engine's ONE detect seam, which sizes the part while detecting, so some scale-selection and dimension-planning work is done and discarded. Measured, that is about 0.02% of an inspection — the cost is recognition and STEP parsing — so the shared seam stays rather than growing a second one that could not be checked against the drawing path. Evidence and method: docs/research/1462-inspect-seam-cost.md.

Raises:

Type Description
OSError

the path could not be read (missing, a directory, permissions).

InspectionUnavailableError

the bytes are not a readable solid STEP body, or the run cannot state its evidence truthfully.

Observe progress and request cancellation

observe_build() reports activity from the existing pipeline. It works around automatic build_drawing() calls, Sheet.build(), and subsequent lint, repair, deferred edits and Drawing.export() calls. It does not select views, change search limits or relax requirements.

from draftwright import BuildCancelled, build_drawing, observe_build

try:
    with observe_build(lambda event: print(event.to_dict())) as control:
        drawing = build_drawing("part.step")
        drawing.export("out", formats=("pdf",))
        # A UI or another thread can call control.cancel("user request").
except BuildCancelled as error:
    print(error.diagnostic)

Each immutable event carries a stage path, phase, elapsed seconds for the observation context and current stage, and details such as projection view or the existing retry/budget reason. There is no estimated percentage. Callbacks run synchronously; keep them short. Ordinary callback exceptions disable notifications with a warning so a broken observer does not abort the drawing. Use control.cancel() to stop deliberately. Observation scopes are context-local; enter the context in the thread doing the build and pass its controller to the cancelling UI.

Cancellation is cooperative. Checkpoints run between stages and during repeated leader candidate/mesh work. A native CAD call must return before a Python checkpoint can execute; this API supplies no hard kernel timeout. Ctrl-C inside the context raises the same BuildCancelled, a KeyboardInterrupt subclass carrying a JSON-serializable diagnostic. Cancelled and failed stages are distinct from finished stages.

No internal search candidate is returned on cancellation. If the public build has finished and cancellation arrives at its publication boundary, error.completed_result contains that normal mutable Drawing; otherwise it is None. It is not a snapshot or a manufacturing approval. During a later export cancellation the caller already owns drawing; no earlier build is attached to the exception. Deferred edits roll back interrupted placement; repair rolls back a provisional change if its critique is interrupted. Already accepted edits and files written before an export interruption are not undone.

The direct-render CLI displays a live stage and elapsed time on stderr in a terminal. When redirected it emits no live controls; --verbose emits plain stage events to stderr. --no-progress disables both forms. Output paths remain on stdout. Ctrl-C prints the cancellation diagnostic to stderr and exits with code 130. --script generation does not use this display.

CLI output destinations

The CLI defaults to writing beside the supplied STEP input. For example, draftwright /parts/frame.step --format all writes /parts/frame.svg, .dxf, .pdf, .png, and .draftwright.json. --script writes /parts/frame.py and its recognition inspection sidecar; replay uses the destination selected when the script was generated and writes /parts/frame.draftwright-assessment.json after a successful build/export. See generated-script replay assessment for why the two JSON files have separate schemas and authority.

Use --out-dir . to write into the current working directory, --out-dir drawings to create/use a destination directory, or --out drawings/revised-frame to choose a prefix. --out and --out-dir are mutually exclusive. Relative overrides are resolved at invocation. For a live object script (module:part or file.py:part), the default is drawing.py in the working directory; --out-dir places that basename in the requested directory.

This changes the CLI default for STEP inputs outside the working directory. Python API output defaults are unchanged. Every CLI prints its written artifact paths on stdout.