import datetime import json import os import re import sys from six import class_types from sphinx.ext.autodoc import ClassDocumenter from stix2.base import _STIXBase from stix2.environment import WEIGHTS from stix2.version import __version__ sys.path.insert(0, os.path.abspath('..')) extensions = [ 'sphinx-prompt', 'nbsphinx', 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'sphinx.ext.napoleon', 'sphinx.ext.todo', ] autodoc_default_flags = [ 'undoc-members', ] autodoc_member_order = 'groupwise' autosummary_generate = True napoleon_numpy_docstring = False # Force consistency, leave only Google napoleon_use_rtype = False add_module_names = False templates_path = ['_templates'] source_suffix = '.rst' master_doc = 'index' project = 'stix2' copyright = '{}, OASIS Open'.format(datetime.date.today().year) author = 'OASIS Open' version = __version__ release = __version__ language = None exclude_patterns = ['_build', '_templates', 'Thumbs.db', '.DS_Store', 'guide/.ipynb_checkpoints'] pygments_style = 'sphinx' todo_include_todos = False html_theme = 'alabaster' html_sidebars = { '**': [ 'about.html', 'navigation.html', 'relations.html', 'searchbox.html', ], } latex_elements = {} latex_documents = [ (master_doc, 'stix2.tex', 'stix2 Documentation', 'OASIS', 'manual'), ] # Add a formatted version of environment.WEIGHTS default_sem_eq_weights = json.dumps(WEIGHTS, indent=4, default=lambda o: o.__name__) default_sem_eq_weights = default_sem_eq_weights.replace('\n', '\n ') default_sem_eq_weights = default_sem_eq_weights.replace(' "', ' ') default_sem_eq_weights = default_sem_eq_weights.replace('"\n', '\n') with open('default_sem_eq_weights.rst', 'w') as f: f.write(".. code-block:: py\n\n {}\n\n".format(default_sem_eq_weights)) def get_property_type(prop): """Convert property classname into pretty string name of property. """ try: prop_class = prop.__name__ except AttributeError: prop_class = prop.__class__.__name__ # Remove 'Property' from the string prop_class = prop_class.split('Property')[0] # Split camelcase with spaces split_camelcase = re.sub('(?!^)([A-Z][a-z]+)', r' \1', prop_class).split() prop_class = ' '.join(split_camelcase) return prop_class class STIXPropertyDocumenter(ClassDocumenter): """Custom Sphinx extension to auto-document STIX properties. Needed because descendants of _STIXBase use `_properties` dictionaries instead of instance variables for STIX 2 objects' properties. """ objtype = 'stixattr' directivetype = 'class' priority = 999 @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, class_types) and \ issubclass(member, _STIXBase) and \ hasattr(member, '_properties') def add_content(self, more_content, no_docstring=False): ClassDocumenter.add_content(self, more_content, no_docstring) obj = self.object self.add_line(':Properties:', '') for prop_name, prop in obj._properties.items(): # Skip 'type' if prop_name == 'type': continue # Add metadata about the property prop_type = get_property_type(prop) if prop_type == 'List': prop_type = 'List of %ss' % get_property_type(prop.contained) if prop.required: prop_type += ', required' if 'Timestamp' in prop_type and hasattr(prop, 'default'): prop_type += ', default: current date/time' prop_str = '**%s** (*%s*)' % (prop_name, prop_type) self.add_line(' - %s' % prop_str, '') self.add_line('', '') def autodoc_skipper(app, what, name, obj, skip, options): """Customize Sphinx to skip some member we don't want documented. Skips anything containing ':autodoc-skip:' in its docstring. """ if obj.__doc__ and ':autodoc-skip:' in obj.__doc__: return skip or True return skip def setup(app): app.add_autodocumenter(STIXPropertyDocumenter) app.connect('autodoc-skip-member', autodoc_skipper)