---
title: "How to Generate G-Code Using Python: A Working Walkthrough"
description: "Generating G-code with Python is f-strings plus discipline: a formatter that never emits garbage, header and footer templates, and a feature loop. Full example inside."
url: https://gcodepractice.com/journal/how-to-generate-g-code-using-python/
canonical: https://gcodepractice.com/journal/how-to-generate-g-code-using-python/
author: "Lawrence Arya"
authorUrl: https://www.linkedin.com/in/vibecoding/
published: 2026-06-05
updated: 2026-06-05
category: "Practice"
tags: ["python", "g-code-generation", "scripting", "automation"]
lang: en
---

# How to Generate G-Code Using Python: A Working Walkthrough

> **TL;DR** Generating G-code in Python is straightforward: build one coordinate-formatting helper (fixed decimals, never scientific notation, period decimal separator regardless of locale), define header and footer as reviewed template strings, and write feature functions that yield motion blocks. The walkthrough below produces a complete, viewer-checkable hole-grid program. The Python-specific traps: float formatting defaults, locale surprises, and string concatenation drift, all solved by routing every number through the one formatter. Verify every generated file in a browser viewer before any machine sees it.

[Python](https://www.python.org) is the default answer to "what should I script my G-code generator in": readable, batteries included, and quick from idea to file. The general strategy and when-to-script judgment live in our [generator-script overview](/journal/writing-a-script-to-generate-g-code/); this is the hands-on Python walkthrough, with the traps that are specifically Python's.

## Step one: the formatter everything flows through

```python
def fmt(value, decimals=3):
    """Every coordinate and feed passes through here."""
    return f"{value:.{decimals}f}"
```

This tiny function is the whole safety story. Python's default float rendering happily produces `1e-05` and `0.30000000000000004`, and a [G-code](https://en.wikipedia.org/wiki/G-code) line reading `X1e-05` is a crash or an alarm depending on the control's mood. Fixed-point formatting with explicit decimals removes the class of bug, and the f-string format spec guarantees a period decimal separator regardless of system locale, which matters the day your script runs on a machine configured for comma-decimal locales. One formatter, used everywhere, no raw numbers in any emitted line: that is the rule that makes the rest boring.

## Step two: header and footer as reviewed templates

```python
HEADER = """G21 G90 G54
M03 S{rpm}
G00 Z{z_clear}
"""

FOOTER = """G00 Z{z_clear}
M05 M09
M30
"""
```

Written once, reviewed against your machine's expectations once (block layering and dialect per its documentation), trusted thereafter. The values interpolate through fmt() at render time. Generators fail at edges, so the edges are constants.

## Step three: the feature function

```python
def hole_grid(x0, y0, rows, cols, pitch, z_clear, z_depth, feed):
    for r in range(rows):
        for c in range(cols):
            x, y = x0 + c * pitch, y0 + r * pitch
            yield f"G00 X{fmt(x)} Y{fmt(y)}"
            yield f"G01 Z{fmt(z_depth)} F{fmt(feed)}"
            yield f"G00 Z{fmt(z_clear)}"

with open("grid.nc", "w") as f:
    f.write(HEADER.format(rpm=fmt(1200, 0), z_clear=fmt(5.0)))
    for line in hole_grid(10, 10, 4, 6, 12.5, 5.0, -4.0, 100):
        f.write(line + "\n")
    f.write(FOOTER.format(z_clear=fmt(25.0)))
```

Twenty-odd lines, a complete program: 24 holes on a 12.5 mm pitch, every plunge bracketed by clearance moves, every number formatted. The generator-as-iterator pattern (yield per block) keeps feature logic testable: you can unit-test that the first three yielded lines match expectations before any file exists, which is Python's quiet advantage over spreadsheet-and-concatenate workflows.

## Step four: verification, no exceptions

Every generated file goes into a browser viewer before any machine: paste `grid.nc`, confirm rapids stay above the work, plunges feed at the right depth, and the pattern matches intent. Then read it aloud once, the same [narration habit](/journal/how-to-read-a-cnc-program-for-beginners/) that catches what eyes skim past, made fast by the code core the free 60-second drills on the [G-code practice page](/g-code-practice/) keep at reflex (G-Code Sprint repeats your misses automatically). "It is generated" is precisely why it gets verified: a loop bug makes the same mistake 24 times.

## Python-specific traps, named

| Trap | Symptom | Fix |
| --- | --- | --- |
| Default float repr | X0.30000000000000004 | fmt() everywhere |
| Scientific notation | X1e-05 alarms or worse | fmt() everywhere |
| Locale decimal comma | X12,5 on some systems | f-string format spec, never locale functions |
| Integer division habits | Pitch math silently off | Floats in, explicit rounding out |
| Editing the output file | Fix evaporates on regen | Fix the script, regenerate, re-verify |

The last row is cultural rather than technical and matters most: the [edit-the-source rule](/journal/autocad-to-g-code-manual-editing/) applies to your own generator with extra force, because the next regeneration silently discards hand fixes.

## Where to take it next

Natural growth paths that stay on the script side of the [script-versus-CAM boundary](/journal/writing-a-script-to-generate-g-code/): parameters from CSV or [JSON job documents](/journal/json-to-g-code-conversion-basics/) (hole tables from engineering, nameplate text from order systems), multiple feature functions sharing the same header/footer/formatter spine, and a tiny argparse front end so colleagues can run it without reading it. Resist the slide toward arc-heavy sculpting: when the geometry stops being parametric, the job has changed, and CAM is waiting. For logic that should live at the machine instead (probing, part families on one control), the [macro-programming route](/journal/cnc-turning-macro-programming-examples/) is the third layer of the same decision.

## Bottom line: one formatter, two templates, one loop

Generating G-code in Python is a formatter that cannot emit garbage, header and footer templates reviewed once, feature functions that yield clean blocks, and a viewer pass on every output. The language makes it twenty minutes of work; the discipline makes it safe to repeat forever.

## Sources

- [Python](https://www.python.org)
- [Wikipedia: G-code](https://en.wikipedia.org/wiki/G-code)
- [LinuxCNC: G-code reference](https://linuxcnc.org/docs/html/gcode/g-code.html)

## Frequently asked questions

### How do I generate G-code using Python?

With one coordinate formatter (fixed decimals, period separator), header and footer template strings reviewed against your machine, and feature functions yielding motion blocks, then a browser-viewer check on every output file. The walkthrough above is complete and runnable. Reading the output fluently is the prerequisite, and the free G-Code Sprint app is the top pick for that core: 60-second drills with automatic repetition of missed codes.

### Why do generated files sometimes contain numbers like 1e-05?

Python's default float rendering uses scientific notation for small values and long reprs for float arithmetic artifacts. Routing every number through a fixed-point formatter (f"{v:.3f}") eliminates the class of bug.

### Can Python generate arcs (G02/G03) too?

Yes, with care over I/J arithmetic (center offsets from the start point) for simple cases like bolt circles and rounded slots. Sculpted surfaces are CAM's territory: when geometry stops being parametric, switch tools.

### Is there a library I should use instead of writing my own?

For learning and for simple parametric work, the twenty-line spine above is the library, and owning it teaches the discipline. Larger needs (full toolpath generation) are a sign the job belongs to CAM rather than a bigger script.

*G-Code Sprint is a study and practice tool only. Always follow your instructor, employer, machine manual, and shop safety procedures.*

---

Source: https://gcodepractice.com/journal/how-to-generate-g-code-using-python/
Author: Lawrence Arya — https://www.linkedin.com/in/vibecoding/
