Skip to content

Commit c94b8b4

Browse files
cursoragentipetrov
andcommitted
Refactor ConnectionNode compatibility patch for Python.NET
Co-authored-by: ipetrov <[email protected]>
1 parent ecdecce commit c94b8b4

File tree

1 file changed

+44
-16
lines changed

1 file changed

+44
-16
lines changed

DSPythonNet3/DSPythonNet3Evaluator.cs

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -804,33 +804,61 @@ private static bool IsMarkedToSkipConversion(PyObject pyObj)
804804
/// </summary>
805805
private static string ConnectionNodeCompatPatchCode()
806806
{
807-
// Keep this snippet extremely defensive: it should be a no-op if the library isn't present
808-
// or if the runtime forbids monkey-patching reflected CLR types.
807+
// Keep this snippet extremely defensive: it should be a no-op if the library isn't present.
808+
// IMPORTANT: Many runtimes forbid setting attributes on CLR *types*. However, Python.NET
809+
// typically allows attaching attributes to CLR *instances* via the wrapper's __dict__.
810+
// So we patch instances coming in through IN (recursively for nested lists).
809811
return $@"
810812
import builtins as __builtins__
811-
if not getattr(__builtins__, '{ConnectionNodeCompatSentinel}', False):
812-
try:
813-
from AdvanceSteel.ConnectionAutomation.Nodes import ConnectionNode as __ConnectionNode
814813
815-
def __bind_node_first_static(__name):
814+
try:
815+
from AdvanceSteel.ConnectionAutomation.Nodes import ConnectionNode as __ConnectionNode
816+
except Exception:
817+
__ConnectionNode = None
818+
819+
if __ConnectionNode is not None:
820+
if not getattr(__builtins__, '{ConnectionNodeCompatSentinel}', False):
821+
def __dspynet3__patch_connnode_instance(__obj):
822+
# Attach instance-callable wrappers for node-first static methods.
816823
try:
817-
__orig = getattr(__ConnectionNode, __name)
824+
if not isinstance(__obj, __ConnectionNode):
825+
return
826+
827+
# Only patch if missing or still the raw reflected method.
828+
def __subnodes(__n, __obj=__obj):
829+
return __ConnectionNode.SubNodesOfSize(__obj, __n)
830+
831+
def __existing(__obj=__obj):
832+
return __ConnectionNode.ExistingConnections(__obj)
833+
834+
try:
835+
setattr(__obj, 'SubNodesOfSize', __subnodes)
836+
except Exception:
837+
pass
838+
try:
839+
setattr(__obj, 'ExistingConnections', __existing)
840+
except Exception:
841+
pass
818842
except Exception:
819-
return
820-
821-
def __inst(self, *args, __orig=__orig, **kwargs):
822-
return __orig(self, *args, **kwargs)
843+
pass
823844
845+
def __dspynet3__walk_and_patch(__x):
824846
try:
825-
setattr(__ConnectionNode, __name, __inst)
847+
if isinstance(__x, (list, tuple)):
848+
for __i in __x:
849+
__dspynet3__walk_and_patch(__i)
850+
else:
851+
__dspynet3__patch_connnode_instance(__x)
826852
except Exception:
827-
# Some runtimes may forbid setting attributes on CLR types.
828853
pass
829854
830-
__bind_node_first_static('SubNodesOfSize')
831-
__bind_node_first_static('ExistingConnections')
832-
855+
setattr(__builtins__, '__dspynet3__walk_and_patch_connnode', __dspynet3__walk_and_patch)
833856
setattr(__builtins__, '{ConnectionNodeCompatSentinel}', True)
857+
858+
# Patch current inputs for this evaluation (IN exists in the module scope).
859+
try:
860+
__inp = globals().get('IN', None)
861+
__builtins__.__dspynet3__walk_and_patch_connnode(__inp)
834862
except Exception:
835863
pass
836864
";

0 commit comments

Comments
 (0)