Move all reflection logic inside helper module

This commit is contained in:
Gorgious
2021-08-04 22:26:58 +02:00
parent f283d2ca3d
commit 72f04a361d
3 changed files with 51 additions and 47 deletions
@@ -1,14 +1,40 @@
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("Error : " + str(e) + "in " + str(submodule))
def extract_docs(patcher, boilerplate_args=None):
def _extract_docs(cls, method_name, boilerplate_args):
inputs = collections.OrderedDict()
function_init = patcher.__init__
node_data = {"patcher": patcher}
method = getattr(cls, method_name)
node_data = {"class": cls}
signature = inspect.signature(function_init)
signature = inspect.signature(method)
for name, parameter in signature.parameters.items():
if name == "self":
continue
@@ -16,7 +42,7 @@ def extract_docs(patcher, boilerplate_args=None):
if isinstance(parameter.default, (str, float, int, bool)):
inputs[name]["default"] = parameter.default
type_hints = typing.get_type_hints(function_init)
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 ??)
@@ -27,7 +53,7 @@ def extract_docs(patcher, boilerplate_args=None):
inputs[name]["type"] = type_hint.__name__
description = ""
doc = function_init.__doc__
doc = method.__doc__
if doc is not None:
for i, line in enumerate(doc.split("\n")):
line = line.strip()
@@ -1,6 +1,7 @@
import os
import bpy
import json
import ifcpatch
from .helper import extract_docs
@@ -47,7 +48,7 @@ class ExecuteIfcPatch(bpy.types.Operator):
return os.path.isfile(input_file) and "ifc" in os.path.splitext(input_file)[1]
def execute(self, context):
import ifcpatch
ifcpatch.execute(
{
@@ -73,37 +74,22 @@ class PopulatePatchArguments(bpy.types.Operator):
recipe: bpy.props.StringProperty()
def execute(self, context):
import importlib
import ifcpatch
patch_args = context.scene.BIMPatchProperties.ifc_patch_args_attr
patch_args.clear()
recipe = self.recipe
spec = importlib.util.spec_from_file_location(recipe, f"{ifcpatch.__path__[0]}/recipes/{recipe}.py")
patcher = importlib.util.module_from_spec(spec)
try:
spec.loader.exec_module(patcher)
try:
docs = extract_docs(patcher.Patcher, boilerplate_args=("src", "file", "logger", "args"))
if "inputs" in docs:
for arg_name in docs["inputs"]:
arg_info = docs["inputs"][arg_name]
new_attr = patch_args.add()
new_attr.data_type = self.TYPE_BINDINGS[arg_info["type"]]
new_attr.name = arg_name
if new_attr.data_type == "string":
new_attr.string_value = arg_info.get("default", "")
elif new_attr.data_type == "float":
new_attr.float_value = arg_info.get("default", 0)
elif new_attr.data_type == "integer":
new_attr.int_value = arg_info.get("default", 0)
elif new_attr.data_type == "boolean":
new_attr.bool_value = arg_info.get("default", False)
new_attr.description =arg_info.get("description", "")
except AttributeError as e:
print(e)
except ModuleNotFoundError as e:
print("Error : " + str(e) + "in " + str(patcher))
docs = extract_docs(ifcpatch, self.recipe, "Patcher", "__init__", ("src", "file", "logger", "args"))
if "inputs" in docs:
for arg_name in docs["inputs"]:
arg_info = docs["inputs"][arg_name]
new_attr = patch_args.add()
new_attr.data_type = self.TYPE_BINDINGS[arg_info["type"]]
new_attr.name = arg_name
if new_attr.data_type == "string":
new_attr.string_value = arg_info.get("default", "")
elif new_attr.data_type == "float":
new_attr.float_value = arg_info.get("default", 0)
elif new_attr.data_type == "integer":
new_attr.int_value = arg_info.get("default", 0)
elif new_attr.data_type == "boolean":
new_attr.bool_value = arg_info.get("default", False)
new_attr.description = arg_info.get("description", "")
return {"FINISHED"}
@@ -26,15 +26,7 @@ class BIM_PT_patch(bpy.types.Panel):
recipe = props.ifc_patch_recipes
spec = importlib.util.spec_from_file_location(recipe, f"{ifcpatch.__path__[0]}/recipes/{recipe}.py")
patcher = importlib.util.module_from_spec(spec)
try:
spec.loader.exec_module(patcher)
try:
docs = extract_docs(patcher.Patcher, boilerplate_args=("src", "file", "logger", "args"))
if "name" in docs:
layout.label(text=docs["name"])
if "description" in docs:
docs = extract_docs(ifcpatch, recipe, "Patcher", "__init__", ("src", "file", "logger", "args"))
for line in docs["description"].split("\n"):
layout.label(text=line)
except AttributeError as e: