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

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

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

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

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

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

@ -2,7 +2,15 @@
(* for line in content.lines -*)
((- render(line) ))
(* 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 ))}
(* for line in content.lines *)
(( render(line) ))

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

@ -16,7 +16,7 @@
}[
by={
(* for author in authors *)
(( author[1] ))(( author[0] ))
(( author[1] )) (( author[0] ))
(*- if not loop.last -*)
,
(* 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}
{title: Title}
{artist: The Beatles}
{artist: Oasis}
{artist: Oasis}
{artist: The the beatles}

4
test/test_chordpro/author_names.tex

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

2
test/test_chordpro/greensleeves.sgc

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

2
test/test_chordpro/greensleeves.tex

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

4
test/test_chordpro/metadata.sgc

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

4
test/test_chordpro/metadata.tex

@ -7,8 +7,8 @@ Subtitle3\\
Subtitle4\\
Subtitle5}[
by={
Author1,
Author2 },
Author1,
Author2 },
album={Album},
copyright={Copyright},
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