<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://mscneuro.neuro.uni-bremen.de/index.php?action=history&amp;feed=atom&amp;title=PyBind11_Stub-Generation</id>
	<title>PyBind11 Stub-Generation - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://mscneuro.neuro.uni-bremen.de/index.php?action=history&amp;feed=atom&amp;title=PyBind11_Stub-Generation"/>
	<link rel="alternate" type="text/html" href="https://mscneuro.neuro.uni-bremen.de/index.php?title=PyBind11_Stub-Generation&amp;action=history"/>
	<updated>2026-07-06T04:16:15Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://mscneuro.neuro.uni-bremen.de/index.php?title=PyBind11_Stub-Generation&amp;diff=531&amp;oldid=prev</id>
		<title>Davrot: Created page with &quot;Questions to [mailto:davrot@uni-bremen.de David Rotermund]  == ModuleStubsGenerator == If you editor / syntax highlighting complains then you might need module stubs. This is the way how I automatically generate my pybind11-stubs&lt;syntaxhighlight lang=&quot;shell&quot;&gt;pip install pybind11-stubgen&lt;/syntaxhighlight&gt;&lt;syntaxhighlight lang=&quot;python&quot;&gt;# Based on # https://github.com/sizmailov/pybind11-stubgen/blob/master/pybind11_stubgen/__init__.py  from __future__ import annotations  im...&quot;</title>
		<link rel="alternate" type="text/html" href="https://mscneuro.neuro.uni-bremen.de/index.php?title=PyBind11_Stub-Generation&amp;diff=531&amp;oldid=prev"/>
		<updated>2025-10-21T16:05:39Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;Questions to [mailto:davrot@uni-bremen.de David Rotermund]  == ModuleStubsGenerator == If you editor / syntax highlighting complains then you might need module stubs. This is the way how I automatically generate my pybind11-stubs&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;pip install pybind11-stubgen&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;# Based on # https://github.com/sizmailov/pybind11-stubgen/blob/master/pybind11_stubgen/__init__.py  from __future__ import annotations  im...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;Questions to [mailto:davrot@uni-bremen.de David Rotermund]&lt;br /&gt;
&lt;br /&gt;
== ModuleStubsGenerator ==&lt;br /&gt;
If you editor / syntax highlighting complains then you might need module stubs. This is the way how I automatically generate my pybind11-stubs&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;pip install pybind11-stubgen&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;# Based on&lt;br /&gt;
# https://github.com/sizmailov/pybind11-stubgen/blob/master/pybind11_stubgen/__init__.py&lt;br /&gt;
&lt;br /&gt;
from __future__ import annotations&lt;br /&gt;
&lt;br /&gt;
import importlib&lt;br /&gt;
import logging&lt;br /&gt;
import re&lt;br /&gt;
from argparse import ArgumentParser, Namespace&lt;br /&gt;
from pathlib import Path&lt;br /&gt;
import glob&lt;br /&gt;
&lt;br /&gt;
from pybind11_stubgen.parser.interface import IParser&lt;br /&gt;
from pybind11_stubgen.parser.mixins.error_handlers import (&lt;br /&gt;
    IgnoreAllErrors,&lt;br /&gt;
    IgnoreInvalidExpressionErrors,&lt;br /&gt;
    IgnoreInvalidIdentifierErrors,&lt;br /&gt;
    IgnoreUnresolvedNameErrors,&lt;br /&gt;
    LogErrors,&lt;br /&gt;
    LoggerData,&lt;br /&gt;
    SuggestCxxSignatureFix,&lt;br /&gt;
    TerminateOnFatalErrors,&lt;br /&gt;
)&lt;br /&gt;
from pybind11_stubgen.parser.mixins.filter import (&lt;br /&gt;
    FilterClassMembers,&lt;br /&gt;
    FilterInvalidIdentifiers,&lt;br /&gt;
    FilterPybind11ViewClasses,&lt;br /&gt;
    FilterPybindInternals,&lt;br /&gt;
    FilterTypingModuleAttributes,&lt;br /&gt;
)&lt;br /&gt;
from pybind11_stubgen.parser.mixins.fix import (&lt;br /&gt;
    FixBuiltinTypes,&lt;br /&gt;
    FixCurrentModulePrefixInTypeNames,&lt;br /&gt;
    FixMissing__all__Attribute,&lt;br /&gt;
    FixMissing__future__AnnotationsImport,&lt;br /&gt;
    FixMissingEnumMembersAnnotation,&lt;br /&gt;
    FixMissingFixedSizeImport,&lt;br /&gt;
    FixMissingImports,&lt;br /&gt;
    FixMissingNoneHashFieldAnnotation,&lt;br /&gt;
    FixNumpyArrayDimAnnotation,&lt;br /&gt;
    FixNumpyArrayDimTypeVar,&lt;br /&gt;
    FixNumpyArrayFlags,&lt;br /&gt;
    FixNumpyArrayRemoveParameters,&lt;br /&gt;
    FixNumpyDtype,&lt;br /&gt;
    FixPEP585CollectionNames,&lt;br /&gt;
    FixPybind11EnumStrDoc,&lt;br /&gt;
    FixRedundantBuiltinsAnnotation,&lt;br /&gt;
    FixRedundantMethodsFromBuiltinObject,&lt;br /&gt;
    FixScipyTypeArguments,&lt;br /&gt;
    FixTypingTypeNames,&lt;br /&gt;
    FixValueReprRandomAddress,&lt;br /&gt;
    OverridePrintSafeValues,&lt;br /&gt;
    RemoveSelfAnnotation,&lt;br /&gt;
    ReplaceReadWritePropertyWithField,&lt;br /&gt;
    RewritePybind11EnumValueRepr,&lt;br /&gt;
)&lt;br /&gt;
from pybind11_stubgen.parser.mixins.parse import (&lt;br /&gt;
    BaseParser,&lt;br /&gt;
    ExtractSignaturesFromPybind11Docstrings,&lt;br /&gt;
    ParserDispatchMixin,&lt;br /&gt;
)&lt;br /&gt;
from pybind11_stubgen.printer import Printer&lt;br /&gt;
from pybind11_stubgen.structs import QualifiedName&lt;br /&gt;
from pybind11_stubgen.writer import Writer&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CLIArgs(Namespace):&lt;br /&gt;
    output_dir: str&lt;br /&gt;
    root_suffix: str&lt;br /&gt;
    ignore_invalid_expressions: re.Pattern | None&lt;br /&gt;
    ignore_invalid_identifiers: re.Pattern | None&lt;br /&gt;
    ignore_unresolved_names: re.Pattern | None&lt;br /&gt;
    ignore_all_errors: bool&lt;br /&gt;
    enum_class_locations: list[tuple[re.Pattern, str]]&lt;br /&gt;
    numpy_array_wrap_with_annotated: bool&lt;br /&gt;
    numpy_array_use_type_var: bool&lt;br /&gt;
    numpy_array_remove_parameters: bool&lt;br /&gt;
    print_invalid_expressions_as_is: bool&lt;br /&gt;
    print_safe_value_reprs: re.Pattern | None&lt;br /&gt;
    exit_code: bool&lt;br /&gt;
    dry_run: bool&lt;br /&gt;
    stub_extension: str&lt;br /&gt;
    module_name: str&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def arg_parser() -&amp;gt; ArgumentParser:&lt;br /&gt;
    def regex(pattern_str: str) -&amp;gt; re.Pattern:&lt;br /&gt;
        try:&lt;br /&gt;
            return re.compile(pattern_str)&lt;br /&gt;
        except re.error as e:&lt;br /&gt;
            raise ValueError(f&amp;quot;Invalid REGEX pattern: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    def regex_colon_path(regex_path: str) -&amp;gt; tuple[re.Pattern, str]:&lt;br /&gt;
        pattern_str, path = regex_path.rsplit(&amp;quot;:&amp;quot;, maxsplit=1)&lt;br /&gt;
        if any(not part.isidentifier() for part in path.split(&amp;quot;.&amp;quot;)):&lt;br /&gt;
            raise ValueError(f&amp;quot;Invalid PATH: {path}&amp;quot;)&lt;br /&gt;
        return regex(pattern_str), path&lt;br /&gt;
&lt;br /&gt;
    parser = ArgumentParser(&lt;br /&gt;
        prog=&amp;quot;pybind11-stubgen&amp;quot;, description=&amp;quot;Generates stubs for specified modules&amp;quot;&lt;br /&gt;
    )&lt;br /&gt;
    parser.add_argument(&lt;br /&gt;
        &amp;quot;-o&amp;quot;,&lt;br /&gt;
        &amp;quot;--output-dir&amp;quot;,&lt;br /&gt;
        help=&amp;quot;The root directory for output stubs&amp;quot;,&lt;br /&gt;
        default=&amp;quot;.&amp;quot;,&lt;br /&gt;
    )&lt;br /&gt;
    parser.add_argument(&lt;br /&gt;
        &amp;quot;--root-suffix&amp;quot;,&lt;br /&gt;
        type=str,&lt;br /&gt;
        default=None,&lt;br /&gt;
        dest=&amp;quot;root_suffix&amp;quot;,&lt;br /&gt;
        help=&amp;quot;Top-level module directory suffix&amp;quot;,&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    parser.add_argument(&lt;br /&gt;
        &amp;quot;--ignore-invalid-expressions&amp;quot;,&lt;br /&gt;
        metavar=&amp;quot;REGEX&amp;quot;,&lt;br /&gt;
        default=None,&lt;br /&gt;
        type=regex,&lt;br /&gt;
        help=&amp;quot;Ignore invalid expressions matching REGEX&amp;quot;,&lt;br /&gt;
    )&lt;br /&gt;
    parser.add_argument(&lt;br /&gt;
        &amp;quot;--ignore-invalid-identifiers&amp;quot;,&lt;br /&gt;
        metavar=&amp;quot;REGEX&amp;quot;,&lt;br /&gt;
        default=None,&lt;br /&gt;
        type=regex,&lt;br /&gt;
        help=&amp;quot;Ignore invalid identifiers matching REGEX&amp;quot;,&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    parser.add_argument(&lt;br /&gt;
        &amp;quot;--ignore-unresolved-names&amp;quot;,&lt;br /&gt;
        metavar=&amp;quot;REGEX&amp;quot;,&lt;br /&gt;
        default=None,&lt;br /&gt;
        type=regex,&lt;br /&gt;
        help=&amp;quot;Ignore unresolved names matching REGEX&amp;quot;,&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    parser.add_argument(&lt;br /&gt;
        &amp;quot;--ignore-all-errors&amp;quot;,&lt;br /&gt;
        default=False,&lt;br /&gt;
        action=&amp;quot;store_true&amp;quot;,&lt;br /&gt;
        help=&amp;quot;Ignore all errors during module parsing&amp;quot;,&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    parser.add_argument(&lt;br /&gt;
        &amp;quot;--enum-class-locations&amp;quot;,&lt;br /&gt;
        dest=&amp;quot;enum_class_locations&amp;quot;,&lt;br /&gt;
        metavar=&amp;quot;REGEX:LOC&amp;quot;,&lt;br /&gt;
        action=&amp;quot;append&amp;quot;,&lt;br /&gt;
        default=[],&lt;br /&gt;
        type=regex_colon_path,&lt;br /&gt;
        help=&amp;quot;Locations of enum classes in &amp;quot;&lt;br /&gt;
        &amp;quot;&amp;lt;enum-class-name-regex&amp;gt;:&amp;lt;path-to-class&amp;gt; format. &amp;quot;&lt;br /&gt;
        &amp;quot;Example: `MyEnum:foo.bar.Baz`&amp;quot;,&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    numpy_array_fix = parser.add_mutually_exclusive_group()&lt;br /&gt;
    numpy_array_fix.add_argument(&lt;br /&gt;
        &amp;quot;--numpy-array-wrap-with-annotated&amp;quot;,&lt;br /&gt;
        default=False,&lt;br /&gt;
        action=&amp;quot;store_true&amp;quot;,&lt;br /&gt;
        help=&amp;quot;Replace numpy/scipy arrays of &amp;quot;&lt;br /&gt;
        &amp;quot;&amp;#039;ARRAY_T[TYPE, [*DIMS], *FLAGS]&amp;#039; format with &amp;quot;&lt;br /&gt;
        &amp;quot;&amp;#039;Annotated[ARRAY_T, TYPE, FixedSize|DynamicSize(*DIMS), *FLAGS]&amp;#039;&amp;quot;,&lt;br /&gt;
    )&lt;br /&gt;
    numpy_array_fix.add_argument(&lt;br /&gt;
        &amp;quot;--numpy-array-use-type-var&amp;quot;,&lt;br /&gt;
        default=False,&lt;br /&gt;
        action=&amp;quot;store_true&amp;quot;,&lt;br /&gt;
        help=&amp;quot;Replace &amp;#039;numpy.ndarray[numpy.float32[m, 1]]&amp;#039; with &amp;quot;&lt;br /&gt;
        &amp;quot;&amp;#039;numpy.ndarray[tuple[M, typing.Literal[1]], numpy.dtype[numpy.float32]]&amp;#039;&amp;quot;,&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    numpy_array_fix.add_argument(&lt;br /&gt;
        &amp;quot;--numpy-array-remove-parameters&amp;quot;,&lt;br /&gt;
        default=False,&lt;br /&gt;
        action=&amp;quot;store_true&amp;quot;,&lt;br /&gt;
        help=&amp;quot;Replace &amp;#039;numpy.ndarray[...]&amp;#039; with &amp;#039;numpy.ndarray&amp;#039;&amp;quot;,&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    parser.add_argument(&lt;br /&gt;
        &amp;quot;--print-invalid-expressions-as-is&amp;quot;,&lt;br /&gt;
        default=False,&lt;br /&gt;
        action=&amp;quot;store_true&amp;quot;,&lt;br /&gt;
        help=&amp;quot;Suppress the replacement with &amp;#039;...&amp;#039; of invalid expressions&amp;quot;&lt;br /&gt;
        &amp;quot;found in annotations&amp;quot;,&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    parser.add_argument(&lt;br /&gt;
        &amp;quot;--print-safe-value-reprs&amp;quot;,&lt;br /&gt;
        metavar=&amp;quot;REGEX&amp;quot;,&lt;br /&gt;
        default=None,&lt;br /&gt;
        type=regex,&lt;br /&gt;
        help=&amp;quot;Override the print-safe check for values matching REGEX&amp;quot;,&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    parser.add_argument(&lt;br /&gt;
        &amp;quot;--exit-code&amp;quot;,&lt;br /&gt;
        action=&amp;quot;store_true&amp;quot;,&lt;br /&gt;
        dest=&amp;quot;exit_code&amp;quot;,&lt;br /&gt;
        help=&amp;quot;On error exits with 1 and skips stub generation&amp;quot;,&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    parser.add_argument(&lt;br /&gt;
        &amp;quot;--dry-run&amp;quot;,&lt;br /&gt;
        action=&amp;quot;store_true&amp;quot;,&lt;br /&gt;
        dest=&amp;quot;dry_run&amp;quot;,&lt;br /&gt;
        help=&amp;quot;Don&amp;#039;t write stubs. Parses module and report errors&amp;quot;,&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    parser.add_argument(&lt;br /&gt;
        &amp;quot;--stub-extension&amp;quot;,&lt;br /&gt;
        type=str,&lt;br /&gt;
        default=&amp;quot;pyi&amp;quot;,&lt;br /&gt;
        metavar=&amp;quot;EXT&amp;quot;,&lt;br /&gt;
        choices=[&amp;quot;pyi&amp;quot;, &amp;quot;py&amp;quot;],&lt;br /&gt;
        help=&amp;quot;The file extension of the generated stubs. &amp;quot;&lt;br /&gt;
        &amp;quot;Must be &amp;#039;pyi&amp;#039; (default) or &amp;#039;py&amp;#039;&amp;quot;,&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    return parser&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def stub_parser_from_args(args: CLIArgs) -&amp;gt; IParser:&lt;br /&gt;
    error_handlers_top: list[type] = [&lt;br /&gt;
        LoggerData,&lt;br /&gt;
        *([IgnoreAllErrors] if args.ignore_all_errors else []),&lt;br /&gt;
        *([IgnoreInvalidIdentifierErrors] if args.ignore_invalid_identifiers else []),&lt;br /&gt;
        *([IgnoreInvalidExpressionErrors] if args.ignore_invalid_expressions else []),&lt;br /&gt;
        *([IgnoreUnresolvedNameErrors] if args.ignore_unresolved_names else []),&lt;br /&gt;
    ]&lt;br /&gt;
    error_handlers_bottom: list[type] = [&lt;br /&gt;
        LogErrors,&lt;br /&gt;
        SuggestCxxSignatureFix,&lt;br /&gt;
        *([TerminateOnFatalErrors] if args.exit_code else []),&lt;br /&gt;
    ]&lt;br /&gt;
&lt;br /&gt;
    numpy_fixes: list[type] = [&lt;br /&gt;
        *([FixNumpyArrayDimAnnotation] if args.numpy_array_wrap_with_annotated else []),&lt;br /&gt;
        *([FixNumpyArrayDimTypeVar] if args.numpy_array_use_type_var else []),&lt;br /&gt;
        *(&lt;br /&gt;
            [FixNumpyArrayRemoveParameters]&lt;br /&gt;
            if args.numpy_array_remove_parameters&lt;br /&gt;
            else []&lt;br /&gt;
        ),&lt;br /&gt;
    ]&lt;br /&gt;
&lt;br /&gt;
    class Parser(&lt;br /&gt;
        *error_handlers_top,  # type: ignore[misc]&lt;br /&gt;
        FixMissing__future__AnnotationsImport,&lt;br /&gt;
        FixMissing__all__Attribute,&lt;br /&gt;
        FixMissingNoneHashFieldAnnotation,&lt;br /&gt;
        FixMissingImports,&lt;br /&gt;
        FilterTypingModuleAttributes,&lt;br /&gt;
        FixPEP585CollectionNames,&lt;br /&gt;
        FixTypingTypeNames,&lt;br /&gt;
        FixScipyTypeArguments,&lt;br /&gt;
        FixMissingFixedSizeImport,&lt;br /&gt;
        FixMissingEnumMembersAnnotation,&lt;br /&gt;
        OverridePrintSafeValues,&lt;br /&gt;
        *numpy_fixes,  # type: ignore[misc]&lt;br /&gt;
        FixNumpyDtype,&lt;br /&gt;
        FixNumpyArrayFlags,&lt;br /&gt;
        FixCurrentModulePrefixInTypeNames,&lt;br /&gt;
        FixBuiltinTypes,&lt;br /&gt;
        RewritePybind11EnumValueRepr,&lt;br /&gt;
        FilterClassMembers,&lt;br /&gt;
        ReplaceReadWritePropertyWithField,&lt;br /&gt;
        FilterInvalidIdentifiers,&lt;br /&gt;
        FixValueReprRandomAddress,&lt;br /&gt;
        FixRedundantBuiltinsAnnotation,&lt;br /&gt;
        FilterPybindInternals,&lt;br /&gt;
        FilterPybind11ViewClasses,&lt;br /&gt;
        FixRedundantMethodsFromBuiltinObject,&lt;br /&gt;
        RemoveSelfAnnotation,&lt;br /&gt;
        FixPybind11EnumStrDoc,&lt;br /&gt;
        ExtractSignaturesFromPybind11Docstrings,&lt;br /&gt;
        ParserDispatchMixin,&lt;br /&gt;
        BaseParser,&lt;br /&gt;
        *error_handlers_bottom,  # type: ignore[misc]&lt;br /&gt;
    ):&lt;br /&gt;
        pass&lt;br /&gt;
&lt;br /&gt;
    parser = Parser()&lt;br /&gt;
&lt;br /&gt;
    if args.enum_class_locations:&lt;br /&gt;
        parser.set_pybind11_enum_locations(dict(args.enum_class_locations))&lt;br /&gt;
    if args.ignore_invalid_identifiers is not None:&lt;br /&gt;
        parser.set_ignored_invalid_identifiers(args.ignore_invalid_identifiers)&lt;br /&gt;
    if args.ignore_invalid_expressions is not None:&lt;br /&gt;
        parser.set_ignored_invalid_expressions(args.ignore_invalid_expressions)&lt;br /&gt;
    if args.ignore_unresolved_names is not None:&lt;br /&gt;
        parser.set_ignored_unresolved_names(args.ignore_unresolved_names)&lt;br /&gt;
    if args.print_safe_value_reprs is not None:&lt;br /&gt;
        parser.set_print_safe_value_pattern(args.print_safe_value_reprs)&lt;br /&gt;
    return parser&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def main() -&amp;gt; None:&lt;br /&gt;
&lt;br /&gt;
    files = glob.glob(&amp;quot;*.so&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    for fid in files:&lt;br /&gt;
        idx: int = fid.find(&amp;quot;.&amp;quot;)&lt;br /&gt;
        module_name: str = fid[:idx]&lt;br /&gt;
        print(&amp;quot;Processing: &amp;quot; + module_name)&lt;br /&gt;
&lt;br /&gt;
        logging.basicConfig(&lt;br /&gt;
            level=logging.INFO,&lt;br /&gt;
            format=&amp;quot;%(name)s - [%(levelname)7s] %(message)s&amp;quot;,&lt;br /&gt;
        )&lt;br /&gt;
        args = arg_parser().parse_args(namespace=CLIArgs())&lt;br /&gt;
&lt;br /&gt;
        parser = stub_parser_from_args(args)&lt;br /&gt;
        printer = Printer(&lt;br /&gt;
            invalid_expr_as_ellipses=not args.print_invalid_expressions_as_is&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        out_dir, sub_dir = to_output_and_subdir(&lt;br /&gt;
            output_dir=args.output_dir,&lt;br /&gt;
            module_name=module_name,&lt;br /&gt;
            root_suffix=args.root_suffix,&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        run(&lt;br /&gt;
            parser,&lt;br /&gt;
            printer,&lt;br /&gt;
            module_name,&lt;br /&gt;
            out_dir,&lt;br /&gt;
            sub_dir=sub_dir,&lt;br /&gt;
            dry_run=args.dry_run,&lt;br /&gt;
            writer=Writer(stub_ext=args.stub_extension),&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def to_output_and_subdir(&lt;br /&gt;
    output_dir: str, module_name: str, root_suffix: str | None&lt;br /&gt;
) -&amp;gt; tuple[Path, Path | None]:&lt;br /&gt;
    out_dir = Path(output_dir)&lt;br /&gt;
&lt;br /&gt;
    module_path = module_name.split(&amp;quot;.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    if root_suffix is None:&lt;br /&gt;
        return out_dir.joinpath(*module_path[:-1]), None&lt;br /&gt;
    else:&lt;br /&gt;
        module_path = [f&amp;quot;{module_path[0]}{root_suffix}&amp;quot;, *module_path[1:]]&lt;br /&gt;
        if len(module_path) == 1:&lt;br /&gt;
            sub_dir = Path(module_path[-1])&lt;br /&gt;
        else:&lt;br /&gt;
            sub_dir = None&lt;br /&gt;
        return out_dir.joinpath(*module_path[:-1]), sub_dir&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def run(&lt;br /&gt;
    parser: IParser,&lt;br /&gt;
    printer: Printer,&lt;br /&gt;
    module_name: str,&lt;br /&gt;
    out_dir: Path,&lt;br /&gt;
    sub_dir: Path | None,&lt;br /&gt;
    dry_run: bool,&lt;br /&gt;
    writer: Writer,&lt;br /&gt;
):&lt;br /&gt;
    module = parser.handle_module(&lt;br /&gt;
        QualifiedName.from_str(module_name), importlib.import_module(module_name)&lt;br /&gt;
    )&lt;br /&gt;
    parser.finalize()&lt;br /&gt;
&lt;br /&gt;
    if module is None:&lt;br /&gt;
        raise RuntimeError(f&amp;quot;Can&amp;#039;t parse {module_name}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    if dry_run:&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    out_dir.mkdir(exist_ok=True, parents=True)&lt;br /&gt;
    writer.write_module(module, printer, to=out_dir, sub_dir=sub_dir)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    main()&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Davrot</name></author>
	</entry>
</feed>