Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate json artifacts #43

Merged
merged 20 commits into from
Jun 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 64 additions & 1 deletion readthedocs_ext/readthedocs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,49 @@
from __future__ import absolute_import

import codecs
import json
import os
import re
import types
from distutils.version import LooseVersion

import sphinx
from sphinx import package_dir
from sphinx.builders.html import StandaloneHTMLBuilder, DirectoryHTMLBuilder, SingleFileHTMLBuilder
from sphinx.builders.html import (DirectoryHTMLBuilder, SingleFileHTMLBuilder,
StandaloneHTMLBuilder)
from sphinx.util.console import bold

from .embed import EmbedDirective
from .mixins import BuilderMixin

try:
# Avaliable from Sphinx 1.6
from sphinx.util.logging import getLogger
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sphinx 1.4 has the logging module, but not getLogger

except ImportError:
from logging import getLogger

log = getLogger(__name__)

MEDIA_MAPPING = {
"_static/jquery.js": "%sjavascript/jquery/jquery-2.0.3.min.js",
"_static/underscore.js": "%sjavascript/underscore.js",
"_static/doctools.js": "%sjavascript/doctools.js",
}


# Whitelist keys that we want to output
# to the json artifacts.
KEYS = [
'body',
'title',
'sourcename',
'current_page_name',
'rellinks',
'toc',
'page_source_suffix',
]


def finalize_media(app):
""" Point media files at our media server. """

Expand Down Expand Up @@ -126,6 +149,44 @@ def rtd_render(self, template, render_context):
app.builder.templates)


def generate_json_artifacts(app, pagename, templatename, context, doctree):
"""
Generate JSON artifacts for each page.

This way we can skip generating this in other build step.
"""
try:
if not app.config.rtd_generate_json_artifacts:
return
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't find a way of using this configuration in the setup step, so I'm checking it here

# We need to get the output directory where the docs are built
# _build/json.
build_json = os.path.abspath(
os.path.join(app.outdir, '..', 'json')
)
outjson = os.path.join(build_json, pagename + '.fjson')
outdir = os.path.dirname(outjson)
if not os.path.exists(outdir):
os.makedirs(outdir)
with open(outjson, 'w+') as json_file:
to_context = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think the field should be blank rather than non existence if the key is missing.
It should be something like

to_context = { key: context.get(key, '') for key in KEYS}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense, so that the JSON always has the same keys.

key: context.get(key, '')
for key in KEYS
}
json.dump(to_context, json_file, indent=4)
except TypeError:
log.exception(
'Fail to encode JSON for page {page}'.format(page=outjson)
)
except IOError:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for this

log.exception(
'Fail to save JSON output for page {page}'.format(page=outjson)
)
except Exception as e:
log.exception(
'Failure in JSON search dump for page {page}'.format(page=outjson)
)


class HtmlBuilderMixin(BuilderMixin):

static_readthedocs_files = [
Expand Down Expand Up @@ -213,11 +274,13 @@ def setup(app):
app.add_builder(ReadtheDocsSingleFileHTMLBuilderLocalMedia)
app.connect('builder-inited', finalize_media)
app.connect('html-page-context', update_body)
app.connect('html-page-context', generate_json_artifacts)

# Embed
app.add_directive('readthedocs-embed', EmbedDirective)
app.add_config_value('readthedocs_embed_project', '', 'html')
app.add_config_value('readthedocs_embed_version', '', 'html')
app.add_config_value('readthedocs_embed_doc', '', 'html')
app.add_config_value('rtd_generate_json_artifacts', False, 'html')

return {}
25 changes: 25 additions & 0 deletions tests/pyexample-json/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-

import os
import sys

sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))

extensions = ['readthedocs_ext.readthedocs']

templates_path = ['_templates']
source_suffix = '.rst'
master_doc = 'index'
project = u'pyexample'
copyright = u'2015, rtfd'
author = u'rtfd'
version = '0.1'
release = '0.1'
language = None
exclude_patterns = ['_build']
pygments_style = 'sphinx'
todo_include_todos = False
html_theme = 'alabaster'
html_static_path = ['_static']
htmlhelp_basename = 'pyexampledoc'
rtd_generate_json_artifacts = True
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This value should be in the conf.py template of the rtd repo :)

9 changes: 9 additions & 0 deletions tests/pyexample-json/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.. pyexample documentation master file, created by
sphinx-quickstart on Fri May 29 13:34:37 2015.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.

Welcome to pyexample's documentation!
=====================================

Hey there friend!
29 changes: 28 additions & 1 deletion tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ class LanguageIntegrationTests(unittest.TestCase):

def _run_test(self, test_dir, test_file, test_string, builder='html'):
with build_output(test_dir, test_file, builder) as data:
self.assertIn(test_string, data)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I replate this to test more easily the output :)

if not isinstance(test_string, list):
test_strings = [test_string]
else:
test_strings = test_string
for string in test_strings:
self.assertIn(string, data)


class IntegrationTests(LanguageIntegrationTests):
Expand Down Expand Up @@ -56,3 +61,25 @@ def test_searchtools_is_patched(self):
HtmlBuilderMixin.REPLACEMENT_PATTERN
)
self.assertIn(HtmlBuilderMixin.REPLACEMENT_TEXT, data)

def test_generate_json_artifacts(self):
self._run_test(
'pyexample-json',
'_build/json/index.fjson',
[
'current_page_name', 'title', 'body',
'toc', 'sourcename', 'rellinks', 'page_source_suffix',
],
)

def test_no_generate_json_artifacts(self):
with self.assertRaises(IOError) as e:
self._run_test(
'pyexample',
'_build/json/index.fjson',
['current_page_name', 'title', 'body', 'toc'],
)
self.assertIn(
"No such file or directory: '_build/json/index.fjson'",
str(e.exception)
)
5 changes: 3 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tox]
envlist =
py{27,35,36}-sphinx{13,14,15,16}
py{27,35,36}-sphinx{13,14,15,16,17}
lint

[testenv]
Expand All @@ -13,7 +13,8 @@ deps =
sphinx13: Sphinx<1.4
sphinx14: Sphinx<1.5
sphinx15: Sphinx<1.6
sphinx16: Sphinx==1.6b3
sphinx16: Sphinx<1.7
sphinx17: Sphinx==1.7.4
commands =
py.test {posargs}

Expand Down