Exemple #1
0
def directive_factory(src_path):
    """Create directive instance out of docutils/sphinx process.

    Used in testing or command-line process
    """
    from sphinx_vcs_changelog.changelog import ChangelogWriter

    state_machine = statemachine.StateMachine(
        state_classes=[statemachine.StateWS], initial_state='StateWS')
    base_dir = path.dirname(src_path)

    _instance = object.__new__(ChangelogWriter)
    _instance.name = DIRECTIVE_CHANGELOG
    _instance.arguments = []
    _instance.options = {}
    _instance.content = ""
    _instance.lineno = 123
    _instance.content_offset = 123
    _instance.block_text = ""
    _instance.state = state_machine.get_state()
    _instance.state_machine = state_machine

    document = new_document(src_path)
    document.settings.update({'env': Values(defaults={'srcdir': base_dir})},
                             frontend.OptionParser())
    setattr(_instance.state, 'document', document)
    _instance.prepare()
    return _instance
def process_mathdef_nodes(app, doctree, fromdocname):
    """
    process_mathdef_nodes
    """
    if not app.config['mathdef_include_mathsext']:
        for node in doctree.traverse(mathdef_node):
            node.parent.remove(node)

    # Replace all mathdeflist nodes with a list of the collected mathsext.
    # Augment each mathdef with a backlink to the original location.
    env = app.builder.env
    if hasattr(env, "settings") and hasattr(env.settings, "language_code"):
        lang = env.settings.language_code
    else:
        lang = "en"

    orig_entry = TITLES[lang]["original entry"]
    mathmes = TITLES[lang]["mathmes"]

    if not hasattr(env, 'mathdef_all_mathsext'):
        env.mathdef_all_mathsext = []

    for ilist, node in enumerate(doctree.traverse(mathdeflist)):
        if 'ids' in node:
            node['ids'] = []
        if not app.config['mathdef_include_mathsext']:
            node.replace_self([])
            continue

        nbmath = 0
        content = []
        mathtag = node["mathtag"]
        add_contents = node["mathcontents"]
        mathdocname = node["docname"]

        if add_contents:
            bullets = nodes.enumerated_list()
            content.append(bullets)

        double_list = [(info.get('mathtitle', ''), info)
                       for info in env.mathdef_all_mathsext]
        double_list.sort(key=lambda x: x[:1])
        for n, mathdef_info_ in enumerate(double_list):
            mathdef_info = mathdef_info_[1]
            if mathdef_info["mathtag"] != mathtag:
                continue

            nbmath += 1
            para = nodes.paragraph(classes=['mathdef-source'])
            if app.config['mathdef_link_only']:
                description = _('<<%s>>' % orig_entry)
            else:
                description = (
                    _(mathmes) %
                    (orig_entry, os.path.split(mathdef_info['source'])[-1],
                     mathdef_info['lineno'])
                )
            desc1 = description[:description.find('<<')]
            desc2 = description[description.find('>>') + 2:]
            para += nodes.Text(desc1, desc1)

            # Create a reference
            newnode = nodes.reference('', '', internal=True)
            innernode = nodes.emphasis('', _(orig_entry))
            try:
                newnode['refuri'] = app.builder.get_relative_uri(
                    fromdocname, mathdef_info['docname'])
                try:
                    newnode['refuri'] += '#' + mathdef_info['target']['refid']
                except Exception as e:  # pragma: no cover
                    raise KeyError("refid in not present in '{0}'".format(
                        mathdef_info['target'])) from e
            except NoUri:  # pragma: no cover
                # ignore if no URI can be determined, e.g. for LaTeX output
                pass
            newnode.append(innernode)
            para += newnode
            para += nodes.Text(desc2, desc2)

            # (Recursively) resolve references in the mathdef content
            mathdef_entry = mathdef_info['mathdef']
            idss = ["index-mathdef-%d-%d" % (ilist, n)]
            # Insert into the mathreflist
            if add_contents:
                title = mathdef_info['mathtitle']
                item = nodes.list_item()
                p = nodes.paragraph()
                item += p
                newnode = nodes.reference('', '', internal=True)
                innernode = nodes.paragraph(text=title)
                try:
                    newnode['refuri'] = app.builder.get_relative_uri(
                        fromdocname, mathdocname)
                    newnode['refuri'] += '#' + idss[0]
                except NoUri:  # pragma: no cover
                    # ignore if no URI can be determined, e.g. for LaTeX output
                    pass
                newnode.append(innernode)
                p += newnode
                bullets += item

            mathdef_entry["ids"] = idss

            if not hasattr(mathdef_entry, "settings"):
                mathdef_entry.settings = Values()
                mathdef_entry.settings.env = env
            # If an exception happens here, see blog 2017-05-21 from the
            # documentation.
            env.resolve_references(mathdef_entry, mathdef_info['docname'],
                                   app.builder)

            # Insert into the mathdeflist
            content.append(mathdef_entry)
            content.append(para)

        node.replace_self(content)
def process_todoext_nodes(app, doctree, fromdocname):
    """
    process_todoext_nodes
    """
    if not app.config['todoext_include_todosext']:
        for node in doctree.traverse(todoext_node):
            node.parent.remove(node)

    # Replace all todoextlist nodes with a list of the collected todosext.
    # Augment each todoext with a backlink to the original location.
    env = app.builder.env
    if hasattr(env, "settings") and hasattr(env.settings, "language_code"):
        lang = env.settings.language_code
    else:
        lang = "en"

    orig_entry = TITLES[lang]["original entry"]
    todomes = TITLES[lang]["todomes"]
    allowed_tsort = {'date', 'prio', 'title', 'release', 'source'}

    if not hasattr(env, 'todoext_all_todosext'):
        env.todoext_all_todosext = []

    for ilist, node in enumerate(doctree.traverse(todoextlist)):
        if 'ids' in node:
            node['ids'] = []
        if not app.config['todoext_include_todosext']:
            node.replace_self([])
            continue

        nbtodo = 0
        fcost = 0
        content = []
        todotag = node["todotag"]
        tsort = node["todosort"]
        if tsort == '':
            tsort = 'source'
        if tsort not in allowed_tsort:
            raise ValueError("option sort must in {0}, '{1}' is not".format(
                allowed_tsort, tsort))

        double_list = [(info.get('todo%s' % tsort,
                                 ''), info.get('todotitle', ''), info)
                       for info in env.todoext_all_todosext]
        double_list.sort(key=lambda x: x[:2])
        for n, todoext_info_ in enumerate(double_list):
            todoext_info = todoext_info_[2]
            if todoext_info["todotag"] != todotag:
                continue

            nbtodo += 1
            fcost += todoext_info.get("todocost", 0.0)

            para = nodes.paragraph(classes=['todoext-source'])
            if app.config['todoext_link_only']:
                description = locale_('<<%s>>' % orig_entry)
            else:
                description = (
                    locale_(todomes) %
                    (orig_entry, os.path.split(
                        todoext_info['source'])[-1], todoext_info['lineno']))
            desc1 = description[:description.find('<<')]
            desc2 = description[description.find('>>') + 2:]
            para += nodes.Text(desc1, desc1)

            # Create a reference
            newnode = nodes.reference('', '', internal=True)
            innernode = nodes.emphasis(locale_(orig_entry),
                                       locale_(orig_entry))
            try:
                newnode['refuri'] = app.builder.get_relative_uri(
                    fromdocname, todoext_info['docname'])
                try:
                    newnode['refuri'] += '#' + todoext_info['target']['refid']
                except Exception as e:
                    raise KeyError("refid in not present in '{0}'".format(
                        todoext_info['target'])) from e
            except NoUri:
                # ignore if no URI can be determined, e.g. for LaTeX output
                pass
            newnode.append(innernode)
            para += newnode
            para += nodes.Text(desc2, desc2)

            # (Recursively) resolve references in the todoext content
            todoext_entry = todoext_info.get('todoext_copy', None)
            if todoext_entry is None:
                todoext_entry = todoext_info['todoext']
            todoext_entry["ids"] = ["index-todoext-%d-%d" % (ilist, n)]
            # it apparently requires an attributes ids

            if not hasattr(todoext_entry, "settings"):
                todoext_entry.settings = Values()
                todoext_entry.settings.env = env
            # If an exception happens here, see blog 2017-05-21 from the
            # documentation.
            env.resolve_references(todoext_entry, todoext_info['docname'],
                                   app.builder)

            # Insert into the todoextlist
            content.append(todoext_entry)
            content.append(para)

        if fcost > 0:
            cost = nodes.paragraph()
            lab = "{0} items, cost: {1}".format(nbtodo, fcost)
            cost += nodes.Text(lab)
            content.append(cost)
        else:
            cost = nodes.paragraph()
            lab = "{0} items".format(nbtodo)
            cost += nodes.Text(lab)
            content.append(cost)

        node.replace_self(content)
Exemple #4
0
def process_blocref_nodes_generic(app, doctree, fromdocname, class_name,
                                  entry_name, class_node, class_node_list):
    """
    process_blocref_nodes and other kinds of nodes,

    If the configuration file specifies a variable ``blocref_include_blocrefs`` equals to False,
    all nodes are removed.
    """
    # logging
    cont = info_blocref(app, doctree, fromdocname, class_name, entry_name,
                        class_node, class_node_list)
    if not cont:
        return

    # check this is something to process
    env = app.builder.env
    attr_name = '%s_all_%ss' % (class_name, class_name)
    if not hasattr(env, attr_name):
        setattr(env, attr_name, [])
    bloc_list_env = getattr(env, attr_name)
    if len(bloc_list_env) == 0:
        return

    # content
    incconf = '%s_include_%ss' % (class_name, class_name)
    if app.config[incconf] and not app.config[incconf]:
        for node in doctree.traverse(class_node):
            node.parent.remove(node)

    # Replace all blocreflist nodes with a list of the collected blocrefs.
    # Augment each blocref with a backlink to the original location.
    if hasattr(env, "settings"):
        settings = env.settings
        if hasattr(settings, "language_code"):
            lang = env.settings.language_code
        else:
            lang = "en"
    else:
        settings = None
        lang = "en"

    orig_entry = TITLES[lang]["original entry"]
    brefmes = TITLES[lang][entry_name]

    for ilist, node in enumerate(doctree.traverse(class_node_list)):
        if 'ids' in node:
            node['ids'] = []
        if not app.config[incconf]:
            node.replace_self([])
            continue

        nbbref = 0
        content = []
        breftag = node["breftag"]
        brefsort = node["brefsort"]
        add_contents = node["brefcontents"]
        brefdocname = node["docname"]

        if add_contents:
            bullets = nodes.enumerated_list()
            content.append(bullets)

        # sorting
        if brefsort == 'title':
            double_list = [(info.get('breftitle', ''), info)
                           for info in bloc_list_env
                           if info['breftag'] == breftag]
            double_list.sort(key=lambda x: x[:1])
        elif brefsort == 'file':
            double_list = [((info.get('breffile',
                                      ''), info.get('brefline', '')), info)
                           for info in bloc_list_env
                           if info['breftag'] == breftag]
            double_list.sort(key=lambda x: x[:1])
        elif brefsort == 'number':
            double_list = [(info.get('brefmid', ''), info)
                           for info in bloc_list_env
                           if info['breftag'] == breftag]
            double_list.sort(key=lambda x: x[:1])
        else:
            raise ValueError("sort option should be file, number, title")

        # printing
        for n, blocref_info_ in enumerate(double_list):
            blocref_info = blocref_info_[1]

            nbbref += 1

            para = nodes.paragraph(classes=['%s-source' % class_name])

            # Create a target?
            int_ids = [
                'index%s-%s' %
                (blocref_info['target']['refid'],
                 env.new_serialno(blocref_info['target']['refid']))
            ]
            int_targetnode = nodes.target(blocref_info['breftitle'],
                                          '',
                                          ids=int_ids)
            para += int_targetnode

            # rest of the content
            if app.config['%s_link_only' % class_name]:
                description = _('<<%s>>' % orig_entry)
            else:
                description = (
                    _(brefmes) %
                    (orig_entry, os.path.split(
                        blocref_info['source'])[-1], blocref_info['lineno']))
            desc1 = description[:description.find('<<')]
            desc2 = description[description.find('>>') + 2:]
            para += nodes.Text(desc1, desc1)

            # Create a reference
            newnode = nodes.reference('', '', internal=True)
            newnode['name'] = _(orig_entry)
            try:
                newnode['refuri'] = app.builder.get_relative_uri(
                    fromdocname, blocref_info['docname'])
                if blocref_info['target'] is None:
                    raise NoUri  # pragma: no cover
                try:
                    newnode['refuri'] += '#' + blocref_info['target']['refid']
                except Exception as e:  # pragma: no cover
                    raise KeyError("refid in not present in '{0}'".format(
                        blocref_info['target'])) from e
            except NoUri:  # pragma: no cover
                # ignore if no URI can be determined, e.g. for LaTeX output
                pass

            newnode.append(nodes.Text(newnode['name']))

            # para is duplicate of the content of the bloc
            para += newnode
            para += nodes.Text(desc2, desc2)

            blocref_entry = blocref_info['blocref']
            idss = ["index-%s-%d-%d" % (class_name, ilist, n)]

            # Inserts into the blocreflist
            # in the list of links at the beginning of the page.
            if add_contents:
                title = blocref_info['breftitle']
                item = nodes.list_item()
                p = nodes.paragraph()
                item += p
                newnode = nodes.reference('', title, internal=True)
                try:
                    newnode['refuri'] = app.builder.get_relative_uri(
                        fromdocname, brefdocname)
                    newnode['refuri'] += '#' + idss[0]
                except NoUri:  # pragma: no cover
                    # ignore if no URI can be determined, e.g. for LaTeX output
                    pass
                p += newnode
                bullets += item

            # Adds the content.
            blocref_entry["ids"] = idss
            if not hasattr(blocref_entry, "settings"):
                blocref_entry.settings = Values()
                blocref_entry.settings.env = env
            # If an exception happens here, see blog 2017-05-21 from the
            # documentation.
            env.resolve_references(blocref_entry, blocref_info['docname'],
                                   app.builder)
            content.append(blocref_entry)
            content.append(para)

        node.replace_self(content)
Exemple #5
0
class SceneScript:
    """Gives access to a Turberfield scene script (.rst) file.

    This class allows discovery and classification of scene files prior to loading
    them in memory.

    Once loaded, it allows entity selection based on the role definitions in the file.
    Casting a selection permits the script to be iterated as a sequence of dialogue items.

    """

    Folder = namedtuple(
        "Folder", ["pkg", "description", "metadata", "paths", "interludes"])

    settings = Values(defaults=dict(
        character_level_inline_markup=False,
        debug=False,
        error_encoding="utf-8",
        error_encoding_error_handler="backslashreplace",
        halt_level=4,
        auto_id_prefix="",
        id_prefix="",
        language_code="en",
        pep_references=1,
        report_level=2,
        rfc_references=1,
        strict_visitor=False,
        tab_width=4,
        warning_stream=sys.stderr,
        raw_enabled=True,
        file_insertion_enabled=True,
        input_encoding="utf-8",
        input_encoding_error_handler="replace",
        line_length_limit=float("inf"),
    ))

    docutils.parsers.rst.directives.register_directive("entity",
                                                       EntityDirective)

    docutils.parsers.rst.directives.register_directive("property",
                                                       PropertyDirective)

    docutils.parsers.rst.directives.register_directive("fx", FXDirective)

    docutils.parsers.rst.directives.register_directive("memory",
                                                       MemoryDirective)

    docutils.parsers.rst.directives.register_directive("condition",
                                                       ConditionDirective)

    @classmethod
    def scripts(cls, pkg, metadata, paths=[], **kwargs):
        """This class method is the preferred way to create SceneScript objects.

        :param str pkg: The dotted name of the package containing the scripts.
        :param metadata: A mapping or data object. This parameter permits searching among
            scripts against particular criteria. Its use is application specific.
        :param list(str) paths: A sequence of file paths to the scripts relative to the package.

        You can satisfy all parameter requirements by passing in a
        :py:class:`~turberfield.dialogue.model.SceneScript.Folder` object
        like this::

            SceneScript.scripts(**folder._asdict())

        The method generates a sequence of
        :py:class:`~turberfield.dialogue.model.SceneScript` objects.
        """
        log_manager = LogManager()
        log = log_manager.clone(log_manager.get_logger("main"),
                                "turberfield.dialogue.model.scenescript")

        for path in paths:
            try:
                fP = pkg_resources.resource_filename(pkg, path)
            except ImportError:
                log.warning("No package called {}".format(pkg))
            else:
                if not os.path.isfile(fP):
                    log.warning("No script file at {}".format(
                        os.path.join(*pkg.split(".") + [path])))
                else:
                    yield cls(fP, metadata)

    @staticmethod
    def read(text, name=None):
        """Read a block of text as a docutils document.

        :param str text: Scene script text.
        :param str name: An optional name for the document.
        :return: A document object.

        """
        doc = docutils.utils.new_document(name, SceneScript.settings)
        parser = docutils.parsers.rst.Parser()
        parser.parse(text, doc)
        return doc

    def __init__(self, fP, metadata=None, doc=None):
        self.log_manager = LogManager()
        self.log = self.log_manager.get_logger(
            "turberfield.dialogue.model.scenescript")
        self.fP = fP
        self.metadata = metadata
        self.doc = doc

    def __enter__(self):
        with open(self.fP, "r") as script:
            self.doc = self.read(script.read())
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        return False

    def select(self, personae, relative=False, roles=1):
        """Select a persona for each entity declared in the scene.

        :param personae: A sequence of Personae.
        :param bool relative: Affects imports from namespace packages.
            Used for testing only.
        :param int roles: The maximum number of roles allocated to each persona.
        :return: An OrderedDict of {Entity: Persona}.

        """
        def constrained(entity):
            return (len(entity["options"].get("types", [])) +
                    len(entity["options"].get("states", [])))

        rv = OrderedDict()
        performing = defaultdict(set)
        pool = list(personae)
        self.log.debug(pool, {"path": self.fP})
        entities = OrderedDict([("".join(entity.attributes["names"]), entity)
                                for entity in sorted(group_by_type(self.doc)[
                                    EntityDirective.Declaration],
                                                     key=constrained,
                                                     reverse=True)])
        for e in entities.values():
            types = tuple(
                filter(None, (e.string_import(t, relative)
                              for t in e["options"].get("types", []))))
            states = tuple(
                filter(
                    None,
                    (int(t) if t.isdigit() else e.string_import(t, relative)
                     for t in e["options"].get("states", []))))
            otherRoles = {i.lower() for i in e["options"].get("roles", [])}
            typ = types or object
            persona = next(
                (i for i in pool if isinstance(i, typ)
                 and getattr(i, "get_state", not states) and all(
                     str(i.get_state(type(s))).startswith(str(s))
                     for s in states) and
                 (performing[i].issubset(otherRoles) or not otherRoles)), None)
            rv[e] = persona
            performing[persona].update(set(e.attributes["names"]))

            if not otherRoles or list(rv.values()).count(persona) == roles:
                try:
                    pool.remove(persona)
                except ValueError:
                    self.log.debug(
                        "No persona for type {0} and states {1} with {2} {3}.".
                        format(typ, states, roles,
                               "role" if roles == 1 else "roles"),
                        {"path": self.fP})
        return rv

    def cast(self, mapping):
        """Allocate the scene script a cast of personae for each of its entities.

        :param mapping: A dictionary of {Entity, Persona}
        :return: The SceneScript object.

        """
        # See 'citation' method in
        # http://docutils.sourceforge.net/docutils/parsers/rst/states.py
        for c, p in mapping.items():
            self.doc.note_citation(c)
            self.doc.note_explicit_target(c, c)
            c.persona = p
            self.log.debug(
                "{0} to be played by {1}".format(c["names"][0].capitalize(),
                                                 p), {"path": self.fP})
        return self

    def run(self):
        """Parse the script file.

        :rtype: :py:class:`~turberfield.dialogue.model.Model`
        """
        model = Model(self.fP, self.doc)
        self.doc.walkabout(model)
        return model