From d6c741d40bed6466537c452ff8dc7549ea7f9991 Mon Sep 17 00:00:00 2001 From: Oliverpool Date: Thu, 17 Sep 2015 14:31:49 +0200 Subject: [PATCH 01/12] 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 02/12] 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 03/12] 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 04/12] 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 05/12] 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 06/12] 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 07/12] 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 08/12] 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 09/12] 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 10/12] 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 11/12] 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 12/12] 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):