Skip to content

Commit

Permalink
Merge pull request #58 from melexis/itemlink_directive
Browse files Browse the repository at this point in the history
Itemlink directive
  • Loading branch information
bavovanachte authored Aug 27, 2018
2 parents 81aa71a + 4bc66af commit fa8985d
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 22 deletions.
19 changes: 19 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,25 @@ The relations to other documentation items can be specified as:
The output will contain hyperlinks to all related items. By default the caption for the target item is displayed for
each of these related items. With the option *nocaptions* these captions can be omited.

.. _adding_relations:

Adding relations outside of the item definitions
================================================

In some cases, it's useful to add relations outside of the definition of the items
involved. In that case, you can use the ``item-link`` directive as follows

.. code-block:: rest
.. item-link::
:sources: RQT1 RQT2
:targets: TST3 TST4 TST5
:type: validates
This directive has no representation in the documentation build output. It will
just add an additional relationship to the items mentioned in ``sources`` and
``targets``

.. _traceability_usage_item_linking:

Manual link to documentation items
Expand Down
40 changes: 40 additions & 0 deletions example/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,46 @@ Another tree that should spawn a warning as the relation in *type* does not exis
:top_relation_filter: depends_on
:type: fulfilled_by fulfills

This is a subtitle that has a ``item-link`` item under it. You shouldn't see anything in the rendering, though
--------------------------------------------------------------------------------------------------------------

.. item-link::
:sources: r001
:targets: r002
:type: trace

.. test: link to later (bottom of this page) defined source, should not warn
.. item-link::
:sources: late001
:type: trace
:targets: r001

.. warning on next item-link due to missing sources:
.. item-link::
:type: trace
:targets: r100

.. warning on next item-link due to missing targets:
.. item-link::
:sources: r100
:type: trace

.. warning on next item-link due to missing relation type:
.. item-link::
:sources: r100
:targets: r001

Extra late requirements
-----------------------

.. item:: late001

Item is added after adding links from it using item-link above. This shouldn't give a warning.

Links and references
====================

Expand Down
69 changes: 69 additions & 0 deletions mlx/traceability.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ class ItemTree(nodes.General, nodes.Element):
pass


class ItemLink(nodes.General, nodes.Element):
'''List of documentation items'''
pass


# -----------------------------------------------------------------------------
# Pending item cross reference node

Expand Down Expand Up @@ -253,6 +258,64 @@ def run(self):
return [item_list_node]


class ItemLinkDirective(Directive):
"""
Directive to add additional relations between lists of items.
Syntax::
.. item-link::
:sources: list_of_items
:targets: list_of_items
:type: relationship_type
"""
final_argument_whitespace = True
# Options
option_spec = {'sources': directives.unchanged,
'targets': directives.unchanged,
'type': directives.unchanged}
# Content disallowed
has_content = False

def run(self):
env = self.state.document.settings.env

node = ItemLink('')
node['sources'] = []
node['targets'] = []
node['type'] = None

if 'sources' in self.options:
node['sources'] = self.options['sources'].split()
else:
report_warning(env, 'sources argument required for item-link directive', env.docname, self.lineno)
return []
if 'targets' in self.options:
node['targets'] = self.options['targets'].split()
else:
report_warning(env, 'targets argument required for item-link directive', env.docname, self.lineno)
return []
if 'type' in self.options:
node['type'] = self.options['type']
else:
report_warning(env, 'type argument required for item-link directive', env.docname, self.lineno)
return []

# Processing of the item-link items. They get added as additional relationships
# to the existing items. Should be done before converting anything to docutils.
for source in node['sources']:
for target in node['targets']:
try:
env.traceability_collection.add_relation(source, node['type'], target)
except TraceabilityException as err:
docname, lineno = get_source_line(node)
report_warning(env, err, docname, lineno)

# The ItemLink node has no final representation, so is removed from the tree
return [node]


class ItemMatrixDirective(Directive):
"""
Directive to generate a matrix of item cross-references, based on
Expand Down Expand Up @@ -548,6 +611,11 @@ def process_item_nodes(app, doctree, fromdocname):
for err in errs.iter():
report_warning(env, err, err.get_document())

# Processing of the item-link items.
for node in doctree.traverse(ItemLink):
# The ItemLink node has no final representation, so is removed from the tree
node.replace_self([])

# Item matrix:
# Create table with related items, printing their target references.
# Only source and target items matching respective regexp shall be included
Expand Down Expand Up @@ -1061,6 +1129,7 @@ def setup(app):
app.add_directive('item-matrix', ItemMatrixDirective)
app.add_directive('item-2d-matrix', Item2DMatrixDirective)
app.add_directive('item-tree', ItemTreeDirective)
app.add_directive('item-link', ItemLinkDirective)

app.connect('doctree-resolved', process_item_nodes)
if sphinx_version >= '1.6.0':
Expand Down
5 changes: 3 additions & 2 deletions mlx/traceable_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,10 @@ def add_relation(self, sourceid, relation, targetid):
relation (str): Relation between source and target item
targetid (str): ID of the target item
'''
# Fail if source item is unknown
# Add placeholder if source item is unknown
if sourceid not in self.items:
raise ValueError('Source item {name} not known'.format(name=sourceid))
src = TraceableItem(sourceid, True)
self.add_item(src)
source = self.items[sourceid]
# Error if relation is unknown
if relation not in self.relations:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from setuptools import setup, find_packages

project_url = 'https://github.com/melexis/sphinx-traceability-extension'
version = '2.6.0'
version = '2.7.0'

requires = ['Sphinx>=0.6', 'docutils']

Expand Down
30 changes: 23 additions & 7 deletions tests/test_traceable_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,18 +124,34 @@ def test_add_item_overwrite(self):
self.assertFalse(item2_out.is_placeholder())

def test_add_relation_unknown_source(self):
# with unknown source item, exception is expected
# with unknown source item, the generation of a placeholder is expected
coll = dut.TraceableCollection()
item2 = item.TraceableItem(self.identification_tgt)
item2.set_document(self.docname)
coll.add_item(item2)
coll.add_relation_pair(self.fwd_relation, self.rev_relation)
with self.assertRaises(ValueError):
coll.add_relation(self.identification_src,
self.fwd_relation,
self.identification_tgt)
# Self test should pass
coll.self_test()
coll.add_relation(self.identification_src,
self.fwd_relation,
self.identification_tgt)
# Assert placeholder item is created
item1 = coll.get_item(self.identification_src)
self.assertIsNotNone(item1)
self.assertEqual(self.identification_src, item1.get_id())
self.assertTrue(item1.is_placeholder())
# Assert explicit forward relation is created
relations = item1.iter_targets(self.fwd_relation, explicit=True, implicit=False)
self.assertEqual(1, len(relations))
self.assertEqual(relations[0], self.identification_tgt)
relations = item1.iter_targets(self.fwd_relation, explicit=False, implicit=True)
# Assert implicit reverse relation is created
relations = item2.iter_targets(self.rev_relation, explicit=False, implicit=True)
self.assertEqual(1, len(relations))
self.assertEqual(relations[0], self.identification_src)
relations = item2.iter_targets(self.fwd_relation, explicit=True, implicit=False)
self.assertEqual(0, len(relations))
# Self test should fail, as we have a placeholder item
with self.assertRaises(dut.MultipleTraceabilityExceptions):
coll.self_test()

def test_add_relation_unknown_relation(self):
# with unknown relation, warning is expected
Expand Down
24 changes: 12 additions & 12 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ whitelist_externals =
mlx-warnings
commands=
bash -c 'make -C example html 2>&1 | tee .tox/doc_html.log'
mlx-warnings --sphinx --maxwarnings 8 --minwarnings 8 .tox/doc_html.log
mlx-warnings --sphinx --maxwarnings 11 --minwarnings 11 .tox/doc_html.log
bash -c 'make -C example latexpdf 2>&1 | tee .tox/doc_pdf.log'
mlx-warnings --sphinx --maxwarnings 8 --minwarnings 8 .tox/doc_pdf.log
mlx-warnings --sphinx --maxwarnings 11 --minwarnings 11 .tox/doc_pdf.log

[testenv:sphinx1.4]
deps=
Expand All @@ -74,9 +74,9 @@ whitelist_externals =
mlx-warnings
commands=
bash -c 'make -C example html 2>&1 | tee .tox/doc_html.log'
mlx-warnings --sphinx --maxwarnings 8 --minwarnings 8 .tox/doc_html.log
mlx-warnings --sphinx --maxwarnings 11 --minwarnings 11 .tox/doc_html.log
bash -c 'make -C example latexpdf 2>&1 | tee .tox/doc_pdf.log'
mlx-warnings --sphinx --maxwarnings 8 --minwarnings 8 .tox/doc_pdf.log
mlx-warnings --sphinx --maxwarnings 11 --minwarnings 11 .tox/doc_pdf.log

[testenv:sphinx1.5]
deps=
Expand All @@ -90,9 +90,9 @@ whitelist_externals =
mlx-warnings
commands=
bash -c 'make -C example html 2>&1 | tee .tox/doc_html.log'
mlx-warnings --sphinx --maxwarnings 8 --minwarnings 8 .tox/doc_html.log
mlx-warnings --sphinx --maxwarnings 11 --minwarnings 11 .tox/doc_html.log
bash -c 'make -C example latexpdf 2>&1 | tee .tox/doc_pdf.log'
mlx-warnings --sphinx --maxwarnings 8 --minwarnings 8 .tox/doc_pdf.log
mlx-warnings --sphinx --maxwarnings 11 --minwarnings 11 .tox/doc_pdf.log

[testenv:sphinx1.6]
deps=
Expand All @@ -106,9 +106,9 @@ whitelist_externals =
mlx-warnings
commands=
bash -c 'make -C example html 2>&1 | tee .tox/doc_html.log'
mlx-warnings --sphinx --maxwarnings 8 --minwarnings 8 .tox/doc_html.log
mlx-warnings --sphinx --maxwarnings 11 --minwarnings 11 .tox/doc_html.log
bash -c 'make -C example latexpdf 2>&1 | tee .tox/doc_pdf.log'
mlx-warnings --sphinx --maxwarnings 8 --minwarnings 8 .tox/doc_pdf.log
mlx-warnings --sphinx --maxwarnings 11 --minwarnings 11 .tox/doc_pdf.log

[testenv:sphinx1.7]
deps=
Expand All @@ -122,9 +122,9 @@ whitelist_externals =
mlx-warnings
commands=
bash -c 'make -C example html 2>&1 | tee .tox/doc_html.log'
mlx-warnings --sphinx --maxwarnings 8 --minwarnings 8 .tox/doc_html.log
mlx-warnings --sphinx --maxwarnings 11 --minwarnings 11 .tox/doc_html.log
bash -c 'make -C example latexpdf 2>&1 | tee .tox/doc_pdf.log'
mlx-warnings --sphinx --maxwarnings 8 --minwarnings 8 .tox/doc_pdf.log
mlx-warnings --sphinx --maxwarnings 11 --minwarnings 11 .tox/doc_pdf.log

[testenv:sphinx-latest]
deps=
Expand All @@ -138,9 +138,9 @@ whitelist_externals =
mlx-warnings
commands=
bash -c 'make -C example html 2>&1 | tee .tox/doc_html.log'
mlx-warnings --sphinx --maxwarnings 8 --minwarnings 8 .tox/doc_html.log
mlx-warnings --sphinx --maxwarnings 11 --minwarnings 11 .tox/doc_html.log
bash -c 'make -C example latexpdf 2>&1 | tee .tox/doc_pdf.log'
mlx-warnings --sphinx --maxwarnings 8 --minwarnings 8 .tox/doc_pdf.log
mlx-warnings --sphinx --maxwarnings 11 --minwarnings 11 .tox/doc_pdf.log

[testenv:coveralls]
deps =
Expand Down

0 comments on commit fa8985d

Please sign in to comment.