Vous explorez vos objets .NET avec dir() avec Python ? Voici une méthode pour aller un peu plus loin !
#DynamoBIM #Python #RevitAPI
#AutodeskExpertElite #AutodeskCommunity 
if this article is not in your language, use the Google Translate
widget ⬈ (bottom of page for Mobile version ⬇)
La fonction
dir()
en Python affiche uniquement la liste brute des membres de l'objet. Mais
elle ne vous dit rien sur leur utilité, ni sur la logique métier derrière
chaque méthode ou propriété et encore moins pour les objets .Net.
Voici une alternative avec une classe Python
NetType_Utils
qui récupère (parse) la documentation XML de l'Assembly de l'objet à
examiner.
Cette classe inspecte les objets .NET avec 2 étapes principales :
-
inspection des Types (méthodes, propriétés, etc.) avec
System.Reflection
- association de la documentation, à condition que le fichier XML soit généré lors de la compilation.
code de la classe
NetType_Utils
(compatible CPython3 / IronPython3 / PythonNet3 )
# Load the Python Standard and DesignScript Libraries
import sys
import System
import clr
from System.Reflection import BindingFlags
import xml.etree.ElementTree as ET
import os
import re
import difflib
from difflib import get_close_matches
class NetType_Utils:
@staticmethod
def get_Type_Infos(obj):
# sub functions
def get_docstring(prefix, class_name, member_name=None, use_diff_lib=True):
if member_name is None:
doc_key = f"{prefix}:{class_name}"
else:
doc_key = f"{prefix}:{class_name}.{member_name}"
docstring = doc_map.get(doc_key, None)
if docstring is None:
docstring = doc_map.get(doc_key.replace(" ", ""), None)
if docstring is None and use_diff_lib:
filter_name_keys = [x for x in doc_map.keys() if x.startswith(f"{prefix}:{class_name}")]
best_matches = get_close_matches(doc_key, filter_name_keys, n=2, cutoff=0.9)
if best_matches:
docstring = doc_map.get(best_matches[0], None)
return docstring if docstring is not None else "No description available."
############ MAIN ############
try:
cls = obj.GetType()
except:
try:
cls = clr.GetClrType(obj)
except:
raise Exception("Error, the input object is not a Net Object")
#
assembly = System.Reflection.Assembly.GetAssembly(cls)
doc_xml_path = os.path.splitext(assembly.Location)[0] + ".xml"
doc_map = {}
#
try:
if os.path.exists(doc_xml_path):
tree = ET.parse(doc_xml_path)
root = tree.getroot()
for member in root.findall(".//member"):
member_name = member.get('name')
if ".#ctor" in member.get('name'):
member_name = member_name.replace(".#ctor", "").strip()
if not member_name.endswith(")"):
member_name += "()"
summary_node = member.find("summary")
if member_name and summary_node is not None and summary_node.text:
# Clean up whitespace from the docstring
doc_map[member_name] = ' '.join(summary_node.text.strip().split())
if "System." in member_name:
alternative_member_name = member_name.replace("System.", "")
doc_map[alternative_member_name] = ' '.join(summary_node.text.strip().split())
except Exception as e:
# If XML fails, we can't get docs, but the script can still run
pass
flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly
markdown_lines = []
# Class Name and Docstring
markdown_lines.append(f"## {cls.FullName}")
class_doc_key = f"T:{cls.FullName}"
class_doc = doc_map.get(class_doc_key, "")
if class_doc:
markdown_lines.append(f"*{class_doc}*")
# add inheritance
inheritance_types = f"- Inheritance Hierarchy : {cls.FullName}"
current = cls.BaseType
for _ in range(10):
if hasattr(current, "FullName") and not inheritance_types.endswith(current.FullName):
inheritance_types += " -> " + current.FullName
current = current.BaseType
#
markdown_lines.append(inheritance_types)
markdown_lines.append("") # Add a blank line for spacing
# --- Process constructors ---
ctor_infos = cls.GetConstructors (flags)
if ctor_infos:
markdown_lines.append("### Constructors")
for ctor_info in sorted(ctor_infos, key=lambda x: x.Name):
constructor_name = ctor_info.ToString().replace("Void .ctor", cls.FullName).strip()
docstring = get_docstring("M", constructor_name)
markdown_lines.append(f"- `{constructor_name}` — {docstring}")
markdown_lines.append("")
# --- Process Methods ---
methods = cls.GetMethods(flags)
if methods:
markdown_lines.append("### Methods")
for method in sorted(methods, key=lambda x: x.Name):
if not method.Name.startswith(("get_","set_", "op_")):
b_method_name = method.ToString()
return_value = "Void"
is_static = "static " if method.IsStatic else ""
check_patern = re.match(r"^(.+?)\s(.*\(.*\))$", b_method_name)
if check_patern is not None:
return_value = check_patern.group(1)
method_name = check_patern.group(2)
docstring = get_docstring("M", cls.FullName, method_name)
markdown_lines.append(f"- `{is_static}{method_name}` → `{return_value}` — {docstring}")
markdown_lines.append("")
# --- Process Properties ---
properties = cls.GetProperties(flags)
if properties:
markdown_lines.append("### Properties")
for prop in sorted(properties, key=lambda x: x.Name):
docstring = get_docstring("P", cls.FullName, prop.Name)
markdown_lines.append(f"- `{prop.Name}` — {docstring}")
markdown_lines.append("")
# --- Process Enumerations ---
if cls.IsEnum:
enums = cls.GetEnumNames()
if enums:
markdown_lines.append("### Enumerations")
for enum in sorted(enums):
docstring = get_docstring("F", cls.FullName, enum)
markdown_lines.append(f"- `{enum}`— {docstring}")
markdown_lines.append("")
return "\n".join(markdown_lines)
OUT = NetType_Utils
...
Voici en vidéo, un exemple d'utilisation
Note :
Dans cet exemple, nous ne listons pas les membres des classes hérités (cela peut être fait avec la propriété
BaseType
).
Autre Finalité ? :
Outre de pouvoir inspecter les membres des objet.Net, nous pouvons, sur le même principe, générer un RAG de l'API Revit (avec des mots clés ) pour un LLM de façon dynamique.
Vous n'avez donc plus à vous soucier de la version de l'API, puisque vous êtes au sein même d'un contexte Revit.
Test d'un RAG sur un simple Agent IA
Outre de pouvoir inspecter les membres des objet.Net, nous pouvons, sur le même principe, générer un RAG de l'API Revit (avec des mots clés ) pour un LLM de façon dynamique.
Vous n'avez donc plus à vous soucier de la version de l'API, puisque vous êtes au sein même d'un contexte Revit.
« L'avenir est une porte, le passé en est la clé. »
Victor Hugo
0 commentaires:
Enregistrer un commentaire