Fix https://github.com/IfcOpenShell/IfcOpenShell/issues/1663 : Extract Docs is bundled with IfcPatch module

This commit is contained in:
Gorgious
2021-08-17 09:36:53 +02:00
parent 912699e63c
commit 2c270a7315
4 changed files with 82 additions and 107 deletions
+80
View File
@@ -22,6 +22,11 @@
import ifcopenshell
import logging
import os
import typing
import inspect
import collections
import importlib
def execute(args, is_library=None):
@@ -51,3 +56,78 @@ def execute(args, is_library=None):
else:
ifc_file.write(args["output"])
print("# All tasks are complete :-)")
def extract_docs(
submodule_name: str,
cls_name: str,
method_name: str="__init__",
boilerplate_args : typing.Iterable[str]=None):
"""Extract class docstrings and method arguments
:param module: Parent module from which to extract the submodule class
:param submodule_name: Submodule from which to extract the class
:param cls_name: Class from which to extract the docstring and method arguments
:param method_name: Class Method name from which to extract arguments
:param boilerplate_args: String iterable containing arguments that shall not be parsed
"""
spec = importlib.util.spec_from_file_location(
submodule_name,
f"{os.path.dirname(inspect.getabsfile(inspect.currentframe()))}/recipes/{submodule_name}.py")
submodule = importlib.util.module_from_spec(spec)
try:
spec.loader.exec_module(submodule)
try:
return _extract_docs(getattr(submodule, cls_name), method_name, boilerplate_args)
except AttributeError as e:
print(e)
except ModuleNotFoundError as e:
print(f"Error : IFCPatch {str(submodule)} could not load because : {str(e)}")
def _extract_docs(cls, method_name, boilerplate_args):
inputs = collections.OrderedDict()
method = getattr(cls, method_name)
docs = {"class": cls}
if boilerplate_args is None:
boilerplate_args = []
signature = inspect.signature(method)
for name, parameter in signature.parameters.items():
if name == "self" or name in boilerplate_args:
continue
inputs[name] = {"name": name}
if isinstance(parameter.default, (str, float, int, bool)):
inputs[name]["default"] = parameter.default
type_hints = typing.get_type_hints(method)
for input_name in inputs.keys():
type_hint = type_hints.get(input_name, None)
if type_hint is None: # The argument is not type-hinted. (Or hinted to None ??)
continue
if isinstance(type_hint, typing._UnionGenericAlias):
inputs[input_name]["type"] = [t.__name__ for t in typing.get_args(type_hint)]
else:
inputs[input_name]["type"] = type_hint.__name__
description = ""
doc = method.__doc__
if doc is not None:
for i, line in enumerate(doc.split("\n")):
line = line.strip()
if i == 0:
docs["name"] = line
elif line.startswith(":return:"):
docs["output"] = {"name": line.split(":")[2].strip(), "description": line.split(":")[3].strip()}
elif line.startswith(":param"):
param_name = line.split(":")[1].strip().replace("param ", "")
if param_name in inputs:
inputs[param_name]["description"] = line.split(":")[2].strip()
elif i == 2:
description += line
elif i > 2:
description += "\n" + line
docs["description"] = description.strip()
docs["inputs"] = inputs
return docs