You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pwndbg/scripts/_docs/extract_configuration_docs.py

112 lines
3.6 KiB
Python

#!/usr/bin/env python
"""
If the PWNDBG_DOCGEN_VERIFY environment variable
is set, then : Exit with non-zero exit status if the docs/configuration/ files
aren't up to date with the sources. Don't modify anything.
If it isn't, this fixes up the docs/configuration/ files to be up
to date with the information from the sources. Except docs/configuration/index.md
which is hand-written.
"""
from __future__ import annotations
import json
from dataclasses import asdict
from typing import Dict
import pwndbg
from pwndbg.lib.config import HELP_DEFAULT_PREFIX
from pwndbg.lib.config import HELP_VALID_VALUES_PREFIX
from pwndbg.lib.config import Parameter
from scripts._docs.configuration_docs_common import ExtractedParam
from scripts._docs.configuration_docs_common import extracted_filename
from scripts._docs.gen_docs_generic import get_debugger
def extract_params() -> Dict[str, list[Parameter]]:
"""
Returns a dictionary that maps a scope name to a list of Parameter's
in that scope.
"""
scope_dict: Dict[str, list[Parameter]] = {}
parameters = pwndbg.config.params
# Could use pwndbg.config.get_params() here but
# we want to catch all scopes.
for param in parameters.values():
scope_name = param.scope.name
if scope_name not in scope_dict:
scope_dict[scope_name] = []
scope_dict[scope_name].append(param)
# Sort the parameters by name.
for scope in scope_dict:
scope_dict[scope].sort(key=lambda p: p.attr_name())
assert len(scope_dict) == len(pwndbg.lib.config.Scope) and (
"The amount of detected scopes "
"does not match the number of scopes defined in the source."
)
return scope_dict
def distill_sources(scoped_params: Dict[str, list[Parameter]]) -> Dict[str, list[ExtractedParam]]:
result: Dict[str, list[ExtractedParam]] = {}
for scope, params in scoped_params.items():
result[scope] = []
for param in params:
set_show_doc = param.set_show_doc
# Uppercase first letter and add dot to make it look like a sentence.
set_show_doc = set_show_doc[0].upper() + set_show_doc[1:] + "."
assert not param.help_docstring or (
param.help_docstring.count(HELP_DEFAULT_PREFIX) == 1
and "The configuration generator expects to find the string "
f"'{HELP_DEFAULT_PREFIX}' exactly once in order to perform proper bolding."
)
assert (
param.help_docstring.count(HELP_VALID_VALUES_PREFIX) <= 1
and "The configuration generator expects to find the string "
f"'{HELP_VALID_VALUES_PREFIX}' exactly once in order to perform proper bolding."
)
help_docstring = param.help_docstring.replace(
HELP_DEFAULT_PREFIX, f"**{HELP_DEFAULT_PREFIX}**"
)
help_docstring = help_docstring.replace(
HELP_VALID_VALUES_PREFIX, f"**{HELP_VALID_VALUES_PREFIX}**"
)
result[scope].append(ExtractedParam(param.name, set_show_doc, help_docstring))
return result
def main():
print("\n== Extracting Configuration ==")
debugger = get_debugger()
scoped_params = extract_params()
extracted = distill_sources(scoped_params)
result = {}
for scope, params in extracted.items():
result[scope] = [asdict(x) for x in params]
# Write to file.
out_path = extracted_filename(debugger)
with open(out_path, "w") as file:
json.dump(result, file)
print("== Finished Extracting Configuration ==")
# Not checking __name__ due to lldb.
main()