1 1
import logging
2

3 1
from six import string_types
4 1
from traitlets import Bool
5 1
from traitlets.config.configurable import Configurable
6

7 1
from ipypublish.utils import handle_error, pathlib
8

9 1
try:
10 1
    from shutil import which as exe_exists
11 0
except ImportError:
12 0
    from distutils.spawn import find_executable as exe_exists  # noqa: F401
13

14

15 1
class IPyPostProcessor(Configurable):
16
    """ an abstract class for post-processors
17
    """
18

19 1
    @property
20 1
    def allowed_mimetypes(self):
21
        """ override in subclasses
22

23
        return a list of allowed mime types
24
        if None, then all are allowed
25

26
        Text based mime-types include: text/plain, text/latex,
27
        text/restructuredtext, text/html, text/x-python, application/json,
28
        text/markdown, text/asciidoc, text/yaml
29

30
        """
31 0
        raise NotImplementedError("allowed_mimetypes")
32

33 1
    @property
34 1
    def requires_path(self):
35
        """ override in subclasses
36

37
        whether the prostprocessor requires the supplied filepath
38
        to have an existing parent directory
39

40
        if True and filepath is None, will raise an IOError, otherwise,
41
        will try to make the directory if it doesn't exist
42

43
        """
44 0
        raise NotImplementedError("requires_path")
45

46 1
    @property
47 1
    def logger_name(self):
48
        """ override in subclass
49
        """
50 0
        return "post-processor"
51

52 1
    @property
53 1
    def logger(self):
54 1
        return logging.getLogger(self.logger_name)
55

56 1
    skip_mime = Bool(
57
        True,
58
        help="if False, raise a TypeError if the mimetype is not allowed, "
59
        "else return without processing",
60
    ).tag(config=True)
61

62 1
    def __init__(self, config=None):
63 1
        super(IPyPostProcessor, self).__init__(config=config)
64

65 1
    def __call__(self, stream, mimetype, filepath, resources=None):
66
        """
67
        See def postprocess() ...
68
        """
69 0
        self.postprocess(stream, mimetype, filepath, resources)
70

71 1
    def postprocess(self, stream, mimetype, filepath, resources=None):
72
        """ Post-process output.
73

74
        Parameters
75
        ----------
76
        stream: str
77
            the main file contents
78
        mimetype: str
79
            the mimetype of the file
80
        filepath: None or str or pathlib.Path
81
            the path to the output file
82
            the path does not have to exist, but must be absolute
83
        resources: None or dict
84
            a resources dict, output from exporter.from_notebook_node
85

86
        Returns
87
        -------
88
        stream: str
89
        filepath: None or str or pathlib.Path
90

91
        """
92

93 1
        if (
94
            self.allowed_mimetypes is not None
95
            and mimetype not in self.allowed_mimetypes
96
        ):
97 1
            if not self.skip_mime:
98 0
                self.handle_error(
99
                    "the mimetype {0} is not in the allowed list: {1}".format(
100
                        mimetype, self.allowed_mimetypes
101
                    ),
102
                    TypeError,
103
                )
104
            else:
105 1
                self.logger.debug("skipping incorrect mime type: {}".format(mimetype))
106 1
                return stream, filepath, resources
107

108 1
        if self.requires_path and filepath is None:
109 0
            self.handle_error(
110
                "the filepath is None, " "but the post-processor requires a folder",
111
                IOError,
112
            )
113

114 1
        if filepath is not None and isinstance(filepath, string_types):
115 1
            filepath = pathlib.Path(filepath)
116

117 1
        if self.requires_path:
118

119 1
            if not filepath.is_absolute():
120 0
                self.handle_error(
121
                    "the post-processor requires an absolute folder path", IOError
122
                )
123

124 1
            if filepath.parent.exists() and not filepath.parent.is_dir():
125 0
                self.handle_error(
126
                    "the filepath's parent is not a folder: {}".format(filepath),
127
                    TypeError,
128
                )
129

130 1
            if not filepath.parent.exists():
131 1
                filepath.parent.mkdir(parents=True)
132

133 1
        if resources is None:
134 1
            resources = {}
135

136 1
        return self.run_postprocess(stream, mimetype, filepath, resources)
137

138 1
    def run_postprocess(self, stream, mimetype, filepath, resources):
139
        """ should not be called directly
140
        override in sub-class
141

142
        Parameters
143
        ----------
144
        stream: str
145
            the main file contents
146
        filepath: None or pathlib.Path
147
            the path to the output file
148
        resources: dict
149
            a resources dict, output from exporter.from_notebook_node
150

151
        Returns
152
        -------
153
        stream: str
154
        filepath: None or pathlib.Path
155
        resources: dict
156

157
        """
158 0
        raise NotImplementedError("run_postprocess")
159

160 1
    def handle_error(self, msg, err_type, raise_msg=None, log_msg=None):
161
        """ handle error by logging it then raising
162
        """
163 0
        handle_error(msg, err_type, self.logger, raise_msg=raise_msg, log_msg=log_msg)
164

165 1
    def check_exe_exists(self, name, error_msg):
166
        """ test if an executable exists
167
        """
168 1
        if not exe_exists(name):
169 0
            self.handle_error(error_msg, RuntimeError)
170 1
        return True
171

172

173 1
if __name__ == "__main__":
174

175 0
    print(IPyPostProcessor.allowed_mimetypes)
176 0
    IPyPostProcessor()("stream", "a")

Read our documentation on viewing source code .

Loading