From 9a17b6cc6e3137a6dabfabe41d29eabd9b96b5ff Mon Sep 17 00:00:00 2001 From: Trevor Bramwell Date: Thu, 9 Nov 2017 14:23:15 -0800 Subject: [PATCH] Initial Commit of Sphinx Config project 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 Signed-off-by: Trevor Bramwell Signed-off-by: Thanh Ha --- .gitignore | 5 +++ README.rst | 65 +++++++++++++++++++++++++++ docs_conf/__init__.py | 99 +++++++++++++++++++++++++++++++++++++++++ docs_conf/defaults/default.yaml | 26 +++++++++++ docs_conf/defaults/lfdocs.yaml | 27 +++++++++++ docs_conf/defaults/opnfv.yaml | 26 +++++++++++ requirements.txt | 4 ++ setup.py | 21 +++++++++ tests/test_simple.py | 62 ++++++++++++++++++++++++++ tox.ini | 10 +++-- 10 files changed, 342 insertions(+), 3 deletions(-) create mode 100644 README.rst create mode 100644 docs_conf/__init__.py create mode 100644 docs_conf/defaults/default.yaml create mode 100644 docs_conf/defaults/lfdocs.yaml create mode 100644 docs_conf/defaults/opnfv.yaml create mode 100644 requirements.txt create mode 100644 setup.py create mode 100644 tests/test_simple.py diff --git a/.gitignore b/.gitignore index bcbf1c0..4654f6c 100644 --- a/.gitignore +++ b/.gitignore @@ -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 index 0000000..dcff0c2 --- /dev/null +++ b/README.rst @@ -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 index 0000000..1d6333b --- /dev/null +++ b/docs_conf/__init__.py @@ -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 index 0000000..bcb2863 --- /dev/null +++ b/docs_conf/defaults/default.yaml @@ -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 index 0000000..601b7a8 --- /dev/null +++ b/docs_conf/defaults/lfdocs.yaml @@ -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 index 0000000..2ccbf63 --- /dev/null +++ b/docs_conf/defaults/opnfv.yaml @@ -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 index 0000000..bac060f --- /dev/null +++ b/requirements.txt @@ -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 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 index 0000000..a19ec88 --- /dev/null +++ b/tests/test_simple.py @@ -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 --- 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 - -- 2.16.6