Skip to main content

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

chordpro_renderer-1.0.2.tar.gz (19.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

chordpro_renderer-1.0.2-py3-none-any.whl (25.9 kB view details)

Uploaded Python 3

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

Hashes for chordpro_renderer-1.0.2.tar.gz
Algorithm Hash digest
SHA256 ac1201cdd1b8d6273ca3f017d8d87e40612efe2417c6b8402f17b68c9327b932
MD5 fd60ec41e784beda362d52a559a1d729
BLAKE2b-256 0f232d168c27053a2319898c4ed4731550eeabddecef483b8c5e58678678fbb9

See more details on using hashes here.

File details

Details for the file chordpro_renderer-1.0.2-py3-none-any.whl.

File metadata

File hashes

Hashes for chordpro_renderer-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 4fc798c2d8ccb1f8209f359513c953b9126d7f6232ab0672452d065ff6ecb34f
MD5 4cd3eed4a7c327a7e88c4ce9104da88c
BLAKE2b-256 70a98347da36968f31799496ed4a7d361d581ef447eb0fd86e157a74ee056fcc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page