melexis/sphinx-traceability-extension

View on GitHub
mlx/traceability/traceable_base_class.py

Summary

Maintainability
A
0 mins
Test Coverage
'''
Base class for traceable stuff
'''

import hashlib

from docutils import nodes
from docutils.statemachine import ViewList
from sphinx.util.nodes import nested_parse_with_titles

from .traceability_exception import TraceabilityException


class TraceableBaseClass:
    '''
    Storage for a traceable base class
    '''

    def __init__(self, name, state=None):
        '''
        Initialize a new base class

        Args:
            name (str): Base class object identification
            state: The state of the state machine, which controls the parsing
        '''
        self.identifier = self.to_id(name)
        self.name = name
        self.caption = None
        self.docname = None
        self.lineno = None
        self.node = None
        self._content = None
        self.content_node = nodes.block_quote()
        self._state = state

    @staticmethod
    def to_id(identifier):
        '''
        Convert a given identification to a storable id

        Args:
            id (str): input identification
        Returns:
            str - Converted storable identification
        '''
        return identifier

    def update(self, other):
        '''
        Update with new object

        Store the sum of both objects
        '''
        if self.identifier != other.identifier:
            raise ValueError('Update error {old} vs {new}'.format(old=self.identifier, new=other.identifier))
        if other.name is not None:
            self.name = other.name
        if other.docname is not None:
            self.docname = other.docname
        if other.lineno is not None:
            self.lineno = other.lineno
        if other.node is not None:
            self.node = other.node
        if other.caption is not None:
            self.caption = other.caption
        if other.content is not None:
            self.content = other.content

    def set_location(self, docname, lineno=0):
        '''
        Set location in document

        Args:
            docname (str): Path to docname
            lineno (int): Line number in given document
        '''
        self.docname = docname
        self.lineno = lineno

    @property
    def content(self):
        return self._content

    @content.setter
    def content(self, content):
        self._content = content
        if self._state:
            template = ViewList(source=self.docname, parent_offset=self.lineno)
            for idx, line in enumerate(content.split('\n')):
                template.append(line, self.docname, idx)
            self.content_node = nodes.block_quote()  # reset
            nested_parse_with_titles(self._state, template, self.content_node)

    def clear_state(self):
        '''
        Clear value of state attribute, which should not be used after directives have been processed
        '''
        self._state = None

    def to_dict(self):
        '''
        Export to dictionary

        Returns:
            (dict) Dictionary representation of the object
        '''
        data = {}
        data['id'] = self.identifier
        data['name'] = self.name
        caption = self.caption
        if caption:
            data['caption'] = caption
        data['document'] = self.docname
        data['line'] = self.lineno
        if self.content:
            data['content-hash'] = hashlib.md5(self.content.encode('utf-8')).hexdigest()
        else:
            data['content-hash'] = "0"
        return data

    def self_test(self):
        '''
        Perform self test on content
        '''
        # should hold a reference to a document
        if self.docname is None:
            raise TraceabilityException("Item '{identification}' has no reference to source document."
                                        .format(identification=self.identifier))