Skip to content

Doctools

Provides tools to assist with generating documentation for SSVC decision points.

Writes the following files for each decision point: - a json example that can be used in the decision point documentation

Examples

To generate the documentation for the decision points, use the following command:

python -m ssvc.doctools --overwrite --jsondir ./tmp/json_out`

To regenerate the existing docs, use the following command:

python -m ssvc.doctools --overwrite --jsondir data/json

EnsureDirExists

A runtime context that ensures that a directory exists or creates it otherwise.

Example:

with EnsureDirExists(dir):
    assert os.path.exists(dir)
Source code in src/ssvc/doctools.py
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
class EnsureDirExists:
    """
    A runtime context that ensures that a directory exists or creates it otherwise.

    Example:

        with EnsureDirExists(dir):
            assert os.path.exists(dir)
    """

    def __init__(self, dir: str):
        """
        Create a new EnsureDirExists context.

        Args:
            dir (str): The directory to ensure exists.

        Returns:
            EnsureDirExists: The new EnsureDirExists context.
        """
        self.dir = dir

    def __enter__(self):
        os.makedirs(self.dir, exist_ok=True)

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

__init__(dir)

Create a new EnsureDirExists context.

Parameters:

Name Type Description Default
dir str

The directory to ensure exists.

required

Returns:

Name Type Description
EnsureDirExists

The new EnsureDirExists context.

Source code in src/ssvc/doctools.py
117
118
119
120
121
122
123
124
125
126
127
def __init__(self, dir: str):
    """
    Create a new EnsureDirExists context.

    Args:
        dir (str): The directory to ensure exists.

    Returns:
        EnsureDirExists: The new EnsureDirExists context.
    """
    self.dir = dir

dump_decision_point(jsondir, dp, overwrite)

Generate the markdown table, json example, and markdown table file for a decision point.

Parameters:

Name Type Description Default
jsondir str

The directory to write the json example to.

required
outdir str

The directory to write the markdown table file to.

required
dp SsvcDecisionPoint

The decision point to generate documentation for.

required
overwrite bool

Whether to overwrite existing files.

required

Returns:

Name Type Description
dict None

A dictionary with the following keys: - include_file: The path to the markdown table file. - symlink: The path to the symlink that points to the markdown table file. - json_file: The path to the json example file.

Source code in src/ssvc/doctools.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def dump_decision_point(
    jsondir: str, dp: SsvcDecisionPoint, overwrite: bool
) -> None:
    """
    Generate the markdown table, json example, and markdown table file for a decision point.

    Args:
        jsondir (str): The directory to write the json example to.
        outdir (str): The directory to write the markdown table file to.
        dp (SsvcDecisionPoint): The decision point to generate documentation for.
        overwrite (bool): Whether to overwrite existing files.

    Returns:
        dict: A dictionary with the following keys:
            - include_file: The path to the markdown table file.
            - symlink: The path to the symlink that points to the markdown table file.
            - json_file: The path to the json example file.
    """
    # make dp.name safe for use in a filename
    basename = (
        _filename_friendly(dp.name) + f"_{_filename_friendly(dp.version)}"
    )
    # - generate json example
    dump_json(basename, dp, jsondir, overwrite)

dump_json(basename, dp, jsondir, overwrite)

Generate the json example for a decision point.

Parameters:

Name Type Description Default
basename str

The basename of the json example file.

required
dp SsvcDecisionPoint

The decision point to generate documentation for.

required
jsondir str

The directory to write the json example to.

required
overwrite bool

Whether to overwrite existing files.

required

Returns:

Name Type Description
str str

The path to the json example file.

Source code in src/ssvc/doctools.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
def dump_json(
    basename: str, dp: DecisionPoint, jsondir: str, overwrite: bool
) -> str:
    """
    Generate the json example for a decision point.

    Args:
        basename (str): The basename of the json example file.
        dp (SsvcDecisionPoint): The decision point to generate documentation for.
        jsondir (str): The directory to write the json example to.
        overwrite (bool): Whether to overwrite existing files.

    Returns:
        str: The path to the json example file.
    """
    # if namespace is ssvc, it goes in jsondir
    filename = f"{basename}.json"
    parts = [
        jsondir,
    ]
    parts.append(_filename_friendly(dp.namespace))
    dirname = os.path.join(*parts)

    parts.append(filename)

    json_file = os.path.join(*parts)

    if overwrite:
        remove_if_exists(json_file)
    with EnsureDirExists(dirname):
        try:
            logger.info(f"Writing {json_file}")
            with open(json_file, "x") as f:
                f.write(dp.model_dump_json(indent=2))
                f.write("\n")  # newline at end of file
        except FileExistsError:
            logger.warning(
                f"File {json_file} already exists, use --overwrite to replace"
            )
    return str(json_file)

find_modules_to_import(directory='../decision_points', package='ssvc.decision_points')

Find all modules that contain decision points and import them.

This is necessary to ensure that all decision points are registered.

Source code in src/ssvc/doctools.py
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
def find_modules_to_import(
    directory: str = "../decision_points",
    package: str = "ssvc.decision_points",
) -> bool:
    """
    Find all modules that contain decision points and import them.

    This is necessary to ensure that all decision points are registered.
    """
    imported_modules = []
    for root, _, files in os.walk(os.path.abspath(directory)):
        for file in files:
            if file.endswith(".py") and not file.startswith("__"):
                # build the module name relative to the package
                relative_path = os.path.relpath(root, directory)
                module_name = os.path.join(relative_path, file[:-3]).replace(
                    os.sep, "."
                )

                full_module_name = f"{package}.{module_name}"
                # import the module
                try:
                    logger.info(f"Importing module {full_module_name}")
                    module = importlib.import_module(full_module_name)
                    imported_modules.append(module)
                except ImportError as e:
                    logger.error(f"Failed to import {full_module_name}: {e}")
    return imported_modules