include.py 2.53 KB
Newer Older
Matthieu Boileau's avatar
Matthieu Boileau committed
1
2
3
4
5
6
7
8
9
10
11
12
13
"""
This plugin comes from the post https://stackoverflow.com/a/38085513/5072688 with the following comment:
Please note that pelican and docutils are both not designed to allow this. Neither a signal is provided which provides
a clean access to the raw contents of a source file before processing begins, nor is there a possibility to intercept
the framework reading the file in "a normal way" (like subclassing, changing hardcoded configuration, etc).
This plugin subclasses the internal class FileInput of RstReader and sets the class reference of RstReader.FileInput to
the subclass. Also python file objects are emulated through SourceWrapper. Nevertheless, this approach works for me and
is not cumbersome in the daily workflow.
"""

import os
from pelican import signals
from pelican.readers import RstReader
14
15
import docutils.io
import docutils.core
Matthieu Boileau's avatar
Matthieu Boileau committed
16
17
18
19
20

class RstReaderWrapper(RstReader):
    enabled = RstReader.enabled
    file_extensions = ['rst']

21
    class FileInput(docutils.io.FileInput):
Matthieu Boileau's avatar
Matthieu Boileau committed
22
        def __init__(self, *args, **kwargs):
23
            super().__init__(*args, **kwargs)
Matthieu Boileau's avatar
Matthieu Boileau committed
24
25
            self.source = RstReaderWrapper.SourceWrapper(self.source)

26
27
28
29
30
31
32
33
    class Publisher(docutils.core.Publisher):
        def __init__(self, *args, **kwargs):
            kwargs['source_class'] = RstReaderWrapper.FileInput
            super().__init__(*args, **kwargs)

    # Hook into docutils to intercept Publisher construction
    # in pelican.RstReader._get_publisher and add the `source_class` arg.
    docutils.core.Publisher = Publisher
Matthieu Boileau's avatar
Matthieu Boileau committed
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

    class SourceWrapper:
        """
            Mimics and wraps the result of a call to `open`
        """
        content_to_prepend = None

        def __init__(self, source):
            self.source = source

        def read(self):
            content = self.source.read()
            if self.content_to_prepend is not None:
                content = "{}\n{}".format(self.content_to_prepend, content)
            return content

        def close(self):
            self.source.close()


def process_settings(pelicanobj):
    include_files = pelicanobj.settings.get('RST_GLOBAL_INCLUDES', []) or []
    base_path = pelicanobj.settings.get('PATH', ".")

    def read(fn):
        with open(os.path.join(base_path, fn), 'r') as res:
            content = res.read()
            return ".. INCLUSION FROM {}\n{}\n".format(fn, content)

    inclusion = "".join(map(read, include_files)) if include_files else None
    RstReaderWrapper.SourceWrapper.content_to_prepend = inclusion


def register():
    signals.initialized.connect(process_settings)