def parse(keyword, config, argument, contentlist): """Return a list songs included in contentlist, whith a different base path. Arguments: - keyword: unused; - config: the current songbook configuration dictionary; - argument: a directory; - contentlist: songbook content, that is parsed by patacrep.content.process_content(). This function adds 'argument' to the directories where songs are searched for, and then processes the content. The 'argument' is added: - first as a relative path to the current directory; - then as a relative path to every path already present in config['songdir']. """ old_songdir = config['_songdir'] config['_songdir'] = ( [argument] + [os.path.join(path, argument) for path in config['_songdir']] + config['_songdir'] ) processed_content = process_content(contentlist, config) config['_songdir'] = old_songdir return processed_content
def parse(keyword, config, argument): """Return a list of songs, whith an another base path. Arguments: - keyword: unused; - config: the current songbook configuration dictionary; - argument: a dict containing: path: string specifying the path to add to the songdirs list; content: songbook content, that is parsed by patacrep.content.process_content(). This function adds 'path' to the directories where songs are searched for, and then processes the content. The 'path' is added as a relative path to the dir of the songbook file. """ subpath = argument['path'] old_songdir = config['_songdir'] config['_songdir'] = [DataSubpath(config['_songbookfile_dir'], subpath)] config['_songdir'].extend(old_songdir) processed_content = process_content(argument.get('content'), config) config['_songdir'] = old_songdir return processed_content
def parse(keyword, config, argument): """Return a list of songs, whith an another base path. Arguments: - keyword: unused; - config: the current songbook configuration dictionary; - argument: a dict containing: path: string specifying the path to add to the songdirs list; content: songbook content, that is parsed by patacrep.content.process_content(). This function adds 'path' to the directories where songs are searched for, and then processes the content. The 'path' is added as a relative path to the dir of the songbook file. """ subpath = argument["path"] old_songdir = config["_songdir"] config["_songdir"] = [DataSubpath(config["_songbookfile_dir"], subpath)] config["_songdir"].extend(old_songdir) processed_content = process_content(argument.get("content"), config) config["_songdir"] = old_songdir return processed_content
def parse(keyword, config, argument): """Return a sorted list of songs. Arguments: - keyword: the string 'sort'; - config: the current songbook configuration dictionary; - argument: a dict of: key: the list of the fields used to sort songs (e.g. "by", "album", "title") content: content to be sorted. If this content contain something else than a song, an exception is raised. """ if argument is None: argument = {} sort = argument.get('key', DEFAULT_SORT) if isinstance(sort, str): sort = [sort] try: songlist = process_content(argument.get('content'), config) except OnlySongsError as error: raise ContentError( keyword, ("Content list of this keyword can be only songs (or content " "that result into songs), and the following are not:" + str(error.not_songs))) return sorted(songlist, key=key_generator(sort))
def parse(keyword, config, argument): """Return a sorted list of songs. Arguments: - keyword: the string 'sort'; - config: the current songbook configuration dictionary; - argument: a dict of: key: the list of the fields used to sort songs (e.g. "by", "album", "title") content: content to be sorted. If this content contain something else than a song, an exception is raised. """ if argument is None: argument = {} sort = argument.get('key', DEFAULT_SORT) if isinstance(sort, str): sort = [sort] try: songlist = process_content(argument.get('content'), config) except OnlySongsError as error: raise ContentError(keyword, ( "Content list of this keyword can be only songs (or content " "that result into songs), and the following are not:" + str(error.not_songs) )) return sorted(songlist, key=key_generator(sort))
def write_tex(self, output): """Build the '.tex' file corresponding to self. Arguments: - output: a file object, in which the file will be written. """ # Updating configuration config = DEFAULT_CONFIG config.update(self.config) renderer = TexRenderer( config['template'], config['datadir'], config['lang'], ) config.update(self.config) config.update(renderer.get_variables()) config['authwords'] = authors.compile_authwords(config['authwords']) self.config = config # Configuration set self.contentlist = content.process_content( self.config.get('content', []), self.config, ) self.config['render_content'] = content.render_content self.config['titleprefixkeys'] = ["after", "sep", "ignore"] self.config['content'] = self.contentlist self.config['filename'] = output.name[:-4] renderer.render_tex(output, self.config)
def parse(keyword, config, argument, contentlist): """Return a sorted list of songs contained in 'contentlist'. Arguments: - keyword: the string 'sorted'; - config: the current songbook configuration dictionary; - argument: the list of the fields used to sort songs, as strings separated by commas (e.g. "by, album, @title"); - contentlist: the list of content to be sorted. If this content contain something else than a song, an exception is raised. """ if argument: sort = [key.strip() for key in argument.split(",")] else: sort = DEFAULT_SORT try: songlist = process_content(contentlist, config) except OnlySongsError as error: return EmptyContentList( errors=[ ContentError( keyword, ( "Content list of this keyword can be only songs (or content " "that result into songs), and the following are not:" + str(error.not_songs) ), ) ] ) return sorted(songlist, key=key_generator(sort))
def write_tex(self, output): """Build the '.tex' file corresponding to self. Arguments: - output: a file object, in which the file will be written. """ # Updating configuration self._config = self._raw_config.copy() renderer = TexBookRenderer( self._config['book']['template'], self._config['_datadir'], self._config['book']['lang'], self._config['book']['encoding'], ) try: self._config['_template'] = renderer.get_all_variables(self._config.get('template', {})) except errors.SchemaError as exception: exception.message = "The songbook file '{}' is not valid\n{}".format( self.basename, exception.message) raise exception self._config['_compiled_authwords'] = authors.compile_authwords( copy.deepcopy(self._config['authors']) ) # Loading custom plugins self._config['_content_plugins'] = files.load_plugins( datadirs=self._config['_datadir'], root_modules=['content'], keyword='CONTENT_PLUGINS', ) self._config['_song_plugins'] = files.load_plugins( datadirs=self._config['_datadir'], root_modules=['songs'], keyword='SONG_RENDERERS', )['tsg'] # Configuration set self._config['render'] = content.render self._config['content'] = content.process_content( self._config.get('content', []), self._config, ) self._config['filename'] = output.name[:-4] self._config['_bookoptions'] = iter_bookoptions(self._config) renderer.render_tex(output, self._config) # Get all errors, and maybe exit program self._errors.extend(renderer.errors) if self._config['_error'] == "failonbook": if self.has_errors(): raise errors.SongbookError("Some songs contain errors. Stopping as requested.")
def get_content_items(self): """Return: a list of ContentItem objects, corresponding to the content to be included in the .tex file. """ content_config = self._raw_config.copy() # Updates the '_langs' key content_items = content.process_content( content_config.get('content', []), content_config, ) content_langs = content_config['_langs'] return content_langs, content_items
def process_songs(content, config=None): """Process content that containt only songs. Call patacrep.content.process_content(), checks if the returned list contains only songs, and raise an exception if not. """ contentlist = process_content(content, config) not_songs = [ item for item in contentlist if not isinstance(item, SongRenderer) ] if not_songs: raise OnlySongsError(not_songs) return contentlist
def write_tex(self, output): """Build the '.tex' file corresponding to self. Arguments: - output: a file object, in which the file will be written. """ # Updating configuration self._config = self._raw_config.copy() renderer = TexBookRenderer( self._config['book']['template'], self._config['_datadir'], self._config['book']['lang'], self._config['book']['encoding'], ) try: self._config['_template'] = renderer.get_all_variables( self._config.get('template', {})) except errors.SchemaError as exception: exception.message = "The songbook file '{}' is not valid\n{}".format( self.basename, exception.message) raise exception self._config['_compiled_authwords'] = authors.compile_authwords( copy.deepcopy(self._config['authors'])) # Configuration set self._config['render'] = content.render self._config['content'] = content.process_content( self._config.get('content', []), self._config, ) self._config['filename'] = output.name[:-4] # Processing special options self._config['_bookoptions'] = iter_bookoptions(self._config) self._config['chords']['_notenames'] = self._get_chord_names( self._config['chords']['notation']) renderer.render_tex(output, self._config) # Get all errors, and maybe exit program self._errors.extend(renderer.errors) if self._config['_error'] == "failonbook": if self.has_errors(): raise errors.SongbookError( "Some songs contain errors. Stopping as requested.")
def test_content(self): """Test that `base.source` produces the correct file list""" sourcename = "{}.source".format(base) with open(sourcename, mode="r", encoding="utf8") as sourcefile: sbcontent = json.load(sourcefile) with logging_reduced('patacrep.content.song'): expandedlist = content.process_content(sbcontent, cls.config.copy()) sourcelist = [cls._clean_path(elem) for elem in expandedlist] controlname = "{}.control".format(base) if not os.path.exists(controlname): raise Exception("Missing control:" + str(sourcelist).replace("'", '"')) with open(controlname, mode="r", encoding="utf8") as controlfile: controllist = json.load(controlfile) self.assertEqual(controllist, sourcelist)
def parse(keyword, config, argument): """Include an external file content. Arguments: - keyword: the string 'include'; - config: the current songbook configuration dictionary; - argument: a list of file paths to be included or a string of the file to include """ new_contentlist = ContentList() if isinstance(argument, str): argument = [argument] for filename in argument: try: filepath = load_from_datadirs( filename, config['_songdir'], config.get('_songbookfile_dir') ) except ContentError as error: new_contentlist.append_error(error) continue content_file = None try: with encoding.open_read( filepath, encoding=config['book']['encoding'] ) as content_file: new_content = yaml.load(content_file) except Exception as error: # pylint: disable=broad-except new_contentlist.append_error(ContentError( keyword="include", message="Error while loading file '{}': {}".format(filepath, error), )) continue config['_datadir'].append(os.path.abspath(os.path.dirname(filepath))) new_contentlist.extend(process_content(new_content, config)) config['_datadir'].pop() return new_contentlist
def test_content(self): """Test that `base.source` produces the correct file list""" sourcename = "{}.source".format(base) with open(sourcename, mode="r", encoding="utf8") as sourcefile: sbcontent = yaml.safe_load(sourcefile) outputdir = os.path.dirname(base) config = cls._generate_config(sbcontent, outputdir, base) with logging_reduced('patacrep.content.song'): expandedlist = content.process_content(sbcontent, config) sourcelist = [cls._clean_path(elem.to_dict()) for elem in expandedlist] controlname = "{}.control".format(base) if not os.path.exists(controlname): raise Exception("Missing control:" + str(controlname).replace("'", '"')) with open(controlname, mode="r", encoding="utf8") as controlfile: controllist = yaml.safe_load(controlfile) self.assertEqual(controllist, sourcelist)
def parse(keyword, config, argument): """Return a list of songs, whith a different base path. Arguments: - keyword: unused; - config: the current songbook configuration dictionary; - argument: a dict containing: path: string specifying the path to append to current songdirs; content: songbook content, that is parsed by patacrep.content.process_content(). The 'path' is added as a relative path to every path already present in config['songdir'] (which are 'songs' dir inside the datadirs). """ subpath = argument['path'] old_songdir = config['_songdir'] config['_songdir'] = [path.clone().join(subpath) for path in config['_songdir']] processed_content = process_content(argument.get('content'), config) config['_songdir'] = old_songdir return processed_content
def test_content(self): """Test that `base.source` produces the correct file list""" sourcename = "{}.source".format(base) with open(sourcename, mode="r", encoding="utf8") as sourcefile: sbcontent = yaml.load(sourcefile) outputdir = os.path.dirname(base) config = cls._generate_config(sbcontent, outputdir, base) with logging_reduced('patacrep.content.song'): expandedlist = content.process_content(sbcontent, config) sourcelist = [cls._clean_path(elem) for elem in expandedlist] controlname = "{}.control".format(base) if not os.path.exists(controlname): raise Exception("Missing control:" + str(sourcelist).replace("'", '"')) with open(controlname, mode="r", encoding="utf8") as controlfile: controllist = [ elem.replace("@TEST_FOLDER@", files.path2posix(resource_filename(__name__, ""))) for elem in yaml.load(controlfile) ] self.assertEqual(controllist, sourcelist)
def write_tex(self, output): """Build the '.tex' file corresponding to self. Arguments: - output: a file object, in which the file will be written. """ # Updating configuration self._config = DEFAULT_CONFIG.copy() self._config.update(self._raw_config) renderer = TexBookRenderer( self._config["template"], self._config["datadir"], self._config["lang"], self._config["encoding"] ) self._config.update(renderer.get_variables()) self._config.update(self._raw_config) self._config["_compiled_authwords"] = authors.compile_authwords(copy.deepcopy(self._config["authwords"])) # Loading custom plugins self._config["_content_plugins"] = files.load_plugins( datadirs=self._config.get("datadir", []), root_modules=["content"], keyword="CONTENT_PLUGINS" ) self._config["_song_plugins"] = files.load_plugins( datadirs=self._config.get("datadir", []), root_modules=["songs"], keyword="SONG_RENDERERS" )["tsg"] # Configuration set self._config["render"] = content.render self._config["content"] = content.process_content(self._config.get("content", []), self._config) self._config["filename"] = output.name[:-4] renderer.render_tex(output, self._config) # Get all errors, and maybe exit program self._errors.extend(renderer.errors) if self.config["_error"] == "failonbook": if self.has_errors(): raise errors.SongbookError("Some songs contain errors. Stopping as requested.")
def parse(keyword, config, argument, contentlist): """Include an external file content. Arguments: - keyword: the string 'include'; - config: the current songbook configuration dictionary; - argument: None; - contentlist: a list of file paths to be included. """ new_contentlist = ContentList() for path in contentlist: try: filepath = load_from_datadirs(path, config.get('datadir', [])) except ContentError as error: new_contentlist.append_error(error) continue content_file = None try: with encoding.open_read( filepath, encoding=config['encoding'] ) as content_file: new_content = json.load(content_file) except Exception as error: # pylint: disable=broad-except new_contentlist.append_error(ContentError( keyword="include", message="Error while loading file '{}': {}".format(filepath, error), )) continue config["datadir"].append(os.path.abspath(os.path.dirname(filepath))) new_contentlist.extend(process_content(new_content, config)) config["datadir"].pop() return new_contentlist