Skip to content

Commit

Permalink
Support for mdx (#64)
Browse files Browse the repository at this point in the history
  • Loading branch information
zsimjee authored Jan 16, 2024
1 parent 264da1a commit 5a645ab
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 30 deletions.
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[metadata]
description-file = README.md
description_file = README.md
license_files = LICENSE

[bdist_wheel]
Expand Down
2 changes: 1 addition & 1 deletion src/lazydocs/_about.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Information about this library. This file will automatically changed."""

__version__ = "0.5.0-dev.bugfix"
__version__ = "0.5.1"
# __author__
# __email__
6 changes: 6 additions & 0 deletions src/lazydocs/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ def generate(
False,
help="If `True`, validate the docstrings via pydocstyle. Requires pydocstyle to be installed.",
),
output_format: Optional[str] = typer.Option(
None,
help="The output format for the creation of the markdown files. This may be 'md' or 'mdx'. Defaults to md.",
)

) -> None:
"""Generates markdown documentation for your Python project based on Google-style docstrings."""

Expand All @@ -52,6 +57,7 @@ def generate(
src_base_url=src_base_url,
remove_package_prefix=remove_package_prefix,
ignored_modules=ignored_modules,
output_format=output_format,
overview_file=overview_file,
watermark=watermark,
validate=validate,
Expand Down
97 changes: 69 additions & 28 deletions src/lazydocs/generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
<a href="{path}"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square" /></a>
"""

_MDX_SOURCE_BADGE_TEMPLATE = """
<a href="{path}"><img align="right" style={{{{"float":"right"}}}} src="https://img.shields.io/badge/-source-cccccc?style=flat-square" /></a>
"""

_SEPARATOR = """
---
"""
Expand Down Expand Up @@ -200,6 +204,7 @@ def to_md_file(
out_path: str = ".",
watermark: bool = True,
disable_markdownlint: bool = True,
is_mdx: bool = False
) -> None:
"""Creates an API docs file from a provided text.
Expand All @@ -215,8 +220,13 @@ def to_md_file(
return

md_file = filename
if not filename.endswith(".md"):
md_file = filename + ".md"

if is_mdx:
if not filename.endswith(".mdx"):
md_file = filename + ".mdx"
else:
if not filename.endswith(".md"):
md_file = filename + ".md"

if disable_markdownlint:
markdown_str = "<!-- markdownlint-disable -->\n" + markdown_str
Expand Down Expand Up @@ -521,7 +531,7 @@ def _get_src_path(self, obj: Any, append_base: bool = True) -> str:

return relative_path

def func2md(self, func: Callable, clsname: str = "", depth: int = 3) -> str:
def func2md(self, func: Callable, clsname: str = "", depth: int = 3, is_mdx: bool = False) -> str:
"""Takes a function (or method) and generates markdown docs.
Args:
Expand Down Expand Up @@ -602,11 +612,14 @@ def func2md(self, func: Callable, clsname: str = "", depth: int = 3) -> str:
)

if path:
markdown = _SOURCE_BADGE_TEMPLATE.format(path=path) + markdown
if is_mdx:
markdown = _MDX_SOURCE_BADGE_TEMPLATE.format(path=path) + markdown
else:
markdown = _SOURCE_BADGE_TEMPLATE.format(path=path) + markdown

return markdown

def class2md(self, cls: Any, depth: int = 2) -> str:
def class2md(self, cls: Any, depth: int = 2, is_mdx: bool = False) -> str:
"""Takes a class and creates markdown text to document its methods and variables.
Args:
Expand Down Expand Up @@ -646,7 +659,7 @@ def class2md(self, cls: Any, depth: int = 2) -> str:
hasattr(cls.__init__, "__module__")
and cls.__init__.__module__ == modname
):
init = self.func2md(cls.__init__, clsname=clsname)
init = self.func2md(cls.__init__, clsname=clsname, is_mdx=is_mdx)
else:
init = ""
except (ValueError, TypeError):
Expand Down Expand Up @@ -698,7 +711,7 @@ def class2md(self, cls: Any, depth: int = 2) -> str:
# object module should be the same as the calling module
and obj.__module__ == modname
):
function_md = self.func2md(obj, clsname=clsname, depth=depth + 1)
function_md = self.func2md(obj, clsname=clsname, depth=depth + 1, is_mdx=is_mdx)
if function_md:
methods.append(_SEPARATOR + function_md)

Expand All @@ -713,11 +726,14 @@ def class2md(self, cls: Any, depth: int = 2) -> str:
)

if path:
markdown = _SOURCE_BADGE_TEMPLATE.format(path=path) + markdown
if is_mdx:
markdown = _MDX_SOURCE_BADGE_TEMPLATE.format(path=path) + markdown
else:
markdown = _SOURCE_BADGE_TEMPLATE.format(path=path) + markdown

return markdown

def module2md(self, module: types.ModuleType, depth: int = 1) -> str:
def module2md(self, module: types.ModuleType, depth: int = 1, is_mdx: bool = False) -> str:
"""Takes an imported module object and create a Markdown string containing functions and classes.
Args:
Expand Down Expand Up @@ -758,7 +774,7 @@ def module2md(self, module: types.ModuleType, depth: int = 1) -> str:
and hasattr(obj, "__module__")
and obj.__module__ == modname
):
class_markdown = self.class2md(obj, depth=depth + 1)
class_markdown = self.class2md(obj, depth=depth + 1, is_mdx=is_mdx)
if class_markdown:
classes.append(_SEPARATOR + class_markdown)
line_nos.append(_get_line_no(obj) or 0)
Expand All @@ -774,7 +790,7 @@ def module2md(self, module: types.ModuleType, depth: int = 1) -> str:
and hasattr(obj, "__module__")
and obj.__module__ == modname
):
function_md = self.func2md(obj, depth=depth + 1)
function_md = self.func2md(obj, depth=depth + 1, is_mdx=is_mdx)
if function_md:
functions.append(_SEPARATOR + function_md)
line_nos.append(_get_line_no(obj) or 0)
Expand Down Expand Up @@ -808,11 +824,14 @@ def module2md(self, module: types.ModuleType, depth: int = 1) -> str:
)

if path:
markdown = _SOURCE_BADGE_TEMPLATE.format(path=path) + markdown
if (is_mdx):
markdown = _MDX_SOURCE_BADGE_TEMPLATE.format(path=path) + markdown
else:
markdown = _SOURCE_BADGE_TEMPLATE.format(path=path) + markdown

return markdown

def import2md(self, obj: Any, depth: int = 1) -> str:
def import2md(self, obj: Any, depth: int = 1, is_mdx: bool = False) -> str:
"""Generates markdown documentation for a selected object/import.
Args:
Expand All @@ -823,16 +842,16 @@ def import2md(self, obj: Any, depth: int = 1) -> str:
str: Markdown documentation of selected object.
"""
if inspect.isclass(obj):
return self.class2md(obj, depth=depth)
return self.class2md(obj, depth=depth, is_mdx=is_mdx)
elif isinstance(obj, types.ModuleType):
return self.module2md(obj, depth=depth)
return self.module2md(obj, depth=depth, is_mdx=is_mdx)
elif callable(obj):
return self.func2md(obj, depth=depth)
return self.func2md(obj, depth=depth, is_mdx=is_mdx)
else:
print(f"Could not generate markdown for object type {str(type(obj))}")
return ""

def overview2md(self) -> str:
def overview2md(self, is_mdx: bool = False) -> str:
"""Generates a documentation overview file based on the generated docs."""

entries_md = ""
Expand All @@ -841,7 +860,10 @@ def overview2md(self) -> str:
):
full_name = obj["full_name"]
if "module" in obj:
link = "./" + obj["module"] + ".md#" + obj["anchor_tag"]
if is_mdx:
link = "./" + obj["module"] + ".mdx#" + obj["anchor_tag"]
else:
link = "./" + obj["module"] + ".md#" + obj["anchor_tag"]
else:
link = "#unknown"

Expand All @@ -857,7 +879,10 @@ def overview2md(self) -> str:
for obj in list(filter(lambda d: d["type"] == "class", self.generated_objects)):
module_name = obj["module"].split(".")[-1]
name = module_name + "." + obj["full_name"]
link = "./" + obj["module"] + ".md#" + obj["anchor_tag"]
if is_mdx:
link = "./" + obj["module"] + ".mdx#" + obj["anchor_tag"]
else:
link = "./" + obj["module"] + ".md#" + obj["anchor_tag"]
description = obj["description"]
entries_md += f"\n- [`{name}`]({link})"
if description:
Expand All @@ -872,7 +897,10 @@ def overview2md(self) -> str:
):
module_name = obj["module"].split(".")[-1]
name = module_name + "." + obj["full_name"]
link = "./" + obj["module"] + ".md#" + obj["anchor_tag"]
if is_mdx:
link = "./" + obj["module"] + ".mdx#" + obj["anchor_tag"]
else:
link = "./" + obj["module"] + ".md#" + obj["anchor_tag"]
description = obj["description"]
entries_md += f"\n- [`{name}`]({link})"
if description:
Expand All @@ -893,6 +921,7 @@ def generate_docs(
src_base_url: Optional[str] = None,
remove_package_prefix: bool = False,
ignored_modules: Optional[List[str]] = None,
output_format: Optional[str] = None,
overview_file: Optional[str] = None,
watermark: bool = True,
validate: bool = False,
Expand All @@ -919,6 +948,10 @@ def generate_docs(
if not ignored_modules:
ignored_modules = list()

if output_format and output_format != 'md' and output_format != 'mdx':
raise Exception(f"Unsupported output format: {output_format}. Choose either 'md' or 'mdx'.")
is_mdx = output_format == 'mdx'

if not src_root_path:
try:
# Set src root path to git root
Expand Down Expand Up @@ -967,7 +1000,7 @@ def generate_docs(
try:
mod_spec = loader.find_spec(module_name)
mod = importlib.util.module_from_spec(mod_spec)
module_md = generator.module2md(mod)
module_md = generator.module2md(mod, is_mdx=is_mdx)
if not module_md:
# Module md is empty -> ignore module and all submodules
# Add module to ignore list, so submodule will also be ignored
Expand All @@ -982,6 +1015,7 @@ def generate_docs(
mod.__name__,
out_path=output_path,
watermark=watermark,
is_mdx=is_mdx,
)
except Exception as ex:
print(
Expand All @@ -1005,7 +1039,7 @@ def generate_docs(
spec.loader.exec_module(mod) # type: ignore

if mod:
module_md = generator.module2md(mod)
module_md = generator.module2md(mod, is_mdx=is_mdx)
if stdout_mode:
print(module_md)
else:
Expand All @@ -1014,6 +1048,7 @@ def generate_docs(
module_name,
out_path=output_path,
watermark=watermark,
is_mdx=is_mdx,
)
else:
raise Exception(f"Failed to generate markdown for {path}")
Expand Down Expand Up @@ -1044,7 +1079,7 @@ def generate_docs(
try:
mod_spec = loader.find_spec(module_name)
mod = importlib.util.module_from_spec(mod_spec)
module_md = generator.module2md(mod)
module_md = generator.module2md(mod, is_mdx=is_mdx)

if not module_md:
# Module MD is empty -> ignore module and all submodules
Expand All @@ -1060,32 +1095,38 @@ def generate_docs(
mod.__name__,
out_path=output_path,
watermark=watermark,
is_mdx=is_mdx
)
except Exception as ex:
print(
f"Failed to generate docs for module {module_name}: "
+ repr(ex)
)
else:
import_md = generator.import2md(obj)
import_md = generator.import2md(obj, is_mdx=is_mdx)
if stdout_mode:
print(import_md)
else:
to_md_file(
import_md, path, out_path=output_path, watermark=watermark
import_md, path, out_path=output_path, watermark=watermark, is_mdx=is_mdx
)
else:
raise Exception(f"Failed to generate markdown for {path}.")

if overview_file and not stdout_mode:
if not overview_file.endswith(".md"):
overview_file = overview_file + ".md"
if is_mdx:
if not overview_file.endswith(".mdx"):
overview_file = overview_file + ".mdx"
else:
if not overview_file.endswith(".md"):
overview_file = overview_file + ".md"

to_md_file(
generator.overview2md(),
generator.overview2md(is_mdx=is_mdx),
overview_file,
out_path=output_path,
watermark=watermark,
is_mdx=is_mdx
)

# Write mkdocs pages file
Expand Down

0 comments on commit 5a645ab

Please sign in to comment.