def finish(self): log = get_logger(__name__) needs = self.env.needs_all_needs filters = self.env.needs_all_filters config = self.env.config version = config.version needs_list = NeedsList(config, self.outdir, self.confdir) needs_list.load_json() # Clean needs_list from already stored needs of the current version. # This is needed as needs could have been removed from documentation and if this is the case, # removed needs would stay in needs_list, if list gets not cleaned. needs_list.wipe_version(version) for key, need in needs.items(): needs_list.add_need(version, need) for key, need_filter in filters.items(): if need_filter['export_id']: needs_list.add_filter(version, need_filter) try: needs_list.write_json() except Exception as e: log.error("Error during writing json file: {0}".format(e)) else: log.info("Needs successfully exported")
def __init__(self, config, outdir, confdir): self.log = get_logger(__name__) self.config = config self.outdir = outdir self.confdir = confdir self.current_version = config.version self.project = config.project self.needs_list = { "project": self.project, "current_version": self.current_version, "created": "", "versions": {} }
from docutils import nodes try: from sphinx.errors import NoUri # Sphinx 3.0 except ImportError: from sphinx.environment import NoUri # Sphinx < 3.0 from sphinx.util.nodes import make_refnode from sphinxcontrib.needs.logging import get_logger from sphinxcontrib.needs.utils import check_and_calc_base_url_rel_path log = get_logger(__name__) class NeedRef(nodes.Inline, nodes.Element): pass def process_need_ref(app, doctree, fromdocname): for node_need_ref in doctree.traverse(NeedRef): env = app.builder.env # Let's create a dummy node, for the case we will not be able to create a real reference new_node_ref = make_refnode( app.builder, fromdocname, fromdocname, "Unknown need", node_need_ref[0].deepcopy(), node_need_ref["reftarget"] + "?", )
def load_config(app: Sphinx, *_args): """ Register extra options and directive based on config from conf.py """ log = get_logger(__name__) types = app.config.needs_types if isinstance(app.config.needs_extra_options, dict): log.info( 'Config option "needs_extra_options" supports list and dict. However new default type since ' "Sphinx-Needs 0.7.2 is list. Please see docs for details." ) existing_extra_options = NEEDS_CONFIG.get("extra_options") for option in app.config.needs_extra_options: if option in existing_extra_options: log.warning(f'extra_option "{option}" already registered.') NEEDS_CONFIG.add("extra_options", {option: directives.unchanged}, dict, True) extra_options = NEEDS_CONFIG.get("extra_options") # Get extra links and create a dictionary of needed options. extra_links_raw = app.config.needs_extra_links extra_links = {} for extra_link in extra_links_raw: extra_links[extra_link["option"]] = directives.unchanged title_optional = app.config.needs_title_optional title_from_content = app.config.needs_title_from_content # Update NeedDirective to use customized options NeedDirective.option_spec.update(extra_options) NeedserviceDirective.option_spec.update(extra_options) # Update NeedDirective to use customized links NeedDirective.option_spec.update(extra_links) NeedserviceDirective.option_spec.update(extra_links) # Update NeedextendDirective with option modifiers. for key, value in NEED_DEFAULT_OPTIONS.items(): # Ignore options like "id" if key in NEEDEXTEND_NOT_ALLOWED_OPTIONS: continue NeedextendDirective.option_spec.update( { key: value, f"+{key}": value, f"-{key}": directives.flag, } ) for key, value in extra_links.items(): NeedextendDirective.option_spec.update( { key: value, f"+{key}": value, f"-{key}": directives.flag, f"{key}_back": value, f"+{key}_back": value, f"-{key}_back": directives.flag, } ) # "links" is not part of the extra_links-dict, so we need # to set the links_back values by hand NeedextendDirective.option_spec.update( { "links_back": NEED_DEFAULT_OPTIONS["links"], "+links_back": NEED_DEFAULT_OPTIONS["links"], "-links_back": directives.flag, } ) for key, value in extra_options.items(): NeedextendDirective.option_spec.update( { key: value, f"+{key}": value, f"-{key}": directives.flag, } ) if title_optional or title_from_content: NeedDirective.required_arguments = 0 NeedDirective.optional_arguments = 1 for t in types: # Register requested types of needs app.add_directive(t["directive"], NeedDirective) existing_warnings = NEEDS_CONFIG.get("warnings") for name, check in app.config.needs_warnings.items(): if name not in existing_warnings: NEEDS_CONFIG.add("warnings", {name: check}, dict, append=True) else: log.warning(f'{name} for "warnings" is already registered.')
def setup(app): log = get_logger(__name__) log.debug("Starting setup of Sphinx-Needs") log.debug("Load Sphinx-Data-Viewer for Sphinx-Needs") app.setup_extension("sphinx_data_viewer") app.add_builder(NeedsBuilder) app.add_config_value( "needs_types", [ {"directive": "req", "title": "Requirement", "prefix": "R_", "color": "#BFD8D2", "style": "node"}, {"directive": "spec", "title": "Specification", "prefix": "S_", "color": "#FEDCD2", "style": "node"}, {"directive": "impl", "title": "Implementation", "prefix": "I_", "color": "#DF744A", "style": "node"}, {"directive": "test", "title": "Test Case", "prefix": "T_", "color": "#DCB239", "style": "node"}, # Kept for backwards compatibility {"directive": "need", "title": "Need", "prefix": "N_", "color": "#9856a5", "style": "node"}, ], "html", ) app.add_config_value("needs_include_needs", True, "html", types=[bool]) app.add_config_value("needs_need_name", "Need", "html", types=[str]) app.add_config_value("needs_spec_name", "Specification", "html", types=[str]) app.add_config_value("needs_id_prefix_needs", "", "html", types=[str]) app.add_config_value("needs_id_prefix_specs", "", "html", types=[str]) app.add_config_value("needs_id_length", 5, "html", types=[int]) app.add_config_value("needs_specs_show_needlist", False, "html", types=[bool]) app.add_config_value("needs_id_required", False, "html", types=[bool]) app.add_config_value( "needs_id_regex", f"^[A-Z0-9_]{{{app.config.needs_id_length},}}", "html", ) app.add_config_value("needs_show_link_type", False, "html", types=[bool]) app.add_config_value("needs_show_link_title", False, "html", types=[bool]) app.add_config_value("needs_file", None, "html") app.add_config_value("needs_table_columns", "ID;TITLE;STATUS;TYPE;OUTGOING;TAGS", "html") app.add_config_value("needs_table_style", "DATATABLES", "html") app.add_config_value("needs_role_need_template", "{title} ({id})", "html") app.add_config_value("needs_role_need_max_title_length", 30, "html", types=[int]) app.add_config_value("needs_extra_options", [], "html") app.add_config_value("needs_title_optional", False, "html", types=[bool]) app.add_config_value("needs_max_title_length", -1, "html", types=[int]) app.add_config_value("needs_title_from_content", False, "html", types=[bool]) app.add_config_value("needs_diagram_template", DEFAULT_DIAGRAM_TEMPLATE, "html") app.add_config_value("needs_functions", [], "html", types=[list]) app.add_config_value("needs_global_options", {}, "html", types=[dict]) app.add_config_value("needs_duration_option", "duration", "html") app.add_config_value("needs_completion_option", "completion", "html") # If given, only the defined status are allowed. # Values needed for each status: # * name # * description # Example: [{"name": "open", "description": "open status"}, {...}, {...}] app.add_config_value("needs_statuses", [], "html") # If given, only the defined tags are allowed. # Values needed for each tag: # * name # * description # Example: [{"name": "new", "description": "new needs"}, {...}, {...}] app.add_config_value("needs_tags", False, "html", types=[bool]) # Path of css file, which shall be used for need style app.add_config_value("needs_css", "modern.css", "html") # Prefix for need_part output in tables app.add_config_value("needs_part_prefix", "\u2192\u00a0", "html") # List of additional links, which can be used by setting related option # Values needed for each new link: # * name (will also be the option name) # * incoming # * copy_link (copy to common links data. Default: True) # * color (used for needflow. Default: #000000) # Example: [{"name": "blocks, "incoming": "is blocked by", "copy_link": True, "color": "#ffcc00"}] app.add_config_value("needs_extra_links", [], "html") app.add_config_value("needs_filter_data", {}, "html") app.add_config_value("needs_flow_show_links", False, "html") app.add_config_value("needs_flow_link_types", ["links"], "html") app.add_config_value("needs_warnings", {}, "html") app.add_config_value("needs_warnings_always_warn", False, "html", types=[bool]) app.add_config_value("needs_layouts", {}, "html") app.add_config_value("needs_default_layout", "clean", "html") app.add_config_value("needs_default_style", None, "html") app.add_config_value("needs_flow_configs", {}, "html") app.add_config_value("needs_template_folder", "needs_templates/", "html") app.add_config_value("needs_services", {}, "html") app.add_config_value("needs_service_all_data", False, "html", types=[bool]) app.add_config_value("needs_debug_no_external_calls", False, "html", types=[bool]) app.add_config_value("needs_external_needs", [], "html") app.add_config_value("needs_builder_filter", "is_external==False", "html", types=[str]) # Additional classes to set for needs and needtable. app.add_config_value("needs_table_classes", NEEDS_TABLES_CLASSES, "html", types=[list]) app.add_config_value("needs_string_links", {}, "html", types=[dict]) # Define nodes app.add_node(Need, html=(html_visit, html_depart), latex=(latex_visit, latex_depart)) app.add_node( Needfilter, ) app.add_node(Needbar) app.add_node(Needimport) app.add_node(Needlist) app.add_node(Needtable) app.add_node(Needflow) app.add_node(Needpie) app.add_node(Needsequence) app.add_node(Needgantt) app.add_node(Needextract) app.add_node(Needservice) app.add_node(Needextend) app.add_node(NeedPart, html=(visitor_dummy, visitor_dummy), latex=(visitor_dummy, visitor_dummy)) ######################################################################## # DIRECTIVES ######################################################################## # Define directives app.add_directive("needbar", NeedbarDirective) app.add_directive("needfilter", NeedfilterDirective) app.add_directive("needlist", NeedlistDirective) app.add_directive("needtable", NeedtableDirective) app.add_directive("needflow", NeedflowDirective) app.add_directive("needpie", NeedpieDirective) app.add_directive("needsequence", NeedsequenceDirective) app.add_directive("needgantt", NeedganttDirective) app.add_directive("needimport", NeedimportDirective) app.add_directive("needextract", NeedextractDirective) app.add_directive("needservice", NeedserviceDirective) app.add_directive("needextend", NeedextendDirective) ######################################################################## # ROLES ######################################################################## # Provides :need:`ABC_123` for inline links. app.add_role("need", XRefRole(nodeclass=NeedRef, innernodeclass=nodes.emphasis, warn_dangling=True)) app.add_role("need_incoming", XRefRole(nodeclass=NeedIncoming, innernodeclass=nodes.emphasis, warn_dangling=True)) app.add_role("need_outgoing", XRefRole(nodeclass=NeedOutgoing, innernodeclass=nodes.emphasis, warn_dangling=True)) app.add_role("need_part", XRefRole(nodeclass=NeedPart, innernodeclass=nodes.inline, warn_dangling=True)) # Shortcut for need_part app.add_role("np", XRefRole(nodeclass=NeedPart, innernodeclass=nodes.inline, warn_dangling=True)) app.add_role("need_count", XRefRole(nodeclass=NeedCount, innernodeclass=nodes.inline, warn_dangling=True)) app.add_role("need_func", XRefRole(nodeclass=NeedFunc, innernodeclass=nodes.inline, warn_dangling=True)) ######################################################################## # EVENTS ######################################################################## # Make connections to events app.connect("env-purge-doc", purge_needs) app.connect("config-inited", load_config) app.connect("env-before-read-docs", prepare_env) app.connect("env-before-read-docs", load_external_needs) app.connect("config-inited", check_configuration) app.connect("env-merge-info", merge_data) # There is also the event doctree-read. # But it looks like in this event no references are already solved, which # makes trouble in our code. # However, some sphinx-internal actions (like image collection) are already called during # doctree-read. So manipulating the doctree may result in conflicts, as e.g. images get not # registered for sphinx. So some sphinx-internal tasks/functions may be called by hand again... # See also https://github.com/sphinx-doc/sphinx/issues/7054#issuecomment-578019701 for an example app.connect("doctree-resolved", add_sections) app.connect("doctree-resolved", process_need_nodes) app.connect("doctree-resolved", process_needextend) # Must be done very early, as it modifies need data app.connect("doctree-resolved", print_need_nodes) app.connect("doctree-resolved", process_needbar) app.connect("doctree-resolved", process_needextract) app.connect("doctree-resolved", process_needfilters) app.connect("doctree-resolved", process_needlist) app.connect("doctree-resolved", process_needtables) app.connect("doctree-resolved", process_needflow) app.connect("doctree-resolved", process_needpie) app.connect("doctree-resolved", process_needsequence) app.connect("doctree-resolved", process_needgantt) app.connect("doctree-resolved", process_need_part) app.connect("doctree-resolved", process_need_ref) app.connect("doctree-resolved", process_need_incoming) app.connect("doctree-resolved", process_need_outgoing) app.connect("doctree-resolved", process_need_count) app.connect("doctree-resolved", process_need_func) app.connect("build-finished", process_warnings) app.connect("env-updated", install_lib_static_files) # Called during consistency check, which if after everything got read in. # app.connect('env-check-consistency', process_warnings) # This should be called last, so that need-styles can override styles from used libraries app.connect("env-updated", install_styles_static_files) # Be sure Sphinx-Needs config gets erased before any events or external API calls get executed. # So never but this inside an event. NEEDS_CONFIG.create("extra_options", dict, overwrite=True) NEEDS_CONFIG.create("warnings", dict, overwrite=True) return { "version": VERSION, "parallel_read_safe": True, "parallel_write_safe": True, }
def __init__(self, *args, **kw): super().__init__(*args, **kw) self.log = get_logger(__name__)
def __init__(self, *args, **kwargs): self.log = get_logger(__name__)
def __init__(self, app): self.app = app self.log = get_logger(__name__) self.services = {}
def __init__(self, *args, **kw): super().__init__(*args, **kw) self.log = get_logger(__name__) self.full_title = self._get_full_title()
def __init__(self, *args, **kw): super(NeedserviceDirective, self).__init__(*args, **kw) self.log = get_logger(__name__)
def setup(app): log = get_logger(__name__) log.debug("Starting setup of sphinx-Needs") app.add_builder(NeedsBuilder) app.add_config_value( 'needs_types', [ dict(directive="req", title="Requirement", prefix="R_", color="#BFD8D2", style="node"), dict(directive="spec", title="Specification", prefix="S_", color="#FEDCD2", style="node"), dict(directive="impl", title="Implementation", prefix="I_", color="#DF744A", style="node"), dict(directive="test", title="Test Case", prefix="T_", color="#DCB239", style="node"), # Kept for backwards compatibility dict(directive="need", title="Need", prefix="N_", color="#9856a5", style="node") ], 'html') app.add_config_value("needs_include_needs", True, "html", types=[bool]) app.add_config_value("needs_need_name", "Need", "html", types=[str]) app.add_config_value("needs_spec_name", "Specification", "html", types=[str]) app.add_config_value("needs_id_prefix_needs", "", "html", types=[str]) app.add_config_value("needs_id_prefix_specs", "", "html", types=[str]) app.add_config_value("needs_id_length", 5, "html", types=[int]) app.add_config_value("needs_specs_show_needlist", False, "html", types=[bool]) app.add_config_value("needs_id_required", False, "html", types=[bool]) app.add_config_value( "needs_id_regex", "^[A-Z0-9_]{{{id_length},}}".format( id_length=app.config.needs_id_length), "html", ) app.add_config_value("needs_show_link_type", False, "html", types=[bool]) app.add_config_value("needs_show_link_title", False, "html", types=[bool]) app.add_config_value("needs_file", "needs.json", "html") app.add_config_value("needs_table_columns", "ID;TITLE;STATUS;TYPE;OUTGOING;TAGS", "html") app.add_config_value("needs_table_style", "DATATABLES", "html") app.add_config_value("needs_role_need_template", u"{title} ({id})", "html") app.add_config_value("needs_role_need_max_title_length", 30, "html", types=[int]) app.add_config_value("needs_extra_options", {}, "html") app.add_config_value("needs_title_optional", False, "html", types=[bool]) app.add_config_value("needs_max_title_length", -1, "html", types=[int]) app.add_config_value("needs_title_from_content", False, "html", types=[bool]) app.add_config_value("needs_diagram_template", DEFAULT_DIAGRAM_TEMPLATE, "html") app.add_config_value("needs_functions", [], "html", types=[list]) app.add_config_value("needs_global_options", {}, "html", types=[dict]) app.add_config_value("needs_duration_option", "duration", "html") app.add_config_value("needs_completion_option", "completion", "html") # If given, only the defined status are allowed. # Values needed for each status: # * name # * description # Example: [{"name": "open", "description": "open status"}, {...}, {...}] app.add_config_value('needs_statuses', [], 'html') # If given, only the defined tags are allowed. # Values needed for each tag: # * name # * description # Example: [{"name": "new", "description": "new needs"}, {...}, {...}] app.add_config_value("needs_tags", False, "html", types=[bool]) # Path of css file, which shall be used for need style app.add_config_value('needs_css', "modern.css", 'html') # Prefix for need_part output in tables app.add_config_value('needs_part_prefix', u'\u2192\u00a0', 'html') # List of additional links, which can be used by setting related option # Values needed for each new link: # * name (will also be the option name) # * incoming # * copy_link (copy to common links data. Default: True) # * color (used for needflow. Default: #000000) # Example: [{"name": "blocks, "incoming": "is blocked by", "copy_link": True, "color": "#ffcc00"}] app.add_config_value('needs_extra_links', [], 'html') app.add_config_value('needs_flow_show_links', False, 'html') app.add_config_value('needs_flow_link_types', ["links"], 'html') app.add_config_value('needs_warnings', {}, 'html') app.add_config_value('needs_layouts', {}, 'html') app.add_config_value('needs_default_layout', 'clean', 'html') app.add_config_value('needs_default_style', None, 'html') app.add_config_value('needs_flow_configs', {}, 'html') app.add_config_value('needs_template_folder', 'needs_templates/', 'html') app.add_config_value("needs_services", {}, "html") app.add_config_value("needs_service_all_data", False, "html", types=[bool]) app.add_config_value("needs_debug_no_external_calls", False, "html", types=[bool]) # Define nodes app.add_node(Need, html=(html_visit, html_depart), latex=(latex_visit, latex_depart)) app.add_node(Needfilter, ) app.add_node(Needimport) app.add_node(Needlist) app.add_node(Needtable) app.add_node(Needflow) app.add_node(Needpie) app.add_node(Needsequence) app.add_node(Needgantt) app.add_node(Needextract) app.add_node(Needservice) app.add_node(NeedPart, html=(visitor_dummy, visitor_dummy), latex=(visitor_dummy, visitor_dummy)) ######################################################################## # DIRECTIVES ######################################################################## # Define directives app.add_directive('needfilter', NeedfilterDirective) app.add_directive('needlist', NeedlistDirective) app.add_directive('needtable', NeedtableDirective) app.add_directive('needflow', NeedflowDirective) app.add_directive('needpie', NeedpieDirective) app.add_directive('needsequence', NeedsequenceDirective) app.add_directive('needgantt', NeedganttDirective) app.add_directive('needimport', NeedimportDirective) app.add_directive('needextract', NeedextractDirective) app.add_directive('needservice', NeedserviceDirective) ######################################################################## # ROLES ######################################################################## # Provides :need:`ABC_123` for inline links. app.add_role( 'need', XRefRole(nodeclass=NeedRef, innernodeclass=nodes.emphasis, warn_dangling=True)) app.add_role( 'need_incoming', XRefRole(nodeclass=NeedIncoming, innernodeclass=nodes.emphasis, warn_dangling=True)) app.add_role( 'need_outgoing', XRefRole(nodeclass=NeedOutgoing, innernodeclass=nodes.emphasis, warn_dangling=True)) app.add_role( 'need_part', XRefRole(nodeclass=NeedPart, innernodeclass=nodes.inline, warn_dangling=True)) # Shortcut for need_part app.add_role( 'np', XRefRole(nodeclass=NeedPart, innernodeclass=nodes.inline, warn_dangling=True)) app.add_role( 'need_count', XRefRole(nodeclass=NeedCount, innernodeclass=nodes.inline, warn_dangling=True)) ######################################################################## # EVENTS ######################################################################## # Make connections to events app.connect('env-purge-doc', purge_needs) app.connect('config-inited', load_config) app.connect('env-before-read-docs', prepare_env) # app.connect('env-before-read-docs', load_config) app.connect('config-inited', check_configuration) # There is also the event doctree-read. # But it looks like in this event no references are already solved, which # makes trouble in our code. # However, some sphinx-internal actions (like image collection) are already called during # doctree-read. So manipulating the doctree may result in conflicts, as e.g. images get not # registered for sphinx. So some sphinx-internal tasks/functions may be called by hand again... # See also https://github.com/sphinx-doc/sphinx/issues/7054#issuecomment-578019701 for an example app.connect('doctree-resolved', add_sections) app.connect('doctree-resolved', process_need_nodes) app.connect('doctree-resolved', process_needextract) app.connect('doctree-resolved', process_needfilters) app.connect('doctree-resolved', process_needlist) app.connect('doctree-resolved', process_needtables) app.connect('doctree-resolved', process_needflow) app.connect('doctree-resolved', process_needpie) app.connect('doctree-resolved', process_needsequence) app.connect('doctree-resolved', process_needgantt) app.connect('doctree-resolved', process_need_part) app.connect('doctree-resolved', process_need_ref) app.connect('doctree-resolved', process_need_incoming) app.connect('doctree-resolved', process_need_outgoing) app.connect('doctree-resolved', process_need_count) app.connect('build-finished', process_warnings) app.connect('env-updated', install_datatables_static_files) # Called during consistency check, which if after everything got read in. # app.connect('env-check-consistency', process_warnings) # Call this after all JS files, which perform DOM manipulation, have been called. # Otherwise newly added dom objects can not be collapsed app.connect('env-updated', install_collapse_static_files) # This should be called last, so that need-styles can override styles from used libraries app.connect('env-updated', install_styles_static_files) return { 'version': VERSION, 'parallel_read_safe': False, # Must be False, otherwise IDs are not found exceptions are raised. 'parallel_write_safe': True }