mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-26 10:11:46 +00:00
Fix https://github.com/IfcOpenShell/IfcOpenShell/issues/1663 : Extract Docs is bundled with IfcPatch module
This commit is contained in:
@@ -1,101 +0,0 @@
|
|||||||
|
|
||||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
|
||||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
|
||||||
#
|
|
||||||
# This file is part of BlenderBIM Add-on.
|
|
||||||
#
|
|
||||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import typing
|
|
||||||
import inspect
|
|
||||||
import collections
|
|
||||||
import importlib
|
|
||||||
from types import ModuleType
|
|
||||||
|
|
||||||
def extract_docs(
|
|
||||||
module: ModuleType,
|
|
||||||
submodule_name: str,
|
|
||||||
cls_name: str,
|
|
||||||
method_name: str,
|
|
||||||
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"{module.__path__[0]}/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 complete because : {str(e)}")
|
|
||||||
except:
|
|
||||||
print(f"Error : IFCPatch {str(submodule)} could not load")
|
|
||||||
|
|
||||||
def _extract_docs(cls, method_name, boilerplate_args):
|
|
||||||
inputs = collections.OrderedDict()
|
|
||||||
method = getattr(cls, method_name)
|
|
||||||
node_data = {"class": cls}
|
|
||||||
|
|
||||||
signature = inspect.signature(method)
|
|
||||||
for name, parameter in signature.parameters.items():
|
|
||||||
if name == "self":
|
|
||||||
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 name, socket_data in inputs.items():
|
|
||||||
type_hint = type_hints.get(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[name]["type"] = [t.__name__ for t in typing.get_args(type_hint)]
|
|
||||||
else:
|
|
||||||
inputs[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:
|
|
||||||
node_data["name"] = line
|
|
||||||
elif line.startswith(":return:"):
|
|
||||||
node_data["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
|
|
||||||
|
|
||||||
node_data["description"] = description.strip()
|
|
||||||
node_data["inputs"] = inputs
|
|
||||||
|
|
||||||
if boilerplate_args is not None:
|
|
||||||
for arg in boilerplate_args: # Remove boilerplate arguments
|
|
||||||
node_data["inputs"].pop(arg, None)
|
|
||||||
return node_data
|
|
||||||
|
|
||||||
@@ -26,8 +26,6 @@ try:
|
|||||||
except:
|
except:
|
||||||
print("IfcPatch not available")
|
print("IfcPatch not available")
|
||||||
|
|
||||||
from .helper import extract_docs
|
|
||||||
|
|
||||||
|
|
||||||
class SelectIfcPatchInput(bpy.types.Operator):
|
class SelectIfcPatchInput(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_ifc_patch_input"
|
bl_idname = "bim.select_ifc_patch_input"
|
||||||
@@ -102,7 +100,7 @@ class UpdateIfcPatchArguments(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
patch_args = context.scene.BIMPatchProperties.ifc_patch_args_attr
|
patch_args = context.scene.BIMPatchProperties.ifc_patch_args_attr
|
||||||
patch_args.clear()
|
patch_args.clear()
|
||||||
docs = extract_docs(ifcpatch, self.recipe, "Patcher", "__init__", ("src", "file", "logger", "args"))
|
docs = ifcpatch.extract_docs(self.recipe, "Patcher", "__init__", ("src", "file", "logger", "args"))
|
||||||
if docs and "inputs" in docs:
|
if docs and "inputs" in docs:
|
||||||
inputs = docs["inputs"]
|
inputs = docs["inputs"]
|
||||||
for arg_name in inputs:
|
for arg_name in inputs:
|
||||||
|
|||||||
@@ -33,8 +33,6 @@ from bpy.props import (
|
|||||||
FloatVectorProperty,
|
FloatVectorProperty,
|
||||||
CollectionProperty,
|
CollectionProperty,
|
||||||
)
|
)
|
||||||
from .helper import extract_docs
|
|
||||||
from .operator import UpdateIfcPatchArguments
|
|
||||||
|
|
||||||
|
|
||||||
ifcpatchrecipes_enum = []
|
ifcpatchrecipes_enum = []
|
||||||
@@ -54,7 +52,7 @@ def getIfcPatchRecipes(self, context):
|
|||||||
f = str(filename.stem)
|
f = str(filename.stem)
|
||||||
if f == "__init__":
|
if f == "__init__":
|
||||||
continue
|
continue
|
||||||
docs = extract_docs(ifcpatch, f, "Patcher", "__init__", ("src", "file", "logger", "args"))
|
docs = ifcpatch.extract_docs(f, "Patcher", "__init__", ("src", "file", "logger", "args"))
|
||||||
ifcpatchrecipes_enum.append((f, f, docs.get("description","") if docs else ""))
|
ifcpatchrecipes_enum.append((f, f, docs.get("description","") if docs else ""))
|
||||||
return ifcpatchrecipes_enum
|
return ifcpatchrecipes_enum
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,11 @@
|
|||||||
|
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
import typing
|
||||||
|
import inspect
|
||||||
|
import collections
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
|
||||||
def execute(args, is_library=None):
|
def execute(args, is_library=None):
|
||||||
@@ -51,3 +56,78 @@ def execute(args, is_library=None):
|
|||||||
else:
|
else:
|
||||||
ifc_file.write(args["output"])
|
ifc_file.write(args["output"])
|
||||||
print("# All tasks are complete :-)")
|
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