コード例 #1
0
    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")
コード例 #2
0
 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": {}
     }
コード例 #3
0
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"] + "?",
        )
コード例 #4
0
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.')
コード例 #5
0
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,
    }
コード例 #6
0
 def __init__(self, *args, **kw):
     super().__init__(*args, **kw)
     self.log = get_logger(__name__)
コード例 #7
0
ファイル: base.py プロジェクト: swaldhoer/sphinxcontrib-needs
 def __init__(self, *args, **kwargs):
     self.log = get_logger(__name__)
コード例 #8
0
    def __init__(self, app):
        self.app = app

        self.log = get_logger(__name__)
        self.services = {}
コード例 #9
0
ファイル: need.py プロジェクト: useblocks/sphinxcontrib-needs
 def __init__(self, *args, **kw):
     super().__init__(*args, **kw)
     self.log = get_logger(__name__)
     self.full_title = self._get_full_title()
コード例 #10
0
 def __init__(self, *args, **kw):
     super(NeedserviceDirective, self).__init__(*args, **kw)
     self.log = get_logger(__name__)
コード例 #11
0
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
    }