pelican/plugins/asfreader.py (64 lines of code) (raw):

#!/usr/bin/python -B # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # # # asfreader.py -- Pelican plugin that processes ezt template Markdown through ezt and then GitHub Flavored Markdown. # import sys import io import os import traceback import re import ezt import pelican.plugins.signals import pelican.readers import pelican.settings # The gfm plugin should have been loaded before this one. ### TODO: maybe buildsite.py can sort gfm to the front of the list. import gfm METADATA_RE = re.compile(r'\[{\s*(?P<meta>[-._:a-zA-Z0-9\[\]]+)\s*}\]') class ASFTemplateReader(ezt.Reader): """Enables inserts relative to the template we loaded.""" def __init__(self, source_path, text): self.source_dir, self.fname = os.path.split(source_path) self.text = text def read_other(self, relative): return ezt._FileReader(os.path.join(self.source_dir, relative)) # pylint: disable=protected-access def filename(self): return self.fname class ASFReader(gfm.GFMReader): """GFM-flavored Reader for the Pelican system that adds ASF data and ezt generation prior to processing the GFM """ def add_data(self, text, metadata): "Mix in ASF data as metadata" asf_metadata = self.settings.get('ASF_DATA', { }).get('metadata') if asf_metadata: metadata.update(asf_metadata) # insert any direct references m = 1 while m: m = METADATA_RE.search(text) if m: this_data = m.group(1).strip() format_string = '{{{0}}}'.format(this_data) try: new_string = format_string.format(**metadata) print(f'{{{{{m.group(1)}}}}} -> {new_string}') except Exception: # the data expression was not found new_string = format_string print(f'{{{{{m.group(1)}}}}} is not found') text = re.sub(METADATA_RE, new_string, text, count=1) return text, metadata def read(self, source_path): "Read metadata and content, process content as ezt template, then render into HTML." try: # read content with embedded ezt - use GFMReader text, metadata = super().read_source(source_path) assert text assert metadata # supplement metadata with ASFData if available text, metadata = self.add_data(text, metadata) # prepare text as an ezt template # compress_whitespace=0 is required as blank lines and indentation have meaning in markdown. template = ezt.Template(compress_whitespace=0) assert template reader = ASFTemplateReader(source_path, text) template.parse(reader, base_format=ezt.FORMAT_HTML) # generate content from ezt template with metadata fp = io.StringIO() template.generate(fp, metadata) # Render the markdown into HTML content = super().render(fp.getvalue().encode('utf-8')).decode('utf-8') assert content except Exception: print('-----', file=sys.stderr) print('ERROR: %s' % (source_path), file=sys.stderr) traceback.print_exc() raise return content, metadata # The following are required or ezmd files are not read instead they are static. # For direct subclasses of BaseReader like GFMReader the following two # callables are optional if the class includes enabled=True and file_extenaions. def add_readers(readers): readers.reader_classes['ezmd'] = ASFReader def register(): pelican.plugins.signals.readers_init.connect(add_readers)