G-code Preprocessing
An embedded Python pre-processor with 150 API bindings. Not post-processing on exported text. Structured access to every move, layer, and setting, inside the slicing pipeline. Bundled Python runtime, no installation required.
Not Post-Processing. Preprocessing.
You know the usual "post-process your G-code files at export" workflow. The slicer is done with the data. You get raw text and a regex library. Good luck.
preFlight does something much deeper. It embeds a Python interpreter directly into the slicing pipeline via pybind11. After G-code generation, the full object structure is exposed to your scripts: layers, moves, feedrates, fan speeds, coordinates, extrusion roles, volumetric flow rates, and more. This isn't parsing text with regex after the fact. It's iterating over objects, within the slicing pipeline.
Preprocessing runs after all G-code is generated and parsed into structured data, but before the preview renders. What your script reads and modifies is the final export G-code (the only thing added after is the M73 progress/time-remaining codes). Changes appear in both the preview and the exported file.
A Complete Example
Want per-feature-type Pressure Advance? This is the complete script. Drop it into your scripts folder, add it to your profile's Preprocessing tab, slice, done. Supports Marlin, Klipper, and RepRapFirmware automatically.
import preFlight
from preFlight import MoveType, ExtrusionRole
PA_VALUES = {
ExtrusionRole.ExternalPerimeter: 0.040,
ExtrusionRole.Perimeter: 0.050,
ExtrusionRole.OverhangPerimeter: 0.020,
ExtrusionRole.TopSolidInfill: 0.040,
ExtrusionRole.SolidInfill: 0.060,
ExtrusionRole.InternalInfill: 0.080,
ExtrusionRole.BridgeInfill: 0.000,
ExtrusionRole.GapFill: 0.030,
ExtrusionRole.SupportMaterial: 0.060,
ExtrusionRole.SupportMaterialInterface: 0.040,
ExtrusionRole.Skirt: 0.050,
ExtrusionRole.Ironing: 0.020,
}
DEFAULT_PA = 0.050
def detect_firmware(gcode: preFlight.GCode):
flavor = gcode.settings.gcode_flavor
if "reprap" in flavor.lower():
return "reprap"
if "klipper" in flavor.lower():
return "klipper"
return "marlin"
def format_command(firmware, pa_value, extruder=0):
if firmware == "klipper":
return f"SET_PRESSURE_ADVANCE ADVANCE={pa_value:.4f}"
if firmware == "reprap":
return f"M572 D{extruder} S={pa_value:.4f}"
return f"M900 K{pa_value:.4f}"
def process(gcode: preFlight.GCode):
firmware = detect_firmware(gcode)
current_pa = None
for layer in gcode.layers:
if layer.id == 0:
continue
for move in layer.moves:
if move.type != MoveType.Extrude:
continue
target_pa = PA_VALUES.get(move.role, DEFAULT_PA)
if target_pa != current_pa:
cmd = format_command(firmware, target_pa, move.extruder_id)
gcode.insert(move.gcode_line_id, cmd, "before")
current_pa = target_pa 150 API Bindings
The entire 400+ setting object model (print, filament, printer values) is exposed as read-only properties with IDE autocomplete baked in. Every move exposes its type, extrusion role, position, feedrate, volumetric flow rate, extrusion width, layer height, and extruder ID. Four properties are writable and propagate directly to G-code: feedrate, filament displacement, fan speed, and temperature.
The motion planner data goes further. Each move carries its distance, junction angle, effective acceleration, and maximum entry speed. These are the same values the firmware's look-ahead planner uses. Scripts can compute whether a move will actually reach its commanded speed, or if it will spend the entire segment accelerating.
Fill region geometry is exposed per-move: the area of the fill region in mm² and the infill pattern name. This lets scripts detect small features and adjust flow or speed to prevent heat buildup in tight areas.
What Else You Can Build
preFlight ships with 19 sample scripts covering real use cases. The per-feature PA script above is just the starting point. The API enables things that aren't possible with text-based post-processing:
Adaptive Pressure Advance
Go beyond per-feature PA. Compute PA per-move from actual post-acceleration feedrate, volumetric flow rate, and junction angle. Corner detection reduces PA at sharp direction changes to prevent decompression gouging. Supports Marlin M900, Klipper SET_PRESSURE_ADVANCE, and RRF M572.
def compute_pa(move) -> float:
base = ROLE_PA_BASE.get(move.role, PA_BASE)
speed = move.actual_feedrate
pa = base + SPEED_FACTOR * speed
if move.actual_volumetric_rate > VOLUMETRIC_THRESHOLD:
pa += VOL_FACTOR * (move.actual_volumetric_rate - VOLUMETRIC_THRESHOLD)
pa -= corner_reduction(move.junction_angle)
return max(PA_MIN, min(PA_MAX, pa)) Small Area Flow Compensation
Detect small fill regions using move.region_area and reduce flow proportionally. Smaller areas get more reduction to prevent bulging from heat accumulation. No geometry parsing required.
for move in gcode.moves:
if move.region_area > 0 and move.region_area < SMALL_AREA_THRESHOLD:
t = (SMALL_AREA_THRESHOLD - move.region_area) / range_size
move.delta_e *= lerp(0.95, 0.85, t)
move.feedrate *= lerp(1.0, 0.8, t) Motion Optimizer
Use distance, junction angle, acceleration, and max entry speed to identify moves that can never reach their commanded feedrate. Clamp to achievable peak speed so flow calculations become accurate and the preview shows realistic values.
v_entry = move.max_entry_speed
v_peak = sqrt(v_entry**2 + move.acceleration * move.distance)
if move.feedrate > v_peak:
move.feedrate = v_peak Print Analyzer
Read-only analysis with zero G-code overhead. Break down time, filament, and move counts by feature type. Identify excessive travel, high retraction counts, or disproportionately slow layers.
for role, usage in gcode.filament_by_role.items():
print(f" {role.name}: {usage.meters:.1f}m, {usage.grams:.1f}g")
for layer_id, z, t in slowest_layers[:10]:
print(f" Layer {layer_id} Z={z:.2f}mm {format_time(t)}") Additional Samples
The remaining sample scripts cover flow limiting per feature type, overhang speed/fan/temperature tuning, temperature tower generation from any model, height-based fan curves, first layer speed ramping, edge slowdown near bed boundaries, per-feature extrusion multipliers, per-feature M204 acceleration, context-aware retraction tuning, color-distance-aware purge reduction for multi-material, and vectorized numpy analysis.
Full Python, Not a Sandbox
Scripts have full Python access. File I/O, network, subprocess, third-party libraries. Some examples of what that enables:
- Reading CSV files for calibration data
- Pulling settings from a database
- Using numpy for complex toolpath math
- Writing analysis reports alongside the G-code
Any packages installed via pip are available to preprocessing scripts. The preFlight module provides the G-code API, but you aren't limited to it.
Bundled Python Runtime
No venv required. The Python interpreter is embedded directly into preFlight via pybind11 using a bundled Python 3.14 runtime. No separate Python installation needed. No system directories, PATH, or registry modified. This solves the locked-down Linux distro issue as well.
For scripts that need third-party packages, a Python Console button in Preferences launches a shell with PATH configured for the bundled runtime so you can pip install directly.
Per-Profile Script Management
Preprocessing is built into all three settings panels: Print Settings, Filament Settings, and Printer Settings. Each one has its own Preprocessing tab with an enable toggle and an ordered script list. This matters because different presets need different scripts.
A pressure advance script belongs in your filament profile, because PA values change with material. A flow limiter might belong in your printer profile, because volumetric limits are hotend-specific. A small area flow compensation script fits in print settings, because the thresholds depend on your quality/speed tradeoffs. When you switch presets, the right scripts come with them.
Scripts from all three profiles run together at slice time. The execution order between categories is configurable in a dedicated Preprocessor tab in Preferences. If your filament scripts need to run before your print scripts, move them up. The same Preferences tab provides a Python Console button that launches a shell with PATH configured for the bundled runtime, so you can pip install numpy, requests, or any other package your scripts need.
Script validation rejects invalid Python identifiers on add, blocks duplicate paths within the same profile, and deduplicates across profiles at slice time. Errors surface as breadcrumb notifications without aborting the slice.
IDE Support
A preFlight.py type stub ships with every release. Drop it next to your script and any Python IDE with type hint support (VS Code + Pylance, PyCharm, etc.) gives you full autocomplete across all 150 API bindings, all 400+ slicer settings, and every move property with types.
Security Model
Preprocessing scripts are Python programs with full system access, the same trust model as post-processing scripts in other slicers. preFlight requires explicit consent before any scripts can run. The first time preprocessing is enabled, a security warning explains the risk and asks for confirmation. Consent can be revoked at any time via Preferences. 3MF project files can store script references (file paths only, not embedded code). When a project references preprocessing scripts, preFlight warns the user and asks for explicit confirmation before adding them to the configuration.
The API at a Glance
| Object | Key Properties |
|---|---|
gcode | layers, moves, settings (400+), bed_shape, extruder config (count, colors, diameters, densities), time estimates, cost data, active preset names, filament usage by role/extruder/color, custom events, performance metrics, conflict detection, annotation (r/w), line-level read/rewrite/insert, find_line, find_moves, remove_moves |
layer | id, z, height, moves, time, prepend/append G-code, filter by type/role, extrusion_length, travel_distance |
move | type, role, x/y/z, extruder_id, gcode_line_id, feedrate (r/w), delta_e (r/w), fan_speed (r/w), temperature (r/w), width (r/w preview), height (r/w preview), annotation (r/w), actual_feedrate, mm3_per_mm, volumetric_rate, actual_volumetric_rate, time, distance, junction_angle, acceleration, max_entry_speed, region_area, fill_pattern |