Skip to content

Commit eb5e24a

Browse files
cursoragentipetrov
andcommitted
Add ConnectionNodeObjectEncoder to preserve .NET members
Co-authored-by: ipetrov <[email protected]>
1 parent e8a86ed commit eb5e24a

File tree

2 files changed

+82
-4
lines changed

2 files changed

+82
-4
lines changed

DSPythonNet3/DSPythonNet3Evaluator.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -514,12 +514,17 @@ def write(self, text):
514514
/// </summary>
515515
private static void InitializeEncoders()
516516
{
517-
var shared = new object[] { new ListEncoderDecoder() };
518-
var encoders = shared.Cast<IPyObjectEncoder>().ToArray();
519-
var decoders = shared.Cast<IPyObjectDecoder>().Concat(new IPyObjectDecoder[]
517+
var listEncoderDecoder = new ListEncoderDecoder();
518+
var encoders = new IPyObjectEncoder[]
520519
{
520+
listEncoderDecoder,
521+
new ConnectionNodeObjectEncoder()
522+
};
523+
var decoders = new IPyObjectDecoder[]
524+
{
525+
listEncoderDecoder,
521526
new DictionaryDecoder()
522-
}).ToArray();
527+
};
523528
Array.ForEach(encoders, e => PyObjectConversions.RegisterEncoder(e));
524529
Array.ForEach(decoders, d => PyObjectConversions.RegisterDecoder(d));
525530
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using System;
2+
using System.Collections;
3+
using System.Reflection;
4+
using Dynamo.Utilities;
5+
using Python.Runtime;
6+
7+
namespace DSPythonNet3.Encoders
8+
{
9+
/// <summary>
10+
/// Prevents Python.NET from auto-encoding certain IEnumerable-based host objects
11+
/// (notably Dynamo for Revit "ConnectionNode") into Python iterables, which
12+
/// strips away callable .NET members like SubNodesOfSize / ExistingConnections.
13+
/// </summary>
14+
internal sealed class ConnectionNodeObjectEncoder : IPyObjectEncoder
15+
{
16+
private static readonly string[] methodNames = new[]
17+
{
18+
"SubNodesOfSize",
19+
"ExistingConnections"
20+
};
21+
22+
public bool CanEncode(Type type)
23+
{
24+
// Don't intercept common primitives / containers.
25+
if (type == typeof(string))
26+
{
27+
return false;
28+
}
29+
30+
if (!typeof(IEnumerable).IsAssignableFrom(type))
31+
{
32+
return false;
33+
}
34+
35+
// Let our existing List encoder/decoder handle IList.
36+
if (typeof(IList).IsAssignableFrom(type))
37+
{
38+
return false;
39+
}
40+
41+
// Don't interfere with dictionary encoding.
42+
if (typeof(IDictionary).IsAssignableFrom(type))
43+
{
44+
return false;
45+
}
46+
47+
// Target by type name (no hard reference to Revit/DynamoRevit assemblies).
48+
if (type.Name.Contains("ConnectionNode", StringComparison.OrdinalIgnoreCase))
49+
{
50+
return true;
51+
}
52+
53+
// Target by known member names used in graphs.
54+
const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public;
55+
foreach (var name in methodNames)
56+
{
57+
if (type.GetMethod(name, flags) != null)
58+
{
59+
return true;
60+
}
61+
}
62+
63+
return false;
64+
}
65+
66+
public PyObject TryEncode(object value)
67+
{
68+
// Wrap as CLR object so methods remain callable in Python.
69+
return PyObject.FromManagedObject(value);
70+
}
71+
}
72+
}
73+

0 commit comments

Comments
 (0)