Skip to main content

yml2gantt

PyPI Python License: MIT

Write your project plan in a small YAML file, get a clean gantt chart (PDF, PNG, SVG, …) drawn with matplotlib.

gantt:
  start: 2026-01-05
  duration: 4w

tasks:
  - {name: Write, duration: 2w}
  - {name: Review, start: 2w, duration: 1w}
  - {name: Ship, start: 3w, milestone: true}
yml2gantt plan.yml -o plan.png

Gantt chart of the roadmap example

Features

  • Plain YAML input: easy to write by hand, to diff and to keep in git
  • Tasks with subtasks (a task without duration spans its subtasks)
  • Milestones, on their own row or placed on a task's bar
  • Interrupted tasks: several bar segments on one row
  • Custom text in the bars (label), e.g. who works on the task
  • Day, week, month, trimester or abstract (numbered) time scales
  • Positions as dates or as offsets (3, "2w"), lengths as durations or end dates
  • A "we are here" line at today's date or at any position
  • Export to any format matplotlib supports (PDF, PNG, SVG, …)
  • A Python API to build charts from code, or to draw into your own figures

Installation

Requires Python 3.11 or newer.

pip install yml2gantt

To use only the command line tool, pipx install yml2gantt or uv tool install yml2gantt install it in its own environment.

Command line

yml2gantt examples/project.yml

This draws the chart, saves it as examples/project.pdf and opens a window with it.

Option Effect
-o, --output F Output file (default: <file>.pdf). The extension sets the format: .pdf, .png, .svg, …
--no-show Do not open a window (for scripts, CI, servers).
--version Print the version and exit.
-h, --help Show the help.
yml2gantt examples/workpackages.yml -o plan.png --no-show
python -m yml2gantt examples/sprint.yml     # same thing

On an error in the file (unknown key, bad date, …) the tool prints a message such as yml2gantt: error: task: unknown keys ['durtion'] and exits with status 1.

The YAML format

A file has two sections: gantt (the time axis) and tasks (the rows).

gantt:
  start: 2026-01-05
  duration: 12w
  scale: week
  title: My project

tasks:
  - name: Kick-off
    start: 0
    duration: 3d
    label: Team A                 # text in the bar (default: the name)
    milestones: [Go]              # diamond at the end of the bar

  - name: Development             # no duration: spans its subtasks
    start: 2026-01-12
    subtasks:
      - {name: Backend, start: 0, duration: 4w}
      - {name: Frontend, start: 2w, duration: 5w,
         milestones: [{name: Demo, at: 50%}]}

  - {name: Release, start: 2026-03-20, milestone: true}

Keys are case-insensitive, and task is accepted instead of name. Unknown keys are reported as errors, so typos do not go unnoticed.

Dates, offsets and durations

You write Meaning
2026-01-05 A date (ISO format).
"2026-W02-1" An ISO week date: Monday of week 2 of 2026 (quote it).
3 or 2.5 A number of days (on the abstract scale: a number of units).
"3d", "2w" 3 days, 2 weeks. Decimals work: "1.5w".

A position (start, end, a milestone's at) is either a date or an offset. Offsets count from the chart start for tasks, from the parent start for subtasks, and from the task start for segments and milestones. There is no month unit, because months have different lengths: use dates for that.

On the abstract scale, all positions and lengths are plain numbers.

gantt section

Key Description
start Start date (2026-01-05, "2025-W38-7"), or a number for the abstract scale. Default 0.
end End of the chart (exclusive). Give end or duration.
duration Length of the chart: days (90) or "3d", "13w".
scale day, week (default), month, trimester or abstract.
title Chart title.
show_today Draw a red line at today's date (default true, dated scales only, ignored when now is set). Nothing is drawn if today is outside the chart.
now Draw the "we are here" line at this position instead of today: a date or an offset (e.g. 5.5 on an abstract scale). Works on every scale.
now_label Text above that line. Default: Now (with now) or Today. "" for no text.
label_format Period label, see below.
label_every Label only one period out of N (default 1).
step Period size for the abstract scale (default 1).

Time scales and labels

Scale One period is Default label Light grid lines
day a day Jan 05 none
week a week (Monday to Sunday) W02 days
month a month Jan 2026 weeks
trimester a quarter Q1 2026 months
abstract step units 1, 2, 3… none

label_format is a Python format string with these placeholders:

Placeholder Value
{date} First day of the period. Takes strftime codes: {date:%b} → Jan, {date:%d/%m} → 05/01.
{q} Quarter number (1 to 4).
{n} Period number, from 1. The only placeholder on the abstract scale.

Use \n (in a double-quoted string) for two-line labels, e.g. "{date:%a}\n{date:%d}" or "Q{q}\n{date:%Y}". A period cut to less than half by the chart start or end is left unlabelled.

Tasks and subtasks

Key Description
name Shown on the y axis, and inside the bar unless label is set. Required.
label Text written inside the bar instead of name (next to the diamond for a milestone row). "" leaves the bar empty.
start A date or an offset (see above). Default 0.
duration Length: a number, "3d" or "2w".
end End date or offset, instead of duration.
color Any matplotlib color (tab:red, "#3366cc", lightgrey, …).
subtasks List of subtasks (same keys, one level deep).
milestone true turns the entry into a milestone row (see below).
milestones Milestones drawn on the bar (see below).
segments Several pieces of bar on the same row (see below).
  • A task without duration/end spans its subtasks (or its segments).
  • Tasks take the next color of the matplotlib color cycle. Subtasks use a lighter version of their parent's color, and write their text in the parent's color.
  • Subtasks can be milestones too (milestone: true), and get the parent's color.

Milestones

There are two kinds of milestone.

On a task's bar, with milestones (tasks and subtasks):

milestones:
  - Done                    # at the end of the bar
  - {name: Review, at: 40%} # 40% of the bar length
  - {name: Demo, at: 4}     # 4 units after the task start
  - {name: Freeze, at: 2026-02-01, color: tab:red}   # a date

at defaults to 100%. Percentages use the drawn bar, so they also work on a task that spans its subtasks. Milestones are black unless color is given. The label goes to the right of the diamond, or to its left in the last 20% of the chart.

On their own row, with milestone: true: the entry takes only a start (no duration, end, subtasks, segments or milestones) and is drawn as a diamond.

- {name: Grand opening, start: 89, milestone: true, color: tab:red}

Segments

A task interrupted in the middle, or split into phases, can have several pieces of bar on its row:

- name: Field trials
  label: "Team B"           # written in each segment
  start: 2
  segments:
    - {start: 0, duration: 3}               # offsets from the task start
    - {start: 5, end: 8, label: "Retest"}   # own label
  milestones: [Report]      # end of the last segment
Key Description
start A date, or an offset from the task start. Default 0.
duration / end Length, or end date/offset (one of them is required).
color Default: color of the task.
label Text in the segment. Default: the task label (or its name). "" leaves it empty.

The task spans from its start to the end of its last segment: this is what its parent and its percentage milestones use. Touching segments are separated by a thin white line. A task with segments takes no duration/end and no subtasks.

Abstract scale

Use scale: abstract when the chart has no calendar dates, e.g. a project plan in quarters or a course in weeks. All positions and lengths are plain numbers.

gantt:
  start: 0
  duration: 16
  scale: abstract
  step: 1               # one column per unit
  label_format: "Q{n}"  # Q1, Q2, …
  title: Work plan
  now: 5.5              # "we are here": middle of Q6

Examples

Each example in examples/ is commented and shows a different part of the format.

File Shows
minimal.yml The smallest useful file
project.yml Week scale, ISO week date, subtasks, milestone rows, offsets in days, short bar labels
sprint.yml Day scale, two-line labels, end dates, people in the bars, now at a date, an interrupted task
roadmap.yml Month scale, dates only, colored milestones on bars, a paused task, no "today" line
house.yml Trimester scale with {q}, percentage milestones, a seasonal task as segments
course.yml Abstract scale in weeks, label_every, fractional positions
workpackages.yml Abstract scale in quarters, a research project in work packages, partners in the bars, many milestones and segments
from_python.py Building a chart from Python

sprint.yml: day scale

Sprint example

house.yml: trimester scale

House building example

course.yml: abstract scale

Course example

workpackages.yml: abstract scale, work packages

Work packages example

Python API

Building a chart

from yml2gantt import Gantt

g = Gantt(start="2026-01-01", end="2027-01-01", scale="trimester",
          title="Board game launch")

g.add_task("Market research", start=0, duration="6w", label="Alice")

design = g.add_task("Game design", start="2026-02-15")
design.add_subtask("Rules", duration="3w")
proto = design.add_subtask("Prototype", start="3w", duration=60)
proto.add_milestone("Demo", at="50%")

tests = g.add_task("Playtests", start="2026-06-01")
tests.add_segment(duration="3w")
tests.add_segment(start="8w", duration="2w", label="Retest")

g.add_task("Shipping", start="2026-12-15", milestone=True)

fig, ax = g.draw()
fig.savefig("plan.pdf")
Call Description
Gantt(start=0, end=None, duration=None, scale="week", title=None, show_today=True, label_format=None, label_every=1, step=1, now=None, now_label=None) Same keys as the gantt section.
g.add_task(name, start=0, duration=None, end=None, color=None, milestone=False, label=None) Adds a top-level task and returns it.
task.add_subtask(...) Same arguments as add_task; start is relative to the parent.
task.add_milestone(name, at="100%", color=None) Adds a milestone on the bar.
task.add_segment(start=0, duration=None, end=None, color=None, label=None) Adds a piece of bar on the task's row.
g.draw(ax=None, figsize=None, lighten=0.3) Draws and returns (fig, ax). lighten sets how light the subtask colors are (0 = white, 1 = parent color).

Wrong input raises ValueError (or TypeError for a value of the wrong type) with a message naming the task.

Loading YAML from Python

from yml2gantt import Gantt, load_yaml, from_dict

g = load_yaml("examples/project.yml")       # or Gantt.from_yaml(...)
g = from_dict({"gantt": {"start": 0, "duration": 10, "scale": "abstract"},
               "tasks": [{"name": "A", "duration": 4}]})   # or Gantt.from_dict(...)

# Edit it before drawing
g.add_task("Extra task", start=5, duration=2)

Customizing the figure

draw returns plain matplotlib objects, so anything matplotlib can do works afterwards:

import matplotlib.pyplot as plt
from yml2gantt import load_yaml

plt.style.use("seaborn-v0_8-whitegrid")     # styles and the color cycle apply

fig, ax = load_yaml("examples/roadmap.yml").draw(figsize=(14, 6))
ax.set_xlabel("2026 - 2027")
ax.tick_params(axis="y", labelsize=11)
fig.savefig("roadmap.svg", bbox_inches="tight")

To place a gantt inside a larger figure, pass your own axes:

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(11, 10))
load_yaml("examples/sprint.yml").draw(ax=ax1)
load_yaml("examples/roadmap.yml").draw(ax=ax2)

Limitations

  • Subtasks go one level deep (a subtask cannot have subtasks).
  • Text in a bar is not shortened: on a short bar, use a short label or label: "".
  • Milestone labels can cover the text of their bar: move the milestone (at) or shorten the bar label.
  • Dated scales work with whole days (no hours).

Development

git clone https://github.com/ldevillez/yml2gantt
cd yml2gantt
poetry install          # or: pip install -e .
poetry run yml2gantt examples/minimal.yml

Project layout:

yml2gantt/
  cli.py          command line (yml2gantt, python -m yml2gantt)
  model.py        Gantt, Task, segment and milestone objects
  scale.py        time scales: dates → x positions, grid and labels
  io.py           loading from a dict or a .yml file
  plot.py         drawing with matplotlib
  colors.py       color helpers
examples/         example charts
docs/images/      pictures used in this README

Releasing a new version

Releases are made by GitHub Actions (release.yml) when a version tag is pushed. It builds the package, publishes it on PyPI and creates the GitHub release, with the changelog entry as notes.

poetry version minor        # or patch / major: updates pyproject.toml
# move the "Unreleased" notes of CHANGELOG.md under the new version
git commit -am "Release v$(poetry version -s)"
git tag "v$(poetry version -s)"
git push origin main --tags

The release stops if the tag does not match the version in pyproject.toml.

License

MIT

Metadata

Release files for yml2gantt 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for yml2gantt 0.1.0
File Size Uploaded Provenance Actions
yml2gantt-0.1.0.tar.gz 21.7 kB With provenance Details for yml2gantt-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for yml2gantt 0.1.0
File Interpreter ABI Platform Actions
yml2gantt-0.1.0-py3-none-any.whl Python 3 none any Details for yml2gantt-0.1.0-py3-none-any.whl

Total release size: 41.3 kB

Release files / yml2gantt-0.1.0.tar.gz

Download URL yml2gantt-0.1.0.tar.gz
Size 21.7 kB
Tags Source
SHA-256 checksum
How to use checksums
44db5ef51ae69da6df93ff86eca69c6e432f22fa0831345241956e28f031fc03
BLAKE2b-256 checksum
How to use checksums
1899a96c107deaae01052fdae2cac414a6e603be29dc59d54dc097e91959f2e2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

Release files / yml2gantt-0.1.0-py3-none-any.whl

Download URL yml2gantt-0.1.0-py3-none-any.whl
Size 19.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
738b4edc9d648a66bb81938b7f2d326d737fbce2dc49e08fbf329c5191135fd9
BLAKE2b-256 checksum
How to use checksums
eb4a3c3915e6b03c47c2340a6fee49f5ffd98a5636ff2cc09ccd0c30fd9405ba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

Security for yml2gantt 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for yml2gantt-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page