Permalink
Browse files

Fixed #20910 -- Added a "snippet" sphinx directive to allow prefixing…

… a filename.

Thanks Marc Tamlyn for the suggestion.
  • Loading branch information...
1 parent e077224 commit d07d6ae1167f93f2e88b3743c070003a66a31b35 @nsmgr8 nsmgr8 committed with timgraham Sep 23, 2013
View
@@ -282,6 +282,7 @@ answer newbie questions, and generally made Django that much better:
Scot Hacker <shacker@birdhouse.org>
dAniel hAhler
hambaloney
+ Nasimul Haque <nasim.haque@gmail.com>
Will Hardy <django@willhardy.com.au>
Brian Harring <ferringb@gmail.com>
Brant Harris
@@ -5,11 +5,15 @@
import os
import re
+from docutils import nodes
+from docutils.parsers.rst import directives
+
from sphinx import addnodes, __version__ as sphinx_ver
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.writers.html import SmartyPantsHTMLTranslator
from sphinx.util.console import bold
from sphinx.util.compat import Directive
+from sphinx.util.nodes import set_source_info
# RE for option descriptions without a '--' prefix
simple_option_desc_re = re.compile(
@@ -53,6 +57,136 @@ def setup(app):
app.add_directive('versionchanged', VersionDirective)
app.add_builder(DjangoStandaloneHTMLBuilder)
+ # register the snippet directive
+ app.add_directive('snippet', SnippetWithFilename)
+ # register a node for snippet directive so that the xml parser
+ # knows how to handle the enter/exit parsing event
+ app.add_node(snippet_with_filename,
+ html=(visit_snippet, depart_snippet_literal),
+ latex=(visit_snippet_latex, depart_snippet_latex),
+ man=(visit_snippet_literal, depart_snippet_literal),
+ text=(visit_snippet_literal, depart_snippet_literal),
+ texinfo=(visit_snippet_literal, depart_snippet_literal))
+
+
+class snippet_with_filename(nodes.literal_block):
+ """
+ Subclass the literal_block to override the visit/depart event handlers
+ """
+ pass
+
+
+def visit_snippet_literal(self, node):
+ """
+ default literal block handler
+ """
+ self.visit_literal_block(node)
+
+
+def depart_snippet_literal(self, node):
+ """
+ default literal block handler
+ """
+ self.depart_literal_block(node)
+
+
+def visit_snippet(self, node):
+ """
+ HTML document generator visit handler
+ """
+ lang = self.highlightlang
+ linenos = node.rawsource.count('\n') >= self.highlightlinenothreshold - 1
+ fname = node['filename']
+ highlight_args = node.get('highlight_args', {})
+ if node.has_key('language'):
+ # code-block directives
+ lang = node['language']
+ highlight_args['force'] = True
+ if node.has_key('linenos'):
+ linenos = node['linenos']
+
+ def warner(msg):
+ self.builder.warn(msg, (self.builder.current_docname, node.line))
+
+ highlighted = self.highlighter.highlight_block(node.rawsource, lang,
+ warn=warner,
+ linenos=linenos,
+ **highlight_args)
+ starttag = self.starttag(node, 'div', suffix='',
+ CLASS='highlight-%s' % lang)
+ self.body.append(starttag)
+ self.body.append('<div class="snippet-filename">%s</div>\n''' % (fname,))
+ self.body.append(highlighted)
+ self.body.append('</div>\n')
+ raise nodes.SkipNode
+
+
+def visit_snippet_latex(self, node):
+ """
+ Latex document generator visit handler
+ """
+ self.verbatim = ''
+
+
+def depart_snippet_latex(self, node):
+ """
+ Latex document generator depart handler.
+ """
+ code = self.verbatim.rstrip('\n')
+ lang = self.hlsettingstack[-1][0]
+ linenos = code.count('\n') >= self.hlsettingstack[-1][1] - 1
+ fname = node['filename']
+ highlight_args = node.get('highlight_args', {})
+ if 'language' in node:
+ # code-block directives
+ lang = node['language']
+ highlight_args['force'] = True
+ if 'linenos' in node:
+ linenos = node['linenos']
+
+ def warner(msg):
+ self.builder.warn(msg, (self.curfilestack[-1], node.line))
+
+ hlcode = self.highlighter.highlight_block(code, lang, warn=warner,
+ linenos=linenos,
+ **highlight_args)
+
+ self.body.append('\n{\\colorbox[rgb]{0.9,0.9,0.9}'
+ '{\\makebox[\\textwidth][l]'
+ '{\\small\\texttt{%s}}}}\n' % (fname,))
+
+ if self.table:
+ hlcode = hlcode.replace('\\begin{Verbatim}',
+ '\\begin{OriginalVerbatim}')
+ self.table.has_problematic = True
+ self.table.has_verbatim = True
+
+ hlcode = hlcode.rstrip()[:-14] # strip \end{Verbatim}
+ hlcode = hlcode.rstrip() + '\n'
+ self.body.append('\n' + hlcode + '\\end{%sVerbatim}\n' %
+ (self.table and 'Original' or ''))
+ self.verbatim = None
+
+
+class SnippetWithFilename(Directive):
+ """
+ The 'snippet' directive that allows to add the filename (optional)
+ of a code snippet in the document. This is modeled after CodeBlock.
+ """
+ has_content = True
+ optional_arguments = 1
+ option_spec = {'filename': directives.unchanged_required}
+
+ def run(self):
+ code = u'\n'.join(self.content)
+
+ literal = snippet_with_filename(code, code)
+ if self.arguments:
+ literal['language'] = self.arguments[0]
+ literal['filename'] = self.options['filename']
+ set_source_info(self, literal)
+ return [literal]
+
class VersionDirective(Directive):
has_content = True
@@ -100,6 +100,9 @@ pre { font-size:small; background:#E0FFB8; border:1px solid #94da3a; border-widt
dt .literal, table .literal { background:none; }
#bd a.reference { text-decoration: none; }
#bd a.reference tt.literal { border-bottom: 1px #234f32 dotted; }
+div.snippet-filename { color: white; background-color: #234F32; margin: 0; padding: 2px 5px; width: 100%; font-family: monospace; font-size: small; line-height: 1.3em; }
+div.snippet-filename + div.highlight > pre { margin-top: 0; }
+div.snippet-filename + pre { margin-top: 0; }
/* Restore colors of pygments hyperlinked code */
#bd .highlight .k a:link, #bd .highlight .k a:visited { color: #000000; text-decoration: none; border-bottom: 1px dotted #000000; }
@@ -120,112 +120,122 @@ this. For a small app like polls, this process isn't too difficult.
1. First, create a parent directory for ``polls``, outside of your Django
project. Call this directory ``django-polls``.
-.. admonition:: Choosing a name for your app
+ .. admonition:: Choosing a name for your app
- When choosing a name for your package, check resources like PyPI to avoid
- naming conflicts with existing packages. It's often useful to prepend
- ``django-`` to your module name when creating a package to distribute.
- This helps others looking for Django apps identify your app as Django
- specific.
+ When choosing a name for your package, check resources like PyPI to avoid
+ naming conflicts with existing packages. It's often useful to prepend
+ ``django-`` to your module name when creating a package to distribute.
+ This helps others looking for Django apps identify your app as Django
+ specific.
2. Move the ``polls`` directory into the ``django-polls`` directory.
-3. Create a file ``django-polls/README.rst`` with the following contents::
+3. Create a file ``django-polls/README.rst`` with the following contents:
- =====
- Polls
- =====
+ .. snippet::
+ :filename: django-polls/README.rst
- Polls is a simple Django app to conduct Web-based polls. For each
- question, visitors can choose between a fixed number of answers.
+ =====
+ Polls
+ =====
- Detailed documentation is in the "docs" directory.
+ Polls is a simple Django app to conduct Web-based polls. For each
+ question, visitors can choose between a fixed number of answers.
- Quick start
- -----------
+ Detailed documentation is in the "docs" directory.
- 1. Add "polls" to your INSTALLED_APPS setting like this::
+ Quick start
+ -----------
- INSTALLED_APPS = (
- ...
- 'polls',
- )
+ 1. Add "polls" to your INSTALLED_APPS setting like this::
- 2. Include the polls URLconf in your project urls.py like this::
+ INSTALLED_APPS = (
+ ...
+ 'polls',
+ )
- url(r'^polls/', include('polls.urls')),
+ 2. Include the polls URLconf in your project urls.py like this::
- 3. Run `python manage.py migrate` to create the polls models.
+ url(r'^polls/', include('polls.urls')),
- 4. Start the development server and visit http://127.0.0.1:8000/admin/
- to create a poll (you'll need the Admin app enabled).
+ 3. Run `python manage.py migrate` to create the polls models.
- 5. Visit http://127.0.0.1:8000/polls/ to participate in the poll.
+ 4. Start the development server and visit http://127.0.0.1:8000/admin/
+ to create a poll (you'll need the Admin app enabled).
+
+ 5. Visit http://127.0.0.1:8000/polls/ to participate in the poll.
4. Create a ``django-polls/LICENSE`` file. Choosing a license is beyond the
-scope of this tutorial, but suffice it to say that code released publicly
-without a license is *useless*. Django and many Django-compatible apps are
-distributed under the BSD license; however, you're free to pick your own
-license. Just be aware that your licensing choice will affect who is able
-to use your code.
+ scope of this tutorial, but suffice it to say that code released publicly
+ without a license is *useless*. Django and many Django-compatible apps are
+ distributed under the BSD license; however, you're free to pick your own
+ license. Just be aware that your licensing choice will affect who is able
+ to use your code.
5. Next we'll create a ``setup.py`` file which provides details about how to
-build and install the app. A full explanation of this file is beyond the
-scope of this tutorial, but the `distribute docs
-<http://packages.python.org/distribute/setuptools.html>`_ have a good explanation.
-Create a file ``django-polls/setup.py`` with the following contents::
-
- import os
- from setuptools import setup
-
- README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read()
-
- # allow setup.py to be run from any path
- os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
-
- setup(
- name='django-polls',
- version='0.1',
- packages=['polls'],
- include_package_data=True,
- license='BSD License', # example license
- description='A simple Django app to conduct Web-based polls.',
- long_description=README,
- url='http://www.example.com/',
- author='Your Name',
- author_email='yourname@example.com',
- classifiers=[
- 'Environment :: Web Environment',
- 'Framework :: Django',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: BSD License', # example license
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- # replace these appropriately if you are using Python 3
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.7',
- 'Topic :: Internet :: WWW/HTTP',
- 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
- ],
- )
-
-.. admonition:: I thought you said we were going to use ``distribute``?
-
- Distribute is a drop-in replacement for ``setuptools``. Even though we
- appear to import from ``setuptools``, since we have ``distribute``
- installed, it will override the import.
+ build and install the app. A full explanation of this file is beyond the
+ scope of this tutorial, but the `distribute docs
+ <http://packages.python.org/distribute/setuptools.html>`_ have a good
+ explanation. Create a file ``django-polls/setup.py`` with the following
+ contents:
+
+ .. snippet::
+ :filename: django-polls/setup.py
+
+ import os
+ from setuptools import setup
+
+ README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read()
+
+ # allow setup.py to be run from any path
+ os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
+
+ setup(
+ name='django-polls',
+ version='0.1',
+ packages=['polls'],
+ include_package_data=True,
+ license='BSD License', # example license
+ description='A simple Django app to conduct Web-based polls.',
+ long_description=README,
+ url='http://www.example.com/',
+ author='Your Name',
+ author_email='yourname@example.com',
+ classifiers=[
+ 'Environment :: Web Environment',
+ 'Framework :: Django',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: BSD License', # example license
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ # replace these appropriately if you are using Python 3
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 2.7',
+ 'Topic :: Internet :: WWW/HTTP',
+ 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
+ ],
+ )
+
+ .. admonition:: I thought you said we were going to use ``distribute``?
+
+ Distribute is a drop-in replacement for ``setuptools``. Even though we
+ appear to import from ``setuptools``, since we have ``distribute``
+ installed, it will override the import.
6. Only Python modules and packages are included in the package by default. To
include additional files, we'll need to create a ``MANIFEST.in`` file. The
distribute docs referred to in the previous step discuss this file in more
details. To include the templates, the ``README.rst`` and our ``LICENSE``
file, create a file ``django-polls/MANIFEST.in`` with the following
- contents::
+ contents:
+
+ .. snippet::
+ :filename: django-polls/MANIFEST.in
- include LICENSE
- include README.rst
- recursive-include polls/static *
- recursive-include polls/templates *
+ include LICENSE
+ include README.rst
+ recursive-include polls/static *
+ recursive-include polls/templates *
7. It's optional, but recommended, to include detailed documentation with your
app. Create an empty directory ``django-polls/docs`` for future
Oops, something went wrong. Retry.

0 comments on commit d07d6ae

Please sign in to comment.