Contributing
The rules for a contribution are kept in one place, CONTRIBUTING.md at the root of the repository: how to set up, the four commands a change must pass, the form a piece of work is proposed in, and the sign-off every commit carries. This page walks one first contribution through them: a new part for the gallery, from an empty file to a signed-off commit. Every command below is run from the root of a checkout, with the environment SETUP.md makes.
1. Start a part
dxcad gallery new writes a starter model.py into docs/examples and adds
its row to the examples' index, in name order. The name is a Python
identifier no example has yet:
dxcad gallery new bracket_two
The starter is three numbers and a box made of them, with a docstring to
fill in: its first line is the part's title in the gallery, the paragraph
after it the line shown below the title, and its Tags: line the group it
is shown in, from solid, sketch, sheet, mates, assembly, drawing and text.
Make the part yours: change the numbers and the model, and write the
docstring. A model imports nothing; every name it needs is in scope, as
the model language lists them.
2. Check it as the suite checks ours
dxcad gallery check runs the part the way the test suite runs the shipped
examples, one line each: the SPDX first line, the docstring's title and
tags, no import, that it builds at least one part within 60 seconds, that
every part is valid, and that it builds again with its first number
changed. It exits 0 only when every line says ok:
dxcad gallery check docs/examples/bracket_two.py
The starter passes as it is written, so a line that fails is something the edit changed.
3. Write the item
Work is queued in BACKLOG.md, one item per change. A part for the gallery is one item, for example:
- [ ] 620. A second bracket for the gallery (S)
A bracket with a gusset, to show a part the sketch tools make.
Accept: dxcad gallery check passes on docs/examples/bracket_two.py and
its picture is in docs/gallery.
Ideas come as an ordinary issue, in your own words, and the owner writes the backlog from them; once your item is queued, check the file's format:
python -m dxcad.backlog check BACKLOG.md
4. The four commands
A change is ready when all four pass. The picture comes last:
python -m dxcad.gallery draws every example into docs/gallery, and
python -m dxcad.docsgen gallery writes the gallery page again.
python -m compileall -q src tests
python -m pytest
python -m ruff check src tests
python -m dxcad.smoke
The performance baseline
python -m dxcad.bench times, for every example, building its model, 30
offscreen frames of it orbiting and making its drawing, and writes
bench.json. CI's bench job runs it on Linux, keeps bench.json as an artifact
of the run, and holds it to bench-baseline.json at the root of the
repository: a measure more than twice its baseline fails the job, naming the
example and the measure. A baseline taken on another platform is not
compared, and the job says so.
A change that is slower on purpose, or a new example, refreshes the
baseline in the same pull request. Download bench.json from the bench job of
a run on main, save it over bench-baseline.json, and say why in the commit.
On your own machine the same is
python -m dxcad.bench --output bench-baseline.json
but a baseline only holds runs on the platform it was taken on, so the one to commit is CI's. The first baseline was taken on Windows, so CI's Linux runs are not compared until it is refreshed from one of them.
A large assembly has a baseline of its own, bench-assembly-baseline.json:
opening and rebuilding a generated 200-part assembly, each the quickest of
five. A slow test, run in the serial pass, fails when either is more than 25
percent slower than it on the platform it was taken on.
python -m dxcad.bench --assembly --output bench-assembly-baseline.json
takes it again, on a quiet machine, after a change that is slower on purpose.
A drawing's annotations have limits and no baseline. python -m dxcad.bench
--drawing opens the drawing of the engine example, 192 parts in four views,
in a window of its own, and times an annotation added, dragged and deleted,
each under 0.1 s with its repaint, and fifty put on as one change, under 1 s.
A slow test in the serial pass runs it and fails on any of them.
A drawing's first changes are held to its fourth. python -m dxcad.bench
--first-changes opens the drawings of the engine and gearbox examples on an
A1 sheet with no parts list and makes four changes to each with a tool of
three clicks, by the sheet's own mouse, the tool's hover shown between the
clicks: the first, the second and the fourth are each under 1 s in total, and
what opening the drawing spends on making ready for them is under 2 s. A slow
test in the serial pass runs it on each example and fails on any of them. The
tool is the benchmark's own and does nothing of its own: what an add-on's
tool works out at its first hover is the add-on's time, not measured here.
Each drawing is then opened afresh and measured with a second tool, made as
an add-on's linear dimension is (item 864): its hover snaps to the edges the
view draws, read with their parts from document.view_lines(view,
parts=True), its run reads the drawing's annotations, its dimension is a
real one on the dimension layer, and its plugin's drawing_ready call is
made as the drawing opens; its rows read "engine_inline4 dimension" and
"gearbox dimension", held to the same limits. Each drawing is then measured
a third time (item 867), the dimension made as Drafting Pro's sweep makes it,
by a hand on the window: the drawing opened in the window's own tab, the
window 1500 by 1000, the tool chosen by its menu entry, QTest's pointer moved
to each point and the window let handle its events, the hover waited for,
and QTest's click on the ends of a line the front view draws; its rows read
"engine_inline4 by hand" and "gearbox by hand", held to the same limits. An
add-on whose own first change is slow works out what its tools need in
registry.drawing_ready: a hover that projects the parts near the pointer
itself, as a hidden-line run per part, spends seconds the first time on
each part and view, on the hover's thread but holding the interpreter, so
the window waits with it.
python -m dxcad.sweep_drawings goes through every drawing command of the
window on the engine and gearbox examples as a user does, a view moved, the
scale, the parts list's columns, Delete, Undo, Redo, Add View and Add Part
Sheets, and writes build/sweep/drawings.md: a row a command with its time,
whether the result is where the clicks put it and inside the sheet, what
Undo calls it, what F1 showed of its steps, whether "Please wait" showed,
and a picture. It fails on a command over a second or out of place. What is
still over a second is named in tests/sweeps/drawings-known.txt, which
--known passes over and which can only shrink. After the models it draws
every sketch tool's figure by its own clicks, a line, a polyline open and
closed, a rectangle, a circle, both arcs, a polygon, a slot, an ellipse and a
spline ended each way, closed by Close Sketch and opened by Break, in a sketch started from the
Sketch menu and in one started by window.start_sketch, and fails on a
figure that is not in the model's own line or that a run of the model does
not make again.
The compiled build reads no annotation as a type: setup.py compiles inside
dxcad.compiled.annotations_as_hints(), since Cython 3.3.0 otherwise holds
what a loop over names: list[str] gives to str exactly, whatever
annotation_typing says, and a compiled DX-CAD then refuses values its
source takes. The build stops if its compiler still writes such a check.
Python the suite runs is not always Python Cython takes: min([low, *reached])
passed every test and stopped the compiled build with "starred expression is
not allowed here". tests/test_cython_takes.py runs Cython's translation step
alone, to C with the build's directives and no C compiler
(dxcad.compiled.translation_errors), on every compiled module that differs
from main, in seconds, and on the whole core in a test marked slow. A
failure names the file, the line and Cython's words; write the line another
way, as min([low] + reached).
5. Commit, signed off
Every commit carries a Developer Certificate of Origin sign-off, which
git commit -s adds from your git name and e-mail; CONTRIBUTING.md says
what it certifies. Then open a pull request that names the item's number.