Skip to content

Commit d8f4b1a

Browse files
committed
Add support for ignoring objects via docstring
1 parent 5ad4c9d commit d8f4b1a

File tree

1 file changed

+52
-17
lines changed

1 file changed

+52
-17
lines changed

src/lazydocs/generation.py

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
_RE_TYPED_ARGSTART = re.compile(r"([\w\[\]_]{1,}?)\s*?\((.*?)\):(.{2,})", re.IGNORECASE)
2525
_RE_ARGSTART = re.compile(r"(.{1,}?):(.{2,})", re.IGNORECASE)
2626

27+
_IGNORE_GENERATION_INSTRUCTION = "lazydocs: ignore"
28+
2729
# String templates
2830

2931
_SOURCE_BADGE_TEMPLATE = """
@@ -179,7 +181,7 @@ def _order_by_line_nos(objs: Any, line_nos: List[int]) -> List[str]:
179181

180182

181183
def to_md_file(
182-
string: str,
184+
markdown_str: str,
183185
filename: str,
184186
out_path: str = ".",
185187
watermark: bool = True,
@@ -188,27 +190,31 @@ def to_md_file(
188190
"""Creates an API docs file from a provided text.
189191
190192
Args:
191-
string (str): String with line breaks to write to file.
193+
markdown_str (str): Markdown string with line breaks to write to file.
192194
filename (str): Filename without the .md
193195
watermark (bool): If `True`, add a watermark with a timestamp to bottom of the markdown files.
194196
disable_markdownlint (bool): If `True`, an inline tag is added to disable markdownlint for this file.
195197
out_path (str): The output directory
196198
"""
199+
if not markdown_str:
200+
# Dont write empty files
201+
return
202+
197203
md_file = filename
198204
if not filename.endswith(".md"):
199205
md_file = filename + ".md"
200206

201207
if disable_markdownlint:
202-
string = "<!-- markdownlint-disable -->\n" + string
208+
markdown_str = "<!-- markdownlint-disable -->\n" + markdown_str
203209

204210
if watermark:
205-
string += _WATERMARK_TEMPLATE.format(
211+
markdown_str += _WATERMARK_TEMPLATE.format(
206212
date=datetime.date.today().strftime("%d %b %Y")
207213
)
208214

209215
print("Writing {}.".format(md_file))
210216
with open(os.path.join(out_path, md_file), "w") as f:
211-
f.write(string)
217+
f.write(markdown_str)
212218

213219

214220
def _code_snippet(snippet: str) -> str:
@@ -254,6 +260,20 @@ def _get_class_that_defined_method(meth: Any) -> Any:
254260
return getattr(meth, "__objclass__", None) # handle special descriptor objects
255261

256262

263+
def _get_docstring(obj: Any) -> str:
264+
return "" if obj.__doc__ is None else inspect.getdoc(obj) or ""
265+
266+
267+
def _is_object_ignored(obj: Any) -> bool:
268+
if (
269+
_IGNORE_GENERATION_INSTRUCTION.replace(" ", "").lower()
270+
in _get_docstring(obj).replace(" ", "").lower()
271+
):
272+
# Do not generate anything if docstring contains ignore instruction
273+
return True
274+
return False
275+
276+
257277
def _get_src_root_path(obj: Any) -> str:
258278
"""Get the root path to a imported module.
259279
@@ -271,9 +291,8 @@ def _get_src_root_path(obj: Any) -> str:
271291

272292

273293
def _get_doc_summary(obj: Any) -> str:
274-
doc = "" if obj.__doc__ is None else inspect.getdoc(obj) or ""
275294
# First line should contain the summary
276-
return doc.split("\n")[0]
295+
return _get_docstring(obj).split("\n")[0]
277296

278297

279298
def _get_anchor_tag(header: str) -> str:
@@ -299,7 +318,7 @@ def _doc2md(obj: Any) -> str:
299318
# the documentation strings are now inherited if not overridden.
300319
# For details see: https://docs.python.org/3.6/library/inspect.html#inspect.getdoc
301320
# doc = getdoc(func) or ""
302-
doc = "" if obj.__doc__ is None else inspect.getdoc(obj) or ""
321+
doc = _get_docstring(obj)
303322

304323
blockindent = 0
305324
argindent = 1
@@ -480,6 +499,10 @@ def func2md(self, func: Callable, clsname: str = "", depth: int = 3) -> str:
480499
Returns:
481500
str: Markdown documentation for selected function.
482501
"""
502+
if _is_object_ignored(func):
503+
# The function is ignored from generation
504+
return ""
505+
483506
section = "#" * depth
484507
funcname = func.__name__
485508
modname = None
@@ -560,6 +583,10 @@ def class2md(self, cls: Any, depth: int = 2) -> str:
560583
Returns:
561584
str: Markdown documentation for selected class.
562585
"""
586+
if _is_object_ignored(cls):
587+
# The class is ignored from generation
588+
return ""
589+
563590
section = "#" * depth
564591
subsection = "#" * (depth + 2)
565592
clsname = cls.__name__
@@ -638,9 +665,9 @@ def class2md(self, cls: Any, depth: int = 2) -> str:
638665
# object module should be the same as the calling module
639666
and obj.__module__ == modname
640667
):
641-
methods.append(
642-
_SEPARATOR + self.func2md(obj, clsname=clsname, depth=depth + 1)
643-
)
668+
function_md = self.func2md(obj, clsname=clsname, depth=depth + 1)
669+
if function_md:
670+
methods.append(_SEPARATOR + function_md)
644671

645672
markdown = _CLASS_TEMPLATE.format(
646673
section=section,
@@ -667,6 +694,10 @@ def module2md(self, module: types.ModuleType, depth: int = 1) -> str:
667694
Returns:
668695
str: Markdown documentation for selected module.
669696
"""
697+
if _is_object_ignored(module):
698+
# The module is ignored from generation
699+
return ""
700+
670701
modname = module.__name__
671702
doc = _doc2md(module)
672703
summary = _get_doc_summary(module)
@@ -694,8 +725,10 @@ def module2md(self, module: types.ModuleType, depth: int = 1) -> str:
694725
and hasattr(obj, "__module__")
695726
and obj.__module__ == modname
696727
):
697-
classes.append(_SEPARATOR + self.class2md(obj, depth=depth + 1))
698-
line_nos.append(_get_line_no(obj) or 0)
728+
class_markdown = self.class2md(obj, depth=depth + 1)
729+
if class_markdown:
730+
classes.append(_SEPARATOR + class_markdown)
731+
line_nos.append(_get_line_no(obj) or 0)
699732
classes = _order_by_line_nos(classes, line_nos)
700733

701734
functions: List[str] = []
@@ -708,8 +741,10 @@ def module2md(self, module: types.ModuleType, depth: int = 1) -> str:
708741
and hasattr(obj, "__module__")
709742
and obj.__module__ == modname
710743
):
711-
functions.append(_SEPARATOR + self.func2md(obj, depth=depth + 1))
712-
line_nos.append(_get_line_no(obj) or 0)
744+
function_md = self.func2md(obj, depth=depth + 1)
745+
if function_md:
746+
functions.append(_SEPARATOR + function_md)
747+
line_nos.append(_get_line_no(obj) or 0)
713748
functions = _order_by_line_nos(functions, line_nos)
714749

715750
variables: List[str] = []
@@ -894,7 +929,7 @@ def generate_docs(
894929
continue
895930

896931
try:
897-
mod = loader.find_module(module_name).load_module(module_name)
932+
mod = loader.find_module(module_name).load_module(module_name) # type: ignore
898933
module_md = generator.module2md(mod)
899934
if stdout_mode:
900935
print(module_md)
@@ -963,7 +998,7 @@ def generate_docs(
963998
if module_name.split(".")[-1].startswith("_"):
964999
continue
9651000
try:
966-
mod = loader.find_module(module_name).load_module(
1001+
mod = loader.find_module(module_name).load_module( # type: ignore
9671002
module_name
9681003
)
9691004
module_md = generator.module2md(mod)

0 commit comments

Comments
 (0)