Import directives/roles from zuul

This imports the current directives from Zuul itself, and adds
an example doc page that exercises them all so this repo is more
self-testing.

Also, use python3 by default to ensure we remain py3 compat.

Change-Id: Ie5b3cedd5e8dfaf0763d09a901fc9ba0e5b63683
This commit is contained in:
James E. Blair 2017-08-07 16:45:27 -07:00
parent 553fdce0cf
commit 27322342f2
5 changed files with 408 additions and 41 deletions

View File

@ -30,6 +30,8 @@ extensions = [
# text edit cycles. # text edit cycles.
# execute "export SPHINX_DEBUG=1" in your terminal to disable # execute "export SPHINX_DEBUG=1" in your terminal to disable
primary_domain = 'zuul'
# The suffix of source filenames. # The suffix of source filenames.
source_suffix = '.rst' source_suffix = '.rst'

132
doc/source/examples.rst Normal file
View File

@ -0,0 +1,132 @@
Examples
========
Jobs
----
.. job:: example-job
This is an example job.
.. var:: foo
This is a variable used by this job.
.. var:: bar
This is a sub key.
.. var:: items
:type: list
This variable is a list.
.. var:: baz
This is an item in a list.
.. job:: example-job
:variant: stable
This is a variant of :job:`example-job` which runs on stable branches.
This is a job role: :job:`example-job`
This is a job variable role: :var:`example-job.foo.bar`
Roles
-----
.. role:: example-role
This is an example role.
**Role Variables**
.. var:: foo
This is a variable used by this role.
.. var:: bar
This is a sub key.
.. var:: items
:type: list
This variable is a list.
.. var:: baz
This is an item in a list.
This is an (Ansible) role (Sphinx) role: :role:`example-role`
This is an (Ansible) role variable (Sphinx) role: :var:`example-role.items.baz`
Configuration Attributes
------------------------
.. attr:: example-attr
:required:
This is an example configuration attribute.
.. attr:: foo
:default: bar
A sub attribute.
.. value:: bar
An attribute value.
.. value:: baz
Another attribute value.
This is an attribute role: :attr:`example-attr.foo`
This is an attribute value role: :value:`example-attr.foo.bar`
Job Variables
-------------
.. var:: example-variable
This is an example variable.
.. var:: foo
This is a variable.
.. var:: bar
This is a sub key.
.. var:: items
:type: list
This variable is a list.
.. var:: baz
This is an item in a list.
This is a variable role: :var:`example-variable.items.baz`
Statistics
----------
.. stat:: example-stat
This is an example statistic.
.. stat:: foo
:type: counter
A sub stat.
This is a statistics role: :stat:`example-stat.foo`

View File

@ -1,9 +1,9 @@
.. include:: ../../README.rst .. include:: ../../README.rst
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
examples
Indices and tables Indices and tables
================== ==================

View File

@ -4,6 +4,7 @@ skipsdist = True
envlist = pep8 envlist = pep8
[testenv] [testenv]
basepython = python3
install_command = pip install {opts} {packages} install_command = pip install {opts} {packages}
deps = -r{toxinidir}/requirements.txt deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt -r{toxinidir}/test-requirements.txt

View File

@ -15,7 +15,10 @@
from sphinx import addnodes from sphinx import addnodes
from docutils.parsers.rst import Directive from docutils.parsers.rst import Directive
from sphinx.domains import Domain, ObjType from sphinx.domains import Domain, ObjType
from sphinx.roles import XRefRole
from sphinx.directives import ObjectDescription from sphinx.directives import ObjectDescription
from sphinx.util.nodes import make_refnode
from docutils import nodes
import os import os
import yaml import yaml
@ -26,7 +29,7 @@ class Layout(object):
self.jobs = [] self.jobs = []
class BaseZuulDirective(Directive): class ZuulDirective(Directive):
has_content = True has_content = True
def find_zuul_yaml(self): def find_zuul_yaml(self):
@ -117,31 +120,235 @@ class BaseZuulDirective(Directive):
return lines return lines
class ZuulJobDirective(BaseZuulDirective, ObjectDescription): class ZuulObjectDescription(ZuulDirective, ObjectDescription):
option_spec = { object_names = {
'variant': lambda x: x, 'attr': 'attribute',
'var': 'variable',
} }
def handle_signature(self, sig, signode): def get_path(self):
signode += addnodes.desc_name(sig, sig) return self.env.ref_context.get('zuul:attr_path', [])
return sig
def get_display_path(self):
return self.env.ref_context.get('zuul:display_attr_path', [])
@property
def parent_pathname(self):
return '.'.join(self.get_display_path())
@property
def full_pathname(self):
name = self.names[-1].lower()
return '.'.join(self.get_path() + [name])
def add_target_and_index(self, name, sig, signode): def add_target_and_index(self, name, sig, signode):
targetname = self.objtype + '-' + name targetname = self.objtype + '-' + self.full_pathname
if 'variant' in self.options:
targetname += '-' + self.options['variant']
if targetname not in self.state.document.ids: if targetname not in self.state.document.ids:
signode['names'].append(targetname) signode['names'].append(targetname)
signode['ids'].append(targetname) signode['ids'].append(targetname)
signode['first'] = (not self.names) signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode) self.state.document.note_explicit_target(signode)
objects = self.env.domaindata['zuul']['objects']
if targetname in objects:
self.state_machine.reporter.warning(
'duplicate object description of %s, ' % targetname +
'other instance in ' +
self.env.doc2path(objects[targetname][0]) +
', use :noindex: for one of them',
line=self.lineno)
objects[targetname] = (self.env.docname, self.objtype)
indextext = '%s (%s)' % (name, self.objtype) objname = self.object_names.get(self.objtype, self.objtype)
if self.parent_pathname:
indextext = '%s (%s of %s)' % (name, objname,
self.parent_pathname)
else:
indextext = '%s (%s)' % (name, objname)
self.indexnode['entries'].append(('single', indextext, self.indexnode['entries'].append(('single', indextext,
targetname, '', None)) targetname, '', None))
class ZuulAutoJobDirective(BaseZuulDirective): ######################################################################
#
# Object description directives
#
class ZuulJobDirective(ZuulObjectDescription):
option_spec = {
'variant': lambda x: x,
}
def before_content(self):
path = self.env.ref_context.setdefault('zuul:attr_path', [])
element = self.names[-1]
path.append(element)
def after_content(self):
path = self.env.ref_context.get('zuul:attr_path')
if path:
path.pop()
def handle_signature(self, sig, signode):
signode += addnodes.desc_name(sig, sig)
return sig
class ZuulRoleDirective(ZuulObjectDescription):
def before_content(self):
path = self.env.ref_context.setdefault('zuul:attr_path', [])
element = self.names[-1]
path.append(element)
def after_content(self):
path = self.env.ref_context.get('zuul:attr_path')
if path:
path.pop()
def handle_signature(self, sig, signode):
signode += addnodes.desc_name(sig, sig)
return sig
class ZuulAttrDirective(ZuulObjectDescription):
has_content = True
option_spec = {
'required': lambda x: x,
'default': lambda x: x,
'noindex': lambda x: x,
}
def before_content(self):
path = self.env.ref_context.setdefault('zuul:attr_path', [])
path.append(self.names[-1])
path = self.env.ref_context.setdefault('zuul:display_attr_path', [])
path.append(self.names[-1])
def after_content(self):
path = self.env.ref_context.get('zuul:attr_path')
if path:
path.pop()
path = self.env.ref_context.get('zuul:display_attr_path')
if path:
path.pop()
def handle_signature(self, sig, signode):
path = self.get_display_path()
signode['is_multiline'] = True
line = addnodes.desc_signature_line()
line['add_permalink'] = True
for x in path:
line += addnodes.desc_addname(x + '.', x + '.')
line += addnodes.desc_name(sig, sig)
if 'required' in self.options:
line += addnodes.desc_annotation(' (required)', ' (required)')
signode += line
if 'default' in self.options:
line = addnodes.desc_signature_line()
line += addnodes.desc_type('Default: ', 'Default: ')
line += nodes.literal(self.options['default'],
self.options['default'])
signode += line
return sig
class ZuulValueDirective(ZuulObjectDescription):
has_content = True
def handle_signature(self, sig, signode):
signode += addnodes.desc_name(sig, sig)
return sig
class ZuulVarDirective(ZuulObjectDescription):
has_content = True
option_spec = {
'type': lambda x: x,
'hidden': lambda x: x,
'noindex': lambda x: x,
}
type_map = {
'list': '[]',
'dict': '{}',
}
def get_type_str(self):
if 'type' in self.options:
return self.type_map[self.options['type']]
return ''
def before_content(self):
path = self.env.ref_context.setdefault('zuul:attr_path', [])
element = self.names[-1]
path.append(element)
path = self.env.ref_context.setdefault('zuul:display_attr_path', [])
element = self.names[-1] + self.get_type_str()
path.append(element)
def after_content(self):
path = self.env.ref_context.get('zuul:attr_path')
if path:
path.pop()
path = self.env.ref_context.get('zuul:display_attr_path')
if path:
path.pop()
def handle_signature(self, sig, signode):
if 'hidden' in self.options:
return sig
path = self.get_display_path()
for x in path:
signode += addnodes.desc_addname(x + '.', x + '.')
signode += addnodes.desc_name(sig, sig)
return sig
class ZuulStatDirective(ZuulObjectDescription):
has_content = True
option_spec = {
'type': lambda x: x,
'hidden': lambda x: x,
'noindex': lambda x: x,
}
def before_content(self):
path = self.env.ref_context.setdefault('zuul:attr_path', [])
element = self.names[-1]
path.append(element)
path = self.env.ref_context.setdefault('zuul:display_attr_path', [])
element = self.names[-1]
path.append(element)
def after_content(self):
path = self.env.ref_context.get('zuul:attr_path')
if path:
path.pop()
path = self.env.ref_context.get('zuul:display_attr_path')
if path:
path.pop()
def handle_signature(self, sig, signode):
if 'hidden' in self.options:
return sig
path = self.get_display_path()
for x in path:
signode += addnodes.desc_addname(x + '.', x + '.')
signode += addnodes.desc_name(sig, sig)
if 'type' in self.options:
t = ' (%s)' % self.options['type']
signode += addnodes.desc_annotation(t, t)
return sig
######################################################################
#
# Autodoc directives
#
class ZuulAutoJobDirective(ZuulDirective):
def run(self): def run(self):
name = self.content[0] name = self.content[0]
lines = self.generate_zuul_job_content(name) lines = self.generate_zuul_job_content(name)
@ -149,7 +356,7 @@ class ZuulAutoJobDirective(BaseZuulDirective):
return [] return []
class ZuulAutoJobsDirective(BaseZuulDirective): class ZuulAutoJobsDirective(ZuulDirective):
has_content = False has_content = False
def run(self): def run(self):
@ -165,25 +372,7 @@ class ZuulAutoJobsDirective(BaseZuulDirective):
return [] return []
class ZuulRoleDirective(BaseZuulDirective, ObjectDescription): class ZuulAutoRoleDirective(ZuulDirective):
def handle_signature(self, sig, signode):
signode += addnodes.desc_name(sig, sig)
return sig
def add_target_and_index(self, name, sig, signode):
targetname = self.objtype + '-' + name
if targetname not in self.state.document.ids:
signode['names'].append(targetname)
signode['ids'].append(targetname)
signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
indextext = '%s (%s)' % (name, self.objtype)
self.indexnode['entries'].append(('single', indextext,
targetname, '', None))
class ZuulAutoRoleDirective(BaseZuulDirective):
def run(self): def run(self):
name = self.content[0] name = self.content[0]
lines = self.generate_zuul_role_content(name) lines = self.generate_zuul_role_content(name)
@ -191,7 +380,7 @@ class ZuulAutoRoleDirective(BaseZuulDirective):
return [] return []
class ZuulAutoRolesDirective(BaseZuulDirective): class ZuulAutoRolesDirective(ZuulDirective):
has_content = False has_content = False
def run(self): def run(self):
@ -202,29 +391,72 @@ class ZuulAutoRolesDirective(BaseZuulDirective):
return [] return []
class ZuulAbbreviatedXRefRole(XRefRole):
def process_link(self, env, refnode, has_explicit_title, title,
target):
title, target = super(ZuulAbbreviatedXRefRole, self).process_link(
env, refnode, has_explicit_title, title, target)
if not has_explicit_title:
title = title.split('.')[-1]
return title, target
class ZuulDomain(Domain): class ZuulDomain(Domain):
name = 'zuul' name = 'zuul'
label = 'Zuul' label = 'Zuul'
object_types = {
'job': ObjType('job'),
'role': ObjType('role'),
}
directives = { directives = {
# Object description directives
'job': ZuulJobDirective, 'job': ZuulJobDirective,
'role': ZuulRoleDirective,
'attr': ZuulAttrDirective,
'value': ZuulValueDirective,
'var': ZuulVarDirective,
'stat': ZuulStatDirective,
# Autodoc directives
'autojob': ZuulAutoJobDirective, 'autojob': ZuulAutoJobDirective,
'autojobs': ZuulAutoJobsDirective, 'autojobs': ZuulAutoJobsDirective,
'role': ZuulRoleDirective,
'autorole': ZuulAutoRoleDirective, 'autorole': ZuulAutoRoleDirective,
'autoroles': ZuulAutoRolesDirective, 'autoroles': ZuulAutoRolesDirective,
} }
roles = {
'job': XRefRole(innernodeclass=nodes.inline, # type: ignore
warn_dangling=True),
'role': XRefRole(innernodeclass=nodes.inline, # type: ignore
warn_dangling=True),
'attr': XRefRole(innernodeclass=nodes.inline, # type: ignore
warn_dangling=True),
'value': ZuulAbbreviatedXRefRole(
innernodeclass=nodes.inline, # type: ignore
warn_dangling=True),
'var': XRefRole(innernodeclass=nodes.inline, # type: ignore
warn_dangling=True),
'stat': XRefRole(innernodeclass=nodes.inline, # type: ignore
warn_dangling=True),
}
initial_data = { initial_data = {
'layout': None, 'layout': None,
'layout_path': None, 'layout_path': None,
'role_paths': None, 'role_paths': None,
} 'objects': {},
} # type: Dict[str, Dict]
def resolve_xref(self, env, fromdocname, builder, type, target,
node, contnode):
objects = self.data['objects']
name = type + '-' + target
obj = objects.get(name)
if obj:
return make_refnode(builder, fromdocname, obj[0], name,
contnode, name)
def clear_doc(self, docname):
for fullname, (fn, _l) in list(self.data['objects'].items()):
if fn == docname:
del self.data['objects'][fullname]
def setup(app): def setup(app):