[Python-modules-commits] [sphinxcontrib-websupport] 01/01: Upstream version 1.0.1
Dmitry Shachnev
mitya57 at moszumanska.debian.org
Sun Jul 9 15:00:44 UTC 2017
This is an automated email from the git hooks/post-receive script.
mitya57 pushed a commit to branch debian/master
in repository sphinxcontrib-websupport.
commit 5d53f4a598582a306984f67de7a04c8cec955dd4
Author: Dmitry Shachnev <mitya57 at gmail.com>
Date: Sun Jul 9 17:59:14 2017 +0300
Upstream version 1.0.1
---
CHANGES | 9 +
LICENSE | 29 ++
MANIFEST.in | 7 +
PKG-INFO | 30 ++
README.rst | 38 ++
setup.cfg | 21 +
setup.py | 65 +++
sphinxcontrib/__init__.py | 2 +
sphinxcontrib/websupport/__init__.py | 16 +
sphinxcontrib/websupport/builder.py | 196 +++++++++
sphinxcontrib/websupport/core.py | 456 +++++++++++++++++++++
sphinxcontrib/websupport/errors.py | 26 ++
sphinxcontrib/websupport/search/__init__.py | 134 ++++++
sphinxcontrib/websupport/search/nullsearch.py | 24 ++
sphinxcontrib/websupport/search/whooshsearch.py | 61 +++
sphinxcontrib/websupport/search/xapiansearch.py | 86 ++++
sphinxcontrib/websupport/storage/__init__.py | 116 ++++++
sphinxcontrib/websupport/storage/differ.py | 86 ++++
sphinxcontrib/websupport/storage/sqlalchemy_db.py | 226 ++++++++++
.../websupport/storage/sqlalchemystorage.py | 177 ++++++++
sphinxcontrib/websupport/utils.py | 18 +
sphinxcontrib/websupport/version.py | 11 +
sphinxcontrib/websupport/writer.py | 46 +++
sphinxcontrib_websupport.egg-info/PKG-INFO | 30 ++
sphinxcontrib_websupport.egg-info/SOURCES.txt | 93 +++++
.../dependency_links.txt | 1 +
.../namespace_packages.txt | 1 +
sphinxcontrib_websupport.egg-info/not-zip-safe | 1 +
sphinxcontrib_websupport.egg-info/requires.txt | 4 +
sphinxcontrib_websupport.egg-info/top_level.txt | 1 +
tests/path.py | 207 ++++++++++
tests/root/Makefile | 67 +++
tests/root/_static/README | 1 +
tests/root/_static/excluded.css | 1 +
tests/root/_static/subdir/foo.css | 1 +
tests/root/_templates/contentssb.html | 2 +
tests/root/_templates/customsb.html | 4 +
tests/root/_templates/layout.html | 15 +
tests/root/autodoc.txt | 49 +++
tests/root/autodoc_fodder.py | 7 +
tests/root/autodoc_missing_imports.py | 18 +
tests/root/bom.po | 12 +
tests/root/bom.txt | 5 +
tests/root/conf.py | 117 ++++++
tests/root/contents.txt | 70 ++++
tests/root/en.lproj/localized.txt | 2 +
tests/root/ext.py | 4 +
tests/root/extapi.txt | 10 +
tests/root/extensions.txt | 28 ++
tests/root/footnote.txt | 57 +++
tests/root/images.txt | 28 ++
tests/root/img.foo.png | Bin 0 -> 66247 bytes
tests/root/img.gif | Bin 0 -> 24976 bytes
tests/root/img.pdf | Bin 0 -> 141783 bytes
tests/root/img.png | Bin 0 -> 66247 bytes
tests/root/includes.txt | 102 +++++
tests/root/lists.txt | 63 +++
tests/root/literal.inc | 13 +
tests/root/literal_orig.inc | 13 +
tests/root/markup.txt | 432 +++++++++++++++++++
tests/root/math.txt | 31 ++
tests/root/metadata.add | 57 +++
tests/root/objects.txt | 213 ++++++++++
tests/root/otherext.foo | 2 +
tests/root/parsermod.py | 12 +
tests/root/quotes.inc | 1 +
tests/root/rimg.png | Bin 0 -> 120 bytes
tests/root/robots.txt | 2 +
tests/root/special/api.h | 1 +
tests/root/special/code.py | 2 +
tests/root/subdir.po | 9 +
tests/root/subdir/excluded.txt | 2 +
tests/root/subdir/images.txt | 6 +
tests/root/subdir/img.png | Bin 0 -> 66247 bytes
tests/root/subdir/include.inc | 5 +
tests/root/subdir/includes.txt | 18 +
tests/root/subdir/simg.png | Bin 0 -> 66247 bytes
tests/root/svgimg.pdf | Bin 0 -> 141783 bytes
tests/root/svgimg.svg | 158 +++++++
tests/root/tabs.inc | 5 +
tests/root/templated.css_t | 2 +
tests/root/test.inc | 3 +
tests/root/testtheme/layout.html | 5 +
tests/root/testtheme/static/staticimg.png | Bin 0 -> 120 bytes
tests/root/testtheme/static/statictmpl.html_t | 2 +
tests/root/testtheme/theme.conf | 7 +
tests/root/wrongenc.inc | 3 +
tests/root/ziptheme.zip | Bin 0 -> 1039 bytes
tests/roots/test-searchadapters/conf.py | 4 +
tests/roots/test-searchadapters/markup.txt | 447 ++++++++++++++++++++
tests/test_searchadapters.py | 69 ++++
tests/test_websupport.py | 274 +++++++++++++
tests/util.py | 364 ++++++++++++++++
tox.ini | 26 ++
94 files changed, 5069 insertions(+)
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..93d87b2
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,9 @@
+Release 1.0.1 (2017-05-07)
+==========================
+
+* Make sqlalchemy and whoosh as optional dependency
+
+Release 1.0.0 (2017-04-23)
+==========================
+
+* Initial release (copied from sphinx package)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3b8fd8e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+License for sphinxcontrib-websupport
+====================================
+
+Copyright (c) 2007-2017 by the Sphinx team
+(see https://github.com/sphinx-doc/sphinx/blob/master/AUTHORS).
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..4db06ab
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,7 @@
+include README.rst
+include LICENSE
+include CHANGES
+
+include tox.ini
+
+recursive-include tests *
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..15089b9
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,30 @@
+Metadata-Version: 1.1
+Name: sphinxcontrib-websupport
+Version: 1.0.1
+Summary: Sphinx API for Web Apps
+Home-page: http://sphinx-doc.org/
+Author: Georg Brandl
+Author-email: georg at python.org
+License: BSD
+Download-URL: https://pypi.python.org/pypi/sphinxcontrib-websupport
+Description:
+ sphinxcontrib-webuspport provides a Python API to easily integrate Sphinx
+ documentation into your Web application.
+
+Platform: any
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Education
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Framework :: Sphinx
+Classifier: Framework :: Sphinx :: Extension
+Classifier: Topic :: Documentation
+Classifier: Topic :: Documentation :: Sphinx
+Classifier: Topic :: Text Processing
+Classifier: Topic :: Utilities
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..c1ab116
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,38 @@
+===================================
+README for sphinxcontrib-websupport
+===================================
+
+This is the Sphinx documentation generator, see http://www.sphinx-doc.org/.
+
+
+Installing
+==========
+
+Install from PyPI::
+
+ pip install -U sphinxcontrib-websupport
+
+Release signatures
+==================
+
+Releases are signed with following keys:
+
+* `498D6B9E <https://pgp.mit.edu/pks/lookup?op=vindex&search=0x102C2C17498D6B9E>`_
+* `5EBA0E07 <https://pgp.mit.edu/pks/lookup?op=vindex&search=0x1425F8CE5EBA0E07>`_
+
+Testing
+=======
+
+To run the tests with the interpreter available as ``python``, use::
+
+ tox
+
+Continuous testing runs on travis: https://travis-ci.org/sphinx-doc/sphinxcontrib-websupport
+
+Contributing
+============
+
+See `CONTRIBUTING.rst`__
+
+.. __: https://github.com/sphinx-doc/sphinx/blob/master/CONTRIBUTING.rst
+
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..4f81bff
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,21 @@
+[egg_info]
+tag_build =
+tag_date = 0
+
+[aliases]
+release = egg_info -Db ''
+upload = upload --sign --identity=36580288
+
+[extract_messages]
+mapping_file = babel.cfg
+output_file = sphinx/locale/sphinx.pot
+keywords = _ l_ lazy_gettext
+
+[wheel]
+universal = 1
+
+[flake8]
+max-line-length = 95
+ignore = E116,E241,E251
+exclude = .git,.tox,tests/*
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..c659309
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+import os
+from setuptools import setup, find_packages
+
+long_desc = '''
+sphinxcontrib-webuspport provides a Python API to easily integrate Sphinx
+documentation into your Web application.
+'''
+
+extras_require = {
+ # Environment Marker works for wheel 0.24 or later
+ 'test': [
+ 'pytest',
+ 'mock', # it would be better for 'test:python_version in 2.7'
+ ],
+}
+
+
+def get_version():
+ """Get version number of the package from version.py without importing core module."""
+ package_dir = os.path.abspath(os.path.dirname(__file__))
+ version_file = os.path.join(package_dir, 'sphinxcontrib/websupport/version.py')
+
+ namespace = {}
+ with open(version_file, 'rt') as f:
+ exec(f.read(), namespace)
+
+ return namespace['__version__']
+
+
+setup(
+ name='sphinxcontrib-websupport',
+ version=get_version(),
+ url='http://sphinx-doc.org/',
+ download_url='https://pypi.python.org/pypi/sphinxcontrib-websupport',
+ license='BSD',
+ author='Georg Brandl',
+ author_email='georg at python.org',
+ description='Sphinx API for Web Apps',
+ long_description=long_desc,
+ zip_safe=False,
+ classifiers=[
+ 'Development Status :: 5 - Production/Stable',
+ 'Environment :: Console',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: Education',
+ 'License :: OSI Approved :: BSD License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 3',
+ 'Framework :: Sphinx',
+ 'Framework :: Sphinx :: Extension',
+ 'Topic :: Documentation',
+ 'Topic :: Documentation :: Sphinx',
+ 'Topic :: Text Processing',
+ 'Topic :: Utilities',
+ ],
+ platforms='any',
+ packages=find_packages(exclude=['tests']),
+ include_package_data=True,
+ extras_require=extras_require,
+ namespace_packages=['sphinxcontrib'],
+)
diff --git a/sphinxcontrib/__init__.py b/sphinxcontrib/__init__.py
new file mode 100644
index 0000000..68c04af
--- /dev/null
+++ b/sphinxcontrib/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+__import__('pkg_resources').declare_namespace(__name__)
diff --git a/sphinxcontrib/websupport/__init__.py b/sphinxcontrib/websupport/__init__.py
new file mode 100644
index 0000000..643ff4a
--- /dev/null
+++ b/sphinxcontrib/websupport/__init__.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinxcontrib.websupport
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ A Python API to easily integrate Sphinx documentation into Web
+ applications.
+
+ :copyright: Copyright 2007-2017 by the Sphinx team, see README.
+ :license: BSD, see LICENSE for details.
+"""
+
+__import__('pkg_resources').declare_namespace(__name__)
+
+from sphinxcontrib.websupport.core import WebSupport # NOQA
+from sphinxcontrib.websupport.version import __version__, __version_info__ # NOQA
diff --git a/sphinxcontrib/websupport/builder.py b/sphinxcontrib/websupport/builder.py
new file mode 100644
index 0000000..d38626e
--- /dev/null
+++ b/sphinxcontrib/websupport/builder.py
@@ -0,0 +1,196 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders.websupport
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Builder for the web support package.
+
+ :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from os import path
+import posixpath
+import shutil
+
+from docutils.io import StringOutput
+
+from sphinx.jinja2glue import BuiltinTemplateLoader
+from sphinx.util.osutil import os_path, relative_uri, ensuredir, copyfile
+from sphinx.builders.html import PickleHTMLBuilder
+from sphinx.writers.websupport import WebSupportTranslator
+
+if False:
+ # For type annotation
+ from typing import Any, Dict, Iterable, Tuple # NOQA
+ from docutils import nodes # NOQA
+ from sphinx.application import Sphinx # NOQA
+
+
+class WebSupportBuilder(PickleHTMLBuilder):
+ """
+ Builds documents for the web support package.
+ """
+ name = 'websupport'
+ versioning_method = 'commentable'
+ versioning_compare = True # for commentable node's uuid stability.
+
+ def init(self):
+ # type: () -> None
+ PickleHTMLBuilder.init(self)
+ # templates are needed for this builder, but the serializing
+ # builder does not initialize them
+ self.init_templates()
+ if not isinstance(self.templates, BuiltinTemplateLoader):
+ raise RuntimeError('websupport builder must be used with '
+ 'the builtin templates')
+ # add our custom JS
+ self.script_files.append('_static/websupport.js')
+
+ def set_webinfo(self, staticdir, virtual_staticdir, search, storage):
+ # type: (unicode, unicode, Any, unicode) -> None
+ self.staticdir = staticdir
+ self.virtual_staticdir = virtual_staticdir
+ self.search = search
+ self.storage = storage
+
+ def init_translator_class(self):
+ # type: () -> None
+ if self.translator_class is None:
+ self.translator_class = WebSupportTranslator
+
+ def prepare_writing(self, docnames):
+ # type: (Iterable[unicode]) -> None
+ PickleHTMLBuilder.prepare_writing(self, docnames)
+ self.globalcontext['no_search_suffix'] = True
+
+ def write_doc(self, docname, doctree):
+ # type: (unicode, nodes.Node) -> None
+ destination = StringOutput(encoding='utf-8')
+ doctree.settings = self.docsettings
+
+ self.secnumbers = self.env.toc_secnumbers.get(docname, {})
+ self.fignumbers = self.env.toc_fignumbers.get(docname, {})
+ self.imgpath = '/' + posixpath.join(self.virtual_staticdir, self.imagedir)
+ self.dlpath = '/' + posixpath.join(self.virtual_staticdir, '_downloads')
+ self.current_docname = docname
+ self.docwriter.write(doctree, destination)
+ self.docwriter.assemble_parts()
+ body = self.docwriter.parts['fragment']
+ metatags = self.docwriter.clean_meta
+
+ ctx = self.get_doc_context(docname, body, metatags)
+ self.handle_page(docname, ctx, event_arg=doctree)
+
+ def write_doc_serialized(self, docname, doctree):
+ # type: (unicode, nodes.Node) -> None
+ self.imgpath = '/' + posixpath.join(self.virtual_staticdir, self.imagedir)
+ self.post_process_images(doctree)
+ title = self.env.longtitles.get(docname)
+ title = title and self.render_partial(title)['title'] or ''
+ self.index_page(docname, doctree, title)
+
+ def load_indexer(self, docnames):
+ # type: (Iterable[unicode]) -> None
+ self.indexer = self.search # type: ignore
+ self.indexer.init_indexing(changed=docnames) # type: ignore
+
+ def _render_page(self, pagename, addctx, templatename, event_arg=None):
+ # type: (unicode, Dict, unicode, unicode) -> Tuple[Dict, Dict]
+ # This is mostly copied from StandaloneHTMLBuilder. However, instead
+ # of rendering the template and saving the html, create a context
+ # dict and pickle it.
+ ctx = self.globalcontext.copy()
+ ctx['pagename'] = pagename
+
+ def pathto(otheruri, resource=False,
+ baseuri=self.get_target_uri(pagename)):
+ # type: (unicode, bool, unicode) -> unicode
+ if resource and '://' in otheruri:
+ return otheruri
+ elif not resource:
+ otheruri = self.get_target_uri(otheruri)
+ return relative_uri(baseuri, otheruri) or '#'
+ else:
+ return '/' + posixpath.join(self.virtual_staticdir, otheruri)
+ ctx['pathto'] = pathto
+ ctx['hasdoc'] = lambda name: name in self.env.all_docs
+ ctx['encoding'] = self.config.html_output_encoding
+ ctx['toctree'] = lambda **kw: self._get_local_toctree(pagename, **kw)
+ self.add_sidebars(pagename, ctx)
+ ctx.update(addctx)
+
+ newtmpl = self.app.emit_firstresult('html-page-context', pagename,
+ templatename, ctx, event_arg)
+ if newtmpl:
+ templatename = newtmpl
+
+ # create a dict that will be pickled and used by webapps
+ doc_ctx = {
+ 'body': ctx.get('body', ''),
+ 'title': ctx.get('title', ''),
+ 'css': ctx.get('css', ''),
+ 'script': ctx.get('script', ''),
+ }
+ # partially render the html template to get at interesting macros
+ template = self.templates.environment.get_template(templatename)
+ template_module = template.make_module(ctx)
+ for item in ['sidebar', 'relbar', 'script', 'css']:
+ if hasattr(template_module, item):
+ doc_ctx[item] = getattr(template_module, item)()
+
+ return ctx, doc_ctx
+
+ def handle_page(self, pagename, addctx, templatename='page.html',
+ outfilename=None, event_arg=None):
+ # type: (unicode, Dict, unicode, unicode, unicode) -> None
+ ctx, doc_ctx = self._render_page(pagename, addctx,
+ templatename, event_arg)
+
+ if not outfilename:
+ outfilename = path.join(self.outdir, 'pickles',
+ os_path(pagename) + self.out_suffix)
+ ensuredir(path.dirname(outfilename))
+ self.dump_context(doc_ctx, outfilename)
+
+ # if there is a source file, copy the source file for the
+ # "show source" link
+ if ctx.get('sourcename'):
+ source_name = path.join(self.staticdir,
+ '_sources', os_path(ctx['sourcename']))
+ ensuredir(path.dirname(source_name))
+ copyfile(self.env.doc2path(pagename), source_name)
+
+ def handle_finish(self):
+ # type: () -> None
+ # get global values for css and script files
+ _, doc_ctx = self._render_page('tmp', {}, 'page.html')
+ self.globalcontext['css'] = doc_ctx['css']
+ self.globalcontext['script'] = doc_ctx['script']
+
+ PickleHTMLBuilder.handle_finish(self)
+
+ # move static stuff over to separate directory
+ directories = [self.imagedir, '_static']
+ for directory in directories:
+ src = path.join(self.outdir, directory)
+ dst = path.join(self.staticdir, directory)
+ if path.isdir(src):
+ if path.isdir(dst):
+ shutil.rmtree(dst)
+ shutil.move(src, dst)
+
+ def dump_search_index(self):
+ # type: () -> None
+ self.indexer.finish_indexing() # type: ignore
+
+
+def setup(app):
+ # type: (Sphinx) -> Dict[unicode, Any]
+ app.add_builder(WebSupportBuilder)
+
+ return {
+ 'version': 'builtin',
+ 'parallel_read_safe': True,
+ 'parallel_write_safe': True,
+ }
diff --git a/sphinxcontrib/websupport/core.py b/sphinxcontrib/websupport/core.py
new file mode 100644
index 0000000..062bf9f
--- /dev/null
+++ b/sphinxcontrib/websupport/core.py
@@ -0,0 +1,456 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinxcontrib.websupport.core
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Base Module for web support functions.
+
+ :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import sys
+import posixpath
+from os import path
+
+from six.moves import cPickle as pickle
+from jinja2 import Environment, FileSystemLoader
+from docutils.core import publish_parts
+
+from sphinx.locale import _
+from sphinx.util.osutil import ensuredir
+from sphinx.util.jsonimpl import dumps as dump_json
+from sphinx.util.pycompat import htmlescape
+from sphinxcontrib.websupport import errors
+from sphinxcontrib.websupport.search import BaseSearch, SEARCH_ADAPTERS
+from sphinxcontrib.websupport.storage import StorageBackend
+
+if False:
+ # For type annotation
+ from typing import Dict # NOQA
+
+
+class WebSupport(object):
+ """The main API class for the web support package. All interactions
+ with the web support package should occur through this class.
+ """
+ def __init__(self,
+ srcdir=None, # only required for building
+ builddir='', # the dir with data/static/doctrees subdirs
+ datadir=None, # defaults to builddir/data
+ staticdir=None, # defaults to builddir/static
+ doctreedir=None, # defaults to builddir/doctrees
+ search=None, # defaults to no search
+ storage=None, # defaults to SQLite in datadir
+ status=sys.stdout,
+ warning=sys.stderr,
+ moderation_callback=None,
+ allow_anonymous_comments=True,
+ docroot='',
+ staticroot='static',
+ ):
+ # directories
+ self.srcdir = srcdir
+ self.builddir = builddir
+ self.outdir = path.join(builddir, 'data')
+ self.datadir = datadir or self.outdir
+ self.staticdir = staticdir or path.join(self.builddir, 'static')
+ self.doctreedir = staticdir or path.join(self.builddir, 'doctrees')
+ # web server virtual paths
+ self.staticroot = staticroot.strip('/')
+ self.docroot = docroot.strip('/')
+
+ self.status = status
+ self.warning = warning
+ self.moderation_callback = moderation_callback
+ self.allow_anonymous_comments = allow_anonymous_comments
+
+ self._init_templating()
+ self._init_search(search)
+ self._init_storage(storage)
+
+ self._globalcontext = None # type: ignore
+
+ self._make_base_comment_options()
+
+ def _init_storage(self, storage):
+ if isinstance(storage, StorageBackend):
+ self.storage = storage
+ else:
+ # If a StorageBackend isn't provided, use the default
+ # SQLAlchemy backend.
+ from sphinxcontrib.websupport.storage.sqlalchemystorage \
+ import SQLAlchemyStorage
+ if not storage:
+ # no explicit DB path given; create default sqlite database
+ db_path = path.join(self.datadir, 'db', 'websupport.db')
+ ensuredir(path.dirname(db_path))
+ storage = 'sqlite:///' + db_path
+ self.storage = SQLAlchemyStorage(storage)
+
+ def _init_templating(self):
+ import sphinx
+ template_path = path.join(sphinx.package_dir,
+ 'themes', 'basic')
+ loader = FileSystemLoader(template_path)
+ self.template_env = Environment(loader=loader)
+
+ def _init_search(self, search):
+ if isinstance(search, BaseSearch):
+ self.search = search
+ else:
+ mod, cls = SEARCH_ADAPTERS[search or 'null']
+ mod = 'sphinxcontrib.websupport.search.' + mod
+ SearchClass = getattr(__import__(mod, None, None, [cls]), cls)
+ search_path = path.join(self.datadir, 'search')
+ self.search = SearchClass(search_path)
+ self.results_template = \
+ self.template_env.get_template('searchresults.html')
+
+ def build(self):
+ """Build the documentation. Places the data into the `outdir`
+ directory. Use it like this::
+
+ support = WebSupport(srcdir, builddir, search='xapian')
+ support.build()
+
+ This will read reStructured text files from `srcdir`. Then it will
+ build the pickles and search index, placing them into `builddir`.
+ It will also save node data to the database.
+ """
+ if not self.srcdir:
+ raise RuntimeError('No srcdir associated with WebSupport object')
+
+ from sphinx.application import Sphinx
+ app = Sphinx(self.srcdir, self.srcdir, self.outdir, self.doctreedir,
+ 'websupport', status=self.status, warning=self.warning)
+ app.builder.set_webinfo(self.staticdir, self.staticroot, # type: ignore
+ self.search, self.storage)
+
+ self.storage.pre_build()
+ app.build()
+ self.storage.post_build()
+
+ def get_globalcontext(self):
+ """Load and return the "global context" pickle."""
+ if not self._globalcontext:
+ infilename = path.join(self.datadir, 'globalcontext.pickle')
+ with open(infilename, 'rb') as f:
+ self._globalcontext = pickle.load(f)
+ return self._globalcontext
+
+ def get_document(self, docname, username='', moderator=False):
+ """Load and return a document from a pickle. The document will
+ be a dict object which can be used to render a template::
+
+ support = WebSupport(datadir=datadir)
+ support.get_document('index', username, moderator)
+
+ In most cases `docname` will be taken from the request path and
+ passed directly to this function. In Flask, that would be something
+ like this::
+
+ @app.route('/<path:docname>')
+ def index(docname):
+ username = g.user.name if g.user else ''
+ moderator = g.user.moderator if g.user else False
+ try:
+ document = support.get_document(docname, username,
+ moderator)
+ except DocumentNotFoundError:
+ abort(404)
+ render_template('doc.html', document=document)
+
+ The document dict that is returned contains the following items
+ to be used during template rendering.
+
+ * **body**: The main body of the document as HTML
+ * **sidebar**: The sidebar of the document as HTML
+ * **relbar**: A div containing links to related documents
+ * **title**: The title of the document
+ * **css**: Links to css files used by Sphinx
+ * **script**: Javascript containing comment options
+
+ This raises :class:`~sphinxcontrib.websupport.errors.DocumentNotFoundError`
+ if a document matching `docname` is not found.
+
+ :param docname: the name of the document to load.
+ """
+ docpath = path.join(self.datadir, 'pickles', docname)
+ if path.isdir(docpath):
+ infilename = docpath + '/index.fpickle'
+ if not docname:
+ docname = 'index'
+ else:
+ docname += '/index'
+ else:
+ infilename = docpath + '.fpickle'
+
+ try:
+ with open(infilename, 'rb') as f:
+ document = pickle.load(f)
+ except IOError:
+ raise errors.DocumentNotFoundError(
+ 'The document "%s" could not be found' % docname)
+
+ comment_opts = self._make_comment_options(username, moderator)
+ comment_meta = self._make_metadata(
+ self.storage.get_metadata(docname, moderator))
+
+ document['script'] = comment_opts + comment_meta + document['script']
+ return document
+
+ def get_search_results(self, q):
+ """Perform a search for the query `q`, and create a set
+ of search results. Then render the search results as html and
+ return a context dict like the one created by
+ :meth:`get_document`::
+
+ document = support.get_search_results(q)
+
+ :param q: the search query
+ """
+ results = self.search.query(q)
+ ctx = {
+ 'q': q,
+ 'search_performed': True,
+ 'search_results': results,
+ 'docroot': '../', # XXX
+ '_': _,
+ }
+ document = {
+ 'body': self.results_template.render(ctx),
+ 'title': 'Search Results',
+ 'sidebar': '',
+ 'relbar': ''
+ }
+ return document
+
+ def get_data(self, node_id, username=None, moderator=False):
+ """Get the comments and source associated with `node_id`. If
+ `username` is given vote information will be included with the
+ returned comments. The default CommentBackend returns a dict with
+ two keys, *source*, and *comments*. *source* is raw source of the
+ node and is used as the starting point for proposals a user can
+ add. *comments* is a list of dicts that represent a comment, each
+ having the following items:
+
+ ============= ======================================================
+ Key Contents
+ ============= ======================================================
+ text The comment text.
+ username The username that was stored with the comment.
+ id The comment's unique identifier.
+ rating The comment's current rating.
+ age The time in seconds since the comment was added.
+ time A dict containing time information. It contains the
+ following keys: year, month, day, hour, minute, second,
+ iso, and delta. `iso` is the time formatted in ISO
+ 8601 format. `delta` is a printable form of how old
+ the comment is (e.g. "3 hours ago").
+ vote If `user_id` was given, this will be an integer
+ representing the vote. 1 for an upvote, -1 for a
+ downvote, or 0 if unvoted.
+ node The id of the node that the comment is attached to.
+ If the comment's parent is another comment rather than
+ a node, this will be null.
+ parent The id of the comment that this comment is attached
+ to if it is not attached to a node.
+ children A list of all children, in this format.
+ proposal_diff An HTML representation of the differences between the
+ the current source and the user's proposed source.
+ ============= ======================================================
+
+ :param node_id: the id of the node to get comments for.
+ :param username: the username of the user viewing the comments.
+ :param moderator: whether the user is a moderator.
+ """
+ return self.storage.get_data(node_id, username, moderator)
+
+ def delete_comment(self, comment_id, username='', moderator=False):
+ """Delete a comment.
+
+ If `moderator` is True, the comment and all descendants will be deleted
+ from the database, and the function returns ``True``.
+
+ If `moderator` is False, the comment will be marked as deleted (but not
+ removed from the database so as not to leave any comments orphaned), but
+ only if the `username` matches the `username` on the comment. The
+ username and text files are replaced with "[deleted]" . In this case,
+ the function returns ``False``.
+
+ This raises :class:`~sphinxcontrib.websupport.errors.UserNotAuthorizedError`
+ if moderator is False and `username` doesn't match username on the
+ comment.
+
+ :param comment_id: the id of the comment to delete.
+ :param username: the username requesting the deletion.
+ :param moderator: whether the requestor is a moderator.
+ """
+ return self.storage.delete_comment(comment_id, username, moderator)
+
+ def add_comment(self, text, node_id='', parent_id='', displayed=True,
+ username=None, time=None, proposal=None,
+ moderator=False):
+ """Add a comment to a node or another comment. Returns the comment
+ in the same format as :meth:`get_comments`. If the comment is being
+ attached to a node, pass in the node's id (as a string) with the
+ node keyword argument::
+
+ comment = support.add_comment(text, node_id=node_id)
+
+ If the comment is the child of another comment, provide the parent's
+ id (as a string) with the parent keyword argument::
+
+ comment = support.add_comment(text, parent_id=parent_id)
+
+ If you would like to store a username with the comment, pass
+ in the optional `username` keyword argument::
+
+ comment = support.add_comment(text, node=node_id,
+ username=username)
+
+ :param parent_id: the prefixed id of the comment's parent.
+ :param text: the text of the comment.
+ :param displayed: for moderation purposes
+ :param username: the username of the user making the comment.
+ :param time: the time the comment was created, defaults to now.
+ """
+ if username is None:
+ if self.allow_anonymous_comments:
+ username = 'Anonymous'
+ else:
+ raise errors.UserNotAuthorizedError()
+ parsed = self._parse_comment_text(text)
+ comment = self.storage.add_comment(parsed, displayed, username,
+ time, proposal, node_id,
+ parent_id, moderator)
+ comment['original_text'] = text
+ if not displayed and self.moderation_callback:
+ self.moderation_callback(comment)
+ return comment
+
+ def process_vote(self, comment_id, username, value):
+ """Process a user's vote. The web support package relies
+ on the API user to perform authentication. The API user will
+ typically receive a comment_id and value from a form, and then
+ make sure the user is authenticated. A unique username must be
+ passed in, which will also be used to retrieve the user's past
+ voting data. An example, once again in Flask::
+
+ @app.route('/docs/process_vote', methods=['POST'])
+ def process_vote():
+ if g.user is None:
+ abort(401)
+ comment_id = request.form.get('comment_id')
+ value = request.form.get('value')
+ if value is None or comment_id is None:
+ abort(400)
+ support.process_vote(comment_id, g.user.name, value)
+ return "success"
+
+ :param comment_id: the comment being voted on
+ :param username: the unique username of the user voting
+ :param value: 1 for an upvote, -1 for a downvote, 0 for an unvote.
+ """
+ value = int(value)
+ if not -1 <= value <= 1:
+ raise ValueError('vote value %s out of range (-1, 1)' % value)
+ self.storage.process_vote(comment_id, username, value)
+
+ def update_username(self, old_username, new_username):
+ """To remain decoupled from a webapp's authentication system, the
+ web support package stores a user's username with each of their
+ comments and votes. If the authentication system allows a user to
+ change their username, this can lead to stagnate data in the web
+ support system. To avoid this, each time a username is changed, this
+ method should be called.
+
+ :param old_username: The original username.
+ :param new_username: The new username.
+ """
+ self.storage.update_username(old_username, new_username)
+
+ def accept_comment(self, comment_id, moderator=False):
+ """Accept a comment that is pending moderation.
+
+ This raises :class:`~sphinxcontrib.websupport.errors.UserNotAuthorizedError`
+ if moderator is False.
+
+ :param comment_id: The id of the comment that was accepted.
+ :param moderator: Whether the user making the request is a moderator.
+ """
+ if not moderator:
+ raise errors.UserNotAuthorizedError()
+ self.storage.accept_comment(comment_id)
+
+ def _make_base_comment_options(self):
+ """Helper method to create the part of the COMMENT_OPTIONS javascript
+ that remains the same throughout the lifetime of the
+ :class:`~sphinxcontrib.websupport.WebSupport` object.
+ """
+ self.base_comment_opts = {} # type: Dict[unicode, unicode]
+
+ if self.docroot != '':
+ comment_urls = [
+ ('addCommentURL', '_add_comment'),
+ ('getCommentsURL', '_get_comments'),
+ ('processVoteURL', '_process_vote'),
+ ('acceptCommentURL', '_accept_comment'),
+ ('deleteCommentURL', '_delete_comment')
+ ]
+ for key, value in comment_urls:
+ self.base_comment_opts[key] = \
+ '/' + posixpath.join(self.docroot, value)
+ if self.staticroot != 'static':
+ static_urls = [
+ ('commentImage', 'comment.png'),
+ ('closeCommentImage', 'comment-close.png'),
+ ('loadingImage', 'ajax-loader.gif'),
+ ('commentBrightImage', 'comment-bright.png'),
+ ('upArrow', 'up.png'),
+ ('upArrowPressed', 'up-pressed.png'),
+ ('downArrow', 'down.png'),
+ ('downArrowPressed', 'down-pressed.png')
+ ]
+ for key, value in static_urls:
+ self.base_comment_opts[key] = \
+ '/' + posixpath.join(self.staticroot, '_static', value)
+
... 4716 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/sphinxcontrib-websupport.git
More information about the Python-modules-commits
mailing list