41
41
from nikola .utils import unicode_str , get_logger , makedirs , write_metadata , STDERR_HANDLER
42
42
43
43
44
+ DOCINFO_PELICAN_NIKOLA_MAPPING = {
45
+ 'modified' : 'updated' ,
46
+ 'authors' : 'author' ,
47
+ 'summary' : 'description'
48
+ }
49
+
44
50
class CompileRest (PageCompiler ):
45
51
46
52
"""Compile reStructuredText into HTML."""
47
53
48
54
name = "rest"
49
55
friendly_name = "reStructuredText"
50
56
demote_headers = True
57
+ metadata_can_be_overridden = True
51
58
logger = None
52
59
53
60
def _read_extra_deps (self , post ):
@@ -63,7 +70,7 @@ def register_extra_dependencies(self, post):
63
70
"""Add dependency to post object to check .dep file."""
64
71
post .add_dependency (lambda : self ._read_extra_deps (post ), 'fragment' )
65
72
66
- def compile_html_string (self , data , source_path = None , is_two_file = True ):
73
+ def compile_html_string (self , data , source_path = None , is_two_file = True , return_publisher = False ):
67
74
"""Compile reST into HTML strings."""
68
75
# If errors occur, this will be added to the line number reported by
69
76
# docutils so the line number matches the actual line number (off by
@@ -74,7 +81,10 @@ def compile_html_string(self, data, source_path=None, is_two_file=True):
74
81
add_ln = len (m_data .splitlines ()) + 1
75
82
76
83
default_template_path = os .path .join (os .path .dirname (__file__ ), 'template.txt' )
77
- output , error_level , deps = rst2html (
84
+ # TODO cache publisher in post object (which requires a ton of
85
+ # refactoring, and might even need v8 and breaking tons of APIs) for
86
+ # speed -- right now, we are publishing each post twice
87
+ publisher = rst2html (
78
88
data , settings_overrides = {
79
89
'initial_header_level' : 1 ,
80
90
'record_dependencies' : True ,
@@ -83,12 +93,16 @@ def compile_html_string(self, data, source_path=None, is_two_file=True):
83
93
'syntax_highlight' : 'short' ,
84
94
'math_output' : 'mathjax' ,
85
95
'template' : default_template_path ,
86
- }, logger = self .logger , source_path = source_path , l_add_ln = add_ln , transforms = self .site .rst_transforms )
87
- if not isinstance (output , unicode_str ):
88
- # To prevent some weird bugs here or there.
89
- # Original issue: empty files. `output` became a bytestring.
90
- output = output .decode ('utf-8' )
91
- return output , error_level , deps
96
+ }, logger = self .logger , source_path = source_path , l_add_ln = add_ln , transforms = self .site .rst_transforms , return_publisher = True )
97
+ if return_publisher :
98
+ return publisher
99
+ else :
100
+ output , error_level , deps = rst_document_tuple (publisher )
101
+ if not isinstance (output , unicode_str ):
102
+ # To prevent some weird bugs here or there.
103
+ # Original issue: empty files. `output` became a bytestring.
104
+ output = output .decode ('utf-8' )
105
+ return output , error_level , deps
92
106
93
107
def compile_html (self , source , dest , is_two_file = True ):
94
108
"""Compile source file into HTML and save as dest."""
@@ -112,6 +126,41 @@ def compile_html(self, source, dest, is_two_file=True):
112
126
else :
113
127
return False
114
128
129
+ def read_metadata (self , post , file_metadata_regexp = None , unslugify_titles = False , lang = None ):
130
+ """Read the metadata from a post, and return a metadata dict."""
131
+ metadata = {}
132
+ source = post .translated_source_path (lang )
133
+ with io .open (source , 'r' , encoding = 'utf-8' ) as in_file :
134
+ data = in_file .read ()
135
+ # This is a bit of a cheat. The method is now abused to create a
136
+ # publisher and not the full document tuple.
137
+ publisher = self .compile_html_string (data , source , post .is_two_file , True )
138
+
139
+ # Get title.
140
+ title_id = publisher .document .first_child_matching_class (docutils .nodes .title )
141
+ if title_id is not None :
142
+ metadata ['title' ] = publisher .document .children [title_id ].astext ()
143
+
144
+ # Get any other metadata that is part of the reST standard docinfo
145
+ # (which is a special field list)
146
+ docinfo_id = publisher .document .first_child_matching_class (docutils .nodes .docinfo )
147
+ if docinfo_id is not None :
148
+ docinfo = publisher .document .children [docinfo_id ]
149
+ for field in docinfo .children :
150
+ fieldname = field .tagname
151
+ if fieldname == 'authors' :
152
+ field .child_text_separator = ', '
153
+ fieldvalue = field .astext ()
154
+ elif fieldname == 'field' :
155
+ fieldname = field .children [0 ].astext ()
156
+ fieldvalue = field .children [1 ].astext ()
157
+ else :
158
+ fieldvalue = field .astext ()
159
+ fieldname = fieldname .lower ()
160
+ fieldname = DOCINFO_PELICAN_NIKOLA_MAPPING .get (fieldname , fieldname )
161
+ metadata [fieldname ] = fieldvalue
162
+ return metadata
163
+
115
164
def create_post (self , path , ** kw ):
116
165
"""Create a new post."""
117
166
content = kw .pop ('content' , None )
@@ -237,11 +286,8 @@ def rst2html(source, source_path=None, source_class=docutils.io.StringInput,
237
286
parser = None , parser_name = 'restructuredtext' , writer = None ,
238
287
writer_name = 'html' , settings = None , settings_spec = None ,
239
288
settings_overrides = None , config_section = None ,
240
- enable_exit_status = None , logger = None , l_add_ln = 0 , transforms = None ):
241
- """Set up & run a ``Publisher``, and return a dictionary of document parts.
242
-
243
- Dictionary keys are the names of parts, and values are Unicode strings;
244
- encoding is up to the client. For programmatic use with string I/O.
289
+ enable_exit_status = None , logger = None , l_add_ln = 0 , transforms = None , return_publisher = False ):
290
+ """Set up & run a ``Publisher``, and return the publisher or the document.
245
291
246
292
For encoded string input, be sure to set the 'input_encoding' setting to
247
293
the desired encoding. Set it to 'unicode' for unencoded Unicode string
@@ -275,4 +321,15 @@ def rst2html(source, source_path=None, source_class=docutils.io.StringInput,
275
321
pub .set_destination (None , destination_path )
276
322
pub .publish (enable_exit_status = enable_exit_status )
277
323
324
+ if return_publisher :
325
+ return pub
326
+ else :
327
+ return rst_document_tuple (pub )
328
+
329
+
330
+ def rst_document_tuple (pub ):
331
+ """Return the document tuple (output, error level, dependencies) for a publisher.
332
+
333
+ Previously, the only output of rst2html.
334
+ """
278
335
return pub .writer .parts ['docinfo' ] + pub .writer .parts ['fragment' ], pub .document .reporter .max_level , pub .settings .record_dependencies
0 commit comments