ChordPro parser, renderer, and Flask extension
Project description
chordpro
A library that renders ChordPro-formatted song content as HTML, plain text, PDF, or Quill Delta, with support for multiple chord notations.
It was originally designed as a Flask extension, but can be installed without Flask to use the command-line conversion tool or the Python API directly.
Installation
# Command-line tool and Python API only:
pip install chordpro
# Flask extension (also installs Flask):
pip install chordpro[flask]
# PDF rendering (also installs reportlab):
pip install chordpro[pdf]
If you use flask-babel for i18n, install the optional extra so section labels are translatable (also installs Flask):
pip install chordpro[babel]
Flask extension
Initialization
Direct:
from flask import Flask
from chordpro import ChordPro
app = Flask(__name__)
ChordPro(app)
Application factory:
from chordpro import ChordPro
chordpro = ChordPro()
def create_app():
app = Flask(__name__)
chordpro.init_app(app)
return app
Template filters
The extension registers two Jinja2 filters.
chordpro
Converts a ChordPro string to the requested format. Chord roots are translated to the active notation (see Chord notation).
{{ song.content | chordpro }} {# HTML (default) #}
{{ song.content | chordpro("text") }} {# plain text #}
{{ song.content | chordpro("quill-delta") }} {# Quill Delta dict #}
{{ song.content | chordpro("pdf") }} {# PDF bytes (requires chordpro[pdf]) #}
Example input:
{start_of_verse}
[G]Amazing [D]grace, how [Em]sweet the [C]sound
{end_of_verse}
Example output (HTML, simplified):
<div class="cp-section" data-section="verse">
<div class="cp-section-label">Verse</div>
<div class="cp-line">
<span class="cp-unit">
<span class="cp-chord" data-chord="G">G</span>
<span class="cp-lyric">Amazing </span>
</span>
<span class="cp-unit">
<span class="cp-chord" data-chord="D">D</span>
<span class="cp-lyric">grace, how </span>
</span>
...
</div>
</div>
format_key
Formats a key integer (0–23) as a human-readable key name in the active notation. Keys 0–11 are major; keys 12–23 are minor.
{{ song.key | format_key }} {# e.g. "C", "F#m" #}
Key integer mapping (standard notation):
key_int |
Major | key_int |
Minor |
|---|---|---|---|
| 0 | A | 12 | F#m |
| 1 | Bb | 13 | Gm |
| 2 | B | 14 | G#m |
| 3 | C | 15 | Am |
| 4 | C# | 16 | Bbm |
| 5 | D | 17 | Bm |
| 6 | D# | 18 | Cm |
| 7 | E | 19 | C#m |
| 8 | F | 20 | Dm |
| 9 | F# | 21 | D#m |
| 10 | G | 22 | Em |
| 11 | Ab | 23 | Fm |
Flask CLI
The extension also registers a flask chordpro command group:
flask chordpro convert song.cho
flask chordpro convert song.cho --format text --notation german
Chord notation
Both filters read flask.g.notation at render time. Set it in a before_request hook to change how chord roots and key names are displayed.
from flask import g
@app.before_request
def set_notation():
g.notation = current_user.notation_preference # e.g. "standard", "german", "latin", "nashville"
If g.notation is not set, "standard" is used.
Supported notations
| Value | Description | C major becomes |
|---|---|---|
"standard" |
English letter names | C |
"german" |
German convention (B♭ → B, B → H) | C |
"latin" |
Solfège (Do Re Mi…) | Do |
"nashville" |
Nashville Number System (relative to key) | 1 |
Nashville notation
When g.notation is "nashville", the filter also reads g.key to determine the root. If g.key is not set, the song's first {key:} metadata value is used, falling back to "C".
@app.before_request
def set_notation():
g.notation = "nashville"
g.key = current_user.key_preference # e.g. "G", "Bb", "Am"
Command-line tool
# Convert to HTML (default)
chordpro song.cho
# Convert to plain text
chordpro song.cho --format text
# Convert to Quill Delta JSON
chordpro song.cho --format quill-delta
# Use German notation
chordpro song.cho --notation german
# Use Nashville notation (key defaults to song metadata or C)
chordpro song.cho --notation nashville --key G
# Read from stdin
cat song.cho | chordpro -
Options:
| Option | Short | Choices | Default |
|---|---|---|---|
--format |
-f |
html, text, quill-delta, pdf |
html |
--notation |
-n |
standard, german, latin, nashville |
standard |
--key |
-k |
any key string (e.g. C, G, Bb) |
song metadata or C |
Supported ChordPro directives
Metadata directives
These populate the SongMeta object and produce no output in the body.
| Directive | Short form | Field |
|---|---|---|
{title: …} |
{t: …} |
meta.title |
{subtitle: …} |
{st: …} |
meta.subtitle |
{artist: …} |
meta.artist |
|
{album: …} |
meta.album |
|
{composer: …} |
meta.composer |
|
{lyricist: …} |
meta.lyricist |
|
{copyright: …} |
meta.copyright |
|
{year: …} |
meta.year |
|
{key: …} |
meta.key |
|
{time: …} |
meta.time |
|
{tempo: …} |
meta.tempo |
|
{duration: …} |
meta.duration |
|
{capo: …} |
meta.capo |
|
{meta: name value} |
meta.meta[name] |
|
{tag: …} |
meta.meta["tag"] |
Fields that may appear multiple times (subtitle, artist, key, etc.) are stored as list[str]. Singular fields (title, year, duration, capo) are str | None.
Section directives
All section directives accept an optional label override: {start_of_verse: Verse 2}.
| Directive | Short form | Section type |
|---|---|---|
{start_of_verse} / {end_of_verse} |
{sov} / {eov} |
Verse |
{start_of_chorus} / {end_of_chorus} |
{soc} / {eoc} |
Chorus |
{start_of_bridge} / {end_of_bridge} |
{sob} / {eob} |
Bridge |
{start_of_prechorus} / {end_of_prechorus} |
PreChorus |
|
{start_of_outro} / {end_of_outro} |
Outro |
|
{start_of_intro} / {end_of_intro} |
Intro |
|
{start_of_tab} / {end_of_tab} |
{sot} / {eot} |
Tab |
{start_of_grid} / {end_of_grid} |
{sog} / {eog} |
Grid |
{start_of_tag} / {end_of_tag} |
Tag |
|
{start_of_interlude} / {end_of_interlude} |
Interlude |
|
{start_of_solo} / {end_of_solo} |
Solo |
|
{start_of_instrumental} / {end_of_instrumental} |
Instrumental |
|
{start_of_abc} / {end_of_abc} |
Abc |
|
{start_of_ly} / {end_of_ly} |
Lilypond |
|
{start_of_svg} / {end_of_svg} |
Svg |
|
{start_of_textblock} / {end_of_textblock} |
TextBlock |
|
{start_of_*} / {end_of_*} |
Section (generic) |
All sections render as <div class="cp-section" data-section="<kind>"> in HTML.
Content directives
| Directive | Short form | HTML output |
|---|---|---|
{comment: text} |
{c: text} |
<div class="cp-comment"> |
{comment_italic: text} |
{ci: text} |
<div class="cp-comment cp-comment-italic"> |
{comment_box: text} |
<div class="cp-comment cp-comment-box"> |
|
{highlight: text} |
<div class="cp-highlight"> |
|
{chorus} / {chorus: label} |
<div class="cp-chorus-ref"> |
|
{image: …} |
<div class="cp-image" data-raw="…"> |
|
{chord: name} |
<div class="cp-chord-diagram" data-chord="…"> |
|
{define: name …} |
Parsed; no visual output | |
{transpose: N} |
<span class="cp-transpose" data-semitones="N" hidden> |
Layout directives
| Directive | Short form | HTML output |
|---|---|---|
{new_page} |
{np} |
<div class="cp-new-page"> |
{new_physical_page} |
{npp} |
<div class="cp-new-physical-page"> |
{column_break} |
{cb} |
<div class="cp-column-break"> |
{columns: N} |
{col: N} |
<div class="cp-columns" data-count="N"> |
{grid} |
{g} |
<span class="cp-grid-on" hidden> |
{no_grid} |
{ng} |
<span class="cp-grid-off" hidden> |
{new_song} |
{ns} |
<hr class="cp-new-song"> |
All other directives are silently ignored.
CSS classes
Style the rendered HTML output with these classes:
| Class | Element |
|---|---|
cp-section |
Section wrapper (div); data-section holds the kind |
cp-section-label |
Section heading (div) |
cp-line |
A line containing chords and lyrics (div) |
cp-unit |
A chord+lyric pair (span) |
cp-chord |
Chord name (span); data-chord holds the standard-notation root |
cp-lyric |
Lyric beneath a chord (span) |
cp-lyric-only |
Lyric with no chord above it (span) |
cp-lyric-line |
A plain lyric line with no chords (div) |
cp-break |
An empty line between paragraphs (div) |
cp-comment |
A comment directive (div) |
cp-comment-italic |
Added alongside cp-comment for {comment_italic} |
cp-comment-box |
Added alongside cp-comment for {comment_box} |
cp-highlight |
A {highlight} directive (div) |
cp-chorus-ref |
A {chorus} reference directive (div) |
cp-chorus-ref-label |
Label inside a chorus reference (span) |
cp-image |
An {image} directive (div); data-raw holds the full value |
cp-chord-diagram |
A {chord} directive (div); data-chord holds the chord name |
cp-transpose |
A {transpose} directive (span, hidden); data-semitones holds the offset |
cp-new-page |
A {new_page} directive (div) |
cp-new-physical-page |
A {new_physical_page} directive (div) |
cp-column-break |
A {column_break} directive (div) |
cp-columns |
A {columns} directive (div); data-count holds the count |
cp-grid-on |
A {grid} directive (span, hidden) |
cp-grid-off |
A {no_grid} directive (span, hidden) |
cp-new-song |
A {new_song} directive (hr) |
Public API
All public symbols are importable directly from chordpro.
Parsing
from chordpro import parse, Song, SongMeta
song = parse(content) # returns a Song dataclass
song.meta.title # str | None
song.meta.artist # list[str]
song.meta.key # list[str]
song.body # list of SongItem (sections and lines)
Rendering
from chordpro import render, build_chord_semi_to_name, build_nashville_semi_to_name, key_to_semitone
semi_to_name = build_chord_semi_to_name("latin")
html = render(song, semi_to_name, format="html")
text = render(song, semi_to_name, format="text")
delta = render(song, semi_to_name, format="quill-delta")
pdf = render(song, semi_to_name, format="pdf") # bytes; requires chordpro[pdf]
# Nashville
semi_to_name = build_nashville_semi_to_name(key_to_semitone("G"))
html = render(song, semi_to_name)
Backward-compatible one-shot helpers are also available:
from chordpro import chordpro_to_html, render_html
html = chordpro_to_html(content, semi_to_name) # parse + render in one call
html = render_html(song, semi_to_name)
PDF rendering
PdfRenderer is built in and registered as "pdf". It requires the reportlab package (pip install chordpro[pdf]).
from chordpro import parse, render
song = parse(content)
pdf_bytes = render(song, format="pdf")
with open("song.pdf", "wb") as f:
f.write(pdf_bytes)
The rendered PDF includes a header with title, subtitle, artist, and key metadata. Each section is labelled. Chord lines use the classic two-row layout — chords printed in blue directly above the corresponding lyric syllables, columns aligned by segment width. {new_page} emits a hard page break.
You can subclass PdfRenderer to adjust fonts, sizes, margins, or page size:
from reportlab.lib.pagesizes import A4
from chordpro import PdfRenderer
class MyRenderer(PdfRenderer):
PAGE_SIZE = A4
MARGIN = 1.5 # inches
LYRIC_SIZE = 12
CHORD_SIZE = 10
Custom renderers
Subclass BaseRenderer, implement render(), and register the class under a name. Once registered, the name works as the format argument to render() and the chordpro Jinja2 filter.
from chordpro import BaseRenderer, register_renderer
class SvgRenderer(BaseRenderer):
def render(self, song, semi_to_name=None):
... # return SVG string
register_renderer("svg", SvgRenderer)
{{ song.content | chordpro("svg") }}
You can also pass a renderer instance directly to the filter:
{{ song.content | chordpro(my_renderer_instance) }}
Development
# Install with dev dependencies
uv sync --group dev
# Run the test suite
uv run pytest
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file chordpro_renderer-1.0.2.tar.gz.
File metadata
- Download URL: chordpro_renderer-1.0.2.tar.gz
- Upload date:
- Size: 19.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ac1201cdd1b8d6273ca3f017d8d87e40612efe2417c6b8402f17b68c9327b932
|
|
| MD5 |
fd60ec41e784beda362d52a559a1d729
|
|
| BLAKE2b-256 |
0f232d168c27053a2319898c4ed4731550eeabddecef483b8c5e58678678fbb9
|
File details
Details for the file chordpro_renderer-1.0.2-py3-none-any.whl.
File metadata
- Download URL: chordpro_renderer-1.0.2-py3-none-any.whl
- Upload date:
- Size: 25.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4fc798c2d8ccb1f8209f359513c953b9126d7f6232ab0672452d065ff6ecb34f
|
|
| MD5 |
4cd3eed4a7c327a7e88c4ce9104da88c
|
|
| BLAKE2b-256 |
70a98347da36968f31799496ed4a7d361d581ef447eb0fd86e157a74ee056fcc
|