From 93e40ebce07cd7fbfd5bb1379e85159c682aa49f Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Mon, 14 Sep 2015 19:58:54 +0200 Subject: [PATCH 01/36] Allow Ukulele defines Fix #85 --- patacrep/songs/chordpro/data/latex/content_define | 4 ++++ test/test_chordpro/ukulelechords.sgc | 5 +++++ test/test_chordpro/ukulelechords.source | 4 ++++ test/test_chordpro/ukulelechords.tex | 14 ++++++++++++++ 4 files changed, 27 insertions(+) create mode 100644 test/test_chordpro/ukulelechords.sgc create mode 100644 test/test_chordpro/ukulelechords.source create mode 100644 test/test_chordpro/ukulelechords.tex diff --git a/patacrep/songs/chordpro/data/latex/content_define b/patacrep/songs/chordpro/data/latex/content_define index ca899ff3..d1294b3a 100644 --- a/patacrep/songs/chordpro/data/latex/content_define +++ b/patacrep/songs/chordpro/data/latex/content_define @@ -1,4 +1,8 @@ +(*- if content.frets|length == 4 -*) +\utab{ +(*- else -*) \gtab{ +(*- endif -*) ((- render(content.key) -)) }{ (*- if content.basefret -*) diff --git a/test/test_chordpro/ukulelechords.sgc b/test/test_chordpro/ukulelechords.sgc new file mode 100644 index 00000000..3b37123d --- /dev/null +++ b/test/test_chordpro/ukulelechords.sgc @@ -0,0 +1,5 @@ +{language: english} +{define: G frets 0 2 3 2} +{define: D7 frets 2 2 2 3 fingers 1 1 1 2} +{define: G frets 3 2 0 0 0 3} +{define: A#+2 base-fret 7 frets 0 1 3 3 x x fingers - 1 2 3 - -} diff --git a/test/test_chordpro/ukulelechords.source b/test/test_chordpro/ukulelechords.source new file mode 100644 index 00000000..e21837f6 --- /dev/null +++ b/test/test_chordpro/ukulelechords.source @@ -0,0 +1,4 @@ +{define: G frets 0 2 3 2} +{define: D7 frets 2 2 2 3 fingers 1 1 1 2} +{define: G frets 3 2 0 0 0 3} +{define: A#+2 base-fret 7 frets 0 1 3 3 x x fingers - 1 2 3 - -} diff --git a/test/test_chordpro/ukulelechords.tex b/test/test_chordpro/ukulelechords.tex new file mode 100644 index 00000000..57c247e9 --- /dev/null +++ b/test/test_chordpro/ukulelechords.tex @@ -0,0 +1,14 @@ +\selectlanguage{english} + +\beginsong{}[ + by={ + }, +] + +\utab{G}{0232} +\utab{D7}{2223:1112} +\gtab{G}{320003} +\gtab{A#+2}{7:0133XX:012300} + + +\endsong From 3b97b5ad140ac524e28e6313334ec2899fd0bbd6 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Mon, 14 Sep 2015 20:53:32 +0200 Subject: [PATCH 02/36] Remove {start/end_of_verse}, fix #91 --- .../chordpro/data/chordpro/content_newline | 1 - .../chordpro/data/chordpro/content_verse | 6 +++ test/test_chordpro/01.sgc | 4 +- test/test_chordpro/08.sgc | 5 +- test/test_chordpro/09.sgc | 5 +- test/test_chordpro/10.sgc | 4 +- test/test_chordpro/11.sgc | 4 +- test/test_chordpro/12.sgc | 4 +- test/test_chordpro/21.sgc | 4 +- test/test_chordpro/28.sgc | 5 +- test/test_chordpro/29.sgc | 5 +- test/test_chordpro/chords.sgc | 32 ++++++------ test/test_chordpro/greensleeves.sgc | 52 +++++++------------ test/test_chordpro/invalid_chord.sgc | 4 +- 14 files changed, 51 insertions(+), 84 deletions(-) diff --git a/patacrep/songs/chordpro/data/chordpro/content_newline b/patacrep/songs/chordpro/data/chordpro/content_newline index 139597f9..8b137891 100644 --- a/patacrep/songs/chordpro/data/chordpro/content_newline +++ b/patacrep/songs/chordpro/data/chordpro/content_newline @@ -1,2 +1 @@ - diff --git a/patacrep/songs/chordpro/data/chordpro/content_verse b/patacrep/songs/chordpro/data/chordpro/content_verse index 465e7697..cd562cea 100644 --- a/patacrep/songs/chordpro/data/chordpro/content_verse +++ b/patacrep/songs/chordpro/data/chordpro/content_verse @@ -1,5 +1,11 @@ +(*- if content.type != 'verse' -*) {start_of_(( content.type ))} (* for line in content.lines *) (( render(line) )) (* endfor *) {end_of_(( content.type ))} +(* else -*) + (*- for line in content.lines -*) + (( render(line) )) + (* endfor -*) +(*- endif -*) diff --git a/test/test_chordpro/01.sgc b/test/test_chordpro/01.sgc index ae7eabc4..25d6a677 100644 --- a/test/test_chordpro/01.sgc +++ b/test/test_chordpro/01.sgc @@ -1,5 +1,3 @@ {language: english} -{start_of_verse} - A verse line -{end_of_verse} +A verse line diff --git a/test/test_chordpro/08.sgc b/test/test_chordpro/08.sgc index 61224d41..9a9f0566 100644 --- a/test/test_chordpro/08.sgc +++ b/test/test_chordpro/08.sgc @@ -1,7 +1,4 @@ {language: english} - -{start_of_verse} - A lot of new lines -{end_of_verse} +A lot of new lines diff --git a/test/test_chordpro/09.sgc b/test/test_chordpro/09.sgc index 942a91e1..193db1b0 100644 --- a/test/test_chordpro/09.sgc +++ b/test/test_chordpro/09.sgc @@ -2,7 +2,4 @@ {title: and a directive} - -{start_of_verse} - A lot of new lines -{end_of_verse} +A lot of new lines diff --git a/test/test_chordpro/10.sgc b/test/test_chordpro/10.sgc index 23bce4fa..e7537b3b 100644 --- a/test/test_chordpro/10.sgc +++ b/test/test_chordpro/10.sgc @@ -1,5 +1,3 @@ {language: english} -{start_of_verse} - A line[A] with a chord -{end_of_verse} +A line[A] with a chord diff --git a/test/test_chordpro/11.sgc b/test/test_chordpro/11.sgc index 79d44702..3610f67c 100644 --- a/test/test_chordpro/11.sgc +++ b/test/test_chordpro/11.sgc @@ -1,5 +1,3 @@ {language: english} -{start_of_verse} - A line ending with a chord[A] -{end_of_verse} +A line ending with a chord[A] diff --git a/test/test_chordpro/12.sgc b/test/test_chordpro/12.sgc index 6afb5bf8..029ccad4 100644 --- a/test/test_chordpro/12.sgc +++ b/test/test_chordpro/12.sgc @@ -1,5 +1,3 @@ {language: english} -{start_of_verse} - [A]A line starting with a chord -{end_of_verse} +[A]A line starting with a chord diff --git a/test/test_chordpro/21.sgc b/test/test_chordpro/21.sgc index ae7eabc4..25d6a677 100644 --- a/test/test_chordpro/21.sgc +++ b/test/test_chordpro/21.sgc @@ -1,5 +1,3 @@ {language: english} -{start_of_verse} - A verse line -{end_of_verse} +A verse line diff --git a/test/test_chordpro/28.sgc b/test/test_chordpro/28.sgc index 61224d41..9a9f0566 100644 --- a/test/test_chordpro/28.sgc +++ b/test/test_chordpro/28.sgc @@ -1,7 +1,4 @@ {language: english} - -{start_of_verse} - A lot of new lines -{end_of_verse} +A lot of new lines diff --git a/test/test_chordpro/29.sgc b/test/test_chordpro/29.sgc index 942a91e1..193db1b0 100644 --- a/test/test_chordpro/29.sgc +++ b/test/test_chordpro/29.sgc @@ -2,7 +2,4 @@ {title: and a directive} - -{start_of_verse} - A lot of new lines -{end_of_verse} +A lot of new lines diff --git a/test/test_chordpro/chords.sgc b/test/test_chordpro/chords.sgc index 942a0223..040dcf07 100644 --- a/test/test_chordpro/chords.sgc +++ b/test/test_chordpro/chords.sgc @@ -1,19 +1,17 @@ {language: english} -{start_of_verse} - [A]Simple - [Bb]Bémol - [C#]Dièse - [Adim]dim - [Dmaj]maj - [Em3]m chiffre - [G4]Nombre - [Emaj3]maj et nombre - [Absus8]bémol, sus et nombre - [A/A]Deux notes - [F/Fb]Deux notes, bémol - [B/C#]Deux notes, dièse - [Ab B#/A]Plusieurs notes à la suite - [E5/A*]Avec une étoile - [B#+8]Avec un plus -{end_of_verse} +[A]Simple +[Bb]Bémol +[C#]Dièse +[Adim]dim +[Dmaj]maj +[Em3]m chiffre +[G4]Nombre +[Emaj3]maj et nombre +[Absus8]bémol, sus et nombre +[A/A]Deux notes +[F/Fb]Deux notes, bémol +[B/C#]Deux notes, dièse +[Ab B#/A]Plusieurs notes à la suite +[E5/A*]Avec une étoile +[B#+8]Avec un plus diff --git a/test/test_chordpro/greensleeves.sgc b/test/test_chordpro/greensleeves.sgc index ba6081a2..566669b0 100644 --- a/test/test_chordpro/greensleeves.sgc +++ b/test/test_chordpro/greensleeves.sgc @@ -8,16 +8,12 @@ {cov: traditionnel} - {partition: greensleeves.ly} - -{start_of_verse} - A[Am]las, my love, ye [G]do me wrong - To [Am]cast me oft dis[E]curteously - And [Am]I have loved [G]you so long - De[Am]lighting [E]in your [Am]companie -{end_of_verse} +A[Am]las, my love, ye [G]do me wrong +To [Am]cast me oft dis[E]curteously +And [Am]I have loved [G]you so long +De[Am]lighting [E]in your [Am]companie {start_of_chorus} @@ -28,33 +24,25 @@ {end_of_chorus} -{start_of_verse} - I [Am]have been ready [G]at your hand - To [Am]grant what ever [E]you would crave - I [Am]have both waged [G]life and land - Your [Am]love and [E]good will [Am]for to have -{end_of_verse} +I [Am]have been ready [G]at your hand +To [Am]grant what ever [E]you would crave +I [Am]have both waged [G]life and land +Your [Am]love and [E]good will [Am]for to have -{start_of_verse} - I [Am]bought thee kerchers [G]to thy head - That [Am]were wrought fine and [E]gallantly - I [Am]kept thee both at [G]boord and bed - Which [Am]cost my [E]purse well [Am]favouredly -{end_of_verse} +I [Am]bought thee kerchers [G]to thy head +That [Am]were wrought fine and [E]gallantly +I [Am]kept thee both at [G]boord and bed +Which [Am]cost my [E]purse well [Am]favouredly -{start_of_verse} - I [Am]bought thee peticotes [G]of the best - The [Am]cloth so fine as [E]fine might be - I [Am]gave thee jewels [G]for thy chest - And [Am]all this [E]cost I [Am]spent on thee -{end_of_verse} +I [Am]bought thee peticotes [G]of the best +The [Am]cloth so fine as [E]fine might be +I [Am]gave thee jewels [G]for thy chest +And [Am]all this [E]cost I [Am]spent on thee -{start_of_verse} - Thy [Am]smock of silke, both [G]faire and white - With [Am]gold embrodered [E]gorgeously - Thy [Am]peticote of [G]sendall right - And [Am]this I [E]bought thee [Am]gladly -{end_of_verse} +Thy [Am]smock of silke, both [G]faire and white +With [Am]gold embrodered [E]gorgeously +Thy [Am]peticote of [G]sendall right +And [Am]this I [E]bought thee [Am]gladly diff --git a/test/test_chordpro/invalid_chord.sgc b/test/test_chordpro/invalid_chord.sgc index 7ec4fd65..acb5444f 100644 --- a/test/test_chordpro/invalid_chord.sgc +++ b/test/test_chordpro/invalid_chord.sgc @@ -1,5 +1,3 @@ {language: english} -{start_of_verse} - This is invalid. -{end_of_verse} +This is invalid. From d6c741d40bed6466537c452ff8dc7549ea7f9991 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Sep 2015 14:31:49 +0200 Subject: [PATCH 03/36] Add getter method to OrderedLifoDict --- patacrep/songs/chordpro/ast.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/patacrep/songs/chordpro/ast.py b/patacrep/songs/chordpro/ast.py index 5cffbb86..cc0c0d63 100644 --- a/patacrep/songs/chordpro/ast.py +++ b/patacrep/songs/chordpro/ast.py @@ -39,6 +39,11 @@ class OrderedLifoDict: def __getitem__(self, key): return self._values[key] + def get(self, key, default=None): + if key not in self._keys: + return default + return self._values[key] + def _indent(string): """Return and indented version of argument.""" return "\n".join([" {}".format(line) for line in string.split('\n')]) From 6e1b47ac86aad92db8b2e69e8895dc33c7b03ef5 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Sep 2015 15:06:29 +0200 Subject: [PATCH 04/36] Add method to retrieve the fullpath of the cover file --- patacrep/songs/__init__.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index fbf02e36..aaee99cf 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -194,6 +194,24 @@ class Song: """ raise NotImplementedError() + def get_cover_fullpath(self, default=None): + filename = self.data.get('cov') + if not filename: + return default + + dirname = os.path.dirname(self.fullpath) + filepath = os.path.join(dirname, str(filename)) + filepath += '.' + + extensions = ['jpg', 'png'] + for extension in extensions: + if os.path.isfile(filepath + extension): + return filepath + extension + + # How to handle when the cover should have been there ? + # raise FileNotFoundError() + return default + def unprefixed_title(title, prefixes): """Remove the first prefix of the list in the beginning of title (if any). """ From 1baf52ec39350c1571aa92e2586170de5a8deadd Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Sep 2015 15:23:18 +0200 Subject: [PATCH 05/36] Nicer getter --- patacrep/songs/chordpro/ast.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/patacrep/songs/chordpro/ast.py b/patacrep/songs/chordpro/ast.py index cc0c0d63..5a9ef430 100644 --- a/patacrep/songs/chordpro/ast.py +++ b/patacrep/songs/chordpro/ast.py @@ -40,9 +40,7 @@ class OrderedLifoDict: return self._values[key] def get(self, key, default=None): - if key not in self._keys: - return default - return self._values[key] + return self._values.get(key, default) def _indent(string): """Return and indented version of argument.""" From ec389a4fbaa7ed676066b978782ab24791546ced Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Sep 2015 15:31:45 +0200 Subject: [PATCH 06/36] Add missing docstrings --- patacrep/songs/__init__.py | 1 + patacrep/songs/chordpro/ast.py | 1 + 2 files changed, 2 insertions(+) diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index aaee99cf..262187c4 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -195,6 +195,7 @@ class Song: raise NotImplementedError() def get_cover_fullpath(self, default=None): + """Return the fullpath of the cover file if found""" filename = self.data.get('cov') if not filename: return default diff --git a/patacrep/songs/chordpro/ast.py b/patacrep/songs/chordpro/ast.py index 5a9ef430..09fc5acd 100644 --- a/patacrep/songs/chordpro/ast.py +++ b/patacrep/songs/chordpro/ast.py @@ -40,6 +40,7 @@ class OrderedLifoDict: return self._values[key] def get(self, key, default=None): + """Same as :meth:`dict.get`.""" return self._values.get(key, default) def _indent(string): From 585b343bc9e69f1f0ea8047b6e34ca9725d5bee0 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Sep 2015 15:48:38 +0200 Subject: [PATCH 07/36] Add property to return key with utf8 alterations --- patacrep/songs/chordpro/ast.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/patacrep/songs/chordpro/ast.py b/patacrep/songs/chordpro/ast.py index 09fc5acd..b2cc6c08 100644 --- a/patacrep/songs/chordpro/ast.py +++ b/patacrep/songs/chordpro/ast.py @@ -341,6 +341,11 @@ class Define(Directive): self.fingers = fingers # Can be None super().__init__("define", None) + @property + def pretty_key(self): + """Return the key with nicer (utf8) alteration""" + return self.key.chord.replace('&', '♭').replace('#', '♯') + def __str__(self): return None From 220d7c6001aeda4e69cbad78afa1332c5500e841 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Sep 2015 16:31:11 +0200 Subject: [PATCH 08/36] Add a default return when search_image fails (breaks some tests) --- patacrep/songs/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index 262187c4..b62d45ba 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -222,7 +222,7 @@ def unprefixed_title(title, prefixes): return match.group(2) return title -def search_image(image, chordprofile, config): +def search_image(image, chordprofile, config, default=None): """Return the file name of an image, so that LaTeX will find it. :param str image: The name, as provided in the chordpro file. @@ -256,4 +256,4 @@ def search_image(image, chordprofile, config): return os.path.join(directory, 'img', image) # Could not find image - return image + return default From 044d9570aea9a8b28e7db8247e5f8b067a88e8a8 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Sep 2015 16:33:48 +0200 Subject: [PATCH 09/36] Use search_image to find cover file --- patacrep/songs/__init__.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index b62d45ba..3c1ddf00 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -194,20 +194,23 @@ class Song: """ raise NotImplementedError() - def get_cover_fullpath(self, default=None): + def get_cover_fullpath(self, datadir, default=None): """Return the fullpath of the cover file if found""" - filename = self.data.get('cov') + filename = str(self.data.get('cov', '')) if not filename: return default - dirname = os.path.dirname(self.fullpath) - filepath = os.path.join(dirname, str(filename)) - filepath += '.' + # Temporary hack: what should it contain exactly? + config = { + 'datadir': datadir, + 'filename': '', + } - extensions = ['jpg', 'png'] + extensions = ['', '.jpg', '.png'] for extension in extensions: - if os.path.isfile(filepath + extension): - return filepath + extension + fullpath = search_image(filename + extension, self.fullpath, config) + if fullpath: + return fullpath # How to handle when the cover should have been there ? # raise FileNotFoundError() From 722ff043aeb9fc69bc80b10ba54e29daeac318e8 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Sep 2015 08:49:11 +0200 Subject: [PATCH 10/36] Add a method to search for a file related to a song --- patacrep/songs/__init__.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index 3c1ddf00..31236604 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -216,6 +216,31 @@ class Song: # raise FileNotFoundError() return default + def search_file(self, filename, extensions=None, directories=None): + """Search for a file name. + + :param str filename: The name, as provided in the chordpro file (with or without extension). + :param list extensions: Possible extensions (with '.') + :param list directories: Other directories where to search for the file + The directory where the Song file is stored is added to the list. + + Returns None if nothing found. + """ + if extensions is None: + extensions = [''] + if directories is None: + directories = [] + + songdir = os.path.dirname(self.fullpath) + directories.insert(0, songdir) + + for directory in directories: + for extension in extensions: + fullpath = os.path.join(directory, filename + extension) + if os.path.isfile(fullpath): + return fullpath + return None + def unprefixed_title(title, prefixes): """Remove the first prefix of the list in the beginning of title (if any). """ From 6353a9da389e923dd6686ebccd8b587fb1ae32f0 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Sep 2015 09:02:03 +0200 Subject: [PATCH 11/36] Add a method to list valid datadirectories of a song --- patacrep/songs/__init__.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index 31236604..c400cb84 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -216,6 +216,17 @@ class Song: # raise FileNotFoundError() return default + def get_datadirs(self, subdir=None): + """Return a list of existing datadirs (with eventually a subdir) + """ + directories = [] + + for directory in self.config['datadir']: + fullpath = os.path.join(directory, subdir) + if os.path.isdir(fullpath): + directories.append(fullpath) + return directories + def search_file(self, filename, extensions=None, directories=None): """Search for a file name. From eab22ad862c2c381f5b67fe2e06149eab1561f82 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Sep 2015 09:11:59 +0200 Subject: [PATCH 12/36] Use the new methods to search for a file related to a song --- patacrep/songs/__init__.py | 11 +++++++++++ patacrep/songs/chordpro/__init__.py | 8 ++++++-- patacrep/songs/chordpro/data/latex/content_image | 2 +- patacrep/songs/chordpro/data/latex/content_partition | 2 +- patacrep/songs/chordpro/data/latex/song | 2 +- 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index c400cb84..b252468b 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -252,6 +252,17 @@ class Song: return fullpath return None + def search_image(self, filename): + """Search for an image file""" + datadir_img = self.get_datadirs('img') + filepath = self.search_file(filename, ['', '.jpg', '.png'], datadir_img) + return filepath if filepath else filename + + def search_partition(self, filename): + """Search for a lilypond file""" + filepath = self.search_file(filename, ['', '.ly']) + return filepath if filepath else filename + def unprefixed_title(title, prefixes): """Remove the first prefix of the list in the beginning of title (if any). """ diff --git a/patacrep/songs/chordpro/__init__.py b/patacrep/songs/chordpro/__init__.py index ae37b79b..3b5ae809 100644 --- a/patacrep/songs/chordpro/__init__.py +++ b/patacrep/songs/chordpro/__init__.py @@ -5,7 +5,7 @@ import pkg_resources import os from patacrep import encoding, files -from patacrep.songs import Song, search_image +from patacrep.songs import Song from patacrep.songs.chordpro.syntax import parse_song from patacrep.templates import Renderer @@ -42,7 +42,11 @@ class ChordproSong(Song): os.path.abspath(pkg_resources.resource_filename(__name__, 'data')), output_format, ))) - self.jinjaenv.filters['search_image'] = search_image + + self.jinjaenv.filters['search_image'] = self.search_image + + self.jinjaenv.filters['search_partition'] = self.search_partition + return self._render_ast( context, self.cached['song'].content, diff --git a/patacrep/songs/chordpro/data/latex/content_image b/patacrep/songs/chordpro/data/latex/content_image index bd132ce6..e4c2befb 100644 --- a/patacrep/songs/chordpro/data/latex/content_image +++ b/patacrep/songs/chordpro/data/latex/content_image @@ -1 +1 @@ -\image{(( content.argument|search_image(path, config) ))} +\image{(( content.argument|search_image ))} diff --git a/patacrep/songs/chordpro/data/latex/content_partition b/patacrep/songs/chordpro/data/latex/content_partition index bcb92a0b..4ce134a1 100644 --- a/patacrep/songs/chordpro/data/latex/content_partition +++ b/patacrep/songs/chordpro/data/latex/content_partition @@ -1 +1 @@ -\lilypond{ ((- content.argument|search_image(path, config) -)) } +\lilypond{ ((- content.argument|search_partition -)) } diff --git a/patacrep/songs/chordpro/data/latex/song b/patacrep/songs/chordpro/data/latex/song index e3be0f83..f7905919 100644 --- a/patacrep/songs/chordpro/data/latex/song +++ b/patacrep/songs/chordpro/data/latex/song @@ -28,7 +28,7 @@ (* endif *) (* endfor *) (* if 'cov' in metadata *) - cov={(( metadata["cov"].argument|search_image(path, config) ))}, + cov={(( metadata["cov"].argument|search_image ))}, (* endif *) (* for key in metadata.keys *) (( key.keyword ))={(( key.argument ))}, From 996652401ef641aa467f85cd8c7aea11460293d5 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Sep 2015 09:17:56 +0200 Subject: [PATCH 13/36] Cleanup methods for search image and cover --- patacrep/songs/__init__.py | 67 +++++--------------------------------- 1 file changed, 9 insertions(+), 58 deletions(-) diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index b252468b..6fd03582 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -194,28 +194,6 @@ class Song: """ raise NotImplementedError() - def get_cover_fullpath(self, datadir, default=None): - """Return the fullpath of the cover file if found""" - filename = str(self.data.get('cov', '')) - if not filename: - return default - - # Temporary hack: what should it contain exactly? - config = { - 'datadir': datadir, - 'filename': '', - } - - extensions = ['', '.jpg', '.png'] - for extension in extensions: - fullpath = search_image(filename + extension, self.fullpath, config) - if fullpath: - return fullpath - - # How to handle when the cover should have been there ? - # raise FileNotFoundError() - return default - def get_datadirs(self, subdir=None): """Return a list of existing datadirs (with eventually a subdir) """ @@ -258,6 +236,15 @@ class Song: filepath = self.search_file(filename, ['', '.jpg', '.png'], datadir_img) return filepath if filepath else filename + @property + def cover_filepath(self): + """Get the path to the cover file (or None if not found)""" + filename = str(self.data.get('cov', '')) + if not filename: + return None + datadir_img = self.get_datadirs('img') + return self.search_file(filename, ['', '.jpg', '.png'], datadir_img) + def search_partition(self, filename): """Search for a lilypond file""" filepath = self.search_file(filename, ['', '.ly']) @@ -271,39 +258,3 @@ def unprefixed_title(title, prefixes): if match: return match.group(2) return title - -def search_image(image, chordprofile, config, default=None): - """Return the file name of an image, so that LaTeX will find it. - - :param str image: The name, as provided in the chordpro file. - :param str chordprofile: The name of the file including this image. - :param dict config: Songbook configuration dictionary. - - The image can be: - - - in the same directory as the including song file; - - in the same directory as the main LaTeX file; - - in some of the `DATADIR/img` directories. - - If image is not found, the `image` argument is returned. - """ - # Image is in the same folder as its song - texdir = os.path.dirname(chordprofile) - if os.path.exists(os.path.join(texdir, image)): - return os.path.join(texdir, image) - - # Image is in the same directory as the main tex file - rootdir = os.path.dirname(os.path.join( - os.getcwd(), - config['filename'], - )) - if os.path.exists(os.path.join(rootdir, image)): - return image - - # Image is in a datadir - for directory in config['datadir']: - if os.path.exists(os.path.join(directory, 'img', image)): - return os.path.join(directory, 'img', image) - - # Could not find image - return default From e49ba0551449a2f34affabc02886144bc3ebcef1 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Fri, 18 Sep 2015 17:21:04 +0200 Subject: [PATCH 14/36] Yield datadirs --- patacrep/songs/__init__.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index 6fd03582..b59883be 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -195,22 +195,19 @@ class Song: raise NotImplementedError() def get_datadirs(self, subdir=None): - """Return a list of existing datadirs (with eventually a subdir) + """Return an iterator of existing datadirs (with eventually a subdir) """ - directories = [] - for directory in self.config['datadir']: fullpath = os.path.join(directory, subdir) if os.path.isdir(fullpath): - directories.append(fullpath) - return directories + yield fullpath def search_file(self, filename, extensions=None, directories=None): """Search for a file name. :param str filename: The name, as provided in the chordpro file (with or without extension). :param list extensions: Possible extensions (with '.') - :param list directories: Other directories where to search for the file + :param iterator directories: Other directories where to search for the file The directory where the Song file is stored is added to the list. Returns None if nothing found. @@ -221,9 +218,8 @@ class Song: directories = [] songdir = os.path.dirname(self.fullpath) - directories.insert(0, songdir) - for directory in directories: + for directory in [songdir] + list(directories): for extension in extensions: fullpath = os.path.join(directory, filename + extension) if os.path.isfile(fullpath): From 7161a08b8b9d4a1586d05179cf01c095bf8230ed Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Sat, 19 Sep 2015 14:06:15 +0200 Subject: [PATCH 15/36] Reorganize chordpro templates --- patacrep/songs/chordpro/data/chordpro/song | 36 ++----------------- .../songs/chordpro/data/chordpro/song_body | 3 ++ .../songs/chordpro/data/chordpro/song_header | 31 ++++++++++++++++ 3 files changed, 36 insertions(+), 34 deletions(-) create mode 100644 patacrep/songs/chordpro/data/chordpro/song_body create mode 100644 patacrep/songs/chordpro/data/chordpro/song_header diff --git a/patacrep/songs/chordpro/data/chordpro/song b/patacrep/songs/chordpro/data/chordpro/song index d721df33..3baa8c22 100644 --- a/patacrep/songs/chordpro/data/chordpro/song +++ b/patacrep/songs/chordpro/data/chordpro/song @@ -1,35 +1,3 @@ -(* if language is defined -*) - {language: (( language ))} -(* endif *) -(* if metadata.columns is defined -*) - {columns: (( metadata.columns ))} -(* endif *) -(* if metadata.capo is defined -*) - {capo: (( metadata.capo ))} -(* endif *) +(* include 'song_header' *) -(*- for title in titles -*) - {title: (( title ))} -(* endfor -*) - -(*- for author in authors -*) - {artist: (( author[1] ))(( author[0] ))} -(* endfor *) - -(*- for key in ['album', 'copyright', 'cov', 'tag'] *) - (* if key in metadata -*) - {(( key )): (( metadata[key] ))} - (* endif *) -(* endfor *) - -(*- for key in metadata.keys -*) - {key: (( key.keyword )): (( key.argument ))} -(* endfor *) - -(*- for chord in metadata['define'] *) - ((- render(chord) )) -(* endfor *) - -(* for item in content -*) - (( render(item) )) -(* endfor *) +(* include 'song_body' *) diff --git a/patacrep/songs/chordpro/data/chordpro/song_body b/patacrep/songs/chordpro/data/chordpro/song_body new file mode 100644 index 00000000..1b7159bf --- /dev/null +++ b/patacrep/songs/chordpro/data/chordpro/song_body @@ -0,0 +1,3 @@ +(* for item in content -*) + (( render(item) )) +(* endfor *) diff --git a/patacrep/songs/chordpro/data/chordpro/song_header b/patacrep/songs/chordpro/data/chordpro/song_header new file mode 100644 index 00000000..b4a59862 --- /dev/null +++ b/patacrep/songs/chordpro/data/chordpro/song_header @@ -0,0 +1,31 @@ +(* if language is defined -*) + {language: (( language ))} +(* endif *) +(* if metadata.columns is defined -*) + {columns: (( metadata.columns ))} +(* endif *) +(* if metadata.capo is defined -*) + {capo: (( metadata.capo ))} +(* endif *) + +(*- for title in titles -*) + {title: (( title ))} +(* endfor -*) + +(*- for author in authors -*) + {artist: (( author[1] ))(( author[0] ))} +(* endfor *) + +(*- for key in ['album', 'copyright', 'cov', 'tag'] *) + (* if key in metadata -*) + {(( key )): (( metadata[key] ))} + (* endif *) +(* endfor *) + +(*- for key in metadata.keys -*) + {key: (( key.keyword )): (( key.argument ))} +(* endfor *) + +(*- for chord in metadata['define'] *) + ((- render(chord) )) +(* endfor *) From fd7216d5cb51f7b686b248f90983425fb4ff1958 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Sat, 19 Sep 2015 14:09:12 +0200 Subject: [PATCH 16/36] Template is an argument of the render method --- patacrep/songs/chordpro/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patacrep/songs/chordpro/__init__.py b/patacrep/songs/chordpro/__init__.py index 3b5ae809..827d23de 100644 --- a/patacrep/songs/chordpro/__init__.py +++ b/patacrep/songs/chordpro/__init__.py @@ -28,7 +28,7 @@ class ChordproSong(Song): 'song': song, } - def render(self, output, output_format): + def render(self, output, output_format, template="song"): context = { 'language': self.languages[0], "path": files.relpath(self.fullpath, os.path.dirname(output)), @@ -50,7 +50,7 @@ class ChordproSong(Song): return self._render_ast( context, self.cached['song'].content, - template="song", + template=template, ) @contextfunction From d4eb28ba9402f87a885153edd63d5d7a868298c0 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Sat, 19 Sep 2015 14:24:58 +0200 Subject: [PATCH 17/36] lint fix --- patacrep/songs/chordpro/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patacrep/songs/chordpro/__init__.py b/patacrep/songs/chordpro/__init__.py index 827d23de..cd9bfd4e 100644 --- a/patacrep/songs/chordpro/__init__.py +++ b/patacrep/songs/chordpro/__init__.py @@ -28,7 +28,7 @@ class ChordproSong(Song): 'song': song, } - def render(self, output, output_format, template="song"): + def render(self, output, output_format, template="song"): # pylint: disable=arguments-differ context = { 'language': self.languages[0], "path": files.relpath(self.fullpath, os.path.dirname(output)), From 6dd9bba5283d531beec1e56e5eb7e3babb04e761 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Sat, 19 Sep 2015 14:25:28 +0200 Subject: [PATCH 18/36] Split latex template with body --- patacrep/songs/chordpro/data/latex/song | 4 +--- patacrep/songs/chordpro/data/latex/song_body | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 patacrep/songs/chordpro/data/latex/song_body diff --git a/patacrep/songs/chordpro/data/latex/song b/patacrep/songs/chordpro/data/latex/song index f7905919..021dd04f 100644 --- a/patacrep/songs/chordpro/data/latex/song +++ b/patacrep/songs/chordpro/data/latex/song @@ -43,8 +43,6 @@ (( render(chord) )) (* endfor *) -(* for item in content -*) - (( render(item) )) -(* endfor *) +(* include 'song_body' *) \endsong diff --git a/patacrep/songs/chordpro/data/latex/song_body b/patacrep/songs/chordpro/data/latex/song_body new file mode 100644 index 00000000..1b7159bf --- /dev/null +++ b/patacrep/songs/chordpro/data/latex/song_body @@ -0,0 +1,3 @@ +(* for item in content -*) + (( render(item) )) +(* endfor *) From 9a834f826a20a51d639b84d408367bb9b983ccc5 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Sat, 19 Sep 2015 14:58:11 +0200 Subject: [PATCH 19/36] Start the html template --- .../songs/chordpro/data/html/content_chord | 1 + .../chordpro/data/html/content_chordlist | 6 ++++ .../songs/chordpro/data/html/content_comment | 1 + .../songs/chordpro/data/html/content_define | 25 +++++++++++++++ .../songs/chordpro/data/html/content_error | 3 ++ .../chordpro/data/html/content_guitar_comment | 1 + .../songs/chordpro/data/html/content_image | 1 + .../songs/chordpro/data/html/content_line | 3 ++ .../songs/chordpro/data/html/content_newline | 1 + .../chordpro/data/html/content_partition | 1 + .../songs/chordpro/data/html/content_space | 1 + .../chordpro/data/html/content_tablature | 5 +++ .../songs/chordpro/data/html/content_verse | 5 +++ .../songs/chordpro/data/html/content_word | 1 + patacrep/songs/chordpro/data/html/song | 3 ++ patacrep/songs/chordpro/data/html/song_body | 3 ++ patacrep/songs/chordpro/data/html/song_header | 31 +++++++++++++++++++ 17 files changed, 92 insertions(+) create mode 100644 patacrep/songs/chordpro/data/html/content_chord create mode 100644 patacrep/songs/chordpro/data/html/content_chordlist create mode 100644 patacrep/songs/chordpro/data/html/content_comment create mode 100644 patacrep/songs/chordpro/data/html/content_define create mode 100644 patacrep/songs/chordpro/data/html/content_error create mode 100644 patacrep/songs/chordpro/data/html/content_guitar_comment create mode 100644 patacrep/songs/chordpro/data/html/content_image create mode 100644 patacrep/songs/chordpro/data/html/content_line create mode 100644 patacrep/songs/chordpro/data/html/content_newline create mode 100644 patacrep/songs/chordpro/data/html/content_partition create mode 100644 patacrep/songs/chordpro/data/html/content_space create mode 100644 patacrep/songs/chordpro/data/html/content_tablature create mode 100644 patacrep/songs/chordpro/data/html/content_verse create mode 100644 patacrep/songs/chordpro/data/html/content_word create mode 100644 patacrep/songs/chordpro/data/html/song create mode 100644 patacrep/songs/chordpro/data/html/song_body create mode 100644 patacrep/songs/chordpro/data/html/song_header diff --git a/patacrep/songs/chordpro/data/html/content_chord b/patacrep/songs/chordpro/data/html/content_chord new file mode 100644 index 00000000..7d5ec6ca --- /dev/null +++ b/patacrep/songs/chordpro/data/html/content_chord @@ -0,0 +1 @@ +((- content.chord -)) diff --git a/patacrep/songs/chordpro/data/html/content_chordlist b/patacrep/songs/chordpro/data/html/content_chordlist new file mode 100644 index 00000000..ad3fc4d0 --- /dev/null +++ b/patacrep/songs/chordpro/data/html/content_chordlist @@ -0,0 +1,6 @@ + + (*- for chord in content.chords -*) + (* if not loop.first *) (* endif -*) + (( render(chord) -)) + (* endfor -*) + diff --git a/patacrep/songs/chordpro/data/html/content_comment b/patacrep/songs/chordpro/data/html/content_comment new file mode 100644 index 00000000..07381cdf --- /dev/null +++ b/patacrep/songs/chordpro/data/html/content_comment @@ -0,0 +1 @@ +{comment: (( content.argument ))} diff --git a/patacrep/songs/chordpro/data/html/content_define b/patacrep/songs/chordpro/data/html/content_define new file mode 100644 index 00000000..47a93abf --- /dev/null +++ b/patacrep/songs/chordpro/data/html/content_define @@ -0,0 +1,25 @@ +{define: (( render(content.key) )) +(*- if content.basefret *) + base-fret ((content.basefret)) +(*- endif *) + frets +(*- for string in content.frets -*) + (( " " -)) + (*- if string is none -*) + x + (*- else -*) + (( string -)) + (*- endif -*) +(*- endfor -*) +(* if content.fingers *) + fingers + (*- for finger in content.fingers -*) + (( " " -)) + (* if finger is none -*) + - + (*- else -*) + (( finger -)) + (* endif -*) + (* endfor -*) +(* endif -*) +} diff --git a/patacrep/songs/chordpro/data/html/content_error b/patacrep/songs/chordpro/data/html/content_error new file mode 100644 index 00000000..bbdb95fd --- /dev/null +++ b/patacrep/songs/chordpro/data/html/content_error @@ -0,0 +1,3 @@ + +ERROR : Template not found for "(( content.__class__.__name__ ))". See the logs for details. + diff --git a/patacrep/songs/chordpro/data/html/content_guitar_comment b/patacrep/songs/chordpro/data/html/content_guitar_comment new file mode 100644 index 00000000..1ca0c269 --- /dev/null +++ b/patacrep/songs/chordpro/data/html/content_guitar_comment @@ -0,0 +1 @@ +{guitar_comment: (( content.argument ))} diff --git a/patacrep/songs/chordpro/data/html/content_image b/patacrep/songs/chordpro/data/html/content_image new file mode 100644 index 00000000..58e7f904 --- /dev/null +++ b/patacrep/songs/chordpro/data/html/content_image @@ -0,0 +1 @@ +{image: (( content.argument ))} diff --git a/patacrep/songs/chordpro/data/html/content_line b/patacrep/songs/chordpro/data/html/content_line new file mode 100644 index 00000000..03ca80b7 --- /dev/null +++ b/patacrep/songs/chordpro/data/html/content_line @@ -0,0 +1,3 @@ +(* for item in content.line -*) + (( render(item) )) +(*- endfor *) diff --git a/patacrep/songs/chordpro/data/html/content_newline b/patacrep/songs/chordpro/data/html/content_newline new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/patacrep/songs/chordpro/data/html/content_newline @@ -0,0 +1 @@ + diff --git a/patacrep/songs/chordpro/data/html/content_partition b/patacrep/songs/chordpro/data/html/content_partition new file mode 100644 index 00000000..362c4f64 --- /dev/null +++ b/patacrep/songs/chordpro/data/html/content_partition @@ -0,0 +1 @@ +{partition: ((content.argument))} diff --git a/patacrep/songs/chordpro/data/html/content_space b/patacrep/songs/chordpro/data/html/content_space new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/patacrep/songs/chordpro/data/html/content_space @@ -0,0 +1 @@ + diff --git a/patacrep/songs/chordpro/data/html/content_tablature b/patacrep/songs/chordpro/data/html/content_tablature new file mode 100644 index 00000000..35cfd425 --- /dev/null +++ b/patacrep/songs/chordpro/data/html/content_tablature @@ -0,0 +1,5 @@ +
+  (* for content in content.content *)
+    ((- content ))
+  (* endfor *)
+
diff --git a/patacrep/songs/chordpro/data/html/content_verse b/patacrep/songs/chordpro/data/html/content_verse new file mode 100644 index 00000000..4b6073a4 --- /dev/null +++ b/patacrep/songs/chordpro/data/html/content_verse @@ -0,0 +1,5 @@ +

+ (*- for line in content.lines -*) + (( render(line) )) + (* endfor -*) +

diff --git a/patacrep/songs/chordpro/data/html/content_word b/patacrep/songs/chordpro/data/html/content_word new file mode 100644 index 00000000..d9dd7a30 --- /dev/null +++ b/patacrep/songs/chordpro/data/html/content_word @@ -0,0 +1 @@ +(( content.value )) diff --git a/patacrep/songs/chordpro/data/html/song b/patacrep/songs/chordpro/data/html/song new file mode 100644 index 00000000..3baa8c22 --- /dev/null +++ b/patacrep/songs/chordpro/data/html/song @@ -0,0 +1,3 @@ +(* include 'song_header' *) + +(* include 'song_body' *) diff --git a/patacrep/songs/chordpro/data/html/song_body b/patacrep/songs/chordpro/data/html/song_body new file mode 100644 index 00000000..1b7159bf --- /dev/null +++ b/patacrep/songs/chordpro/data/html/song_body @@ -0,0 +1,3 @@ +(* for item in content -*) + (( render(item) )) +(* endfor *) diff --git a/patacrep/songs/chordpro/data/html/song_header b/patacrep/songs/chordpro/data/html/song_header new file mode 100644 index 00000000..b4a59862 --- /dev/null +++ b/patacrep/songs/chordpro/data/html/song_header @@ -0,0 +1,31 @@ +(* if language is defined -*) + {language: (( language ))} +(* endif *) +(* if metadata.columns is defined -*) + {columns: (( metadata.columns ))} +(* endif *) +(* if metadata.capo is defined -*) + {capo: (( metadata.capo ))} +(* endif *) + +(*- for title in titles -*) + {title: (( title ))} +(* endfor -*) + +(*- for author in authors -*) + {artist: (( author[1] ))(( author[0] ))} +(* endfor *) + +(*- for key in ['album', 'copyright', 'cov', 'tag'] *) + (* if key in metadata -*) + {(( key )): (( metadata[key] ))} + (* endif *) +(* endfor *) + +(*- for key in metadata.keys -*) + {key: (( key.keyword )): (( key.argument ))} +(* endfor *) + +(*- for chord in metadata['define'] *) + ((- render(chord) )) +(* endfor *) From 176db619295416275e8ddb97d83ada86c8bd854b Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Sat, 19 Sep 2015 15:17:31 +0200 Subject: [PATCH 20/36] Add a pretty_chord property to Chord for HTML rendering --- patacrep/songs/chordpro/ast.py | 5 +++++ patacrep/songs/chordpro/data/html/content_chord | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/patacrep/songs/chordpro/ast.py b/patacrep/songs/chordpro/ast.py index b2cc6c08..0227d457 100644 --- a/patacrep/songs/chordpro/ast.py +++ b/patacrep/songs/chordpro/ast.py @@ -153,6 +153,11 @@ class Chord(AST): # pylint: disable=too-many-arguments self.chord = chord + @property + def pretty_chord(self): + """Return the chord with nicer (utf8) alteration""" + return self.chord.replace('b', '♭').replace('#', '♯') + class Verse(AST): """A verse (or bridge, or chorus)""" _template = "verse" diff --git a/patacrep/songs/chordpro/data/html/content_chord b/patacrep/songs/chordpro/data/html/content_chord index 7d5ec6ca..baa97f7e 100644 --- a/patacrep/songs/chordpro/data/html/content_chord +++ b/patacrep/songs/chordpro/data/html/content_chord @@ -1 +1 @@ -((- content.chord -)) +((- content.pretty_chord -)) From 5ab211315ea73127c49c8304d51893931aa495cb Mon Sep 17 00:00:00 2001 From: Louis Date: Sat, 19 Sep 2015 19:55:54 +0200 Subject: [PATCH 21/36] Improved sub-template rendering Using the existing jinja rendere, instead of re-instantiting a new one. --- patacrep/songs/chordpro/__init__.py | 34 ++++++++++------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/patacrep/songs/chordpro/__init__.py b/patacrep/songs/chordpro/__init__.py index cd9bfd4e..42011244 100644 --- a/patacrep/songs/chordpro/__init__.py +++ b/patacrep/songs/chordpro/__init__.py @@ -14,7 +14,6 @@ class ChordproSong(Song): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.jinjaenv = None def parse(self, config): """Parse content, and return the dictionary of song data.""" @@ -37,37 +36,28 @@ class ChordproSong(Song): "metadata": self.data, "render": self._render_ast, "config": self.config, + "content": self.cached['song'].content, } - self.jinjaenv = Environment(loader=FileSystemLoader(os.path.join( + jinjaenv = Environment(loader=FileSystemLoader(os.path.join( os.path.abspath(pkg_resources.resource_filename(__name__, 'data')), output_format, ))) + jinjaenv.filters['search_image'] = self.search_image + jinjaenv.filters['search_partition'] = self.search_partition - self.jinjaenv.filters['search_image'] = self.search_image - - self.jinjaenv.filters['search_partition'] = self.search_partition - - return self._render_ast( - context, - self.cached['song'].content, - template=template, - ) - - @contextfunction - def _render_ast(self, context, content, template=None): - """Render ``content``.""" - if isinstance(context, dict): - context['content'] = content - else: - context.vars['content'] = content - if template is None: - template = content.template() return Renderer( template=template, encoding='utf8', - jinjaenv=self.jinjaenv, + jinjaenv=jinjaenv, ).template.render(context) + @staticmethod + @contextfunction + def _render_ast(context, content): + """Render ``content``.""" + context.vars['content'] = content + return context.environment.get_template(content.template()).render(context) + SONG_PARSERS = { 'sgc': ChordproSong, } From 51d6ecd8a757011021f5631ea44add8bba68d2b9 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Sun, 20 Sep 2015 16:10:55 +0200 Subject: [PATCH 22/36] Allow to render only selected elements of a song --- patacrep/songs/chordpro/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/patacrep/songs/chordpro/__init__.py b/patacrep/songs/chordpro/__init__.py index 42011244..f6c12ad3 100644 --- a/patacrep/songs/chordpro/__init__.py +++ b/patacrep/songs/chordpro/__init__.py @@ -27,7 +27,9 @@ class ChordproSong(Song): 'song': song, } - def render(self, output, output_format, template="song"): # pylint: disable=arguments-differ + def render(self, output, output_format, content=None, template="song"): # pylint: disable=arguments-differ + if content is None: + content = self.cached['song'].content context = { 'language': self.languages[0], "path": files.relpath(self.fullpath, os.path.dirname(output)), @@ -36,7 +38,7 @@ class ChordproSong(Song): "metadata": self.data, "render": self._render_ast, "config": self.config, - "content": self.cached['song'].content, + "content": content, } jinjaenv = Environment(loader=FileSystemLoader(os.path.join( os.path.abspath(pkg_resources.resource_filename(__name__, 'data')), From efdb79cac94ccee4ec976d2c91cce508d52fd94d Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Mon, 21 Sep 2015 07:41:04 +0200 Subject: [PATCH 23/36] search_file returns absolute filepath --- patacrep/songs/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index b59883be..69403502 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -223,7 +223,7 @@ class Song: for extension in extensions: fullpath = os.path.join(directory, filename + extension) if os.path.isfile(fullpath): - return fullpath + return os.path.abspath(fullpath) return None def search_image(self, filename): From b9e542cf54b07920ae655e629c20ef5186e3d5f1 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Tue, 22 Sep 2015 14:54:54 +0200 Subject: [PATCH 24/36] Complete HTML template --- .../songs/chordpro/data/html/content_comment | 2 +- .../songs/chordpro/data/html/content_define | 56 ++++++++++--------- .../chordpro/data/html/content_define_list | 3 + .../chordpro/data/html/content_guitar_comment | 2 +- .../songs/chordpro/data/html/content_image | 2 +- .../chordpro/data/html/content_metadata_cover | 3 + .../chordpro/data/html/content_partition | 2 +- .../songs/chordpro/data/html/content_verse | 1 + patacrep/songs/chordpro/data/html/song | 2 + patacrep/songs/chordpro/data/html/song_header | 33 ++++++----- 10 files changed, 60 insertions(+), 46 deletions(-) create mode 100644 patacrep/songs/chordpro/data/html/content_define_list create mode 100644 patacrep/songs/chordpro/data/html/content_metadata_cover diff --git a/patacrep/songs/chordpro/data/html/content_comment b/patacrep/songs/chordpro/data/html/content_comment index 07381cdf..d9dfd2f1 100644 --- a/patacrep/songs/chordpro/data/html/content_comment +++ b/patacrep/songs/chordpro/data/html/content_comment @@ -1 +1 @@ -{comment: (( content.argument ))} +
(( content.argument ))
diff --git a/patacrep/songs/chordpro/data/html/content_define b/patacrep/songs/chordpro/data/html/content_define index 47a93abf..4998f0bd 100644 --- a/patacrep/songs/chordpro/data/html/content_define +++ b/patacrep/songs/chordpro/data/html/content_define @@ -1,25 +1,31 @@ -{define: (( render(content.key) )) -(*- if content.basefret *) - base-fret ((content.basefret)) -(*- endif *) - frets -(*- for string in content.frets -*) - (( " " -)) - (*- if string is none -*) - x - (*- else -*) - (( string -)) - (*- endif -*) -(*- endfor -*) -(* if content.fingers *) - fingers - (*- for finger in content.fingers -*) - (( " " -)) - (* if finger is none -*) - - - (*- else -*) - (( finger -)) - (* endif -*) - (* endfor -*) -(* endif -*) -} + \ No newline at end of file diff --git a/patacrep/songs/chordpro/data/html/content_define_list b/patacrep/songs/chordpro/data/html/content_define_list new file mode 100644 index 00000000..923f37c2 --- /dev/null +++ b/patacrep/songs/chordpro/data/html/content_define_list @@ -0,0 +1,3 @@ +(*- for chord in metadata['define'] *) + ((- render(chord) )) +(* endfor *) diff --git a/patacrep/songs/chordpro/data/html/content_guitar_comment b/patacrep/songs/chordpro/data/html/content_guitar_comment index 1ca0c269..770cc6b0 100644 --- a/patacrep/songs/chordpro/data/html/content_guitar_comment +++ b/patacrep/songs/chordpro/data/html/content_guitar_comment @@ -1 +1 @@ -{guitar_comment: (( content.argument ))} +
(( content.argument ))
diff --git a/patacrep/songs/chordpro/data/html/content_image b/patacrep/songs/chordpro/data/html/content_image index 58e7f904..4e1889b7 100644 --- a/patacrep/songs/chordpro/data/html/content_image +++ b/patacrep/songs/chordpro/data/html/content_image @@ -1 +1 @@ -{image: (( content.argument ))} + diff --git a/patacrep/songs/chordpro/data/html/content_metadata_cover b/patacrep/songs/chordpro/data/html/content_metadata_cover new file mode 100644 index 00000000..96fc7718 --- /dev/null +++ b/patacrep/songs/chordpro/data/html/content_metadata_cover @@ -0,0 +1,3 @@ +(* if 'cov' in metadata -*) +
+(* endif *) \ No newline at end of file diff --git a/patacrep/songs/chordpro/data/html/content_partition b/patacrep/songs/chordpro/data/html/content_partition index 362c4f64..bf2d3164 100644 --- a/patacrep/songs/chordpro/data/html/content_partition +++ b/patacrep/songs/chordpro/data/html/content_partition @@ -1 +1 @@ -{partition: ((content.argument))} +((content.argument)) diff --git a/patacrep/songs/chordpro/data/html/content_verse b/patacrep/songs/chordpro/data/html/content_verse index 4b6073a4..eab4d361 100644 --- a/patacrep/songs/chordpro/data/html/content_verse +++ b/patacrep/songs/chordpro/data/html/content_verse @@ -1,5 +1,6 @@

(*- for line in content.lines -*) + (* if not loop.first *)
(* endif -*) (( render(line) )) (* endfor -*)

diff --git a/patacrep/songs/chordpro/data/html/song b/patacrep/songs/chordpro/data/html/song index 3baa8c22..e774cdff 100644 --- a/patacrep/songs/chordpro/data/html/song +++ b/patacrep/songs/chordpro/data/html/song @@ -1,3 +1,5 @@ (* include 'song_header' *) +
(* include 'song_body' *) +
diff --git a/patacrep/songs/chordpro/data/html/song_header b/patacrep/songs/chordpro/data/html/song_header index b4a59862..cc23ad17 100644 --- a/patacrep/songs/chordpro/data/html/song_header +++ b/patacrep/songs/chordpro/data/html/song_header @@ -1,31 +1,30 @@ -(* if language is defined -*) - {language: (( language ))} -(* endif *) -(* if metadata.columns is defined -*) - {columns: (( metadata.columns ))} -(* endif *) -(* if metadata.capo is defined -*) - {capo: (( metadata.capo ))} -(* endif *) - (*- for title in titles -*) - {title: (( title ))} + (* if loop.first *) +

(( title ))

+ (* else *) +

(( title ))

+ (* endif *) (* endfor -*) (*- for author in authors -*) - {artist: (( author[1] ))(( author[0] ))} +

(( author[1] ))(( author[0] ))

(* endfor *) -(*- for key in ['album', 'copyright', 'cov', 'tag'] *) + +(*- for key in ['album', 'copyright', 'tag', 'columns', 'capo'] *) (* if key in metadata -*) - {(( key )): (( metadata[key] ))} + (( key|capitalize )): (( metadata[key] ))
(* endif *) (* endfor *) +(* if language is defined -*) + Language: (( language ))
+(* endif *) + +(* include 'content_metadata_cover' *) + (*- for key in metadata.keys -*) {key: (( key.keyword )): (( key.argument ))} (* endfor *) -(*- for chord in metadata['define'] *) - ((- render(chord) )) -(* endfor *) +(* include 'content_define_list' *) From 56c7c39b05f2cd0203315a525c4f3e28935cc188 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Tue, 22 Sep 2015 15:07:22 +0200 Subject: [PATCH 25/36] Allow custom path to song templates --- patacrep/songs/chordpro/__init__.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/patacrep/songs/chordpro/__init__.py b/patacrep/songs/chordpro/__init__.py index f6c12ad3..30264445 100644 --- a/patacrep/songs/chordpro/__init__.py +++ b/patacrep/songs/chordpro/__init__.py @@ -14,6 +14,15 @@ class ChordproSong(Song): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.template_paths = [os.path.abspath(pkg_resources.resource_filename(__name__, 'data'))] + + def add_template_path(self, absolute_path): + """Add a template path (at the beginning, so that it's choosen first).""" + self.template_paths.insert(0, absolute_path) + + def get_template_paths(self, output_format): + """Get the template path for a given output_format.""" + return [os.path.join(path, output_format) for path in self.template_paths] def parse(self, config): """Parse content, and return the dictionary of song data.""" @@ -40,10 +49,9 @@ class ChordproSong(Song): "config": self.config, "content": content, } - jinjaenv = Environment(loader=FileSystemLoader(os.path.join( - os.path.abspath(pkg_resources.resource_filename(__name__, 'data')), - output_format, - ))) + jinjaenv = Environment(loader=FileSystemLoader( + self.get_template_paths(output_format) + )) jinjaenv.filters['search_image'] = self.search_image jinjaenv.filters['search_partition'] = self.search_partition From af893a15de9d62d3da542aed8bcbb196957720eb Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Tue, 22 Sep 2015 16:06:35 +0200 Subject: [PATCH 26/36] Search for the images and partitions in HTML template --- patacrep/songs/chordpro/data/html/content_image | 2 +- patacrep/songs/chordpro/data/html/content_partition | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/patacrep/songs/chordpro/data/html/content_image b/patacrep/songs/chordpro/data/html/content_image index 4e1889b7..0da4bcfe 100644 --- a/patacrep/songs/chordpro/data/html/content_image +++ b/patacrep/songs/chordpro/data/html/content_image @@ -1 +1 @@ - + diff --git a/patacrep/songs/chordpro/data/html/content_partition b/patacrep/songs/chordpro/data/html/content_partition index bf2d3164..1075cced 100644 --- a/patacrep/songs/chordpro/data/html/content_partition +++ b/patacrep/songs/chordpro/data/html/content_partition @@ -1 +1 @@ -((content.argument)) +((content.argument)) From 3a9f5aadce19874e6370b60dbadd57b38cd20b93 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 23 Sep 2015 17:37:19 +0200 Subject: [PATCH 27/36] Renamed `Song.parse()` to `Song._parse()` (as this method is private) --- patacrep/songs/__init__.py | 4 ++-- patacrep/songs/chordpro/__init__.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index 69403502..0b7cabeb 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -128,7 +128,7 @@ class Song: self.titles = [] self.data = {} self.cached = None - self.parse(config) + self._parse(config) # Post processing of data self.datadir = datadir @@ -177,7 +177,7 @@ class Song: return getattr(self, method)(output) raise NotImplementedError() - def parse(self, config): # pylint: disable=no-self-use + def _parse(self, config): # pylint: disable=no-self-use """Parse song. It set the following attributes: diff --git a/patacrep/songs/chordpro/__init__.py b/patacrep/songs/chordpro/__init__.py index 30264445..c0328d4b 100644 --- a/patacrep/songs/chordpro/__init__.py +++ b/patacrep/songs/chordpro/__init__.py @@ -24,7 +24,7 @@ class ChordproSong(Song): """Get the template path for a given output_format.""" return [os.path.join(path, output_format) for path in self.template_paths] - def parse(self, config): + def _parse(self, config): """Parse content, and return the dictionary of song data.""" with encoding.open_read(self.fullpath, encoding=self.encoding) as song: song = parse_song(song.read(), self.fullpath) From fe26591b7268bd87aa0b0fa6e07005c703e45813 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 23 Sep 2015 17:56:45 +0200 Subject: [PATCH 28/36] Deleted useless parameter. May be added later (I think it does break some tests) --- patacrep/songs/chordpro/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patacrep/songs/chordpro/__init__.py b/patacrep/songs/chordpro/__init__.py index c0328d4b..bcaa68bd 100644 --- a/patacrep/songs/chordpro/__init__.py +++ b/patacrep/songs/chordpro/__init__.py @@ -41,7 +41,6 @@ class ChordproSong(Song): content = self.cached['song'].content context = { 'language': self.languages[0], - "path": files.relpath(self.fullpath, os.path.dirname(output)), "titles": self.titles, "authors": self.authors, "metadata": self.data, @@ -49,6 +48,7 @@ class ChordproSong(Song): "config": self.config, "content": content, } + jinjaenv = Environment(loader=FileSystemLoader( self.get_template_paths(output_format) )) From b73027c865b29ff199bca0b086b9eacca1404062 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 23 Sep 2015 19:38:37 +0200 Subject: [PATCH 29/36] Clarified signature of `patacrep.songs.ChordproSong.render()` --- patacrep/songs/__init__.py | 6 +++--- patacrep/songs/chordpro/__init__.py | 6 ++---- patacrep/songs/latex/__init__.py | 2 ++ test/test_chordpro/test_parser.py | 1 - 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index 0b7cabeb..0eb0b7e2 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -165,16 +165,16 @@ class Song: def __repr__(self): return repr((self.titles, self.data, self.fullpath)) - def render(self, output, output_format): + def render(self, output_format, output=None, *args, **kwargs): """Return the code rendering this song. Arguments: - - output: Name of the output file. - output_format: Format of the output file (latex, chordpro...) + - output: Name of the output file, or `None` if irrelevant. """ method = "render_{}".format(output_format) if hasattr(self, method): - return getattr(self, method)(output) + return getattr(self, method)(output, *args, **kwargs) raise NotImplementedError() def _parse(self, config): # pylint: disable=no-self-use diff --git a/patacrep/songs/chordpro/__init__.py b/patacrep/songs/chordpro/__init__.py index bcaa68bd..3ba87169 100644 --- a/patacrep/songs/chordpro/__init__.py +++ b/patacrep/songs/chordpro/__init__.py @@ -36,9 +36,7 @@ class ChordproSong(Song): 'song': song, } - def render(self, output, output_format, content=None, template="song"): # pylint: disable=arguments-differ - if content is None: - content = self.cached['song'].content + def render(self, output_format, output=None, template="song"): context = { 'language': self.languages[0], "titles": self.titles, @@ -46,7 +44,7 @@ class ChordproSong(Song): "metadata": self.data, "render": self._render_ast, "config": self.config, - "content": content, + "content": self.cached['song'].content, } jinjaenv = Environment(loader=FileSystemLoader( diff --git a/patacrep/songs/latex/__init__.py b/patacrep/songs/latex/__init__.py index a0d9c945..805f77f2 100644 --- a/patacrep/songs/latex/__init__.py +++ b/patacrep/songs/latex/__init__.py @@ -30,6 +30,8 @@ class LatexSong(Song): def render_latex(self, output): """Return the code rendering the song.""" + if output is None: + raise ValueError(output) path = files.path2posix(files.relpath( self.fullpath, os.path.dirname(output) diff --git a/test/test_chordpro/test_parser.py b/test/test_chordpro/test_parser.py index 2bdd15aa..796fdb2a 100644 --- a/test/test_chordpro/test_parser.py +++ b/test/test_chordpro/test_parser.py @@ -48,7 +48,6 @@ class TestParsingRendering(unittest.TestCase): with self.subTest(base=os.path.basename(base), format=dest): self.assertMultiLineEqual( ChordproSong(None, chordproname, config).render( - output=chordproname, output_format=LANGUAGES[dest], ).strip(), expectfile.read().strip(), From 1a6507f175e57d3542f996742f71e7b7f29abd6b Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 23 Sep 2015 19:50:45 +0200 Subject: [PATCH 30/36] Template directories is now an argument to the `render()` method --- patacrep/songs/chordpro/__init__.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/patacrep/songs/chordpro/__init__.py b/patacrep/songs/chordpro/__init__.py index 3ba87169..a04d6dd5 100644 --- a/patacrep/songs/chordpro/__init__.py +++ b/patacrep/songs/chordpro/__init__.py @@ -14,15 +14,12 @@ class ChordproSong(Song): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.template_paths = [os.path.abspath(pkg_resources.resource_filename(__name__, 'data'))] - def add_template_path(self, absolute_path): - """Add a template path (at the beginning, so that it's choosen first).""" - self.template_paths.insert(0, absolute_path) - - def get_template_paths(self, output_format): - """Get the template path for a given output_format.""" - return [os.path.join(path, output_format) for path in self.template_paths] + @staticmethod + def iter_template_paths(templatedirs, output_format): + for directory in templatedirs: + yield os.path.join(directory, output_format) + yield os.path.join(os.path.abspath(pkg_resources.resource_filename(__name__, 'data')), output_format) def _parse(self, config): """Parse content, and return the dictionary of song data.""" @@ -36,7 +33,10 @@ class ChordproSong(Song): 'song': song, } - def render(self, output_format, output=None, template="song"): + def render(self, output_format, output=None, template="song", templatedirs=None): + if templatedirs is None: + templatedirs = [] + context = { 'language': self.languages[0], "titles": self.titles, @@ -48,7 +48,7 @@ class ChordproSong(Song): } jinjaenv = Environment(loader=FileSystemLoader( - self.get_template_paths(output_format) + self.iter_template_paths(templatedirs, output_format) )) jinjaenv.filters['search_image'] = self.search_image jinjaenv.filters['search_partition'] = self.search_partition From becea663ea1cab7305732e16d7dc4d6e74eecbcb Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 23 Sep 2015 20:18:47 +0200 Subject: [PATCH 31/36] Silent changes to `seach_*()` methods; using `search_*()` filters in templates --- patacrep/songs/__init__.py | 25 ++++++++++++------- .../chordpro/data/chordpro/content_image | 2 +- .../chordpro/data/chordpro/content_partition | 2 +- .../songs/chordpro/data/chordpro/song_header | 5 +++- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index 0eb0b7e2..792347d3 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -206,16 +206,20 @@ class Song: """Search for a file name. :param str filename: The name, as provided in the chordpro file (with or without extension). - :param list extensions: Possible extensions (with '.') + :param list extensions: Possible extensions (with '.'). Default is no extension. :param iterator directories: Other directories where to search for the file The directory where the Song file is stored is added to the list. Returns None if nothing found. + + This function can also be used as a preprocessor for a renderer: for + instance, it can compile a file, place it in a temporary folder, and + return the path to the compiled file. """ if extensions is None: extensions = [''] if directories is None: - directories = [] + directories = self.config['datadir'] songdir = os.path.dirname(self.fullpath) @@ -228,8 +232,16 @@ class Song: def search_image(self, filename): """Search for an image file""" - datadir_img = self.get_datadirs('img') - filepath = self.search_file(filename, ['', '.jpg', '.png'], datadir_img) + filepath = self.search_file( + filename, + ['', '.jpg', '.png'], + self.get_datadirs('img'), + ) + return filepath if filepath else filename + + def search_partition(self, filename): + """Search for a lilypond file""" + filepath = self.search_file(filename, ['', '.ly']) return filepath if filepath else filename @property @@ -241,11 +253,6 @@ class Song: datadir_img = self.get_datadirs('img') return self.search_file(filename, ['', '.jpg', '.png'], datadir_img) - def search_partition(self, filename): - """Search for a lilypond file""" - filepath = self.search_file(filename, ['', '.ly']) - return filepath if filepath else filename - def unprefixed_title(title, prefixes): """Remove the first prefix of the list in the beginning of title (if any). """ diff --git a/patacrep/songs/chordpro/data/chordpro/content_image b/patacrep/songs/chordpro/data/chordpro/content_image index 58e7f904..4f9bf06c 100644 --- a/patacrep/songs/chordpro/data/chordpro/content_image +++ b/patacrep/songs/chordpro/data/chordpro/content_image @@ -1 +1 @@ -{image: (( content.argument ))} +{image: (( content.argument|search_image ))} diff --git a/patacrep/songs/chordpro/data/chordpro/content_partition b/patacrep/songs/chordpro/data/chordpro/content_partition index 362c4f64..43b8761e 100644 --- a/patacrep/songs/chordpro/data/chordpro/content_partition +++ b/patacrep/songs/chordpro/data/chordpro/content_partition @@ -1 +1 @@ -{partition: ((content.argument))} +{partition: ((content.argument|search_partition))} diff --git a/patacrep/songs/chordpro/data/chordpro/song_header b/patacrep/songs/chordpro/data/chordpro/song_header index b4a59862..d3e65f0b 100644 --- a/patacrep/songs/chordpro/data/chordpro/song_header +++ b/patacrep/songs/chordpro/data/chordpro/song_header @@ -16,11 +16,14 @@ {artist: (( author[1] ))(( author[0] ))} (* endfor *) -(*- for key in ['album', 'copyright', 'cov', 'tag'] *) +(*- for key in ['album', 'copyright', 'tag'] *) (* if key in metadata -*) {(( key )): (( metadata[key] ))} (* endif *) (* endfor *) +(* if 'cov' in metadata -*) + {(( 'cov' )): (( metadata['cov'].argument|search_image ))} +(* endif *) (*- for key in metadata.keys -*) {key: (( key.keyword )): (( key.argument ))} From 3779567fea21d4c1f45154fdfa01963550ce17f0 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 23 Sep 2015 21:27:26 +0200 Subject: [PATCH 32/36] Deleted method `cover_filepath` --- patacrep/songs/__init__.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index 792347d3..d1f24a99 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -244,15 +244,6 @@ class Song: filepath = self.search_file(filename, ['', '.ly']) return filepath if filepath else filename - @property - def cover_filepath(self): - """Get the path to the cover file (or None if not found)""" - filename = str(self.data.get('cov', '')) - if not filename: - return None - datadir_img = self.get_datadirs('img') - return self.search_file(filename, ['', '.jpg', '.png'], datadir_img) - def unprefixed_title(title, prefixes): """Remove the first prefix of the list in the beginning of title (if any). """ From 8e480a3342b709dd1e12193acdca61b7e7e579ca Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 23 Sep 2015 21:47:51 +0200 Subject: [PATCH 33/36] Pylint --- patacrep/songs/chordpro/__init__.py | 14 ++++++++++++-- patacrep/songs/latex/__init__.py | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/patacrep/songs/chordpro/__init__.py b/patacrep/songs/chordpro/__init__.py index a04d6dd5..6d4c0e39 100644 --- a/patacrep/songs/chordpro/__init__.py +++ b/patacrep/songs/chordpro/__init__.py @@ -17,9 +17,19 @@ class ChordproSong(Song): @staticmethod def iter_template_paths(templatedirs, output_format): + """Iterate over paths in which templates are to be searched. + + :param iterator templatedirs: Iterators of additional directories (the + default hard-coded template directory is returned last). + :param str output_format: Song output format, which is appended to + each directory. + """ for directory in templatedirs: yield os.path.join(directory, output_format) - yield os.path.join(os.path.abspath(pkg_resources.resource_filename(__name__, 'data')), output_format) + yield os.path.join( + os.path.abspath(pkg_resources.resource_filename(__name__, 'data')), + output_format, + ) def _parse(self, config): """Parse content, and return the dictionary of song data.""" @@ -33,7 +43,7 @@ class ChordproSong(Song): 'song': song, } - def render(self, output_format, output=None, template="song", templatedirs=None): + def render(self, output_format, output=None, template="song", templatedirs=None): # pylint: disable=arguments-differ if templatedirs is None: templatedirs = [] diff --git a/patacrep/songs/latex/__init__.py b/patacrep/songs/latex/__init__.py index 805f77f2..6ac54c81 100644 --- a/patacrep/songs/latex/__init__.py +++ b/patacrep/songs/latex/__init__.py @@ -14,7 +14,7 @@ from patacrep.songs import Song class LatexSong(Song): """LaTeX song parser.""" - def parse(self, __config): + def _parse(self, __config): """Parse content, and return the dictinory of song data.""" with encoding.open_read(self.fullpath, encoding=self.encoding) as song: self.data = parse_song(song.read(), self.fullpath) From 8b79dd881e98a569c0f05c2aa2ea0c41dc1542c2 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 23 Sep 2015 23:27:22 +0200 Subject: [PATCH 34/36] Close file after reading --- patacrep/encoding.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/patacrep/encoding.py b/patacrep/encoding.py index b8bdc8e6..fd58fc2f 100644 --- a/patacrep/encoding.py +++ b/patacrep/encoding.py @@ -16,7 +16,8 @@ def open_read(filename, mode='r', encoding=None): If `encoding` is set, use it as the encoding (do not guess). """ if encoding is None: - fileencoding = chardet.detect(open(filename, 'rb').read())['encoding'] + with open(filename, 'rb') as file: + fileencoding = chardet.detect(file.read())['encoding'] else: fileencoding = encoding From f21bdda2ef520cc57b097595f245ea7d61fc1692 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 23 Sep 2015 23:27:38 +0200 Subject: [PATCH 35/36] [test] One class contain one test method per file Instead of one method with one subtest per file Closes #92 --- test/test_chordpro/test_parser.py | 75 ++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/test/test_chordpro/test_parser.py b/test/test_chordpro/test_parser.py index 2bdd15aa..8f857156 100644 --- a/test/test_chordpro/test_parser.py +++ b/test/test_chordpro/test_parser.py @@ -16,22 +16,15 @@ LANGUAGES = { 'sgc': 'chordpro', } -class TestParsingRendering(unittest.TestCase): - """Test parsing and rendering""" +class FileTestMeta(type): + """Metaclass that creates on-the-fly test function according to files. - maxDiff = None + See the :class:`FileTest` documentation for more information. + """ - def test_all(self): - """Test of chorpro parser, and several renderers. + def __init__(cls, name, bases, nmspc): + super().__init__(name, bases, nmspc) - For any given `foo.source`, it is parsed as a chordpro file, and - should be rendered as `foo.sgc` with the chordpro renderer, and - `foo.tex` with the latex renderer. - """ - config = DEFAULT_CONFIG.copy() - config.update({ - 'encoding': 'utf8', - }) for source in sorted(glob.glob(os.path.join( os.path.dirname(__file__), '*.source', @@ -41,15 +34,47 @@ class TestParsingRendering(unittest.TestCase): destname = "{}.{}".format(base, dest) if not os.path.exists(destname): continue - with open(destname, 'r', encoding='utf8') as expectfile: - chordproname = "{}.source".format(base) - config['filename'] = chordproname - with disable_logging(): - with self.subTest(base=os.path.basename(base), format=dest): - self.assertMultiLineEqual( - ChordproSong(None, chordproname, config).render( - output=chordproname, - output_format=LANGUAGES[dest], - ).strip(), - expectfile.read().strip(), - ) + setattr( + cls, + "test_{}_{}".format(os.path.basename(base), dest), + cls._create_test(base, dest), + ) + + @staticmethod + def _create_test(base, dest): + """Return a function testing that `base` compilation in `dest` format. + """ + + def test_parse_render(self): + """Test that `base` is correctly parsed and rendered.""" + if base is None or dest is None: + return + destname = "{}.{}".format(base, dest) + with open(destname, 'r', encoding='utf8') as expectfile: + chordproname = "{}.source".format(base) + with disable_logging(): + self.assertMultiLineEqual( + ChordproSong(None, chordproname, DEFAULT_CONFIG).render( + output=chordproname, + output_format=LANGUAGES[dest], + ).strip(), + expectfile.read().strip(), + ) + + test_parse_render.__doc__ = ( + "Test that '{base}' is correctly parsed and rendererd into '{format}' format." + ).format(base=os.path.basename(base), format=dest) + return test_parse_render + +class FileTest(unittest.TestCase, metaclass=FileTestMeta): + """Test of chorpro parser, and several renderers. + + For any given `foo.source`, it is parsed as a chordpro file, and should be + rendered as `foo.sgc` with the chordpro renderer, and `foo.tex` with the + latex renderer. + + This class does nothing by itself, but its metaclass populates it with test + methods testing parser and renderers. + """ + + maxDiff = None From c11a59db7bf7f36a54688334509100d872bdbb04 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 24 Sep 2015 10:49:42 +0200 Subject: [PATCH 36/36] Allow the search_image and search_partition to return None --- patacrep/songs/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index d1f24a99..297a993e 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -230,19 +230,19 @@ class Song: return os.path.abspath(fullpath) return None - def search_image(self, filename): + def search_image(self, filename, none_if_not_found=False): """Search for an image file""" filepath = self.search_file( filename, ['', '.jpg', '.png'], self.get_datadirs('img'), ) - return filepath if filepath else filename + return filepath if none_if_not_found or filepath else filename - def search_partition(self, filename): + def search_partition(self, filename, none_if_not_found=False): """Search for a lilypond file""" filepath = self.search_file(filename, ['', '.ly']) - return filepath if filepath else filename + return filepath if none_if_not_found or filepath else filename def unprefixed_title(title, prefixes): """Remove the first prefix of the list in the beginning of title (if any).