mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-06 12:26:26 +00:00
Fix https://github.com/IfcOpenShell/IfcOpenShell/issues/1663 : Extract Docs is bundled with IfcPatch module
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user