Skip to content

Commit 4364c61

Browse files
committed
Update djangodocs extension
1 parent 382e001 commit 4364c61

File tree

1 file changed

+143
-112
lines changed

1 file changed

+143
-112
lines changed

_ext/djangodocs.py

Lines changed: 143 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,32 @@
22
"""
33
Sphinx plugins for Django documentation.
44
"""
5+
import os
6+
import re
57

6-
import docutils.nodes
7-
import docutils.transforms
8-
import sphinx
9-
import sphinx.addnodes
10-
import sphinx.builder
11-
import sphinx.directives
12-
import sphinx.environment
13-
import sphinx.htmlwriter
8+
from docutils import nodes, transforms
9+
try:
10+
import json
11+
except ImportError:
12+
try:
13+
import simplejson as json
14+
except ImportError:
15+
try:
16+
from django.utils import simplejson as json
17+
except ImportError:
18+
json = None
19+
20+
from sphinx import addnodes, roles, __version__ as sphinx_ver
21+
from sphinx.builders.html import StandaloneHTMLBuilder
22+
from sphinx.writers.html import SmartyPantsHTMLTranslator
23+
from sphinx.util.console import bold
24+
from sphinx.util.compat import Directive
1425

15-
def populate_index_to_rebuilds(app, doctree):
16-
app.builder.env.files_to_rebuild['index'] = set([])
26+
# RE for option descriptions without a '--' prefix
27+
simple_option_desc_re = re.compile(
28+
r'([-_a-zA-Z0-9]+)(\s*.*?)(?=,\s+(?:/|-|--)|$)')
1729

1830
def setup(app):
19-
app.connect('doctree-read', populate_index_to_rebuilds)
2031
app.add_crossref_type(
2132
directivename = "setting",
2233
rolename = "setting",
@@ -47,79 +58,93 @@ def setup(app):
4758
directivename = "django-admin-option",
4859
rolename = "djadminopt",
4960
indextemplate = u"pair: %s; django-admin コマンドラインオプション",
50-
parse_node = lambda env, sig, signode: sphinx.directives.parse_option_desc(signode, sig),
61+
parse_node = parse_django_adminopt_node,
5162
)
52-
app.add_transform(SuppressBlockquotes)
53-
54-
# Monkeypatch PickleHTMLBuilder so that it doesn't die in Sphinx 0.4.2
55-
if sphinx.__version__ == '0.4.2':
56-
monkeypatch_pickle_builder()
57-
58-
class SuppressBlockquotes(docutils.transforms.Transform):
59-
"""
60-
Remove the default blockquotes that encase indented list, tables, etc.
61-
"""
62-
default_priority = 300
63-
64-
suppress_blockquote_child_nodes = (
65-
docutils.nodes.bullet_list,
66-
docutils.nodes.enumerated_list,
67-
docutils.nodes.definition_list,
68-
docutils.nodes.literal_block,
69-
docutils.nodes.doctest_block,
70-
docutils.nodes.line_block,
71-
docutils.nodes.table
72-
)
73-
74-
def apply(self):
75-
for node in self.document.traverse(docutils.nodes.block_quote):
76-
if len(node.children) == 1 and isinstance(node.children[0], self.suppress_blockquote_child_nodes):
77-
node.replace_self(node.children[0])
63+
app.add_config_value('django_next_version', '0.0', True)
64+
app.add_directive('versionadded', VersionDirective)
65+
app.add_directive('versionchanged', VersionDirective)
66+
app.add_builder(DjangoStandaloneHTMLBuilder)
67+
7868

79-
class DjangoHTMLTranslator(sphinx.htmlwriter.SmartyPantsHTMLTranslator):
69+
class VersionDirective(Directive):
70+
has_content = True
71+
required_arguments = 1
72+
optional_arguments = 1
73+
final_argument_whitespace = True
74+
option_spec = {}
75+
76+
def run(self):
77+
env = self.state.document.settings.env
78+
arg0 = self.arguments[0]
79+
is_nextversion = env.config.django_next_version == arg0
80+
ret = []
81+
node = addnodes.versionmodified()
82+
ret.append(node)
83+
if not is_nextversion:
84+
if len(self.arguments) == 1:
85+
linktext = u'リリースノートを参照してください </releases/%s>' % (arg0)
86+
xrefs = roles.XRefRole()('doc', linktext, linktext, self.lineno, self.state)
87+
node.extend(xrefs[0])
88+
node['version'] = arg0
89+
else:
90+
node['version'] = "Development version"
91+
node['type'] = self.name
92+
if len(self.arguments) == 2:
93+
inodes, messages = self.state.inline_text(self.arguments[1], self.lineno+1)
94+
node.extend(inodes)
95+
if self.content:
96+
self.state.nested_parse(self.content, self.content_offset, node)
97+
ret = ret + messages
98+
env.note_versionchange(node['type'], node['version'], node, self.lineno)
99+
return ret
100+
101+
102+
class DjangoHTMLTranslator(SmartyPantsHTMLTranslator):
80103
"""
81104
Django-specific reST to HTML tweaks.
82105
"""
83106

84107
# Don't use border=1, which docutils does by default.
85108
def visit_table(self, node):
109+
self._table_row_index = 0 # Needed by Sphinx
86110
self.body.append(self.starttag(node, 'table', CLASS='docutils'))
87-
111+
88112
# <big>? Really?
89113
def visit_desc_parameterlist(self, node):
90114
self.body.append('(')
91115
self.first_param = 1
92-
116+
self.param_separator = node.child_text_separator
117+
93118
def depart_desc_parameterlist(self, node):
94119
self.body.append(')')
95-
pass
96-
97-
#
98-
# Don't apply smartypants to literal blocks
99-
#
100-
def visit_literal_block(self, node):
101-
self.no_smarty += 1
102-
sphinx.htmlwriter.SmartyPantsHTMLTranslator.visit_literal_block(self, node)
103-
104-
def depart_literal_block(self, node):
105-
sphinx.htmlwriter.SmartyPantsHTMLTranslator.depart_literal_block(self, node)
106-
self.no_smarty -= 1
107-
120+
121+
if sphinx_ver < '1.0.8':
122+
#
123+
# Don't apply smartypants to literal blocks
124+
#
125+
def visit_literal_block(self, node):
126+
self.no_smarty += 1
127+
SmartyPantsHTMLTranslator.visit_literal_block(self, node)
128+
129+
def depart_literal_block(self, node):
130+
SmartyPantsHTMLTranslator.depart_literal_block(self, node)
131+
self.no_smarty -= 1
132+
108133
#
109-
# Turn the "new in version" stuff (versoinadded/versionchanged) into a
134+
# Turn the "new in version" stuff (versionadded/versionchanged) into a
110135
# better callout -- the Sphinx default is just a little span,
111136
# which is a bit less obvious that I'd like.
112137
#
113-
# FIXME: these messages are all hardcoded in English. We need to chanage
138+
# FIXME: these messages are all hardcoded in English. We need to change
114139
# that to accomodate other language docs, but I can't work out how to make
115-
# that work and I think it'll require Sphinx 0.5 anyway.
140+
# that work.
116141
#
117142
version_text = {
118143
'deprecated': u'Django %s で撤廃されました',
119144
'versionchanged': u'Django %s で変更されました',
120145
'versionadded': u'Django %s で新たに登場しました',
121146
}
122-
147+
123148
def visit_versionmodified(self, node):
124149
self.body.append(
125150
self.starttag(node, 'div', CLASS=node['type'])
@@ -129,71 +154,77 @@ def visit_versionmodified(self, node):
129154
len(node) and ":" or "."
130155
)
131156
self.body.append('<span class="title">%s</span> ' % title)
132-
157+
133158
def depart_versionmodified(self, node):
134159
self.body.append("</div>\n")
135-
160+
136161
# Give each section a unique ID -- nice for custom CSS hooks
137-
# This is different on docutils 0.5 vs. 0.4...
138-
139-
# The docutils 0.4 override.
140-
if hasattr(sphinx.htmlwriter.SmartyPantsHTMLTranslator, 'start_tag_with_title'):
141-
def start_tag_with_title(self, node, tagname, **atts):
142-
node = {
143-
'classes': node.get('classes', []),
144-
'ids': ['s-%s' % i for i in node.get('ids', [])]
145-
}
146-
return self.starttag(node, tagname, **atts)
147-
148-
# The docutils 0.5 override.
149-
else:
150-
def visit_section(self, node):
151-
old_ids = node.get('ids', [])
152-
node['ids'] = ['s-' + i for i in old_ids]
153-
sphinx.htmlwriter.SmartyPantsHTMLTranslator.visit_section(self, node)
154-
node['ids'] = old_ids
162+
def visit_section(self, node):
163+
old_ids = node.get('ids', [])
164+
node['ids'] = ['s-' + i for i in old_ids]
165+
node['ids'].extend(old_ids)
166+
SmartyPantsHTMLTranslator.visit_section(self, node)
167+
node['ids'] = old_ids
155168

156169
def parse_django_admin_node(env, sig, signode):
157170
command = sig.split(' ')[0]
158171
env._django_curr_admin_command = command
159172
title = "django-admin.py %s" % sig
160-
signode += sphinx.addnodes.desc_name(title, title)
173+
signode += addnodes.desc_name(title, title)
161174
return sig
162175

163-
def monkeypatch_pickle_builder():
164-
import shutil
165-
from os import path
166-
try:
167-
import cPickle as pickle
168-
except ImportError:
169-
import pickle
170-
from sphinx.util.console import bold
171-
172-
def handle_finish(self):
173-
# dump the global context
174-
outfilename = path.join(self.outdir, 'globalcontext.pickle')
175-
f = open(outfilename, 'wb')
176-
try:
177-
pickle.dump(self.globalcontext, f, 2)
178-
finally:
179-
f.close()
176+
def parse_django_adminopt_node(env, sig, signode):
177+
"""A copy of sphinx.directives.CmdoptionDesc.parse_signature()"""
178+
from sphinx.domains.std import option_desc_re
179+
count = 0
180+
firstname = ''
181+
for m in option_desc_re.finditer(sig):
182+
optname, args = m.groups()
183+
if count:
184+
signode += addnodes.desc_addname(', ', ', ')
185+
signode += addnodes.desc_name(optname, optname)
186+
signode += addnodes.desc_addname(args, args)
187+
if not count:
188+
firstname = optname
189+
count += 1
190+
if not count:
191+
for m in simple_option_desc_re.finditer(sig):
192+
optname, args = m.groups()
193+
if count:
194+
signode += addnodes.desc_addname(', ', ', ')
195+
signode += addnodes.desc_name(optname, optname)
196+
signode += addnodes.desc_addname(args, args)
197+
if not count:
198+
firstname = optname
199+
count += 1
200+
if not firstname:
201+
raise ValueError
202+
return firstname
180203

181-
self.info(bold('dumping search index...'))
182-
self.indexer.prune(self.env.all_docs)
183-
f = open(path.join(self.outdir, 'searchindex.pickle'), 'wb')
184-
try:
185-
self.indexer.dump(f, 'pickle')
186-
finally:
187-
f.close()
188204

189-
# copy the environment file from the doctree dir to the output dir
190-
# as needed by the web app
191-
shutil.copyfile(path.join(self.doctreedir, sphinx.builder.ENV_PICKLE_FILENAME),
192-
path.join(self.outdir, sphinx.builder.ENV_PICKLE_FILENAME))
205+
class DjangoStandaloneHTMLBuilder(StandaloneHTMLBuilder):
206+
"""
207+
Subclass to add some extra things we need.
208+
"""
193209

194-
# touch 'last build' file, used by the web application to determine
195-
# when to reload its environment and clear the cache
196-
open(path.join(self.outdir, sphinx.builder.LAST_BUILD_FILENAME), 'w').close()
210+
name = 'djangohtml'
197211

198-
sphinx.builder.PickleHTMLBuilder.handle_finish = handle_finish
199-
212+
def finish(self):
213+
super(DjangoStandaloneHTMLBuilder, self).finish()
214+
if json is None:
215+
self.warn("cannot create templatebuiltins.js due to missing simplejson dependency")
216+
return
217+
self.info(bold("writing templatebuiltins.js..."))
218+
xrefs = self.env.domaindata["std"]["objects"]
219+
templatebuiltins = {
220+
"ttags": [n for ((t, n), (l, a)) in xrefs.items()
221+
if t == "templatetag" and l == "ref/templates/builtins"],
222+
"tfilters": [n for ((t, n), (l, a)) in xrefs.items()
223+
if t == "templatefilter" and l == "ref/templates/builtins"],
224+
}
225+
outfilename = os.path.join(self.outdir, "templatebuiltins.js")
226+
f = open(outfilename, 'wb')
227+
f.write('var django_template_builtins = ')
228+
json.dump(templatebuiltins, f)
229+
f.write(';\n')
230+
f.close();

0 commit comments

Comments
 (0)