Skip to main content

draftwright

CI codecov PyPI Python License: AGPL v3 Ruff

API reference · Examples and source

Automated technical-drawing generation for build123d. Point it at a solid (or a STEP file) and get a fully-annotated multi-view engineering drawing — orthographic views, dimensions, section A–A, ISO hatching, title block — ready to export as PDF, SVG, and DXF.

Example engineering drawing generated by draftwright

A mounting plate, generated automatically: three dimensioned orthographic views, a counterbored bore callout (⌀18 THRU ⊔ ⌀30 ↓14), a 4× ⌀8 THRU hole-pattern callout, Section A–A with ISO hatching, an isometric, and an ISO 7200 title block — every annotation placed by the engine.

Quick start

pip install draftwright

Command line

Point it at a STEP file — that's the whole workflow:

draftwright my_part.step --title "Mounting Plate" --number DWG-001
# writes my_part.pdf (the default)

Choose formats, scale, and page; or emit an editable drawing script:

draftwright my_part.step --format pdf,dxf     # also: svg, all
draftwright my_part.step --scale 2 --page A3  # override the auto scale / page
draftwright my_part.step --script             # write an editable declarative Sheet script

draftwright --help lists every flag; --version prints the version.

Python

One call turns a build123d solid (or a STEP file) into a drawing. This is the exact part in the image above:

from build123d import Box, Cylinder, Pos
from draftwright import make_drawing

part = (
    Box(100, 70, 24)
    - Pos(0, 0, 0) * Cylinder(9, 40)     # central bore, counterbored below
    - Pos(0, 0, 8) * Cylinder(15, 20)    # counterbore → triggers Section A–A
    - Pos(-38, 24, 0) * Cylinder(4, 40)  # 4× corner holes (recognised as a pattern)
    - Pos(38, 24, 0) * Cylinder(4, 40)
    - Pos(-38, -24, 0) * Cylinder(4, 40)
    - Pos(38, -24, 0) * Cylinder(4, 40)
)
make_drawing(part, out="my_part", title="Mounting Plate", number="DWG-001")
# writes my_part.svg and my_part.dxf   (or from a STEP file: make_drawing(step_file="p.step", out="my_part"))

For inspection and editing, get a composable Drawing:

from draftwright import build_drawing

dwg = build_drawing(part, title="Mounting Plate")
issues = dwg.lint()                       # list[LintIssue] — coverage, page bounds, ISO
paths = dwg.export("my_part", formats=("svg", "dxf"))   # {"svg": ..., "dxf": ...}

export() with no formats= returns the old (svg, dxf) tuple; that shape is deprecated and removed in 0.5.0 (see docs/deprecations.md).

Declarative drawings — reference features, declare intent

Instead of relying on detection, reference the build123d objects you built and declare only the drawing intent — geometry supplies the sizes (⌀ read off the object), you supply tolerances, fits, datums, GD&T, and surface finish. The fluent Sheet skips detection and dimensions exactly what you declare (ADR 0011):

from build123d import Box, Cylinder, Pos
from draftwright import Sheet

plate = Box(120, 80, 20)
bore = Pos(0, 0, 0) * Cylinder(4, 20)

sheet = Sheet(plate - bore, title="Plate", number="DWG-002")
env = sheet.envelope()
sheet.datum("A", plate.faces().sort_by()[-1])             # datum A on the top face
hole = sheet.hole(bore).finish("1.6").note("M3x0.5 TAP")  # ⌀8 bore, Ra 1.6, tapped
sheet.control(hole).position(0.1, to="A", diameter=True)  # ⌀0.1 position wrt A

sheet.authored_dimensions()                               # THIS is the complete set
sheet.dimension(env, "width.length")                      # (ADR 0016: a build says where
sheet.dimension(env, "height.length")                     #  its dimensions come from, and
sheet.dimension(hole, "bore.diameter")                    #  omitting one means suppress it)

sheet.export("plate")                                     # writes plate.pdf

Every aspect the geometry can't carry is a declared verb: .tolerance(±) / .fit("H7") (ISO 286), .finish("Ra") (ISO 1302), sheet.datum(letter, ref) (ISO 5459), and sheet.control(ref) with all 14 ISO 1101 characteristics (.position/.flatness/.perpendicularity/.circular_runout/…), plus .thread("M3x0.5") (a tap/thread spec folded onto the hole callout), .knurl("0.8"), and sheet.note(text, ref) / .note(text) for any other free-text shop callout (DEBURR, chip-relief). A feature verb returns a chainable handle (sheet.hole(bore)) that the aspect and control(...) verbs decorate; targets are placed automatically — the view and strip are derived from the referenced feature or face, with view=/side= overrides.

From a part to an object-referenced script

draftwright part.step --script writes an editable Sheet script. When you built the part yourself (rather than importing a STEP), pass your live source as a module:attr (or file.py:attr) object spec, and the script binds part back to your real object:

draftwright mymodule:thumbwheel --script

The emitted values are detected off the geometry — honest, and a good starting point. Since you have the objects, swap each numbered line for a reference so the object stays the single source of truth (ADR 0011 — the size is read off the object, no numbers restated):

# generated (detected):
sheet.step(diameter=8, length=25, at=(0, 0, 12), axis="z")

# edited to reference your objects:
features = mymodule.thumbwheel_features()
part = features.part
sheet.step(features.journal)
sheet.hole(features.m3_bore).thread("M3x0.5").finish("1.6")   # tapped + Ra on the same hole

An object-sourced script carries an inline tip pointing at exactly this edit; a STEP-sourced script keeps the detected numbers (there's no object to reference).

Every generated script ends by naming the drawing it built, so you can critique or inspect it before (or instead of) exporting — one build, not two:

drawing = sheet.build()

for issue in drawing.lint():                  # ISO/coverage critique of THIS drawing
    print(issue.severity, issue.code, issue.message)

drawing.export("part", formats=("pdf",))

sheet.export(...) remains the one-liner for handwritten scripts that just want the file.

See docs/multi-feature-object-reference-workflow.md for a complete, runnable walkthrough of this pattern on a multi-feature turned part — including the backward-compatible refactor, external threads (no .thread() on a step — use .note(...)), and why an object-sourced script should point at the cheap geometry variant of your source.

What it produces

  • Three orthographic views (front, plan, side) sized and scaled automatically to the page
  • Dimensions on every principal envelope face, plus bore callouts (diameter, depth, counterbore, spotface) on all holes, and ø leader-callouts for the external stepped diameters of turned parts
  • Section A–A with ISO 128-44 solid filled cutting-plane arrows and ISO 128-50 45° hatching on the cut face, triggered automatically when blind or stepped holes would otherwise be hidden-line-only
  • Title block (ISO 7200) with part name, drawing number, scale, tolerance, and date
  • LintDrawing.lint() checks annotation coverage, page bounds, and ISO compliance and returns structured LintIssue objects

All output is real build123d geometry, so PDF, SVG, and DXF all come from the same source and dimensions are live on the DXF layer.

Requires Python ≥ 3.10 and build123d ≥ 0.9.0. Annotation primitives are provided by build123d-drafting-helpers, installed automatically as a dependency.

Going further

Scale and page control

from draftwright import choose_scale

# Auto-select the best ISO/ASME standard scale for an A3 sheet
scale, page_w, page_h, n_steps = choose_scale(80, 60, 20, page="A3")

# Override
make_drawing(part, out="drawing", scale=2.0, page="A2")

Edit, critique, and self-repair

Edit a Drawing in domain vocabulary — the engine places annotations automatically, so you say what to dimension, not where:

dwg = build_drawing(part)

# Inspect detected features and add a pinned dimension in domain terms:
env = next(f for f in dwg.model().features if f.kind == "envelope")
dwg.dimension(env, "length", role="width", side="below", pin=True)

crit = dwg.lint_summary()   # {"passed", "score", "by_code", "issues":[…suggestion]}
dwg.repair()                # auto-fix mechanically-fixable lint; never worsens

Each LintIssue carries a domain-meaningful code and, when computable, a ready-to-apply suggestion. See docs/adr/ for the design (deterministic generation, the lint→repair loop, and collect-then-solve placement).

Architecture

draftwright is structured as a part-drawing compiler (ADR 0015): recognised or declared features converge on a PartModel IR, then planner-fed and sanctioned model-routed render intents feed shared placement, projection, and export. Coverage lint independently compares recognised geometry with the placed drawing, so an upstream omission cannot hide from verification. It builds on two libraries:

draftwright
    └── build123d-drafting-helpers  — Dimension, Leader, HoleCallout, …
    └── build123d                   — CAD kernel

It owns feature recognition (recognition/) and linting (linting/); annotation primitives (Dimension, Leader, etc.) live in build123d-drafting-helpers and can be used independently. The compiler is largely converged in production — turned dims/lengths, centre marks, envelope, slots, holes (callouts/locations/grouping), the section A–A trigger, the prismatic step-ladder + rotational furniture, and PMI/GD&T are all on the IR. See docs/target-architecture.md and docs/layout-algorithm-primer.md for a short walkthrough, plus docs/adr/. The engine handles view layout (strip/zone model), scale selection, annotation placement, and section rendering.

The codebase has been entirely written by LLM (Claude Code) under detailed guidance from a person.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

draftwright-0.4.2.tar.gz (3.7 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

draftwright-0.4.2-py3-none-any.whl (774.1 kB view details)

Uploaded Python 3

File details

Details for the file draftwright-0.4.2.tar.gz.

File metadata

  • Download URL: draftwright-0.4.2.tar.gz
  • Upload date:
  • Size: 3.7 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for draftwright-0.4.2.tar.gz
Algorithm Hash digest
SHA256 d8830b241e7fdf56bdb78402cedeb70626918ca0bca7a99706f4b23300b3a723
MD5 81b24f78a3566a5f26ffc26c94800d7c
BLAKE2b-256 c73df0785ebe35c5e361e3e4cc500b6bb1ec8bb4afcf25c321298fb5c7600a7c

See more details on using hashes here.

Provenance

The following attestation bundles were made for draftwright-0.4.2.tar.gz:

Publisher: publish.yml on pzfreo/draftwright

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file draftwright-0.4.2-py3-none-any.whl.

File metadata

  • Download URL: draftwright-0.4.2-py3-none-any.whl
  • Upload date:
  • Size: 774.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for draftwright-0.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d4a1f2f41250961a355f5d0cfa2d4fca84be56fa2699b436266ea4c0cf9b4d2a
MD5 3af6a8f6988920be6ccba10d34750b4f
BLAKE2b-256 45c19d39737a0e6350d75358e14979ab9c0f0df2be6c8fe50113fb4f8ccf98a2

See more details on using hashes here.

Provenance

The following attestation bundles were made for draftwright-0.4.2-py3-none-any.whl:

Publisher: publish.yml on pzfreo/draftwright

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.4.9

2 files

0.4.8

2 files

0.4.7

2 files

0.4.6

2 files

0.4.5

2 files

0.4.4

2 files

0.4.3

2 files

This release

0.4.2 This release

2 files

0.4.1

2 files

0.4.0

2 files

0.3.9

2 files

0.3.8

2 files

0.3.7

2 files

0.3.6

2 files

0.3.5

2 files

0.3.4

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.2.13

2 files

0.2.12

2 files

0.2.11

2 files

0.2.10

2 files

0.2.9

2 files

0.2.8

2 files

0.2.7

2 files

0.2.6

2 files

0.2.5

2 files

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.13

2 files

0.1.12

2 files

0.1.11

2 files

0.1.10

2 files

0.1.9

2 files

0.1.8

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page