# How to Regenerate URDF Files from Python Source in text-to-cad

> Easily regenerate URDF files from Python source using text-to-cad. Learn to implement gen_urdf() and run the CLI module for automatic generation, annotation, and validation.

- Repository: [earthtojake/text-to-cad](https://github.com/earthtojake/text-to-cad)
- Tags: how-to-guide
- Published: 2026-07-28

---

**To regenerate URDF files from Python source in the earthtojake/text-to-cad repository, implement a zero-argument `gen_urdf()` function that returns an XML Element, string, or dict, then run the CLI module at [`skills/urdf/scripts/urdf/cli.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/cli.py) to automatically generate, annotate with CAD metadata, and validate the output.**

The earthtojake/text-to-cad repository provides a streamlined pipeline to regenerate URDF files from Python source code, enabling programmatic robot description generation with built-in validation. This workflow allows you to define robot kinematics and geometry using pure Python, then convert those definitions into validated, metadata-enriched URDF files ready for ROS and MoveIt.

## The URDF Regeneration Pipeline

The regeneration system is implemented in the `skills/urdf` package and follows a strict sequence of module loading, execution, normalization, and validation before producing the final file.

### CLI Entry Point and Target Resolution

The process begins in [`skills/urdf/scripts/urdf/cli.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/cli.py) at the `generate_urdf_targets` function (line 38). This CLI resolves the input Python script path and determines the output `.urdf` file location. It uses `_load_generator_module` (line 58) to import your script into an isolated namespace via `importlib.util`, ensuring no side effects from existing code contaminate the generation process.

### The `gen_urdf` Contract

Your Python script must expose a **zero-argument** function named `gen_urdf`. According to the source implementation, this function may return one of three formats:

- An `xml.etree.ElementTree.Element` representing the root `<robot>` node
- A raw XML string containing the URDF content  
- A dictionary with a single key `{"xml": ...}` containing the XML string

### Payload Normalization and Metadata Injection

The helper `_normalize_urdf_payload` (line 86) standardizes these varied return types into a consistent format. The CLI then calls `_write_urdf_payload` to write the XML to the destination path using `output_path.write_text`. During this write operation, the **CAD-metadata wrapper** from [`packages/cadpy_metadata/src/cadpy_metadata/generator.py`](https://github.com/earthtojake/text-to-cad/blob/main/packages/cadpy_metadata/src/cadpy_metadata/generator.py)—specifically `xml_with_text_to_cad_metadata`—injects provenance data including the source file hash and generation timestamp.

### Strict Validation with `read_urdf_source`

Immediately after writing, the pipeline validates the URDF using `read_urdf_source` from [`skills/urdf/scripts/urdf/source.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/source.py) (line 70). This function enforces a strict schema matching the ROS URDF specification: it verifies unique link and joint names, confirms a single-root tree structure, checks joint limits, validates positive inertia values, and ensures mesh references exist. It utilizes `classify_mesh_uri` (line 28) for URI handling and raises `UrdfSourceError` on any validation failure.

## Creating a Python URDF Generator

To regenerate URDF files from Python source, create a script that implements the required `gen_urdf` interface.

### Minimal ElementTree Example

The most common approach builds the URDF using Python's `xml.etree.ElementTree`:

```python

# my_robot.py

import xml.etree.ElementTree as ET

def gen_urdf():
    root = ET.Element("robot", {"name": "my_robot"})
    link = ET.SubElement(root, "link", {"name": "base_link"})
    visual = ET.SubElement(link, "visual")
    geometry = ET.SubElement(visual, "geometry")
    mesh = ET.SubElement(geometry, "mesh", {"filename": "meshes/base.stl"})
    joint = ET.SubElement(root, "joint", {"name": "joint1", "type": "fixed"})
    ET.SubElement(joint, "parent", {"link": "base_link"})
    ET.SubElement(joint, "child", {"link": "link1"})
    return root

```

### Returning Raw XML Strings

Alternatively, return a raw XML string directly:

```python
def gen_urdf():
    return """<?xml version="1.0"?>
<robot name="my_robot">
  <link name="base_link"/>
</robot>"""

```

### Using the Envelope Dict Format

For future-proofing or additional metadata wrapping, return a dictionary:

```python
def gen_urdf():
    return {"xml": "<robot name='my_robot'><link name='base_link'/></robot>"}

```

## Running the Regeneration CLI

Execute the regeneration using the Python module execution syntax.

### Basic Usage

Run the CLI against your generator script to create the URDF beside the source file:

```bash
python -m skills.urdf.scripts.urdf.cli my_robot.py

```

Output:

```

Wrote URDF: my_robot.urdf

```

### Specifying Custom Output Paths

Use the `-o` flag to define a specific output location:

```bash
python -m skills.urdf.scripts.urdf.cli my_robot.py -o /tmp/my_robot_generated.urdf

```

## Understanding the Validation Layer

The `read_urdf_source` function in [`skills/urdf/scripts/urdf/source.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/source.py) performs comprehensive validation immediately after generation. It parses the XML and checks link/joint consistency, confirms mesh references exist via `resolve_mesh_uri` (line 59), and validates the `package_map` for `package://` URIs. This ensures the generated URDF is immediately importable by ROS components without manual inspection.

## Summary

- The regeneration pipeline lives in [`skills/urdf/scripts/urdf/cli.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/cli.py) and is driven by the `generate_urdf_targets` function.
- Your Python script must expose a zero-argument `gen_urdf()` function returning an Element, string, or dict.
- The CLI normalizes output via `_normalize_urdf_payload` and injects metadata using `xml_with_text_to_cad_metadata` from the `cadpy_metadata` package.
- Automatic validation occurs through `read_urdf_source` in [`skills/urdf/scripts/urdf/source.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/source.py), enforcing ROS URDF specification compliance including unique names and valid mesh references.
- Mesh URI resolution handles both relative paths and `package://` references through `classify_mesh_uri` and `resolve_mesh_uri`.

## Frequently Asked Questions

### What happens if my `gen_urdf` function accepts arguments?

The CLI strictly expects a zero-argument function signature. If your generator requires parameters, you must wrap it in a zero-argument function or use closure variables and module-level constants to pass configuration data before the CLI imports the module.

### How does the pipeline handle `package://` mesh URIs?

The validation layer uses `resolve_mesh_uri` with a `package_map` argument to resolve `package://` references into absolute filesystem paths. You can provide this mapping via environment variables or the `CADPY_METADATA` tracking system during CLI execution, which the `read_urdf_source` function picks up automatically.

### Why does the CLI re-read the file immediately after generation?

The `read_urdf_source` validation step guarantees that the produced URDF conforms to the strict ROS specification, checking for unique link/joint names, valid joint limits, positive inertia values, and existing mesh references before marking the generation as successful. This prevents generating broken URDFs that would fail when loaded into ROS or MoveIt.

### Where is the provenance metadata stored in the output URDF?

The `xml_with_text_to_cad_metadata` function from [`packages/cadpy_metadata/src/cadpy_metadata/generator.py`](https://github.com/earthtojake/text-to-cad/blob/main/packages/cadpy_metadata/src/cadpy_metadata/generator.py) injects generation metadata—including source file hashes and timestamps—directly into the XML structure during the `_write_urdf_payload` operation, ensuring traceability from generated artifact back to source code.