diff --git a/patacrep/content/song.py b/patacrep/content/song.py index 4f9ab809..45bbfeac 100755 --- a/patacrep/content/song.py +++ b/patacrep/content/song.py @@ -33,6 +33,7 @@ class SongRenderer(Content): """Return the string to end a block.""" return r'\end{songs}' + #pylint: disable=unused-argument def render(self, context): """Return the string that will render the song.""" return textwrap.dedent("""\ @@ -43,7 +44,7 @@ class SongRenderer(Content): """).format( separator="%"*80, path=files.path2posix(self.song.subpath), - song=self.song.render(output=context['filename']), + song=self.song.render(), ) def __lt__(self, other): diff --git a/patacrep/data/templates/layout.tex b/patacrep/data/templates/layout.tex index 099772a4..28dba5e8 100644 --- a/patacrep/data/templates/layout.tex +++ b/patacrep/data/templates/layout.tex @@ -28,7 +28,7 @@ \makeatletter \def\input@path{ % (* for dir in datadir *) - {(( path2posix(dir) ))/latex/} % + {(( dir | path2posix ))/latex/} % (* endfor *) } \makeatother diff --git a/patacrep/data/templates/songs.tex b/patacrep/data/templates/songs.tex index e3562897..da48fdd7 100644 --- a/patacrep/data/templates/songs.tex +++ b/patacrep/data/templates/songs.tex @@ -81,15 +81,15 @@ (( super() )) (* for lang in _langs|sort -*) - \PassOptionsToPackage{(( lang2babel(lang) ))}{babel} + \PassOptionsToPackage{(( lang | lang2babel ))}{babel} (* endfor *) -\usepackage[(( lang2babel(lang) ))]{babel} -\lang{(( lang2babel(lang) ))} +\usepackage[(( lang | lang2babel ))]{babel} +\lang{(( lang | lang2babel ))} \usepackage{graphicx} \graphicspath{ % (* for dir in datadir *) - {(( path2posix(dir) ))/} % + {(( dir | path2posix ))/} % (* endfor *) } diff --git a/patacrep/data/templates/songs/chordpro/latex/song b/patacrep/data/templates/songs/chordpro/latex/song index e8016107..cb29cc29 100644 --- a/patacrep/data/templates/songs/chordpro/latex/song +++ b/patacrep/data/templates/songs/chordpro/latex/song @@ -1,5 +1,5 @@ (* if lang is defined -*) - \selectlanguage{(( lang2babel(lang) ))} + \selectlanguage{(( lang | lang2babel ))} (* endif *) (*- if metadata.columns is defined *) diff --git a/patacrep/songs/__init__.py b/patacrep/songs/__init__.py index 922ecb00..d19e22df 100644 --- a/patacrep/songs/__init__.py +++ b/patacrep/songs/__init__.py @@ -80,7 +80,7 @@ class Song: # Version format of cached song. Increment this number if we update # information stored in cache. - CACHE_VERSION = 2 + CACHE_VERSION = 3 # List of attributes to cache cached_attributes = [ @@ -88,7 +88,6 @@ class Song: "unprefixed_titles", "cached", "data", - "subpath", "lang", "authors", "_filehash", @@ -98,44 +97,30 @@ class Song: def __init__(self, subpath, config, *, datadir=None): if datadir is None: self.datadir = "" + # Only songs in datadirs may be cached + self.use_cache = False else: self.datadir = datadir + self.use_cache = config.get('_cache', False) + self.fullpath = os.path.join(self.datadir, subpath) + self.subpath = subpath + self._filehash = None self.encoding = config["encoding"] + self.default_lang = config["lang"] self.config = config - if self.datadir and self.config['_cache']: - # Only songs in datadirs are cached - self._filehash = hashlib.md5( - open(self.fullpath, 'rb').read() - ).hexdigest() - if os.path.exists(cached_name(datadir, subpath)): - try: - cached = pickle.load(open( - cached_name(datadir, subpath), - 'rb', - )) - if ( - cached['_filehash'] == self._filehash - and cached['_version'] == self.CACHE_VERSION - ): - for attribute in self.cached_attributes: - setattr(self, attribute, cached[attribute]) - return - except: # pylint: disable=bare-except - LOGGER.warning("Could not use cached version of {}.".format( - self.fullpath - )) + if self._cache_retrieved(): + return # Data extraction from the latex song self.titles = [] self.data = {} self.cached = None self.lang = None - self._parse(config) + self._parse() # Post processing of data - self.subpath = subpath self.unprefixed_titles = [ unprefixed_title( title, @@ -153,22 +138,52 @@ class Song: self._version = self.CACHE_VERSION self._write_cache() + @property + def cached_name(self): + """Name of the file used for the cache""" + return cached_name(self.datadir, self.subpath) + + @property + def filehash(self): + """Compute (and cache) the md5 hash of the file""" + if self._filehash is None: + self._filehash = hashlib.md5( + open(self.fullpath, 'rb').read() + ).hexdigest() + return self._filehash + + def _cache_retrieved(self): + """If relevant, retrieve self from the cache.""" + if self.use_cache and os.path.exists(self.cached_name): + try: + cached = pickle.load(open(self.cached_name, 'rb',)) + if ( + cached['_filehash'] == self.filehash + and cached['_version'] == self.CACHE_VERSION + ): + for attribute in self.cached_attributes: + setattr(self, attribute, cached[attribute]) + return True + except: # pylint: disable=bare-except + LOGGER.warning("Could not use cached version of {}.".format( + self.fullpath + )) + return False + def _write_cache(self): """If relevant, write a dumbed down version of self to the cache.""" - if self.datadir and self.config['_cache']: - cached = {} - for attribute in self.cached_attributes: - cached[attribute] = getattr(self, attribute) + if self.use_cache: + cached = {attr: getattr(self, attr) for attr in self.cached_attributes} pickle.dump( cached, - open(cached_name(self.datadir, self.subpath), 'wb'), + open(self.cached_name, 'wb'), protocol=-1 ) def __repr__(self): return repr((self.titles, self.data, self.fullpath)) - def render(self, output=None, *args, **kwargs): + def render(self, *args, **kwargs): """Return the code rendering this song. Arguments: @@ -176,14 +191,14 @@ class Song: """ raise NotImplementedError() - def _parse(self, config): # pylint: disable=no-self-use + def _parse(self): # pylint: disable=no-self-use """Parse song. It set the following attributes: - titles: the list of (raw) titles. This list will be processed to remove prefixes. - - lang: the main language of the song, as language code.. + - lang: the main language of the song, as language code. - authors: the list of (raw) authors. This list will be processed to 'clean' it (see function :func:`patacrep.authors.processauthors`). - data: song metadata. Used (among others) to sort the songs. diff --git a/patacrep/songs/chordpro/__init__.py b/patacrep/songs/chordpro/__init__.py index 0be23592..330b2f3e 100644 --- a/patacrep/songs/chordpro/__init__.py +++ b/patacrep/songs/chordpro/__init__.py @@ -25,26 +25,25 @@ class ChordproSong(Song): output_language = None - def _parse(self, config): + def _parse(self): """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) self.authors = song.authors self.titles = song.titles - self.lang = song.get_data_argument('language', self.config['lang']) + self.lang = song.get_data_argument('language', self.default_lang) self.data = song.meta self.cached = { 'song': song, } - def render(self, output=None, template="song"): # pylint: disable=arguments-differ + def render(self, template="song"): # pylint: disable=arguments-differ context = { 'lang': self.lang, "titles": self.titles, "authors": self.authors, "metadata": self.data, "render": self._render_ast, - "config": self.config, "content": self.cached['song'].content, } diff --git a/patacrep/songs/latex/__init__.py b/patacrep/songs/latex/__init__.py index 8dd8d15b..d19cd10e 100644 --- a/patacrep/songs/latex/__init__.py +++ b/patacrep/songs/latex/__init__.py @@ -15,7 +15,7 @@ class Latex2LatexSong(Song): """Song written in LaTeX, rendered in LaTeX""" # pylint: disable=abstract-method - def _parse(self, __config): + def _parse(self): """Parse content, and return the dictionary of song data.""" with encoding.open_read(self.fullpath, encoding=self.encoding) as song: self.data = parse_song(song.read(), self.fullpath) @@ -29,16 +29,12 @@ class Latex2LatexSong(Song): else: self.authors = [] - def render(self, output): + def render(self): """Return the code rendering the song.""" # pylint: disable=signature-differs - if output is None: - raise ValueError(output) - path = files.path2posix(files.relpath( - self.fullpath, - os.path.dirname(output) - )) - return r'\import{{{}/}}{{{}}}'.format(os.path.dirname(path), os.path.basename(path)) + filename = os.path.basename(self.fullpath) + path = os.path.abspath(os.path.dirname(self.fullpath)) + return r'\import{{{}/}}{{{}}}'.format(files.path2posix(path), filename) def set_lang(self, language): """Set the language code""" diff --git a/patacrep/templates.py b/patacrep/templates.py index 6a799295..612a99e4 100644 --- a/patacrep/templates.py +++ b/patacrep/templates.py @@ -84,8 +84,8 @@ class Renderer: self.jinjaenv.filters['escape_tex'] = _escape_tex self.jinjaenv.trim_blocks = True self.jinjaenv.lstrip_blocks = True - self.jinjaenv.globals["path2posix"] = files.path2posix - self.jinjaenv.globals["lang2babel"] = lang2babel + self.jinjaenv.filters["path2posix"] = files.path2posix + self.jinjaenv.filters["lang2babel"] = lang2babel self.template = self.jinjaenv.get_template(template) diff --git a/test/test_chordpro/test_parser.py b/test/test_chordpro/test_parser.py index 39c37052..6834af4b 100644 --- a/test/test_chordpro/test_parser.py +++ b/test/test_chordpro/test_parser.py @@ -62,7 +62,7 @@ class FileTest(unittest.TestCase, metaclass=dynamic.DynamicTest): with disable_logging(): song = self.song_plugins[LANGUAGES[destformat]]['sgc'](sourcename, self.config) self.assertMultiLineEqual( - song.render(output=sourcename).strip(), + song.render().strip(), expectfile.read().strip(), ) diff --git a/test/test_compilation/datadir.tex.control b/test/test_compilation/datadir.tex.control index a003cd42..14b11cb1 100644 --- a/test/test_compilation/datadir.tex.control +++ b/test/test_compilation/datadir.tex.control @@ -92,7 +92,7 @@ guitar, %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% songs/./datadir.sg -\import{datadir_datadir/songs/}{datadir.sg} +\import{@TEST_FOLDER@/datadir_datadir/songs/}{datadir.sg} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% songs/./datadir.sgc @@ -119,7 +119,7 @@ Chordpro}[ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% songs/./datadir2.sg -\import{datadir_datadir/songs/}{datadir2.sg} +\import{@TEST_FOLDER@/datadir_datadir/songs/}{datadir2.sg} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% songs/./datadir2.sgc @@ -146,7 +146,7 @@ Chordpro}[ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% songs/./relative.sg -\import{datadir_datadir/songs/}{relative.sg} +\import{@TEST_FOLDER@/datadir_datadir/songs/}{relative.sg} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% songs/./relative.sgc @@ -173,7 +173,7 @@ Chordpro}[ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% songs/./subdir/subdir.sg -\import{datadir_datadir/songs/subdir/}{subdir.sg} +\import{@TEST_FOLDER@/datadir_datadir/songs/subdir/}{subdir.sg} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% songs/./subdir/subdir.sgc