summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames E. Blair <jeblair@redhat.com>2017-08-07 16:45:27 -0700
committerJames E. Blair <jeblair@redhat.com>2017-08-07 16:45:27 -0700
commit27322342f2e0b95dd390b2ba4a89b571b3ae6acf (patch)
tree5b73994e7c39a9b1fcb672753eafeeda384f692b
parent553fdce0cfc1cd1f7f25fc20481179ae15a45069 (diff)
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
Notes
Notes (review): Code-Review+2: Paul Belanger <pabelanger@redhat.com> Code-Review+2: Monty Taylor <mordred@inaugust.com> Workflow+1: Monty Taylor <mordred@inaugust.com> Verified+2: Jenkins Submitted-by: Jenkins Submitted-at: Wed, 09 Aug 2017 16:41:15 +0000 Reviewed-on: https://review.openstack.org/491635 Project: openstack-infra/zuul-sphinx Branch: refs/heads/master
-rwxr-xr-xdoc/source/conf.py2
-rw-r--r--doc/source/examples.rst132
-rw-r--r--doc/source/index.rst2
-rw-r--r--tox.ini1
-rw-r--r--zuul_sphinx/zuul.py314
5 files changed, 409 insertions, 42 deletions
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 92b67a6..9eeca78 100755
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -30,6 +30,8 @@ extensions = [
30# text edit cycles. 30# text edit cycles.
31# execute "export SPHINX_DEBUG=1" in your terminal to disable 31# execute "export SPHINX_DEBUG=1" in your terminal to disable
32 32
33primary_domain = 'zuul'
34
33# The suffix of source filenames. 35# The suffix of source filenames.
34source_suffix = '.rst' 36source_suffix = '.rst'
35 37
diff --git a/doc/source/examples.rst b/doc/source/examples.rst
new file mode 100644
index 0000000..9ab3866
--- /dev/null
+++ b/doc/source/examples.rst
@@ -0,0 +1,132 @@
1Examples
2========
3
4Jobs
5----
6
7.. job:: example-job
8
9 This is an example job.
10
11 .. var:: foo
12
13 This is a variable used by this job.
14
15 .. var:: bar
16
17 This is a sub key.
18
19 .. var:: items
20 :type: list
21
22 This variable is a list.
23
24 .. var:: baz
25
26 This is an item in a list.
27
28.. job:: example-job
29 :variant: stable
30
31 This is a variant of :job:`example-job` which runs on stable branches.
32
33This is a job role: :job:`example-job`
34
35This is a job variable role: :var:`example-job.foo.bar`
36
37Roles
38-----
39
40.. role:: example-role
41
42 This is an example role.
43
44 **Role Variables**
45
46 .. var:: foo
47
48 This is a variable used by this role.
49
50 .. var:: bar
51
52 This is a sub key.
53
54 .. var:: items
55 :type: list
56
57 This variable is a list.
58
59 .. var:: baz
60
61 This is an item in a list.
62
63This is an (Ansible) role (Sphinx) role: :role:`example-role`
64
65This is an (Ansible) role variable (Sphinx) role: :var:`example-role.items.baz`
66
67Configuration Attributes
68------------------------
69
70.. attr:: example-attr
71 :required:
72
73 This is an example configuration attribute.
74
75 .. attr:: foo
76 :default: bar
77
78 A sub attribute.
79
80 .. value:: bar
81
82 An attribute value.
83
84 .. value:: baz
85
86 Another attribute value.
87
88This is an attribute role: :attr:`example-attr.foo`
89
90This is an attribute value role: :value:`example-attr.foo.bar`
91
92
93Job Variables
94-------------
95
96.. var:: example-variable
97
98 This is an example variable.
99
100 .. var:: foo
101
102 This is a variable.
103
104 .. var:: bar
105
106 This is a sub key.
107
108 .. var:: items
109 :type: list
110
111 This variable is a list.
112
113 .. var:: baz
114
115 This is an item in a list.
116
117This is a variable role: :var:`example-variable.items.baz`
118
119
120Statistics
121----------
122
123.. stat:: example-stat
124
125 This is an example statistic.
126
127 .. stat:: foo
128 :type: counter
129
130 A sub stat.
131
132This is a statistics role: :stat:`example-stat.foo`
diff --git a/doc/source/index.rst b/doc/source/index.rst
index fffa3da..31cbc73 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -1,9 +1,9 @@
1.. include:: ../../README.rst 1.. include:: ../../README.rst
2 2
3
4.. toctree:: 3.. toctree::
5 :maxdepth: 2 4 :maxdepth: 2
6 5
6 examples
7 7
8Indices and tables 8Indices and tables
9================== 9==================
diff --git a/tox.ini b/tox.ini
index 872157f..ac89a7f 100644
--- a/tox.ini
+++ b/tox.ini
@@ -4,6 +4,7 @@ skipsdist = True
4envlist = pep8 4envlist = pep8
5 5
6[testenv] 6[testenv]
7basepython = python3
7install_command = pip install {opts} {packages} 8install_command = pip install {opts} {packages}
8deps = -r{toxinidir}/requirements.txt 9deps = -r{toxinidir}/requirements.txt
9 -r{toxinidir}/test-requirements.txt 10 -r{toxinidir}/test-requirements.txt
diff --git a/zuul_sphinx/zuul.py b/zuul_sphinx/zuul.py
index 80d98c9..d8383a9 100644
--- a/zuul_sphinx/zuul.py
+++ b/zuul_sphinx/zuul.py
@@ -15,7 +15,10 @@
15from sphinx import addnodes 15from sphinx import addnodes
16from docutils.parsers.rst import Directive 16from docutils.parsers.rst import Directive
17from sphinx.domains import Domain, ObjType 17from sphinx.domains import Domain, ObjType
18from sphinx.roles import XRefRole
18from sphinx.directives import ObjectDescription 19from sphinx.directives import ObjectDescription
20from sphinx.util.nodes import make_refnode
21from docutils import nodes
19import os 22import os
20 23
21import yaml 24import yaml
@@ -26,7 +29,7 @@ class Layout(object):
26 self.jobs = [] 29 self.jobs = []
27 30
28 31
29class BaseZuulDirective(Directive): 32class ZuulDirective(Directive):
30 has_content = True 33 has_content = True
31 34
32 def find_zuul_yaml(self): 35 def find_zuul_yaml(self):
@@ -117,31 +120,235 @@ class BaseZuulDirective(Directive):
117 return lines 120 return lines
118 121
119 122
120class ZuulJobDirective(BaseZuulDirective, ObjectDescription): 123class ZuulObjectDescription(ZuulDirective, ObjectDescription):
121 option_spec = { 124 object_names = {
122 'variant': lambda x: x, 125 'attr': 'attribute',
126 'var': 'variable',
123 } 127 }
124 128
125 def handle_signature(self, sig, signode): 129 def get_path(self):
126 signode += addnodes.desc_name(sig, sig) 130 return self.env.ref_context.get('zuul:attr_path', [])
127 return sig 131
132 def get_display_path(self):
133 return self.env.ref_context.get('zuul:display_attr_path', [])
134
135 @property
136 def parent_pathname(self):
137 return '.'.join(self.get_display_path())
138
139 @property
140 def full_pathname(self):
141 name = self.names[-1].lower()
142 return '.'.join(self.get_path() + [name])
128 143
129 def add_target_and_index(self, name, sig, signode): 144 def add_target_and_index(self, name, sig, signode):
130 targetname = self.objtype + '-' + name 145 targetname = self.objtype + '-' + self.full_pathname
131 if 'variant' in self.options:
132 targetname += '-' + self.options['variant']
133 if targetname not in self.state.document.ids: 146 if targetname not in self.state.document.ids:
134 signode['names'].append(targetname) 147 signode['names'].append(targetname)
135 signode['ids'].append(targetname) 148 signode['ids'].append(targetname)
136 signode['first'] = (not self.names) 149 signode['first'] = (not self.names)
137 self.state.document.note_explicit_target(signode) 150 self.state.document.note_explicit_target(signode)
138 151 objects = self.env.domaindata['zuul']['objects']
139 indextext = '%s (%s)' % (name, self.objtype) 152 if targetname in objects:
153 self.state_machine.reporter.warning(
154 'duplicate object description of %s, ' % targetname +
155 'other instance in ' +
156 self.env.doc2path(objects[targetname][0]) +
157 ', use :noindex: for one of them',
158 line=self.lineno)
159 objects[targetname] = (self.env.docname, self.objtype)
160
161 objname = self.object_names.get(self.objtype, self.objtype)
162 if self.parent_pathname:
163 indextext = '%s (%s of %s)' % (name, objname,
164 self.parent_pathname)
165 else:
166 indextext = '%s (%s)' % (name, objname)
140 self.indexnode['entries'].append(('single', indextext, 167 self.indexnode['entries'].append(('single', indextext,
141 targetname, '', None)) 168 targetname, '', None))
142 169
143 170
144class ZuulAutoJobDirective(BaseZuulDirective): 171######################################################################
172#
173# Object description directives
174#
175
176class ZuulJobDirective(ZuulObjectDescription):
177 option_spec = {
178 'variant': lambda x: x,
179 }
180
181 def before_content(self):
182 path = self.env.ref_context.setdefault('zuul:attr_path', [])
183 element = self.names[-1]
184 path.append(element)
185
186 def after_content(self):
187 path = self.env.ref_context.get('zuul:attr_path')
188 if path:
189 path.pop()
190
191 def handle_signature(self, sig, signode):
192 signode += addnodes.desc_name(sig, sig)
193 return sig
194
195
196class ZuulRoleDirective(ZuulObjectDescription):
197 def before_content(self):
198 path = self.env.ref_context.setdefault('zuul:attr_path', [])
199 element = self.names[-1]
200 path.append(element)
201
202 def after_content(self):
203 path = self.env.ref_context.get('zuul:attr_path')
204 if path:
205 path.pop()
206
207 def handle_signature(self, sig, signode):
208 signode += addnodes.desc_name(sig, sig)
209 return sig
210
211
212class ZuulAttrDirective(ZuulObjectDescription):
213 has_content = True
214
215 option_spec = {
216 'required': lambda x: x,
217 'default': lambda x: x,
218 'noindex': lambda x: x,
219 }
220
221 def before_content(self):
222 path = self.env.ref_context.setdefault('zuul:attr_path', [])
223 path.append(self.names[-1])
224 path = self.env.ref_context.setdefault('zuul:display_attr_path', [])
225 path.append(self.names[-1])
226
227 def after_content(self):
228 path = self.env.ref_context.get('zuul:attr_path')
229 if path:
230 path.pop()
231 path = self.env.ref_context.get('zuul:display_attr_path')
232 if path:
233 path.pop()
234
235 def handle_signature(self, sig, signode):
236 path = self.get_display_path()
237 signode['is_multiline'] = True
238 line = addnodes.desc_signature_line()
239 line['add_permalink'] = True
240 for x in path:
241 line += addnodes.desc_addname(x + '.', x + '.')
242 line += addnodes.desc_name(sig, sig)
243 if 'required' in self.options:
244 line += addnodes.desc_annotation(' (required)', ' (required)')
245 signode += line
246 if 'default' in self.options:
247 line = addnodes.desc_signature_line()
248 line += addnodes.desc_type('Default: ', 'Default: ')
249 line += nodes.literal(self.options['default'],
250 self.options['default'])
251 signode += line
252 return sig
253
254
255class ZuulValueDirective(ZuulObjectDescription):
256 has_content = True
257
258 def handle_signature(self, sig, signode):
259 signode += addnodes.desc_name(sig, sig)
260 return sig
261
262
263class ZuulVarDirective(ZuulObjectDescription):
264 has_content = True
265
266 option_spec = {
267 'type': lambda x: x,
268 'hidden': lambda x: x,
269 'noindex': lambda x: x,
270 }
271
272 type_map = {
273 'list': '[]',
274 'dict': '{}',
275 }
276
277 def get_type_str(self):
278 if 'type' in self.options:
279 return self.type_map[self.options['type']]
280 return ''
281
282 def before_content(self):
283 path = self.env.ref_context.setdefault('zuul:attr_path', [])
284 element = self.names[-1]
285 path.append(element)
286 path = self.env.ref_context.setdefault('zuul:display_attr_path', [])
287 element = self.names[-1] + self.get_type_str()
288 path.append(element)
289
290 def after_content(self):
291 path = self.env.ref_context.get('zuul:attr_path')
292 if path:
293 path.pop()
294 path = self.env.ref_context.get('zuul:display_attr_path')
295 if path:
296 path.pop()
297
298 def handle_signature(self, sig, signode):
299 if 'hidden' in self.options:
300 return sig
301 path = self.get_display_path()
302 for x in path:
303 signode += addnodes.desc_addname(x + '.', x + '.')
304 signode += addnodes.desc_name(sig, sig)
305 return sig
306
307
308class ZuulStatDirective(ZuulObjectDescription):
309 has_content = True
310
311 option_spec = {
312 'type': lambda x: x,
313 'hidden': lambda x: x,
314 'noindex': lambda x: x,
315 }
316
317 def before_content(self):
318 path = self.env.ref_context.setdefault('zuul:attr_path', [])
319 element = self.names[-1]
320 path.append(element)
321 path = self.env.ref_context.setdefault('zuul:display_attr_path', [])
322 element = self.names[-1]
323 path.append(element)
324
325 def after_content(self):
326 path = self.env.ref_context.get('zuul:attr_path')
327 if path:
328 path.pop()
329 path = self.env.ref_context.get('zuul:display_attr_path')
330 if path:
331 path.pop()
332
333 def handle_signature(self, sig, signode):
334 if 'hidden' in self.options:
335 return sig
336 path = self.get_display_path()
337 for x in path:
338 signode += addnodes.desc_addname(x + '.', x + '.')
339 signode += addnodes.desc_name(sig, sig)
340 if 'type' in self.options:
341 t = ' (%s)' % self.options['type']
342 signode += addnodes.desc_annotation(t, t)
343 return sig
344
345
346######################################################################
347#
348# Autodoc directives
349#
350
351class ZuulAutoJobDirective(ZuulDirective):
145 def run(self): 352 def run(self):
146 name = self.content[0] 353 name = self.content[0]
147 lines = self.generate_zuul_job_content(name) 354 lines = self.generate_zuul_job_content(name)
@@ -149,7 +356,7 @@ class ZuulAutoJobDirective(BaseZuulDirective):
149 return [] 356 return []
150 357
151 358
152class ZuulAutoJobsDirective(BaseZuulDirective): 359class ZuulAutoJobsDirective(ZuulDirective):
153 has_content = False 360 has_content = False
154 361
155 def run(self): 362 def run(self):
@@ -165,25 +372,7 @@ class ZuulAutoJobsDirective(BaseZuulDirective):
165 return [] 372 return []
166 373
167 374
168class ZuulRoleDirective(BaseZuulDirective, ObjectDescription): 375class ZuulAutoRoleDirective(ZuulDirective):
169 def handle_signature(self, sig, signode):
170 signode += addnodes.desc_name(sig, sig)
171 return sig
172
173 def add_target_and_index(self, name, sig, signode):
174 targetname = self.objtype + '-' + name
175 if targetname not in self.state.document.ids:
176 signode['names'].append(targetname)
177 signode['ids'].append(targetname)
178 signode['first'] = (not self.names)
179 self.state.document.note_explicit_target(signode)
180
181 indextext = '%s (%s)' % (name, self.objtype)
182 self.indexnode['entries'].append(('single', indextext,
183 targetname, '', None))
184
185
186class ZuulAutoRoleDirective(BaseZuulDirective):
187 def run(self): 376 def run(self):
188 name = self.content[0] 377 name = self.content[0]
189 lines = self.generate_zuul_role_content(name) 378 lines = self.generate_zuul_role_content(name)
@@ -191,7 +380,7 @@ class ZuulAutoRoleDirective(BaseZuulDirective):
191 return [] 380 return []
192 381
193 382
194class ZuulAutoRolesDirective(BaseZuulDirective): 383class ZuulAutoRolesDirective(ZuulDirective):
195 has_content = False 384 has_content = False
196 385
197 def run(self): 386 def run(self):
@@ -202,29 +391,72 @@ class ZuulAutoRolesDirective(BaseZuulDirective):
202 return [] 391 return []
203 392
204 393
394class ZuulAbbreviatedXRefRole(XRefRole):
395
396 def process_link(self, env, refnode, has_explicit_title, title,
397 target):
398 title, target = super(ZuulAbbreviatedXRefRole, self).process_link(
399 env, refnode, has_explicit_title, title, target)
400 if not has_explicit_title:
401 title = title.split('.')[-1]
402 return title, target
403
404
205class ZuulDomain(Domain): 405class ZuulDomain(Domain):
206 name = 'zuul' 406 name = 'zuul'
207 label = 'Zuul' 407 label = 'Zuul'
208 408
209 object_types = {
210 'job': ObjType('job'),
211 'role': ObjType('role'),
212 }
213
214 directives = { 409 directives = {
410 # Object description directives
215 'job': ZuulJobDirective, 411 'job': ZuulJobDirective,
412 'role': ZuulRoleDirective,
413 'attr': ZuulAttrDirective,
414 'value': ZuulValueDirective,
415 'var': ZuulVarDirective,
416 'stat': ZuulStatDirective,
417 # Autodoc directives
216 'autojob': ZuulAutoJobDirective, 418 'autojob': ZuulAutoJobDirective,
217 'autojobs': ZuulAutoJobsDirective, 419 'autojobs': ZuulAutoJobsDirective,
218 'role': ZuulRoleDirective,
219 'autorole': ZuulAutoRoleDirective, 420 'autorole': ZuulAutoRoleDirective,
220 'autoroles': ZuulAutoRolesDirective, 421 'autoroles': ZuulAutoRolesDirective,
221 } 422 }
222 423
424 roles = {
425 'job': XRefRole(innernodeclass=nodes.inline, # type: ignore
426 warn_dangling=True),
427 'role': XRefRole(innernodeclass=nodes.inline, # type: ignore
428 warn_dangling=True),
429 'attr': XRefRole(innernodeclass=nodes.inline, # type: ignore
430 warn_dangling=True),
431 'value': ZuulAbbreviatedXRefRole(
432 innernodeclass=nodes.inline, # type: ignore
433 warn_dangling=True),
434 'var': XRefRole(innernodeclass=nodes.inline, # type: ignore
435 warn_dangling=True),
436 'stat': XRefRole(innernodeclass=nodes.inline, # type: ignore
437 warn_dangling=True),
438 }
439
223 initial_data = { 440 initial_data = {
224 'layout': None, 441 'layout': None,
225 'layout_path': None, 442 'layout_path': None,
226 'role_paths': None, 443 'role_paths': None,
227 } 444 'objects': {},
445 } # type: Dict[str, Dict]
446
447 def resolve_xref(self, env, fromdocname, builder, type, target,
448 node, contnode):
449 objects = self.data['objects']
450 name = type + '-' + target
451 obj = objects.get(name)
452 if obj:
453 return make_refnode(builder, fromdocname, obj[0], name,
454 contnode, name)
455
456 def clear_doc(self, docname):
457 for fullname, (fn, _l) in list(self.data['objects'].items()):
458 if fn == docname:
459 del self.data['objects'][fullname]
228 460
229 461
230def setup(app): 462def setup(app):