File: examples.py

package info (click to toggle)
cvc5 1.3.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 87,260 kB
  • sloc: cpp: 383,850; java: 12,207; python: 12,090; sh: 5,679; ansic: 4,729; lisp: 763; perl: 208; makefile: 38
file content (137 lines) | stat: -rw-r--r-- 4,878 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
###############################################################################
# Top contributors (to current version):
#   Gereon Kremer
#
# This file is part of the cvc5 project.
#
# Copyright (c) 2009-2025 by the authors listed in the file AUTHORS
# in the top-level source directory and their institutional affiliations.
# All rights reserved.  See the file COPYING in the top-level source
# directory for licensing information.
# #############################################################################
#
# Sphinx extension, implements directive 'api-examples'.
##

import os
import re

from docutils import nodes
from docutils.statemachine import StringList
from sphinx.util import logging
from sphinx.util.docutils import SphinxDirective


class APIExamples(SphinxDirective):
    """Add directive `api-examples` to be used as follows:

        .. api-examples::
            file1
            file2

        The arguments should be proper filenames to source files.
        This directives tries to detect the language from the file extension
        and supports the file extensions specified in `examples_types`.
        Additionally, `examples_file_patterns` allows to specify file name
        patterns that allow using files from fixed directories more easily, and
        to add proper download links.

        examples_types:
            '<regex>': {
                'title': '<tab title>',
                'lang': '<language identifier for syntax highlighting>',
                'group': '<group identifier to detect missing examples>',
            }

        examples_file_patterns:
            '<regex>': { # match groups are used to format the strings below
                'local': '<pseudo-absolute path to local file>',
                'url': '<url to download this file>', # optional
                'urlname': '<text for the download link>',
            }
    """

    # The "arguments" are actually the content of the directive
    has_content = True
    option_spec = {
        'skip': lambda x: [s.strip() for s in x.split(',')],
    }

    logger = logging.getLogger(__name__)

    srcdir = None

    def run(self):
        self.state.document.settings.env.note_dependency(__file__)
        # collect everything in a list of strings
        content = ['.. tabs::', '']

        remaining = set([t['group'] for t in self.env.config.examples_types.values()])
        location = '{}:{}'.format(*self.get_source_info())

        # remove skipped groups
        skipped = set(self.options.get('skip', []))
        remaining.difference_update(skipped)

        for file in self.content:
            # detect file extension
            lang = None
            title = None
            for pattern,data in self.env.config.examples_types.items():
                if re.search(pattern, file) != None:
                    lang = data['lang']
                    title = data['title']
                    remaining.discard(data['group'])
                    break
            if lang == None:
                self.logger.warning(
                    f'file type of {location} could not be detected')
                title = os.path.splitext(file)[1]
                lang = title

            url = None
            urlname = None
            for k, v in self.env.config.examples_file_patterns.items():
                m = re.match(k, file)
                if m is not None:
                    file = v['local'].format(*m.groups())
                    if 'url' in v:
                        url = v['url'].format(*m.groups())
                        urlname = v['urlname'].format(*m.groups())
                    break

            # generate tabs
            content.append(f'    .. tab:: {title}')
            content.append(f'')

            if url is not None:
                # we can provide a download link
                content.append(f'        .. rst-class:: fa fa-download icon-margin')
                content.append(f'        ')
                content.append(f'        `{urlname} <{url}>`_')
                content.append(f'')

            content.append(f'        .. literalinclude:: {file}')
            content.append(f'            :language: {lang}')
            content.append(f'            :linenos:')

        for r in remaining:
            self.logger.warning(f'{location} has no {r} example!')

        # parse the string list
        node = nodes.Element()
        self.state.nested_parse(StringList(content), 0, node)
        return node.children


def setup(app):
    APIExamples.srcdir = app.srcdir
    app.setup_extension('sphinx_tabs.tabs')
    app.add_config_value('examples_types', {}, 'env')
    app.add_config_value('examples_file_patterns', {}, 'env')
    app.add_directive("api-examples", APIExamples)
    return {
        'version': '0.1',
        'parallel_read_safe': True,
        'parallel_write_safe': True,
    }