We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
rest plugins should know which page is being compiled
I want to access some of the metadata of the current page from a rest plugin and I can't find a way to get the current page in self.state.

I found out that by adding a reference to the "source" path when calling the rest compiler, I can retrieve it as self.state.document.settings._source, then find a matching page. Is it a good solution? Could this (or something similar) be integrated in the default rest compiler?


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `nikola/plugins/compile/rest/__init__.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 
3 # Copyright © 2012-2014 Roberto Alsina and others.
4 
5 # Permission is hereby granted, free of charge, to any
6 # person obtaining a copy of this software and associated
7 # documentation files (the "Software"), to deal in the
8 # Software without restriction, including without limitation
9 # the rights to use, copy, modify, merge, publish,
10 # distribute, sublicense, and/or sell copies of the
11 # Software, and to permit persons to whom the Software is
12 # furnished to do so, subject to the following conditions:
13 #
14 # The above copyright notice and this permission notice
15 # shall be included in all copies or substantial portions of
16 # the Software.
17 #
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
19 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
20 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
21 # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
22 # OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24 # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 
27 from __future__ import unicode_literals
28 import io
29 import os
30 import re
31 
32 try:
33     import docutils.core
34     import docutils.nodes
35     import docutils.utils
36     import docutils.io
37     import docutils.readers.standalone
38     import docutils.writers.html4css1
39     has_docutils = True
40 except ImportError:
41     has_docutils = False
42 
43 from nikola.plugin_categories import PageCompiler
44 from nikola.utils import get_logger, makedirs, req_missing, write_metadata
45 
46 
47 class CompileRest(PageCompiler):
48     """Compile reSt into HTML."""
49 
50     name = "rest"
51     demote_headers = True
52     logger = None
53 
54     def compile_html(self, source, dest, is_two_file=True):
55         """Compile reSt into HTML."""
56 
57         if not has_docutils:
58             req_missing(['docutils'], 'build this site (compile reStructuredText)')
59         makedirs(os.path.dirname(dest))
60         error_level = 100
61         with io.open(dest, "w+", encoding="utf8") as out_file:
62             with io.open(source, "r", encoding="utf8") as in_file:
63                 data = in_file.read()
64                 add_ln = 0
65                 if not is_two_file:
66                     spl = re.split('(\n\n|\r\n\r\n)', data, maxsplit=1)
67                     data = spl[-1]
68                     if len(spl) != 1:
69                         # If errors occur, this will be added to the line
70                         # number reported by docutils so the line number
71                         # matches the actual line number (off by 7 with default
72                         # metadata, could be more or less depending on the post
73                         # author).
74                         add_ln = len(spl[0].splitlines()) + 1
75 
76                 default_template_path = os.path.join(os.path.dirname(__file__), 'template.txt')
77                 output, error_level, deps = rst2html(
78                     data, settings_overrides={
79                         'initial_header_level': 1,
80                         'record_dependencies': True,
81                         'stylesheet_path': None,
82                         'link_stylesheet': True,
83                         'syntax_highlight': 'short',
84                         'math_output': 'mathjax',
85                         'template': default_template_path,
86                     }, logger=self.logger, l_source=source, l_add_ln=add_ln)
87                 out_file.write(output)
88             deps_path = dest + '.dep'
89             if deps.list:
90                 with io.open(deps_path, "w+", encoding="utf8") as deps_file:
91                     deps_file.write('\n'.join(deps.list))
92             else:
93                 if os.path.isfile(deps_path):
94                     os.unlink(deps_path)
95         if error_level < 3:
96             return True
97         else:
98             return False
99 
100     def create_post(self, path, **kw):
101         content = kw.pop('content', None)
102         onefile = kw.pop('onefile', False)
103         # is_page is not used by create_post as of now.
104         kw.pop('is_page', False)
105         metadata = {}
106         metadata.update(self.default_metadata)
107         metadata.update(kw)
108         makedirs(os.path.dirname(path))
109         if not content.endswith('\n'):
110             content += '\n'
111         with io.open(path, "w+", encoding="utf8") as fd:
112             if onefile:
113                 fd.write(write_metadata(metadata))
114             fd.write('\n' + content)
115 
116     def set_site(self, site):
117         for plugin_info in site.plugin_manager.getPluginsOfCategory("RestExtension"):
118             if plugin_info.name in site.config['DISABLED_PLUGINS']:
119                 site.plugin_manager.removePluginFromCategory(plugin_info, "RestExtension")
120                 continue
121 
122             site.plugin_manager.activatePluginByName(plugin_info.name)
123             plugin_info.plugin_object.set_site(site)
124             plugin_info.plugin_object.short_help = plugin_info.description
125 
126         self.logger = get_logger('compile_rest', site.loghandlers)
127         if not site.debug:
128             self.logger.level = 4
129 
130         return super(CompileRest, self).set_site(site)
131 
132 
133 def get_observer(settings):
134     """Return an observer for the docutils Reporter."""
135     def observer(msg):
136         """Report docutils/rest messages to a Nikola user.
137 
138         Error code mapping:
139 
140         +------+---------+------+----------+
141         | dNUM |   dNAME | lNUM |    lNAME |    d = docutils, l = logbook
142         +------+---------+------+----------+
143         |    0 |   DEBUG |    1 |    DEBUG |
144         |    1 |    INFO |    2 |     INFO |
145         |    2 | WARNING |    4 |  WARNING |
146         |    3 |   ERROR |    5 |    ERROR |
147         |    4 |  SEVERE |    6 | CRITICAL |
148         +------+---------+------+----------+
149         """
150         errormap = {0: 1, 1: 2, 2: 4, 3: 5, 4: 6}
151         text = docutils.nodes.Element.astext(msg)
152         line = msg['line'] + settings['add_ln'] if 'line' in msg else 0
153         out = '[{source}{colon}{line}] {text}'.format(
154             source=settings['source'], colon=(':' if line else ''),
155             line=line, text=text)
156         settings['logger'].log(errormap[msg['level']], out)
157 
158     return observer
159 
160 
161 class NikolaReader(docutils.readers.standalone.Reader):
162 
163     def new_document(self):
164         """Create and return a new empty document tree (root node)."""
165         document = docutils.utils.new_document(self.source.source_path, self.settings)
166         document.reporter.stream = False
167         document.reporter.attach_observer(get_observer(self.l_settings))
168         return document
169 
170 
171 def add_node(node, visit_function=None, depart_function=None):
172     """
173     Register a Docutils node class.
174     This function is completely optional. It is a same concept as
175     `Sphinx add_node function <http://sphinx-doc.org/ext/appapi.html#sphinx.application.Sphinx.add_node>`_.
176 
177     For example::
178 
179         class Plugin(RestExtension):
180 
181             name = "rest_math"
182 
183             def set_site(self, site):
184                 self.site = site
185                 directives.register_directive('math', MathDirective)
186                 add_node(MathBlock, visit_Math, depart_Math)
187                 return super(Plugin, self).set_site(site)
188 
189         class MathDirective(Directive):
190             def run(self):
191                 node = MathBlock()
192                 return [node]
193 
194         class Math(docutils.nodes.Element): pass
195 
196         def visit_Math(self, node):
197             self.body.append(self.starttag(node, 'math'))
198 
199         def depart_Math(self, node):
200             self.body.append('</math>')
201 
202     For full example, you can refer to `Microdata plugin <http://plugins.getnikola.com/#microdata>`_
203     """
204     docutils.nodes._add_node_class_names([node.__name__])
205     if visit_function:
206         setattr(docutils.writers.html4css1.HTMLTranslator, 'visit_' + node.__name__, visit_function)
207     if depart_function:
208         setattr(docutils.writers.html4css1.HTMLTranslator, 'depart_' + node.__name__, depart_function)
209 
210 
211 def rst2html(source, source_path=None, source_class=docutils.io.StringInput,
212              destination_path=None, reader=None,
213              parser=None, parser_name='restructuredtext', writer=None,
214              writer_name='html', settings=None, settings_spec=None,
215              settings_overrides=None, config_section=None,
216              enable_exit_status=None, logger=None, l_source='', l_add_ln=0):
217     """
218     Set up & run a `Publisher`, and return a dictionary of document parts.
219     Dictionary keys are the names of parts, and values are Unicode strings;
220     encoding is up to the client.  For programmatic use with string I/O.
221 
222     For encoded string input, be sure to set the 'input_encoding' setting to
223     the desired encoding.  Set it to 'unicode' for unencoded Unicode string
224     input.  Here's how::
225 
226         publish_parts(..., settings_overrides={'input_encoding': 'unicode'})
227 
228     Parameters: see `publish_programmatically`.
229 
230     WARNING: `reader` should be None (or NikolaReader()) if you want Nikola to report
231              reStructuredText syntax errors.
232     """
233     if reader is None:
234         reader = NikolaReader()
235         # For our custom logging, we have special needs and special settings we
236         # specify here.
237         # logger    a logger from Nikola
238         # source   source filename (docutils gets a string)
239         # add_ln   amount of metadata lines (see comment in compile_html above)
240         reader.l_settings = {'logger': logger, 'source': l_source,
241                              'add_ln': l_add_ln}
242 
243     pub = docutils.core.Publisher(reader, parser, writer, settings=settings,
244                                   source_class=source_class,
245                                   destination_class=docutils.io.StringOutput)
246     pub.set_components(None, parser_name, writer_name)
247     pub.process_programmatic_settings(
248         settings_spec, settings_overrides, config_section)
249     pub.set_source(source, source_path)
250     pub.set_destination(None, destination_path)
251     pub.publish(enable_exit_status=enable_exit_status)
252 
253     return pub.writer.parts['docinfo'] + pub.writer.parts['fragment'], pub.document.reporter.max_level, pub.settings.record_dependencies
254
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/nikola/plugins/compile/rest/__init__.py b/nikola/plugins/compile/rest/__init__.py
--- a/nikola/plugins/compile/rest/__init__.py
+++ b/nikola/plugins/compile/rest/__init__.py
@@ -83,7 +83,7 @@
                         'syntax_highlight': 'short',
                         'math_output': 'mathjax',
                         'template': default_template_path,
-                    }, logger=self.logger, l_source=source, l_add_ln=add_ln)
+                    }, logger=self.logger, source_path=source, l_add_ln=add_ln)
                 out_file.write(output)
             deps_path = dest + '.dep'
             if deps.list:
@@ -213,7 +213,7 @@
              parser=None, parser_name='restructuredtext', writer=None,
              writer_name='html', settings=None, settings_spec=None,
              settings_overrides=None, config_section=None,
-             enable_exit_status=None, logger=None, l_source='', l_add_ln=0):
+             enable_exit_status=None, logger=None, l_add_ln=0):
     """
     Set up & run a `Publisher`, and return a dictionary of document parts.
     Dictionary keys are the names of parts, and values are Unicode strings;
@@ -237,7 +237,7 @@
         # logger    a logger from Nikola
         # source   source filename (docutils gets a string)
         # add_ln   amount of metadata lines (see comment in compile_html above)
-        reader.l_settings = {'logger': logger, 'source': l_source,
+        reader.l_settings = {'logger': logger, 'source': source_path,
                              'add_ln': l_add_ln}
 
     pub = docutils.core.Publisher(reader, parser, writer, settings=settings,
@@ -246,7 +246,8 @@
     pub.set_components(None, parser_name, writer_name)
     pub.process_programmatic_settings(
         settings_spec, settings_overrides, config_section)
-    pub.set_source(source, source_path)
+    pub.set_source(source, None)
+    pub.settings._nikola_source_path = source_path
     pub.set_destination(None, destination_path)
     pub.publish(enable_exit_status=enable_exit_status)

{"golden_diff": "diff --git a/nikola/plugins/compile/rest/__init__.py b/nikola/plugins/compile/rest/__init__.py\n--- a/nikola/plugins/compile/rest/__init__.py\n+++ b/nikola/plugins/compile/rest/__init__.py\n@@ -83,7 +83,7 @@\n                         'syntax_highlight': 'short',\n                         'math_output': 'mathjax',\n                         'template': default_template_path,\n-                    }, logger=self.logger, l_source=source, l_add_ln=add_ln)\n+                    }, logger=self.logger, source_path=source, l_add_ln=add_ln)\n                 out_file.write(output)\n             deps_path = dest + '.dep'\n             if deps.list:\n@@ -213,7 +213,7 @@\n              parser=None, parser_name='restructuredtext', writer=None,\n              writer_name='html', settings=None, settings_spec=None,\n              settings_overrides=None, config_section=None,\n-             enable_exit_status=None, logger=None, l_source='', l_add_ln=0):\n+             enable_exit_status=None, logger=None, l_add_ln=0):\n     \"\"\"\n     Set up & run a `Publisher`, and return a dictionary of document parts.\n     Dictionary keys are the names of parts, and values are Unicode strings;\n@@ -237,7 +237,7 @@\n         # logger    a logger from Nikola\n         # source   source filename (docutils gets a string)\n         # add_ln   amount of metadata lines (see comment in compile_html above)\n-        reader.l_settings = {'logger': logger, 'source': l_source,\n+        reader.l_settings = {'logger': logger, 'source': source_path,\n                              'add_ln': l_add_ln}\n \n     pub = docutils.core.Publisher(reader, parser, writer, settings=settings,\n@@ -246,7 +246,8 @@\n     pub.set_components(None, parser_name, writer_name)\n     pub.process_programmatic_settings(\n         settings_spec, settings_overrides, config_section)\n-    pub.set_source(source, source_path)\n+    pub.set_source(source, None)\n+    pub.settings._nikola_source_path = source_path\n     pub.set_destination(None, destination_path)\n     pub.publish(enable_exit_status=enable_exit_status)\n", "issue": "rest plugins should know which page is being compiled\nI want to access some of the metadata of the current page from a rest plugin and I can't find a way to get the current page in self.state.\n\nI found out that by adding a reference to the \"source\" path when calling the rest compiler, I can retrieve it as self.state.document.settings._source, then find a matching page. Is it a good solution? Could this (or something similar) be integrated in the default rest compiler?\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\n# Copyright \u00a9 2012-2014 Roberto Alsina and others.\n\n# Permission is hereby granted, free of charge, to any\n# person obtaining a copy of this software and associated\n# documentation files (the \"Software\"), to deal in the\n# Software without restriction, including without limitation\n# the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the\n# Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice\n# shall be included in all copies or substantial portions of\n# the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\n# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\n# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\n# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\n# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nfrom __future__ import unicode_literals\nimport io\nimport os\nimport re\n\ntry:\n    import docutils.core\n    import docutils.nodes\n    import docutils.utils\n    import docutils.io\n    import docutils.readers.standalone\n    import docutils.writers.html4css1\n    has_docutils = True\nexcept ImportError:\n    has_docutils = False\n\nfrom nikola.plugin_categories import PageCompiler\nfrom nikola.utils import get_logger, makedirs, req_missing, write_metadata\n\n\nclass CompileRest(PageCompiler):\n    \"\"\"Compile reSt into HTML.\"\"\"\n\n    name = \"rest\"\n    demote_headers = True\n    logger = None\n\n    def compile_html(self, source, dest, is_two_file=True):\n        \"\"\"Compile reSt into HTML.\"\"\"\n\n        if not has_docutils:\n            req_missing(['docutils'], 'build this site (compile reStructuredText)')\n        makedirs(os.path.dirname(dest))\n        error_level = 100\n        with io.open(dest, \"w+\", encoding=\"utf8\") as out_file:\n            with io.open(source, \"r\", encoding=\"utf8\") as in_file:\n                data = in_file.read()\n                add_ln = 0\n                if not is_two_file:\n                    spl = re.split('(\\n\\n|\\r\\n\\r\\n)', data, maxsplit=1)\n                    data = spl[-1]\n                    if len(spl) != 1:\n                        # If errors occur, this will be added to the line\n                        # number reported by docutils so the line number\n                        # matches the actual line number (off by 7 with default\n                        # metadata, could be more or less depending on the post\n                        # author).\n                        add_ln = len(spl[0].splitlines()) + 1\n\n                default_template_path = os.path.join(os.path.dirname(__file__), 'template.txt')\n                output, error_level, deps = rst2html(\n                    data, settings_overrides={\n                        'initial_header_level': 1,\n                        'record_dependencies': True,\n                        'stylesheet_path': None,\n                        'link_stylesheet': True,\n                        'syntax_highlight': 'short',\n                        'math_output': 'mathjax',\n                        'template': default_template_path,\n                    }, logger=self.logger, l_source=source, l_add_ln=add_ln)\n                out_file.write(output)\n            deps_path = dest + '.dep'\n            if deps.list:\n                with io.open(deps_path, \"w+\", encoding=\"utf8\") as deps_file:\n                    deps_file.write('\\n'.join(deps.list))\n            else:\n                if os.path.isfile(deps_path):\n                    os.unlink(deps_path)\n        if error_level < 3:\n            return True\n        else:\n            return False\n\n    def create_post(self, path, **kw):\n        content = kw.pop('content', None)\n        onefile = kw.pop('onefile', False)\n        # is_page is not used by create_post as of now.\n        kw.pop('is_page', False)\n        metadata = {}\n        metadata.update(self.default_metadata)\n        metadata.update(kw)\n        makedirs(os.path.dirname(path))\n        if not content.endswith('\\n'):\n            content += '\\n'\n        with io.open(path, \"w+\", encoding=\"utf8\") as fd:\n            if onefile:\n                fd.write(write_metadata(metadata))\n            fd.write('\\n' + content)\n\n    def set_site(self, site):\n        for plugin_info in site.plugin_manager.getPluginsOfCategory(\"RestExtension\"):\n            if plugin_info.name in site.config['DISABLED_PLUGINS']:\n                site.plugin_manager.removePluginFromCategory(plugin_info, \"RestExtension\")\n                continue\n\n            site.plugin_manager.activatePluginByName(plugin_info.name)\n            plugin_info.plugin_object.set_site(site)\n            plugin_info.plugin_object.short_help = plugin_info.description\n\n        self.logger = get_logger('compile_rest', site.loghandlers)\n        if not site.debug:\n            self.logger.level = 4\n\n        return super(CompileRest, self).set_site(site)\n\n\ndef get_observer(settings):\n    \"\"\"Return an observer for the docutils Reporter.\"\"\"\n    def observer(msg):\n        \"\"\"Report docutils/rest messages to a Nikola user.\n\n        Error code mapping:\n\n        +------+---------+------+----------+\n        | dNUM |   dNAME | lNUM |    lNAME |    d = docutils, l = logbook\n        +------+---------+------+----------+\n        |    0 |   DEBUG |    1 |    DEBUG |\n        |    1 |    INFO |    2 |     INFO |\n        |    2 | WARNING |    4 |  WARNING |\n        |    3 |   ERROR |    5 |    ERROR |\n        |    4 |  SEVERE |    6 | CRITICAL |\n        +------+---------+------+----------+\n        \"\"\"\n        errormap = {0: 1, 1: 2, 2: 4, 3: 5, 4: 6}\n        text = docutils.nodes.Element.astext(msg)\n        line = msg['line'] + settings['add_ln'] if 'line' in msg else 0\n        out = '[{source}{colon}{line}] {text}'.format(\n            source=settings['source'], colon=(':' if line else ''),\n            line=line, text=text)\n        settings['logger'].log(errormap[msg['level']], out)\n\n    return observer\n\n\nclass NikolaReader(docutils.readers.standalone.Reader):\n\n    def new_document(self):\n        \"\"\"Create and return a new empty document tree (root node).\"\"\"\n        document = docutils.utils.new_document(self.source.source_path, self.settings)\n        document.reporter.stream = False\n        document.reporter.attach_observer(get_observer(self.l_settings))\n        return document\n\n\ndef add_node(node, visit_function=None, depart_function=None):\n    \"\"\"\n    Register a Docutils node class.\n    This function is completely optional. It is a same concept as\n    `Sphinx add_node function <http://sphinx-doc.org/ext/appapi.html#sphinx.application.Sphinx.add_node>`_.\n\n    For example::\n\n        class Plugin(RestExtension):\n\n            name = \"rest_math\"\n\n            def set_site(self, site):\n                self.site = site\n                directives.register_directive('math', MathDirective)\n                add_node(MathBlock, visit_Math, depart_Math)\n                return super(Plugin, self).set_site(site)\n\n        class MathDirective(Directive):\n            def run(self):\n                node = MathBlock()\n                return [node]\n\n        class Math(docutils.nodes.Element): pass\n\n        def visit_Math(self, node):\n            self.body.append(self.starttag(node, 'math'))\n\n        def depart_Math(self, node):\n            self.body.append('</math>')\n\n    For full example, you can refer to `Microdata plugin <http://plugins.getnikola.com/#microdata>`_\n    \"\"\"\n    docutils.nodes._add_node_class_names([node.__name__])\n    if visit_function:\n        setattr(docutils.writers.html4css1.HTMLTranslator, 'visit_' + node.__name__, visit_function)\n    if depart_function:\n        setattr(docutils.writers.html4css1.HTMLTranslator, 'depart_' + node.__name__, depart_function)\n\n\ndef rst2html(source, source_path=None, source_class=docutils.io.StringInput,\n             destination_path=None, reader=None,\n             parser=None, parser_name='restructuredtext', writer=None,\n             writer_name='html', settings=None, settings_spec=None,\n             settings_overrides=None, config_section=None,\n             enable_exit_status=None, logger=None, l_source='', l_add_ln=0):\n    \"\"\"\n    Set up & run a `Publisher`, and return a dictionary of document parts.\n    Dictionary keys are the names of parts, and values are Unicode strings;\n    encoding is up to the client.  For programmatic use with string I/O.\n\n    For encoded string input, be sure to set the 'input_encoding' setting to\n    the desired encoding.  Set it to 'unicode' for unencoded Unicode string\n    input.  Here's how::\n\n        publish_parts(..., settings_overrides={'input_encoding': 'unicode'})\n\n    Parameters: see `publish_programmatically`.\n\n    WARNING: `reader` should be None (or NikolaReader()) if you want Nikola to report\n             reStructuredText syntax errors.\n    \"\"\"\n    if reader is None:\n        reader = NikolaReader()\n        # For our custom logging, we have special needs and special settings we\n        # specify here.\n        # logger    a logger from Nikola\n        # source   source filename (docutils gets a string)\n        # add_ln   amount of metadata lines (see comment in compile_html above)\n        reader.l_settings = {'logger': logger, 'source': l_source,\n                             'add_ln': l_add_ln}\n\n    pub = docutils.core.Publisher(reader, parser, writer, settings=settings,\n                                  source_class=source_class,\n                                  destination_class=docutils.io.StringOutput)\n    pub.set_components(None, parser_name, writer_name)\n    pub.process_programmatic_settings(\n        settings_spec, settings_overrides, config_section)\n    pub.set_source(source, source_path)\n    pub.set_destination(None, destination_path)\n    pub.publish(enable_exit_status=enable_exit_status)\n\n    return pub.writer.parts['docinfo'] + pub.writer.parts['fragment'], pub.document.reporter.max_level, pub.settings.record_dependencies\n", "path": "nikola/plugins/compile/rest/__init__.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\n# Copyright \u00a9 2012-2014 Roberto Alsina and others.\n\n# Permission is hereby granted, free of charge, to any\n# person obtaining a copy of this software and associated\n# documentation files (the \"Software\"), to deal in the\n# Software without restriction, including without limitation\n# the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the\n# Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice\n# shall be included in all copies or substantial portions of\n# the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\n# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\n# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\n# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\n# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nfrom __future__ import unicode_literals\nimport io\nimport os\nimport re\n\ntry:\n    import docutils.core\n    import docutils.nodes\n    import docutils.utils\n    import docutils.io\n    import docutils.readers.standalone\n    import docutils.writers.html4css1\n    has_docutils = True\nexcept ImportError:\n    has_docutils = False\n\nfrom nikola.plugin_categories import PageCompiler\nfrom nikola.utils import get_logger, makedirs, req_missing, write_metadata\n\n\nclass CompileRest(PageCompiler):\n    \"\"\"Compile reSt into HTML.\"\"\"\n\n    name = \"rest\"\n    demote_headers = True\n    logger = None\n\n    def compile_html(self, source, dest, is_two_file=True):\n        \"\"\"Compile reSt into HTML.\"\"\"\n\n        if not has_docutils:\n            req_missing(['docutils'], 'build this site (compile reStructuredText)')\n        makedirs(os.path.dirname(dest))\n        error_level = 100\n        with io.open(dest, \"w+\", encoding=\"utf8\") as out_file:\n            with io.open(source, \"r\", encoding=\"utf8\") as in_file:\n                data = in_file.read()\n                add_ln = 0\n                if not is_two_file:\n                    spl = re.split('(\\n\\n|\\r\\n\\r\\n)', data, maxsplit=1)\n                    data = spl[-1]\n                    if len(spl) != 1:\n                        # If errors occur, this will be added to the line\n                        # number reported by docutils so the line number\n                        # matches the actual line number (off by 7 with default\n                        # metadata, could be more or less depending on the post\n                        # author).\n                        add_ln = len(spl[0].splitlines()) + 1\n\n                default_template_path = os.path.join(os.path.dirname(__file__), 'template.txt')\n                output, error_level, deps = rst2html(\n                    data, settings_overrides={\n                        'initial_header_level': 1,\n                        'record_dependencies': True,\n                        'stylesheet_path': None,\n                        'link_stylesheet': True,\n                        'syntax_highlight': 'short',\n                        'math_output': 'mathjax',\n                        'template': default_template_path,\n                    }, logger=self.logger, source_path=source, l_add_ln=add_ln)\n                out_file.write(output)\n            deps_path = dest + '.dep'\n            if deps.list:\n                with io.open(deps_path, \"w+\", encoding=\"utf8\") as deps_file:\n                    deps_file.write('\\n'.join(deps.list))\n            else:\n                if os.path.isfile(deps_path):\n                    os.unlink(deps_path)\n        if error_level < 3:\n            return True\n        else:\n            return False\n\n    def create_post(self, path, **kw):\n        content = kw.pop('content', None)\n        onefile = kw.pop('onefile', False)\n        # is_page is not used by create_post as of now.\n        kw.pop('is_page', False)\n        metadata = {}\n        metadata.update(self.default_metadata)\n        metadata.update(kw)\n        makedirs(os.path.dirname(path))\n        if not content.endswith('\\n'):\n            content += '\\n'\n        with io.open(path, \"w+\", encoding=\"utf8\") as fd:\n            if onefile:\n                fd.write(write_metadata(metadata))\n            fd.write('\\n' + content)\n\n    def set_site(self, site):\n        for plugin_info in site.plugin_manager.getPluginsOfCategory(\"RestExtension\"):\n            if plugin_info.name in site.config['DISABLED_PLUGINS']:\n                site.plugin_manager.removePluginFromCategory(plugin_info, \"RestExtension\")\n                continue\n\n            site.plugin_manager.activatePluginByName(plugin_info.name)\n            plugin_info.plugin_object.set_site(site)\n            plugin_info.plugin_object.short_help = plugin_info.description\n\n        self.logger = get_logger('compile_rest', site.loghandlers)\n        if not site.debug:\n            self.logger.level = 4\n\n        return super(CompileRest, self).set_site(site)\n\n\ndef get_observer(settings):\n    \"\"\"Return an observer for the docutils Reporter.\"\"\"\n    def observer(msg):\n        \"\"\"Report docutils/rest messages to a Nikola user.\n\n        Error code mapping:\n\n        +------+---------+------+----------+\n        | dNUM |   dNAME | lNUM |    lNAME |    d = docutils, l = logbook\n        +------+---------+------+----------+\n        |    0 |   DEBUG |    1 |    DEBUG |\n        |    1 |    INFO |    2 |     INFO |\n        |    2 | WARNING |    4 |  WARNING |\n        |    3 |   ERROR |    5 |    ERROR |\n        |    4 |  SEVERE |    6 | CRITICAL |\n        +------+---------+------+----------+\n        \"\"\"\n        errormap = {0: 1, 1: 2, 2: 4, 3: 5, 4: 6}\n        text = docutils.nodes.Element.astext(msg)\n        line = msg['line'] + settings['add_ln'] if 'line' in msg else 0\n        out = '[{source}{colon}{line}] {text}'.format(\n            source=settings['source'], colon=(':' if line else ''),\n            line=line, text=text)\n        settings['logger'].log(errormap[msg['level']], out)\n\n    return observer\n\n\nclass NikolaReader(docutils.readers.standalone.Reader):\n\n    def new_document(self):\n        \"\"\"Create and return a new empty document tree (root node).\"\"\"\n        document = docutils.utils.new_document(self.source.source_path, self.settings)\n        document.reporter.stream = False\n        document.reporter.attach_observer(get_observer(self.l_settings))\n        return document\n\n\ndef add_node(node, visit_function=None, depart_function=None):\n    \"\"\"\n    Register a Docutils node class.\n    This function is completely optional. It is a same concept as\n    `Sphinx add_node function <http://sphinx-doc.org/ext/appapi.html#sphinx.application.Sphinx.add_node>`_.\n\n    For example::\n\n        class Plugin(RestExtension):\n\n            name = \"rest_math\"\n\n            def set_site(self, site):\n                self.site = site\n                directives.register_directive('math', MathDirective)\n                add_node(MathBlock, visit_Math, depart_Math)\n                return super(Plugin, self).set_site(site)\n\n        class MathDirective(Directive):\n            def run(self):\n                node = MathBlock()\n                return [node]\n\n        class Math(docutils.nodes.Element): pass\n\n        def visit_Math(self, node):\n            self.body.append(self.starttag(node, 'math'))\n\n        def depart_Math(self, node):\n            self.body.append('</math>')\n\n    For full example, you can refer to `Microdata plugin <http://plugins.getnikola.com/#microdata>`_\n    \"\"\"\n    docutils.nodes._add_node_class_names([node.__name__])\n    if visit_function:\n        setattr(docutils.writers.html4css1.HTMLTranslator, 'visit_' + node.__name__, visit_function)\n    if depart_function:\n        setattr(docutils.writers.html4css1.HTMLTranslator, 'depart_' + node.__name__, depart_function)\n\n\ndef rst2html(source, source_path=None, source_class=docutils.io.StringInput,\n             destination_path=None, reader=None,\n             parser=None, parser_name='restructuredtext', writer=None,\n             writer_name='html', settings=None, settings_spec=None,\n             settings_overrides=None, config_section=None,\n             enable_exit_status=None, logger=None, l_add_ln=0):\n    \"\"\"\n    Set up & run a `Publisher`, and return a dictionary of document parts.\n    Dictionary keys are the names of parts, and values are Unicode strings;\n    encoding is up to the client.  For programmatic use with string I/O.\n\n    For encoded string input, be sure to set the 'input_encoding' setting to\n    the desired encoding.  Set it to 'unicode' for unencoded Unicode string\n    input.  Here's how::\n\n        publish_parts(..., settings_overrides={'input_encoding': 'unicode'})\n\n    Parameters: see `publish_programmatically`.\n\n    WARNING: `reader` should be None (or NikolaReader()) if you want Nikola to report\n             reStructuredText syntax errors.\n    \"\"\"\n    if reader is None:\n        reader = NikolaReader()\n        # For our custom logging, we have special needs and special settings we\n        # specify here.\n        # logger    a logger from Nikola\n        # source   source filename (docutils gets a string)\n        # add_ln   amount of metadata lines (see comment in compile_html above)\n        reader.l_settings = {'logger': logger, 'source': source_path,\n                             'add_ln': l_add_ln}\n\n    pub = docutils.core.Publisher(reader, parser, writer, settings=settings,\n                                  source_class=source_class,\n                                  destination_class=docutils.io.StringOutput)\n    pub.set_components(None, parser_name, writer_name)\n    pub.process_programmatic_settings(\n        settings_spec, settings_overrides, config_section)\n    pub.set_source(source, None)\n    pub.settings._nikola_source_path = source_path\n    pub.set_destination(None, destination_path)\n    pub.publish(enable_exit_status=enable_exit_status)\n\n    return pub.writer.parts['docinfo'] + pub.writer.parts['fragment'], pub.document.reporter.max_level, pub.settings.record_dependencies\n", "path": "nikola/plugins/compile/rest/__init__.py"}]}