Browse Source

Merge branch 'master' into newline

pull/112/head
Louis 9 years ago
parent
commit
b3c5b2984b
  1. 121
      patacrep/authors.py
  2. 19
      patacrep/data/examples/datadir2/img/datadir2.ly
  3. BIN
      patacrep/data/examples/datadir2/img/datadir2.png
  4. 1
      patacrep/data/examples/example-subdir.sb
  5. 13
      patacrep/data/examples/example-test.sb
  6. 10
      patacrep/data/examples/songs/subdir/datadir2.sg
  7. 6
      patacrep/data/examples/songs/subdir/datadir2.sgc
  8. 0
      patacrep/data/examples/songs/tests/chords.sgc
  9. 0
      patacrep/data/examples/songs/tests/errors.sgc
  10. 11
      patacrep/data/examples/songs/tests/nolyrics.sgc
  11. 9
      patacrep/songs/chordpro/ast.py
  12. 2
      patacrep/songs/chordpro/data/chordpro/song_header
  13. 2
      patacrep/songs/chordpro/data/html/song_header
  14. 10
      patacrep/songs/chordpro/data/latex/content_verse
  15. 2
      patacrep/songs/chordpro/data/latex/song
  16. 73
      test/test_authors.py
  17. 2
      test/test_chordpro/author_names.sgc
  18. 4
      test/test_chordpro/author_names.tex
  19. 2
      test/test_chordpro/greensleeves.sgc
  20. 2
      test/test_chordpro/greensleeves.tex
  21. 4
      test/test_chordpro/metadata.sgc
  22. 4
      test/test_chordpro/metadata.tex
  23. 14
      test/test_chordpro/nolyrics.sgc
  24. 9
      test/test_chordpro/nolyrics.source
  25. 34
      test/test_chordpro/nolyrics.tex

121
patacrep/authors.py

@ -10,6 +10,8 @@ DEFAULT_AUTHWORDS = {
"ignore": ["unknown"], "ignore": ["unknown"],
"sep": ["and"], "sep": ["and"],
} }
RE_AFTER = r"^.*\b{}\b(.*)$"
RE_SEPARATOR = r"^(.*)\b *{} *(\b.*)?$"
def compile_authwords(authwords): def compile_authwords(authwords):
"""Convert strings of authwords to compiled regular expressions. """Convert strings of authwords to compiled regular expressions.
@ -23,11 +25,11 @@ def compile_authwords(authwords):
# Compilation # Compilation
authwords['after'] = [ authwords['after'] = [
re.compile(r"^.*\b%s\b(.*)$" % word, re.LOCALE) re.compile(RE_AFTER.format(word), re.LOCALE)
for word in authwords['after'] for word in authwords['after']
] ]
authwords['sep'] = [ authwords['sep'] = [
re.compile(r"^(.*)%s +(.*)$" % word, re.LOCALE) re.compile(RE_SEPARATOR.format(word), re.LOCALE)
for word in ([" %s" % word for word in authwords['sep']] + [',', ';']) for word in ([" %s" % word for word in authwords['sep']] + [',', ';'])
] ]
@ -37,31 +39,23 @@ def compile_authwords(authwords):
def split_author_names(string): def split_author_names(string):
r"""Split author between first and last name. r"""Split author between first and last name.
The last space separates first and last name, but spaces following a The last space separates first and last name. LaTeX commands are ignored.
backslash or a command are not separators.
Examples: >>> split_author_names("Edgar Allan Poe")
- Edgar Allan Poe => Poe, Edgar Allan ('Poe', 'Edgar Allan')
- Edgar Allan \emph {Poe} => \emph {Poe}, Edgar Allan >>> split_author_names("Edgar Allan \emph {Poe}")
- The Rolling\ Stones => Rolling\ Stones, The ('{Poe}', 'Edgar Allan \\emph')
- The {Rolling Stones} => {Rolling Stones}, The >>> split_author_names(r"The Rolling\ Stones")
('Stones', 'The Rolling\\')
>>> split_author_names("The {Rolling Stones}")
('Stones}', 'The {Rolling')
>>> split_author_names("The Rolling Stones")
('Rolling\xa0Stones', 'The')
>>> split_author_names(" John Doe ")
('Doe', 'John')
""" """
ignore_space = False chunks = string.strip().split(" ")
last_space = index = 0 return (chunks[-1].strip(), " ".join(chunks[:-1]).strip())
brace_count = 0
for char in string.strip():
index += 1
if brace_count == 0:
if char == "\\":
ignore_space = True
elif not char.isalnum() and ignore_space:
ignore_space = False
elif char == " ":
last_space = index
if char == "}":
brace_count += 1
if char == "{":
brace_count -= 1
return string[last_space:], string[:last_space]
def split_sep_author(string, sep): def split_sep_author(string, sep):
@ -71,16 +65,19 @@ def split_sep_author(string, sep):
- string: string containing authors names ; - string: string containing authors names ;
- sep: regexp matching a separator. - sep: regexp matching a separator.
>>> split_sep_author("Tintin and Milou", re.compile('^(.*) and (.*)$')) >>> split_sep_author("Tintin and Milou", re.compile(RE_SEPARATOR.format("and")))
['Tintin', 'Milou'] ['Tintin', 'Milou']
>>> split_sep_author("Tintin,", re.compile(RE_SEPARATOR.format(",")))
['Tintin']
""" """
authors = [] authors = []
match = sep.match(string) match = sep.match(string)
while match: while match:
authors.append(match.group(2)) if match.group(2) is not None:
authors.append(match.group(2).strip())
string = match.group(1) string = match.group(1)
match = sep.match(string) match = sep.match(string)
authors.insert(0, string) authors.insert(0, string.strip())
return authors return authors
################################################################################ ################################################################################
@ -91,6 +88,9 @@ def processauthors_removeparen(authors_string):
"""Remove parentheses """Remove parentheses
See docstring of processauthors() for more information. See docstring of processauthors() for more information.
>>> processauthors_removeparen("This (foo) string (bar) contains (baz) parenthesis")
'This string contains parenthesis'
""" """
opening = 0 opening = 0
dest = "" dest = ""
@ -107,6 +107,16 @@ def processauthors_split_string(authors_string, sep):
"""Split strings """Split strings
See docstring of processauthors() for more information. See docstring of processauthors() for more information.
>>> processauthors_split_string("Tintin and Milou", [re.compile(RE_SEPARATOR.format("and"))])
['Tintin', 'Milou']
>>> processauthors_split_string("Tintin, Milou", [re.compile(RE_SEPARATOR.format(","))])
['Tintin', 'Milou']
>>> processauthors_split_string(
... "Tintin, and Milou",
... [re.compile(RE_SEPARATOR.format(word)) for word in ['and', ',']]
... )
['Tintin', 'Milou']
""" """
authors_list = [authors_string] authors_list = [authors_string]
for sepword in sep: for sepword in sep:
@ -160,45 +170,47 @@ def processauthors_clean_authors(authors_list):
] ]
def processauthors(authors_string, after=None, ignore=None, sep=None): def processauthors(authors_string, after=None, ignore=None, sep=None):
r"""Return a list of authors r"""Return an iterator of authors
For example, we are processing: For example, in the following call:
# processauthors(
# [ >>> set(processauthors(
# " ... (
# Lyrics by William Blake (from Milton, 1808), ... "Lyrics by William Blake (from Milton, 1808), "
# music by Hubert Parry (1916), ... "music by Hubert Parry (1916), "
# and sung by The Royal\ Choir~of~Nowhere ... "and sung by The Royal~Choir~of~FooBar "
# (just here to show you how processing is done) ... "(just here to show you how processing is done)"
# ", ... ),
# ], ... **compile_authwords({
# after = ["by"], ... 'after': ["by"],
# ignore = ["anonymous"], ... 'ignore': ["anonymous"],
# sep = [re.compile('^(.*) and (.*)$')], ... 'sep': ["and", ","],
# ) ... })
... )) == {("Blake", "William"), ("Parry", "Hubert"), ("Royal~Choir~of~FooBar", "The")}
True
The "authors_string" is processed as: The "authors_string" is processed as:
1) First, parenthesis (and its content) are removed. 1) First, parenthesis (and its content) are removed.
# "Lyrics by William Blake, music by Hubert Parry, # "Lyrics by William Blake, music by Hubert Parry,
and sung by The Royal\ Choir~of~Nowhere" and sung by The Royal~Choir~of~FooBar"
2) String is split, separators being comma and words from "sep". 2) String is split, separators being comma and words from "sep".
# ["Lyrics by William Blake", "music by Hubert Parry", # ["Lyrics by William Blake", "music by Hubert Parry",
"sung by The Royal\ Choir~of~Nowhere"] "sung by The Royal~Choir~of~FooBar"]
3) Everything before words in "after" is removed. 3) Everything before words in "after" is removed.
# ["William Blake", "Hubert Parry", "The Royal\ Choir~of~Nowhere"] # ["William Blake", "Hubert Parry", "The Royal~Choir~of~FooBar"]
4) Strings containing words of "ignore" are dropped. 4) Strings containing words of "ignore" are dropped.
# ["William Blake", "Hubert Parry", The Royal\ Choir~of~Nowhere"] # ["William Blake", "Hubert Parry", The Royal~Choir~of~FooBar"]
5) First and last names are splitted 5) First and last names are splitted
# [ # [
# ("Blake", "William"), # ("Blake", "William"),
# ("Parry", "Hubert"), # ("Parry", "Hubert"),
# ("Royal\ Choir~of~Nowhere", "The"), # ("Royal~Choir~of~FooBar", "The"),
# ] # ]
""" """
@ -209,10 +221,7 @@ def processauthors(authors_string, after=None, ignore=None, sep=None):
if not ignore: if not ignore:
ignore = [] ignore = []
return [ for author in processauthors_clean_authors(
split_author_names(author)
for author
in processauthors_clean_authors(
processauthors_ignore_authors( processauthors_ignore_authors(
processauthors_remove_after( processauthors_remove_after(
processauthors_split_string( processauthors_split_string(
@ -222,8 +231,8 @@ def processauthors(authors_string, after=None, ignore=None, sep=None):
sep), sep),
after), after),
ignore) ignore)
) ):
] yield split_author_names(author)
def process_listauthors(authors_list, after=None, ignore=None, sep=None): def process_listauthors(authors_list, after=None, ignore=None, sep=None):
"""Process a list of authors, and return the list of resulting authors.""" """Process a list of authors, and return the list of resulting authors."""

19
patacrep/data/examples/datadir2/img/datadir2.ly

@ -0,0 +1,19 @@
\include "_lilypond/header"
\paper{paper-height = 6.5\cm}
{
\key a \minor
\time 6/8
\partial 8 a'8
\relative c''{
c4 d8 e8. (f16) e8 d4 b8 g8. (a16) b8
c4 a8 a8. (gis16) a8 b4 gis8 e4 a8
c4 d8 e8. (f16 e8) d4 b8 g8. (a16) b8
c8. (b16) a8 gis8. (fis16) gis8 a4 a8 a4.
g'4. g8. (fis16) e8 d4 b8 g8. (a16) b8
c4 (a8) a8. (gis16) a8 b4 gis8 e4.
g'4. g8. (fis16) e8 d4 b8 g8. (a16) b8
c8. (b16) a8 gis8. (fis16) gis8 a4. a4.
}
}

BIN
patacrep/data/examples/datadir2/img/datadir2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

1
patacrep/data/examples/example-subdir.sb

@ -5,6 +5,7 @@
"lilypond", "lilypond",
"pictures" "pictures"
], ],
"datadir": ["datadir2"],
"booktype" : "chorded", "booktype" : "chorded",
"template" : "patacrep.tex", "template" : "patacrep.tex",
"lang" : "french", "lang" : "french",

13
patacrep/data/examples/example-test.sb

@ -0,0 +1,13 @@
{
"bookoptions" : [
"diagram",
"repeatchords",
"lilypond",
"pictures"
],
"booktype" : "chorded",
"template" : "patacrep.tex",
"encoding": "utf8",
"content": ["tests/*.sg", "tests/*.sgc"]
}

10
patacrep/data/examples/songs/subdir/datadir2.sg

@ -0,0 +1,10 @@
\beginsong{Image included from a different datadir\\\LaTeX}
[cov={datadir2}]
\cover
\lilypond{datadir2.ly}
\image{datadir2}
\endsong

6
patacrep/data/examples/songs/subdir/datadir2.sgc

@ -0,0 +1,6 @@
{title : Image included from a different datadir}
{subtitle: Chordpro}
{cover: datadir2.png}
{partition: datadir2.ly}
{image: datadir2.png}

0
patacrep/data/examples/songs/chords.sgc → patacrep/data/examples/songs/tests/chords.sgc

0
patacrep/data/examples/songs/errors.sgc → patacrep/data/examples/songs/tests/errors.sgc

11
patacrep/data/examples/songs/tests/nolyrics.sgc

@ -0,0 +1,11 @@
{title: No lyrics}
{subtitle: Test of verses without any lyrics}
[A]This is a [Bb]verse
With [C#]lyrics
[Adim] [Dmaj]
[Em3]
[G4]This is [Emaj3]another
[Absus8]With lyrics

9
patacrep/songs/chordpro/ast.py

@ -157,6 +157,15 @@ class Verse(AST):
return False return False
return True return True
@property
def nolyrics(self):
"""Return `True` iff verse contains only notes (no lyrics)"""
for line in self.lines:
for item in line.line:
if not (isinstance(item, Space) or isinstance(item, ChordList)):
return False
return True
class Chorus(Verse): class Chorus(Verse):
"""Chorus""" """Chorus"""
type = 'chorus' type = 'chorus'

2
patacrep/songs/chordpro/data/chordpro/song_header

@ -13,7 +13,7 @@
(* endfor -*) (* endfor -*)
(*- for author in authors -*) (*- for author in authors -*)
{artist: (( author[1] ))(( author[0] ))} {artist: (( author[1] )) (( author[0] ))}
(* endfor *) (* endfor *)
(*- for key in ['album', 'copyright', 'tag'] *) (*- for key in ['album', 'copyright', 'tag'] *)

2
patacrep/songs/chordpro/data/html/song_header

@ -7,7 +7,7 @@
(* endfor -*) (* endfor -*)
(*- for author in authors -*) (*- for author in authors -*)
<h2 class="song-artist">(( author[1] ))(( author[0] ))</h2> <h2 class="song-artist">(( author[1] )) (( author[0] ))</h2>
(* endfor *) (* endfor *)

10
patacrep/songs/chordpro/data/latex/content_verse

@ -2,7 +2,15 @@
(* for line in content.lines -*) (* for line in content.lines -*)
((- render(line) )) ((- render(line) ))
(* endfor -*) (* endfor -*)
(*- else -*) (*- elif content.nolyrics -*)
\ifchorded
\begin{verse*}
(* for line in content.lines *)
\musicnote {\nolyrics (( render(line) ))}
(* endfor *)
\end{verse*}
\fi
(*- else *)
\begin{(( content.type ))} \begin{(( content.type ))}
(* for line in content.lines *) (* for line in content.lines *)
(( render(line) )) (( render(line) ))

2
patacrep/songs/chordpro/data/latex/song

@ -16,7 +16,7 @@
}[ }[
by={ by={
(* for author in authors *) (* for author in authors *)
(( author[1] ))(( author[0] )) (( author[1] )) (( author[0] ))
(*- if not loop.last -*) (*- if not loop.last -*)
, ,
(* endif *) (* endif *)

73
test/test_authors.py

@ -0,0 +1,73 @@
"""Tests of author parsing."""
# pylint: disable=too-few-public-methods
import unittest
from patacrep import authors
SPLIT_AUTHORS_DATA = [
("Edgar Allan Poe", ("Poe", "Edgar Allan")),
("Richard M. Stallman", ("Stallman", "Richard M.")),
("Georges Brassens", ("Brassens", "Georges")),
("The Who", ("Who", "The")),
("Cher", ("Cher", "")),
("Red~Hot~Chili~Peppers", ("Red~Hot~Chili~Peppers", "")),
("The mamas~and~the~papas", ("mamas~and~the~papas", "The")),
("The mamas and the papas", ("mamas and the papas", "The")), # Unbreakable spaces
(r"\LaTeX command", ("command", r"\LaTeX")), # LaTeX commands are ignored
(r"\emph{Some braces}", ("braces}", r"\emph{Some")), # LaTeX commands are ignored
(r"The Rolling\ Stones", ("Stones", 'The Rolling\\')), # LaTeX commands are ignored
]
PROCESS_AUTHORS_DATA = [
(
(
"Lyrics by William Blake (from Milton, 1808), music by Hubert "
"Parry (1916), and sung by The Royal~Choir~of~FooBar (just here to "
"show you how processing is done)"
),
[
("Blake", "William"),
("Parry", "Hubert"),
("Royal~Choir~of~FooBar", "The"),
]
),
(
"Anonyme (1967)",
[],
),
(
"Lucky Luke et Jolly Jumper",
[
("Luke", "Lucky"),
("Jumper", "Jolly"),
],
),
]
AUTHWORDS = authors.compile_authwords({
"after": ["by"],
"ignore": ["anonymous", "Anonyme", "anonyme"],
"sep": ['and', 'et'],
})
class TestAutors(unittest.TestCase):
"""Test of author parsing."""
def test_split_author_names(self):
"""Test of :func:`patacrep.authors.split_author_names` function."""
for argument, expected in SPLIT_AUTHORS_DATA:
with self.subTest(argument=argument, expected=expected):
self.assertEqual(authors.split_author_names(argument), expected)
def test_processauthors(self):
"""Test of :func:`patacrep.authors.processauthors` function."""
for argument, expected in PROCESS_AUTHORS_DATA:
with self.subTest(argument=argument, expected=expected):
self.assertEqual(
set(
authors.processauthors(argument, **AUTHWORDS)
),
set(expected)
)

2
test/test_chordpro/author_names.sgc

@ -1,6 +1,6 @@
{language: english} {language: english}
{title: Title} {title: Title}
{artist: The Beatles} {artist: The Beatles}
{artist: Oasis} {artist: Oasis}
{artist: The the beatles} {artist: The the beatles}

4
test/test_chordpro/author_names.tex

@ -3,10 +3,10 @@
\beginsong{Title}[ \beginsong{Title}[
by={ by={
The Beatles, The Beatles,
Oasis, Oasis,
The the beatles }, The the beatles },
] ]
\endsong \endsong

2
test/test_chordpro/greensleeves.sgc

@ -3,7 +3,7 @@
{title: Greensleeves} {title: Greensleeves}
{title: Un autre sous-titre} {title: Un autre sous-titre}
{title: Un sous titre} {title: Un sous titre}
{artist: Traditionnel} {artist: Traditionnel}
{album: Angleterre} {album: Angleterre}
{cov: traditionnel} {cov: traditionnel}

2
test/test_chordpro/greensleeves.tex

@ -5,7 +5,7 @@
Un autre sous-titre\\ Un autre sous-titre\\
Un sous titre}[ Un sous titre}[
by={ by={
Traditionnel }, Traditionnel },
album={Angleterre}, album={Angleterre},
cov={traditionnel}, cov={traditionnel},
] ]

4
test/test_chordpro/metadata.sgc

@ -6,8 +6,8 @@
{title: Subtitle3} {title: Subtitle3}
{title: Subtitle4} {title: Subtitle4}
{title: Subtitle5} {title: Subtitle5}
{artist: Author1} {artist: Author1}
{artist: Author2} {artist: Author2}
{album: Album} {album: Album}
{copyright: Copyright} {copyright: Copyright}
{cov: Cover} {cov: Cover}

4
test/test_chordpro/metadata.tex

@ -7,8 +7,8 @@ Subtitle3\\
Subtitle4\\ Subtitle4\\
Subtitle5}[ Subtitle5}[
by={ by={
Author1, Author1,
Author2 }, Author2 },
album={Album}, album={Album},
copyright={Copyright}, copyright={Copyright},
cov={Cover}, cov={Cover},

14
test/test_chordpro/nolyrics.sgc

@ -0,0 +1,14 @@
{language: english}
A chorus [A]with lyrics
[Emaj3]maj et nombre
[A B#]
[A] [B]
[C]
A chorus [C]with lyrics

9
test/test_chordpro/nolyrics.source

@ -0,0 +1,9 @@
A chorus [A]with lyrics
[Emaj3]maj et nombre
[A B#]
[A] [B]
[C]
A chorus [C]with lyrics

34
test/test_chordpro/nolyrics.tex

@ -0,0 +1,34 @@
\selectlanguage{english}
\beginsong{}[
by={
},
]
\begin{verse}
A chorus \[A]with lyrics
\[Emaj3]maj et nombre
\end{verse}
\ifchorded
\begin{verse*}
\musicnote {\nolyrics \[A B#]}
\end{verse*}
\fi
\ifchorded
\begin{verse*}
\musicnote {\nolyrics \[A] \[B]}
\musicnote {\nolyrics \[C]}
\end{verse*}
\fi
\begin{verse}
A chorus \[C]with lyrics
\end{verse}
\endsong
Loading…
Cancel
Save