Skip to content

Commit 190b79b

Browse files
committed
added proper doc strings to field setters/getters etc.
1 parent 87017d6 commit 190b79b

1 file changed

Lines changed: 80 additions & 10 deletions

File tree

stubgen/dotnet_stubs.py

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@
2020
- Preserve actual pythonnet runtime behavior in generated stubs
2121
2222
Usage:
23-
python dotnet_stubsv3.py <dll_folder> [output_folder]
23+
python dotnet_stubs.py <dll_folder> [output_folder]
2424
2525
Example:
26-
python dotnet_stubsv3.py "C:\Program Files\Varian\RTM\18.0\esapi\API" stubs_v3
26+
python dotnet_stubs.py "C:\Program Files\Varian\RTM\18.0\esapi\API" stubs
2727
"""
2828

2929
import sys
@@ -99,6 +99,8 @@ class MethodInfo:
9999
is_constructor: bool = False
100100
is_generic: bool = False
101101
generic_parameters: List[str] = field(default_factory=list)
102+
# Store original .NET parameter types for XML documentation lookup
103+
original_parameter_types: List[str] = field(default_factory=list)
102104

103105

104106
@dataclass
@@ -654,19 +656,38 @@ def _extract_assembly_types(self, assembly) -> Dict[str, TypeInfo]:
654656
try:
655657
# Get all types from assembly
656658
assembly_types = assembly.GetTypes()
659+
logger.info(f"Found {len(assembly_types)} total types in assembly {assembly.GetName().Name}")
660+
661+
public_count = 0
662+
skipped_count = 0
663+
extracted_count = 0
657664

658665
for net_type in assembly_types:
659666
try:
667+
# Check if type is public
668+
if hasattr(net_type, 'IsPublic') and not net_type.IsPublic:
669+
continue
670+
671+
public_count += 1
672+
660673
# Skip compiler-generated and private types
661674
if self._should_skip_type(net_type):
675+
skipped_count += 1
676+
logger.debug(f"Skipping type: {net_type.FullName}")
662677
continue
663678

664679
type_info = self._extract_type_info(net_type)
665680
if type_info:
666681
types[type_info.full_name] = type_info
682+
extracted_count += 1
683+
logger.debug(f"Extracted type: {type_info.full_name}")
684+
else:
685+
logger.debug(f"Failed to extract type info for: {net_type.FullName}")
667686

668687
except Exception as e:
669688
logger.debug(f"Failed to extract type info for {net_type}: {e}")
689+
690+
logger.info(f"Assembly {assembly.GetName().Name}: {public_count} public types, {skipped_count} skipped, {extracted_count} extracted")
670691

671692
except Exception as e:
672693
logger.debug(f"Failed to get types from assembly {assembly}: {e}")
@@ -747,6 +768,8 @@ def _extract_type_info(self, net_type) -> Optional[TypeInfo]:
747768
simple_name = net_type.Name
748769
namespace = net_type.Namespace or ""
749770

771+
logger.debug(f"Processing type: {full_name}")
772+
750773
# Handle nested types
751774
if '+' in full_name:
752775
full_name = full_name.replace('+', '.')
@@ -761,6 +784,8 @@ def _extract_type_info(self, net_type) -> Optional[TypeInfo]:
761784
is_enum = hasattr(net_type, 'IsEnum') and net_type.IsEnum
762785
is_struct = hasattr(net_type, 'IsValueType') and net_type.IsValueType and not is_enum
763786

787+
logger.debug(f"Type {full_name}: class={is_class}, interface={is_interface}, enum={is_enum}, struct={is_struct}")
788+
764789
# Get base type
765790
base_type = None
766791
if hasattr(net_type, 'BaseType') and net_type.BaseType:
@@ -799,10 +824,13 @@ def _extract_type_info(self, net_type) -> Optional[TypeInfo]:
799824
self._extract_fields(net_type, type_info)
800825
self._extract_constructors(net_type, type_info)
801826

827+
logger.debug(f"Successfully extracted type info for {full_name}")
802828
return type_info
803829

804830
except Exception as e:
805-
logger.debug(f"Failed to extract type info for {net_type}: {e}")
831+
logger.warning(f"Failed to extract type info for {net_type}: {e}")
832+
import traceback
833+
logger.debug(traceback.format_exc())
806834
return None
807835

808836
def _extract_properties(self, net_type, type_info: TypeInfo):
@@ -862,14 +890,15 @@ def _should_include_method(self, method, type_info: TypeInfo, docs: Dict[str, Do
862890

863891
def _extract_methods(self, net_type, type_info: TypeInfo):
864892
"""Extract method information"""
865-
logger.debug(f"Considering method: {method.Name}, IsSpecialName: {method.IsSpecialName}, IsPublic: {method.IsPublic}, IsStatic: {method.IsStatic}")
866893
try:
867894
# Get all methods (public only, including inherited methods for complete API surface)
868895
binding_flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static
869896
methods = net_type.GetMethods(binding_flags)
870897

871898
for method in methods:
872899
try:
900+
logger.debug(f"Considering method: {method.Name}, IsSpecialName: {method.IsSpecialName}, IsPublic: {method.IsPublic}, IsStatic: {method.IsStatic}")
901+
873902
# Use the filtering method to determine if this method should be included
874903
if not self._should_include_method(method, type_info, self.docs):
875904
continue
@@ -886,6 +915,10 @@ def _extract_methods(self, net_type, type_info: TypeInfo):
886915
param_name = param.Name or f"param{param.Position}"
887916
param_type = NetTypeToPythonConverter.convert_type(param.ParameterType)
888917

918+
# Store original .NET type name for XML documentation lookup
919+
original_type_name = param.ParameterType.FullName or str(param.ParameterType)
920+
method_info.original_parameter_types.append(original_type_name)
921+
889922
param_info = ParameterInfo(
890923
name=self._sanitize_identifier(param_name),
891924
type_str=param_type,
@@ -1701,7 +1734,7 @@ def _generate_class_stub(self, type_info: TypeInfo) -> List[str]:
17011734

17021735
# Generate fields as class variables
17031736
for field_name, field_info in sorted(type_info.fields.items()):
1704-
lines.extend(self._generate_field_stub(field_info))
1737+
lines.extend(self._generate_field_stub(type_info, field_info))
17051738

17061739
# Ensure class has at least one member
17071740
if len(lines) <= 3: # Just class def, docstring, and empty line
@@ -1730,6 +1763,15 @@ def _generate_enum_stub(self, type_info: TypeInfo) -> List[str]:
17301763
for field_name, field_info in sorted(type_info.fields.items()):
17311764
if field_info.is_static:
17321765
sanitized_field_name = self._sanitize_identifier(field_name)
1766+
1767+
# Look up field documentation for enum values
1768+
field_doc_key = f"{type_info.full_name}.{field_info.name}"
1769+
doc_info = self.docs.get(field_doc_key)
1770+
1771+
# Add documentation comment if available
1772+
if doc_info and doc_info.summary:
1773+
lines.append(f" # {doc_info.summary}")
1774+
17331775
lines.append(f" {sanitized_field_name}: {type_info.simple_name}")
17341776

17351777
if not type_info.fields:
@@ -1801,7 +1843,13 @@ def _generate_property_stub(self, type_info: TypeInfo, prop_info: PropertyInfo)
18011843
lines.append(f" def {prop_name}(cls, value: {prop_info.type_str}) -> None:")
18021844
else:
18031845
lines.append(f" def {prop_name}(self, value: {prop_info.type_str}) -> None:")
1804-
lines.append(" \"\"\"Set property value.\"\"\"")
1846+
1847+
# Use the same documentation for setter as getter, but modify it slightly
1848+
if doc_info and doc_info.summary:
1849+
setter_docstring = f'"""Set {prop_info.type_str}: {doc_info.summary}"""'
1850+
else:
1851+
setter_docstring = f'"""Set {prop_info.type_str}: Property setter."""'
1852+
lines.append(f" {setter_docstring}")
18051853
lines.append(" ...")
18061854

18071855
return lines
@@ -1849,21 +1897,43 @@ def _generate_method_stub(self, type_info: TypeInfo, method_info: MethodInfo, is
18491897
params_str = ", ".join(params)
18501898
lines.append(f" def {method_name}({params_str}) -> {method_info.return_type_str}:")
18511899

1852-
# Method docstring
1853-
method_doc_key = f"{type_info.full_name}.{method_info.name}"
1900+
# Method docstring - build documentation key with parameter types for overload support
1901+
if method_info.original_parameter_types:
1902+
# Build full method signature for XML documentation lookup (like XML format)
1903+
param_types_str = ",".join(method_info.original_parameter_types)
1904+
method_doc_key = f"{type_info.full_name}.{method_info.name}({param_types_str})"
1905+
else:
1906+
# Fallback to simple key for parameterless methods
1907+
method_doc_key = f"{type_info.full_name}.{method_info.name}"
1908+
18541909
doc_info = self.docs.get(method_doc_key)
1910+
1911+
# If no exact match found, try simple key as fallback
1912+
if not doc_info:
1913+
simple_key = f"{type_info.full_name}.{method_info.name}"
1914+
doc_info = self.docs.get(simple_key)
1915+
18551916
docstring = DocstringGenerator.generate_method_docstring(method_info, doc_info)
18561917
lines.append(f" {docstring}")
18571918
lines.append(" ...")
18581919

18591920
return lines
18601921

1861-
def _generate_field_stub(self, field_info: FieldInfo) -> List[str]:
1862-
"""Generate field stub as class variable"""
1922+
def _generate_field_stub(self, type_info: TypeInfo, field_info: FieldInfo) -> List[str]:
1923+
"""Generate field stub as class variable with documentation"""
18631924
lines = []
18641925

18651926
field_name = self._sanitize_identifier(field_info.name)
18661927

1928+
# Look up field documentation
1929+
field_doc_key = f"{type_info.full_name}.{field_info.name}"
1930+
doc_info = self.docs.get(field_doc_key)
1931+
1932+
# Add documentation comment if available
1933+
if doc_info and doc_info.summary:
1934+
# Add docstring comment above the field annotation
1935+
lines.append(f" # {doc_info.summary}")
1936+
18671937
if field_info.is_static:
18681938
lines.append(f" {field_name}: {field_info.type_str}")
18691939
else:

0 commit comments

Comments
 (0)