Skip to main content

- Resource Pack
- Liteyuki Function
- Liteyuki Plugin
- Communication
- - liteyuki
  - liteyuki.config
  - liteyuki.exception
  - liteyuki.log
  - liteyuki.mkdoc
  - liteyuki.utils

# liteyuki.mkdoc

SnowyKami
About 5 min
API

On This Page

- def get_relative_path(base_path: str, target_path: str) -> str
- def write_to_files(file_data: dict[str, str]) -> None
- def get_file_list(module_folder: str) -> None
- def get_module_info_normal(file_path: str, ignore_private: bool) -> ModuleInfo
- def generate_markdown(module_info: ModuleInfo, front_matter: Any) -> str
- def generate_docs(module_folder: str, output_dir: str, with_top: bool, ignored_paths: Any) -> None
- class DefType(Enum)
- attr FUNCTION: 'function'
- attr METHOD: 'method'
- attr STATIC_METHOD: 'staticmethod'
- attr CLASS_METHOD: 'classmethod'
- attr PROPERTY: 'property'
- class FunctionInfo(BaseModel)
- class AttributeInfo(BaseModel)
- class ClassInfo(BaseModel)
- class ModuleInfo(BaseModel)
- var NO_TYPE_ANY = 'Any'
- var NO_TYPE_HINT = 'NoTypeHint'
- var FUNCTION = 'function'
- var METHOD = 'method'
- var STATIC_METHOD = 'staticmethod'
- var CLASS_METHOD = 'classmethod'
- var PROPERTY = 'property'
- var file_list = []
- var dot_sep_module_path = file_path.replace(os.sep, '.').replace('.py', '').replace('.pyi', '')
- var module_docstring = ast.get_docstring(tree)
- var module_info = ModuleInfo(module_path=dot_sep_module_path, functions=[], classes=[], attributes=[], docstring=module_docstring if module_docstring else '')
- var content = ''
- var front_matter = '---\n' + '\n'.join([f'{k}: {v}' for (k, v) in front_matter.items()]) + '\n---\n\n'
- var file_list = get_file_list(module_folder)
- var replace_data = {'__init__': 'README', '.py': '.md'}
- var file_content = file.read()
- var tree = ast.parse(file_content)
- var args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] else arg[0] for arg in func.args]
- var ignored_paths = []
- var no_module_name_pyfile_path = get_relative_path(module_folder, pyfile_path)
- var rel_md_path = pyfile_path if with_top else no_module_name_pyfile_path
- var abs_md_path = os.path.join(output_dir, rel_md_path)
- var module_info = get_module_info_normal(pyfile_path)
- var md_content = generate_markdown(module_info, front_matter)
- var inherit = f"({', '.join(cls.inherit)})" if cls.inherit else ''
- var rel_md_path = rel_md_path.replace(rk, rv)
- var front_matter = {'title': module_info.module_path.replace('.__init__', '').replace('_', '\\n'), 'index': 'true', 'icon': 'laptop-code', 'category': 'API'}
- var front_matter = {'title': module_info.module_path.replace('_', '\\n'), 'order': '1', 'icon': 'laptop-code', 'category': 'API'}
- var function_docstring = ast.get_docstring(node)
- var func_info = FunctionInfo(name=node.name, args=[(arg.arg, ast.unparse(arg.annotation) if arg.annotation else NO_TYPE_ANY) for arg in node.args.args], return_type=ast.unparse(node.returns) if node.returns else 'None', docstring=function_docstring if function_docstring else '', type=DefType.FUNCTION, is_async=isinstance(node, ast.AsyncFunctionDef), source_code=ast.unparse(node))
- var class_docstring = ast.get_docstring(node)
- var class_info = ClassInfo(name=node.name, docstring=class_docstring if class_docstring else '', methods=[], attributes=[], inherit=[ast.unparse(base) for base in node.bases])
- var args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] else arg[0] for arg in method.args]
- var args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] and arg[0] != 'self' else arg[0] for arg in method.args]
- var first_arg = node.args.args[0]
- var method_docstring = ast.get_docstring(class_node)
- var def_type = DefType.METHOD
- var def_type = DefType.STATIC_METHOD
- var attr_type = NO_TYPE_HINT
- var def_type = DefType.CLASS_METHOD
- var attr_type = ast.unparse(node.value.annotation)
- var def_type = DefType.PROPERTY

### def `get_relative_path(base_path: str, target_path: str) -> str`

获取相对路径

Args:

    base_path: 基础路径

    target_path: 目标路径

源代码

    def get_relative_path(base_path: str, target_path: str) -> str:
        """
        获取相对路径
        Args:
            base_path: 基础路径
            target_path: 目标路径
        """
        return os.path.relpath(target_path, base_path)

### def `write_to_files(file_data: dict[str, str]) -> None`

输出文件

Args:

    file_data: 文件数据 相对路径

源代码

    def write_to_files(file_data: dict[str, str]):
        """
        输出文件
        Args:
            file_data: 文件数据 相对路径
        """
        for (rp, data) in file_data.items():
            if not os.path.exists(os.path.dirname(rp)):
                os.makedirs(os.path.dirname(rp))
            with open(rp, 'w', encoding='utf-8') as f:
                f.write(data)

### def `get_file_list(module_folder: str) -> None`

源代码

    def get_file_list(module_folder: str):
        file_list = []
        for (root, dirs, files) in os.walk(module_folder):
            for file in files:
                if file.endswith(('.py', '.pyi')):
                    file_list.append(os.path.join(root, file))
        return file_list

### def `get_module_info_normal(file_path: str, ignore_private: bool) -> ModuleInfo`

获取函数和类

Args:

    file_path: Python 文件路径

    ignore_private: 忽略私有函数和类

Returns:

    模块信息

源代码

    def get_module_info_normal(file_path: str, ignore_private: bool=True) -> ModuleInfo:
        """
        获取函数和类
        Args:
            file_path: Python 文件路径
            ignore_private: 忽略私有函数和类
        Returns:
            模块信息
        """
        with open(file_path, 'r', encoding='utf-8') as file:
            file_content = file.read()
            tree = ast.parse(file_content)
        dot_sep_module_path = file_path.replace(os.sep, '.').replace('.py', '').replace('.pyi', '')
        module_docstring = ast.get_docstring(tree)
        module_info = ModuleInfo(module_path=dot_sep_module_path, functions=[], classes=[], attributes=[], docstring=module_docstring if module_docstring else '')
        for node in ast.walk(tree):
            if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
                if not any((isinstance(parent, ast.ClassDef) for parent in ast.iter_child_nodes(node))) and (not ignore_private or not node.name.startswith('_')):
                    if node.args.args:
                        first_arg = node.args.args[0]
                        if first_arg.arg in ('self', 'cls'):
                            continue
                    function_docstring = ast.get_docstring(node)
                    func_info = FunctionInfo(name=node.name, args=[(arg.arg, ast.unparse(arg.annotation) if arg.annotation else NO_TYPE_ANY) for arg in node.args.args], return_type=ast.unparse(node.returns) if node.returns else 'None', docstring=function_docstring if function_docstring else '', type=DefType.FUNCTION, is_async=isinstance(node, ast.AsyncFunctionDef), source_code=ast.unparse(node))
                    module_info.functions.append(func_info)
            elif isinstance(node, ast.ClassDef):
                class_docstring = ast.get_docstring(node)
                class_info = ClassInfo(name=node.name, docstring=class_docstring if class_docstring else '', methods=[], attributes=[], inherit=[ast.unparse(base) for base in node.bases])
                for class_node in node.body:
                    if isinstance(class_node, ast.FunctionDef) and (not ignore_private or not class_node.name.startswith('_') or class_node.name == '__init__'):
                        method_docstring = ast.get_docstring(class_node)
                        def_type = DefType.METHOD
                        if class_node.decorator_list:
                            if any((isinstance(decorator, ast.Name) and decorator.id == 'staticmethod' for decorator in class_node.decorator_list)):
                                def_type = DefType.STATIC_METHOD
                            elif any((isinstance(decorator, ast.Name) and decorator.id == 'classmethod' for decorator in class_node.decorator_list)):
                                def_type = DefType.CLASS_METHOD
                            elif any((isinstance(decorator, ast.Name) and decorator.id == 'property' for decorator in class_node.decorator_list)):
                                def_type = DefType.PROPERTY
                        class_info.methods.append(FunctionInfo(name=class_node.name, args=[(arg.arg, ast.unparse(arg.annotation) if arg.annotation else NO_TYPE_ANY) for arg in class_node.args.args], return_type=ast.unparse(class_node.returns) if class_node.returns else 'None', docstring=method_docstring if method_docstring else '', type=def_type, is_async=isinstance(class_node, ast.AsyncFunctionDef), source_code=ast.unparse(class_node)))
                    elif isinstance(class_node, ast.Assign):
                        for target in class_node.targets:
                            if isinstance(target, ast.Name):
                                class_info.attributes.append(AttributeInfo(name=target.id, type=ast.unparse(class_node.value)))
                module_info.classes.append(class_info)
            elif isinstance(node, ast.Assign):
                if not any((isinstance(parent, (ast.ClassDef, ast.FunctionDef)) for parent in ast.iter_child_nodes(node))):
                    for target in node.targets:
                        if isinstance(target, ast.Name) and (not ignore_private or not target.id.startswith('_')):
                            attr_type = NO_TYPE_HINT
                            if isinstance(node.value, ast.AnnAssign) and node.value.annotation:
                                attr_type = ast.unparse(node.value.annotation)
                            module_info.attributes.append(AttributeInfo(name=target.id, type=attr_type, value=ast.unparse(node.value) if node.value else None))
        return module_info

### def `generate_markdown(module_info: ModuleInfo, front_matter: Any) -> str`

生成模块的Markdown

你可在此自定义生成的Markdown格式

Args:

    module_info: 模块信息

    front_matter: 自定义选项title, index, icon, category

Returns:

    Markdown 字符串

源代码

    def generate_markdown(module_info: ModuleInfo, front_matter=None) -> str:
        """
        生成模块的Markdown
        你可在此自定义生成的Markdown格式
        Args:
            module_info: 模块信息
            front_matter: 自定义选项title, index, icon, category
        Returns:
            Markdown 字符串
        """
        content = ''
        front_matter = '---\n' + '\n'.join([f'{k}: {v}' for (k, v) in front_matter.items()]) + '\n---\n\n'
        content += front_matter
        for func in module_info.functions:
            args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] else arg[0] for arg in func.args]
            content += f"### ***{('async ' if func.is_async else '')}def*** `{func.name}({', '.join(args_with_type)}) -> {func.return_type}`\n\n"
            func.docstring = func.docstring.replace('\n', '\n\n')
            content += f'{func.docstring}\n\n'
            content += f'<details>\n<summary>源代码</summary>\n\n```python\n{func.source_code}\n```\n</details>\n\n'
        for cls in module_info.classes:
            if cls.inherit:
                inherit = f"({', '.join(cls.inherit)})" if cls.inherit else ''
                content += f'### ***class*** `{cls.name}{inherit}`\n\n'
            else:
                content += f'### ***class*** `{cls.name}`\n\n'
            cls.docstring = cls.docstring.replace('\n', '\n\n')
            content += f'{cls.docstring}\n\n'
            for method in cls.methods:
                if method.type != DefType.METHOD:
                    args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] else arg[0] for arg in method.args]
                    content += f'### &emsp; ***@{method.type.value}***\n'
                else:
                    args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] and arg[0] != 'self' else arg[0] for arg in method.args]
                content += f"### &emsp; ***{('async ' if method.is_async else '')}def*** `{method.name}({', '.join(args_with_type)}) -> {method.return_type}`\n\n"
                method.docstring = method.docstring.replace('\n', '\n\n')
                content += f'&emsp;{method.docstring}\n\n'
                content += f'<details>\n<summary>源代码</summary>\n\n```python\n{method.source_code}\n```\n</details>\n\n'
            for attr in cls.attributes:
                content += f'### &emsp; ***attr*** `{attr.name}: {attr.type}`\n\n'
        for attr in module_info.attributes:
            if attr.type == NO_TYPE_HINT:
                content += f'### ***var*** `{attr.name} = {attr.value}`\n\n'
            else:
                content += f'### ***var*** `{attr.name}: {attr.type} = {attr.value}`\n\n'
            attr.docstring = attr.docstring.replace('\n', '\n\n')
            content += f'{attr.docstring}\n\n'
        return content

### def `generate_docs(module_folder: str, output_dir: str, with_top: bool, ignored_paths: Any) -> None`

生成文档

Args:

    module_folder: 模块文件夹

    output_dir: 输出文件夹

    with_top: 是否包含顶层文件夹 False时例如docs/api/module_a, docs/api/module_b， True时例如docs/api/module/module_a.md， docs/api/module/module_b.md

    ignored_paths: 忽略的路径

源代码

    def generate_docs(module_folder: str, output_dir: str, with_top: bool=False, ignored_paths=None):
        """
        生成文档
        Args:
            module_folder: 模块文件夹
            output_dir: 输出文件夹
            with_top: 是否包含顶层文件夹 False时例如docs/api/module_a, docs/api/module_b， True时例如docs/api/module/module_a.md， docs/api/module/module_b.md
            ignored_paths: 忽略的路径
        """
        if ignored_paths is None:
            ignored_paths = []
        file_data: dict[str, str] = {}
        file_list = get_file_list(module_folder)
        shutil.rmtree(output_dir, ignore_errors=True)
        os.mkdir(output_dir)
        replace_data = {'__init__': 'README', '.py': '.md'}
        for pyfile_path in file_list:
            if any((ignored_path.replace('\\', '/') in pyfile_path.replace('\\', '/') for ignored_path in ignored_paths)):
                continue
            no_module_name_pyfile_path = get_relative_path(module_folder, pyfile_path)
            rel_md_path = pyfile_path if with_top else no_module_name_pyfile_path
            for (rk, rv) in replace_data.items():
                rel_md_path = rel_md_path.replace(rk, rv)
            abs_md_path = os.path.join(output_dir, rel_md_path)
            module_info = get_module_info_normal(pyfile_path)
            if 'README' in abs_md_path:
                front_matter = {'title': module_info.module_path.replace('.__init__', '').replace('_', '\\n'), 'index': 'true', 'icon': 'laptop-code', 'category': 'API'}
            else:
                front_matter = {'title': module_info.module_path.replace('_', '\\n'), 'order': '1', 'icon': 'laptop-code', 'category': 'API'}
            md_content = generate_markdown(module_info, front_matter)
            print(f'Generate {pyfile_path} -> {abs_md_path}')
            file_data[abs_md_path] = md_content
        write_to_files(file_data)

### class `DefType(Enum)`

### attr `FUNCTION: 'function'`

### attr `METHOD: 'method'`

### attr `STATIC_METHOD: 'staticmethod'`

### attr `CLASS_METHOD: 'classmethod'`

### attr `PROPERTY: 'property'`

### class `FunctionInfo(BaseModel)`

### class `AttributeInfo(BaseModel)`

### class `ClassInfo(BaseModel)`

### class `ModuleInfo(BaseModel)`

### var `NO_TYPE_ANY = 'Any'`

### var `NO_TYPE_HINT = 'NoTypeHint'`

### var `FUNCTION = 'function'`

### var `METHOD = 'method'`

### var `STATIC_METHOD = 'staticmethod'`

### var `CLASS_METHOD = 'classmethod'`

### var `PROPERTY = 'property'`

### var `file_list = []`

### var `dot_sep_module_path = file_path.replace(os.sep, '.').replace('.py', '').replace('.pyi', '')`

### var `module_docstring = ast.get_docstring(tree)`

### var `module_info = ModuleInfo(module_path=dot_sep_module_path, functions=[], classes=[], attributes=[], docstring=module_docstring if module_docstring else '')`

### var `content = ''`

### var `front_matter = '---\n' + '\n'.join([f'{k}: {v}' for (k, v) in front_matter.items()]) + '\n---\n\n'`

### var `file_list = get_file_list(module_folder)`

### var `replace_data = {'__init__': 'README', '.py': '.md'}`

### var `file_content = file.read()`

### var `tree = ast.parse(file_content)`

### var `args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] else arg[0] for arg in func.args]`

### var `ignored_paths = []`

### var `no_module_name_pyfile_path = get_relative_path(module_folder, pyfile_path)`

### var `rel_md_path = pyfile_path if with_top else no_module_name_pyfile_path`

### var `abs_md_path = os.path.join(output_dir, rel_md_path)`

### var `module_info = get_module_info_normal(pyfile_path)`

### var `md_content = generate_markdown(module_info, front_matter)`

### var `inherit = f"({', '.join(cls.inherit)})" if cls.inherit else ''`

### var `rel_md_path = rel_md_path.replace(rk, rv)`

### var `front_matter = {'title': module_info.module_path.replace('.__init__', '').replace('_', '\\n'), 'index': 'true', 'icon': 'laptop-code', 'category': 'API'}`

### var `front_matter = {'title': module_info.module_path.replace('_', '\\n'), 'order': '1', 'icon': 'laptop-code', 'category': 'API'}`

### var `function_docstring = ast.get_docstring(node)`

### var `func_info = FunctionInfo(name=node.name, args=[(arg.arg, ast.unparse(arg.annotation) if arg.annotation else NO_TYPE_ANY) for arg in node.args.args], return_type=ast.unparse(node.returns) if node.returns else 'None', docstring=function_docstring if function_docstring else '', type=DefType.FUNCTION, is_async=isinstance(node, ast.AsyncFunctionDef), source_code=ast.unparse(node))`

### var `class_docstring = ast.get_docstring(node)`

### var `class_info = ClassInfo(name=node.name, docstring=class_docstring if class_docstring else '', methods=[], attributes=[], inherit=[ast.unparse(base) for base in node.bases])`

### var `args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] else arg[0] for arg in method.args]`

### var `args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] and arg[0] != 'self' else arg[0] for arg in method.args]`

### var `first_arg = node.args.args[0]`

### var `method_docstring = ast.get_docstring(class_node)`

### var `def_type = DefType.METHOD`

### var `def_type = DefType.STATIC_METHOD`

### var `attr_type = NO_TYPE_HINT`

### var `def_type = DefType.CLASS_METHOD`

### var `attr_type = ast.unparse(node.value.annotation)`

### var `def_type = DefType.PROPERTY`