Initial Commit of Sphinx Config project 63/7463/7
authorTrevor Bramwell <tbramwell@linuxfoundation.org>
Thu, 9 Nov 2017 22:23:15 +0000 (14:23 -0800)
committerThanh Ha <thanh.ha@linuxfoundation.org>
Wed, 17 Jan 2018 02:57:33 +0000 (21:57 -0500)
Creates a lfdocs-conf package that is pip installable.

The minimal conf.yaml consists of only a project_cfg configuration
with the name of the project eg opendaylight, opnfv, lfdocs:

   ---
   project_cfg: lfdocs

Limitations:

intersphinx_mapping is not currently mappable via yaml due to
it being a complex data set of a tuple + data type (None). Projects
can override this by setting it in their local conf.py or accept the
defaults provided by the lfdocs-conf project.

Change-Id: Ia695047dc92adc00e4e9ec1f6d3ed824d10c0959
Co-authored-by: Thanh Ha <thanh.ha@linuxfoundation.org>
Signed-off-by: Trevor Bramwell <tbramwell@linuxfoundation.org>
Signed-off-by: Thanh Ha <thanh.ha@linuxfoundation.org>
.gitignore
README.rst [new file with mode: 0644]
docs_conf/__init__.py [new file with mode: 0644]
docs_conf/defaults/default.yaml [new file with mode: 0644]
docs_conf/defaults/lfdocs.yaml [new file with mode: 0644]
docs_conf/defaults/opnfv.yaml [new file with mode: 0644]
requirements.txt [new file with mode: 0644]
setup.py [new file with mode: 0644]
tests/test_simple.py [new file with mode: 0644]
tox.ini

index bcbf1c0..4654f6c 100644 (file)
@@ -5,3 +5,8 @@
 .tox/
 docs/_build/
 MANIFEST
+build
+dist
+*.egg-info
+*.pyc
+.cache
diff --git a/README.rst b/README.rst
new file mode 100644 (file)
index 0000000..dcff0c2
--- /dev/null
@@ -0,0 +1,65 @@
+LF Docs Config
+==============
+
+The purpose of this project is to allow LF projects a centralized location for
+storing common project configuration.
+
+To use this a project should create a conf.yaml file in the same
+directory as their conf.py. Conf.py should contain at minimum::
+
+    from docs_conf import *
+
+The conf.yaml file should contain at minimum::
+
+    ---
+    project_cfg=myproject
+
+If defaults for 'myproject' exist, they will be loaded from this
+package, otherwise the basic Sphinx defaults will be set.
+
+Configuration precedence for configuration is as follows:
+
+#. project/conf.py
+#. project/conf.yaml
+#. docs_conf/{project_cfg}.yaml
+#. docs_conf/default.yaml
+#. docs_conf/__init__.py
+
+conf.py structure and documentation:
+  http://www.sphinx-doc.org/en/stable/config.html
+
+TODO
+----
+
+- [ ] Define the minimum set of config values to release initial version.
+      These can probably come from ODL/OPNFV site conf.py files.
+
+- [ ] Use sane defaults, and don't error out if something is not set.
+      Each config needs to be imported gracefully (if it doesn't
+      exist, set None or something; similar to dict.get
+
+- [ ] Create own documentation for project detailing use of 'conf.cfg'
+      file as some values will require subkeys given that they're
+      dictionaries or expect a list of tuples.
+
+- [ ] Setup and document section. The documentation already is organized
+      by section, so the config should also contain these section and look
+      for their values under them.
+
+      Sections:
+
+        - general (aka sphinx)
+        - project
+        - i18n
+        - html_output
+        - apple_help
+        - epub_output
+        - latex_output
+        - text_output
+        - manpage_output
+        - texinfo_output
+        - linkcheck
+        - xml
+        - cplusplus
+
+- [ ] Configure pre-plugin sections, and reference by plugin listing.
diff --git a/docs_conf/__init__.py b/docs_conf/__init__.py
new file mode 100644 (file)
index 0000000..1d6333b
--- /dev/null
@@ -0,0 +1,99 @@
+"""
+Sphinx Docs Config
+
+Configure sphinx-doc through an ini file.
+"""
+
+import imp
+import os.path
+import pkg_resources
+
+import sphinx_bootstrap_theme
+import yaml
+
+
+def _merge_yaml(x, y):
+    """Merges dictionary 'y' into 'x'
+
+    This transaction will overwrite existing data values in "y" with values
+    from "x".
+    """
+    z = x.copy()
+    z.update(y)
+    return z
+
+
+def collect_project_and_config():
+    """Pull project and configuration by merging all config sources
+
+    Order of precedence:
+
+    1) local conf.yaml
+    2) defaults/PROJECT.yaml
+    3) defaults/default.yaml
+
+    Return the project name and merged configs from the calling project
+    and per-project defaults.
+    """
+    if not os.path.isfile('conf.yaml'):
+        raise IOError("No conf.yaml file found at: {}".format(os.getcwd()))
+
+    with open('conf.yaml', 'r') as f:
+        local_config = yaml.load(f)
+
+    project_cfg = local_config.get('project_cfg', None)
+
+    _, docs_path, _ = imp.find_module('docs_conf')
+
+    default_cfg = os.path.join(docs_path, 'defaults', 'default.yaml')
+    with open(os.path.join(docs_path, default_cfg), 'r') as f:
+        effective_config = yaml.load(f)
+
+    project_cfg_file = os.path.join(docs_path, 'defaults', '{}.yaml'.format(project_cfg))
+    if os.path.isfile(project_cfg_file):
+        with open(os.path.join(docs_path, project_cfg_file), 'r') as f:
+            _project_cfg_data = yaml.load(f)
+        effective_config = _merge_yaml(effective_config, _project_cfg_data)
+
+    effective_config = _merge_yaml(effective_config, local_config)
+
+    return effective_config
+
+cfg = collect_project_and_config()
+
+# Parse the config and pull in sphinx conf.py settings
+project = cfg.get('project')
+release = cfg.get('release')
+version = cfg.get('version')
+author = cfg.get('author')
+copyright = cfg.get('copyright')
+
+needs_sphinx = cfg.get('needs_sphinx', '1.0')
+exclude_patterns = cfg.get('exclude_patterns', [])
+extensions = cfg.get('extensions', [])
+language = cfg.get('language', None)
+master_doc = cfg.get('master_doc', 'index')
+pygments_style = cfg.get('pygments_style', 'sphinx')
+source_suffix = cfg.get('source_suffix', '.rst')
+templates_path = cfg.get('templates_path', ['_templates'])
+todo_include_todos = cfg.get('todo_include_todos', False)
+
+html_extra_path = cfg.get('html_extra_path', [])
+html_favicon = cfg.get('html_favicon', 'favicon.ico')
+html_logo = cfg.get('html_logo', '_static/logo.png')
+html_sidebars = cfg.get('html_sidebars', {'**': ['localtoc.html', 'relations.html'],})
+html_static_path = cfg.get('html_static_path', ['_static'])
+html_theme = cfg.get('html_theme', 'bootstrap')
+html_theme_options = cfg.get('html_theme_options', {
+    'bootswatch_theme': "cerulean",
+    'navbar_sidebarrel': False,
+    'source_link_position': "footer",
+})
+html_theme_path = cfg.get('html_theme_path', sphinx_bootstrap_theme.get_html_theme_path())
+htmlhelp_basename = cfg.get('htmlhelp_basename', 'DocsConf')
+
+intersphinx_mapping = {
+    'global-jjb': ('http://global-jjb.releng.linuxfoundation.org/en/latest/', None),
+    'lftools': ('http://lftools.releng.linuxfoundation.org/en/latest/', None),
+    'python': ('https://docs.python.org/', None),
+}
diff --git a/docs_conf/defaults/default.yaml b/docs_conf/defaults/default.yaml
new file mode 100644 (file)
index 0000000..bcb2863
--- /dev/null
@@ -0,0 +1,26 @@
+---
+project: DEFAULT_PROJECT
+release: master
+version: master
+author: DEFAULT_AUTHOR
+copyright: 2018, DEFAULT_PROJECT
+
+needs_sphinx: '1.6.6'
+extensions:
+  - sphinx.ext.autodoc
+  - sphinx.ext.doctest
+  - sphinx.ext.intersphinx
+  - sphinx.ext.todo
+  - sphinx.ext.coverage
+  - sphinx.ext.viewcode
+exclude_patterns:
+  - .DS_Store
+  - _build
+  - Thumbs.db
+todo_include_todos: False
+
+html_theme: bootstrap
+html_theme_options:
+  bootswatch_theme: cerulean
+  navbar_sidebarrel: False
+  source_link_position: footer
diff --git a/docs_conf/defaults/lfdocs.yaml b/docs_conf/defaults/lfdocs.yaml
new file mode 100644 (file)
index 0000000..601b7a8
--- /dev/null
@@ -0,0 +1,27 @@
+---
+project: lf-releng-docs
+release: master
+version: master
+author: Linux Foundation Releng
+copyright: 2017-2018, The Linux Foundation
+
+needs_sphinx: '1.6.6'
+extensions:
+  - sphinx.ext.autodoc
+  - sphinx.ext.doctest
+  - sphinx.ext.intersphinx
+  - sphinx.ext.todo
+  - sphinx.ext.coverage
+  - sphinx.ext.viewcode
+exclude_patterns:
+  - .DS_Store
+  - _build
+  - Thumbs.db
+todo_include_todos: False
+
+html_logo: _static/lf-logo-small.png
+html_theme: bootstrap
+html_theme_options:
+  bootswatch_theme: cerulean
+  navbar_sidebarrel: False
+  source_link_position: footer
diff --git a/docs_conf/defaults/opnfv.yaml b/docs_conf/defaults/opnfv.yaml
new file mode 100644 (file)
index 0000000..2ccbf63
--- /dev/null
@@ -0,0 +1,26 @@
+---
+project: OPNFV
+release: Latest
+version: Latest
+author: Open Platform for NFV
+copyright: 2017, Open Platform for NFV. Licensed under CC BY 4.0
+
+needs_sphinx: '1.3'
+extensions:
+  - sphinxcontrib.httpdomain
+  - sphinx.ext.autodoc
+  - sphinx.ext.napoleon
+  - sphinx.ext.viewcode
+version: Latest
+release: Latest
+todo_include_todos: False
+
+html_theme: bootstrap
+html_theme_options:
+  bootswatch_theme: journal
+  navbar_sidebarrel: False
+html_logo: _static/opnfv-logo.png
+html_favicon: _static/favicon.ico
+html_static_path:
+  - _static
+htmlhelp_basename: OPNFV
diff --git a/requirements.txt b/requirements.txt
new file mode 100644 (file)
index 0000000..bac060f
--- /dev/null
@@ -0,0 +1,4 @@
+pytest
+pyyaml
+sphinx>=1.6.6
+sphinx_bootstrap_theme>=0.6.2
diff --git a/setup.py b/setup.py
new file mode 100644 (file)
index 0000000..5a32f2f
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,21 @@
+"""
+Setup for Docs Configuration
+"""
+from setuptools import setup, find_packages
+
+setup(
+    name='lfdocs_conf',
+    packages=['docs_conf'],
+    version='0.1.2',
+    author="Linux Foundation Releng",
+    author_email="releng@linuxfoundation.org",
+    url="https://gerrit.linuxfoundation.org/docs-conf",
+    package_data={
+        'docs_conf': ['defaults/*']
+    },
+    install_requires=[
+        'pyyaml',
+        'sphinx',
+        'sphinx_bootstrap_theme'
+    ]
+)
diff --git a/tests/test_simple.py b/tests/test_simple.py
new file mode 100644 (file)
index 0000000..a19ec88
--- /dev/null
@@ -0,0 +1,62 @@
+"""
+Docs Conf Tests
+"""
+import importlib
+import os
+import pytest
+import sys
+
+@pytest.fixture()
+def config(tmpdir):
+    """
+    Create a basic conf.py and conf.cfg file for each test
+    """
+    # Create the base 'conf.py'
+    confpy = tmpdir.join('conf.py')
+    confpy.write("from docs_conf import *")
+
+    # Create conf.cfg file with test defaults
+    # TODO: Make this dynamic so each test can set their own conf.cfg
+    # config.
+    confcfg = tmpdir.join('conf.yaml')
+    confcfg.write("---\nproject: myproject\nauthor: Pythonista")
+
+    # Change to the tmpdir location so relative file lookups succeed
+    os.chdir(str(tmpdir))
+
+    # Import the 'conf.py' file
+    sys.path.append(str(tmpdir))
+    conf_module = importlib.import_module('conf')
+
+    return conf_module
+
+def test_config(config):
+    """
+    Assert some basic assumption about how configurations are pulled in
+    """
+    assert config.project == 'myproject'
+    assert config.author == 'Pythonista'
+    #assert 'latex_documents' in dir(config)
+
+def test_defaults(config):
+    """
+    Test the defaults are set and the only thing required is a conf.py
+    w/import *
+    """
+    # TODO
+    assert True
+
+def test_project_override(config):
+    """
+    Test that setting sphinx.project pulls in the project specific
+    defaults
+    """
+    # TODO
+    assert True
+
+def test_theme_import(config):
+    """
+    Test setting sphinx.html_theme_module imports the correct theme
+    """
+    # TODO
+    pass
diff --git a/tox.ini b/tox.ini
index 94a664c..6771e70 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -3,8 +3,13 @@ minversion = 1.6
 envlist =
     coala,
     docs,
-    docs-linkcheck
-skipsdist=true
+    docs-linkcheck,
+    py27
+
+[testenv]
+deps = -rrequirements.txt
+commands =
+    pytest --basetemp={envtmpdir} {posargs}
 
 [testenv:coala]
 basepython = python3
@@ -15,4 +20,3 @@ deps =
 commands =
     nodeenv -p
     coala --non-interactive
-