Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ We will try to keep this up-to-date with pythonnet and upstream changes that mig
Changes relative to pythonnet:

* Revert of `#1240 <https://github.com/pythonnet/pythonnet/pull/1240>`_.
* Enum REPR `#2239 <https://github.com/pythonnet/pythonnet/pull/2239>`_ is included in this release of version 3.0.2, but is unreleased in pythonnet
* Opt-into explicit interface wrapping, `#19 <https://github.com/ansys/ansys-pythonnet/pull/19>`_. This opts into the behavior that became the default in #1240 if ToPythonAs<T> is explicitly used
* Option to bind explicit interface implementations, `#23 <https://github.com/ansys/ansys-pythonnet/pull/23>`_. This provides a runtime option to expose C# explicit interface implementations to Python.
72 changes: 72 additions & 0 deletions src/runtime/BindingOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Python.Runtime
{
public class BindingOptions
{
private bool _SuppressDocs = false;
private bool _SuppressOverloads = false;
private bool _AllowExplicitInterfaceImplementation = false;

//[ModuleProperty]
public bool SuppressDocs
{
get { return _SuppressDocs; }
set { _SuppressDocs = value; }
}

//[ModuleProperty]
public bool SuppressOverloads
{
get { return _SuppressOverloads; }
set { _SuppressOverloads = value; }
}

public bool AllowExplicitInterfaceImplementation
{
get { return _AllowExplicitInterfaceImplementation; }
set { _AllowExplicitInterfaceImplementation = value; }
}
}

public class BindingManager
{
static IDictionary<Type, BindingOptions> _typeOverrides = new Dictionary<Type, BindingOptions>();
static IDictionary<Assembly, BindingOptions> _assemblyOverrides = new Dictionary<Assembly, BindingOptions>();
static BindingOptions _defaultBindingOptions = new BindingOptions();

public static BindingOptions GetBindingOptions(Type type)
{
if (_typeOverrides.ContainsKey(type))
{
return _typeOverrides[type];
}

if (_assemblyOverrides.ContainsKey(type.Assembly))
{
return _assemblyOverrides[type.Assembly];
}
return _defaultBindingOptions;
}

public static BindingOptions DefaultBindingOptions => _defaultBindingOptions;

public static void SetBindingOptions(Type type, BindingOptions options)
{
_typeOverrides[type] = options;
}

public static void SetBindingOptions(Assembly assembly, BindingOptions options)
{
_assemblyOverrides[assembly] = options;
}

public static void Clear()
{
_typeOverrides.Clear();
_assemblyOverrides.Clear();
}
}
}
55 changes: 40 additions & 15 deletions src/runtime/ClassManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,9 @@ internal static void InitClassBase(Type type, ClassBase impl, ReflectedClrType p
// information, including generating the member descriptors
// that we'll be putting in the Python class __dict__.

ClassInfo info = GetClassInfo(type, impl);
var bindingOptions = BindingManager.GetBindingOptions(type);

ClassInfo info = GetClassInfo(type, impl, bindingOptions);

impl.indexer = info.indexer;
impl.richcompare.Clear();
Expand Down Expand Up @@ -254,7 +256,7 @@ internal static void InitClassBase(Type type, ClassBase impl, ReflectedClrType p
if (co.NumCtors > 0 && !co.HasCustomNew())
{
// Implement Overloads on the class object
if (!CLRModule._SuppressOverloads)
if (!bindingOptions.SuppressOverloads)
{
// HACK: __init__ points to instance constructors.
// When unbound they fully instantiate object, so we get overloads for free from MethodBinding.
Expand All @@ -265,7 +267,7 @@ internal static void InitClassBase(Type type, ClassBase impl, ReflectedClrType p
}

// don't generate the docstring if one was already set from a DocStringAttribute.
if (!CLRModule._SuppressDocs && doc.IsNull())
if (!bindingOptions.SuppressDocs && doc.IsNull())
{
doc = co.GetDocString();
Runtime.PyDict_SetItem(dict, PyIdentifier.__doc__, doc.Borrow());
Expand All @@ -288,10 +290,20 @@ internal static void InitClassBase(Type type, ClassBase impl, ReflectedClrType p
Runtime.PyType_Modified(pyType.Reference);
}

internal static bool ShouldBindMethod(MethodBase mb)
internal static bool ShouldBindMethod(MethodBase mb, BindingOptions bindingOptions)
{
if (mb is null) throw new ArgumentNullException(nameof(mb));
return (mb.IsPublic || mb.IsFamily || mb.IsFamilyOrAssembly);

bool rejectByVisibility = false;
if (!mb.IsPublic)
{
if (bindingOptions.AllowExplicitInterfaceImplementation)
// detect explicit interface implementation
rejectByVisibility = !mb.IsVirtual;
else
rejectByVisibility = true;
}
return (!rejectByVisibility || mb.IsFamily || mb.IsFamilyOrAssembly);
}

internal static bool ShouldBindField(FieldInfo fi)
Expand All @@ -300,7 +312,7 @@ internal static bool ShouldBindField(FieldInfo fi)
return (fi.IsPublic || fi.IsFamily || fi.IsFamilyOrAssembly);
}

internal static bool ShouldBindProperty(PropertyInfo pi)
internal static bool ShouldBindProperty(PropertyInfo pi, BindingOptions bindingOptions)
{
MethodInfo? mm;
try
Expand All @@ -323,16 +335,27 @@ internal static bool ShouldBindProperty(PropertyInfo pi)
return false;
}

return ShouldBindMethod(mm);
return ShouldBindMethod(mm, bindingOptions);
}

internal static bool ShouldBindEvent(EventInfo ei)
internal static bool ShouldBindEvent(EventInfo ei, BindingOptions bindingOptions)
{
return ei.GetAddMethod(true) is { } add && ShouldBindMethod(add);
return ei.GetAddMethod(true) is { } add && ShouldBindMethod(add, bindingOptions);
}

private static ClassInfo GetClassInfo(Type type, ClassBase impl)
private static string SanitizeName(string name)
{
// For explicit interface implementation, the name of the attribute will be:
// `Class.Interface.Attribute`
// In that case, only use the last token for the real attribute name to bind.
if (name.Contains("."))
name = name.Substring(name.LastIndexOf(".")+1);
return name;
}

private static ClassInfo GetClassInfo(Type type, ClassBase impl, BindingOptions bindingOptions)
{
var typeName = type.Name;
var ci = new ClassInfo();
var methods = new Dictionary<string, List<MethodBase>>();
MethodInfo meth;
Expand Down Expand Up @@ -436,11 +459,12 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl)
{
case MemberTypes.Method:
meth = (MethodInfo)mi;
if (!ShouldBindMethod(meth))
if (!ShouldBindMethod(meth, bindingOptions))
{
continue;
}
name = meth.Name;

name = SanitizeName(meth.Name);

//TODO mangle?
if (name == "__init__" && !impl.HasCustomNew())
Expand Down Expand Up @@ -471,7 +495,7 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl)
case MemberTypes.Property:
var pi = (PropertyInfo)mi;

if(!ShouldBindProperty(pi))
if(!ShouldBindProperty(pi, bindingOptions))
{
continue;
}
Expand All @@ -490,8 +514,9 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl)
continue;
}

var propertyName = SanitizeName(pi.Name);
ob = new PropertyObject(pi);
ci.members[pi.Name] = ob.AllocObject();
ci.members[propertyName] = ob.AllocObject();
continue;

case MemberTypes.Field:
Expand All @@ -506,7 +531,7 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl)

case MemberTypes.Event:
var ei = (EventInfo)mi;
if (!ShouldBindEvent(ei))
if (!ShouldBindEvent(ei, bindingOptions))
{
continue;
}
Expand Down
9 changes: 5 additions & 4 deletions src/runtime/StateSerialization/MaybeMemberInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,10 @@ internal MaybeMemberInfo(SerializationInfo serializationInfo, StreamingContext c
var tp = Type.GetType(serializationInfo.GetString(SerializationType));
if (tp != null)
{
var bindingOptions = BindingManager.GetBindingOptions(tp);
var memberName = serializationInfo.GetString(SerializationMemberName);
MemberInfo? mi = Get(tp, memberName, ClassManager.BindingFlags);
if (mi != null && ShouldBindMember(mi))
if (mi != null && ShouldBindMember(mi, bindingOptions))
{
info = mi;
}
Expand All @@ -92,19 +93,19 @@ internal MaybeMemberInfo(SerializationInfo serializationInfo, StreamingContext c
// based on it's setter/getter (which is a method
// info) visibility and events based on their
// AddMethod visibility.
static bool ShouldBindMember(MemberInfo mi)
static bool ShouldBindMember(MemberInfo mi, BindingOptions bindingOptions)
{
if (mi is PropertyInfo pi)
{
return ClassManager.ShouldBindProperty(pi);
return ClassManager.ShouldBindProperty(pi, bindingOptions);
}
else if (mi is FieldInfo fi)
{
return ClassManager.ShouldBindField(fi);
}
else if (mi is EventInfo ei)
{
return ClassManager.ShouldBindEvent(ei);
return ClassManager.ShouldBindEvent(ei, bindingOptions);
}

return false;
Expand Down
4 changes: 3 additions & 1 deletion src/runtime/StateSerialization/MaybeMethodBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,11 @@ static MethodBase ScanForMethod(Type declaringType, string name, int genericCoun

var visibility = flags & MaybeMethodFlags.Visibility;

var bindingOptions = BindingManager.GetBindingOptions(declaringType);

var result = alternatives.Cast<MethodBase>().FirstOrDefault(m
=> MatchesGenericCount(m, genericCount) && MatchesSignature(m, parameters)
&& (Visibility(m) == visibility || ClassManager.ShouldBindMethod(m)));
&& (Visibility(m) == visibility || ClassManager.ShouldBindMethod(m, bindingOptions)));

if (result is null)
throw new MissingMethodException($"Matching overload not found for {declaringType}.{name}");
Expand Down
13 changes: 4 additions & 9 deletions src/runtime/Types/ClrModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ internal class CLRModule : ModuleObject
protected static bool interactive_preload = true;
internal static bool preload;
// XXX Test performance of new features //
internal static bool _SuppressDocs = false;
internal static bool _SuppressOverloads = false;

static CLRModule()
Expand All @@ -39,10 +38,6 @@ public static void Reset()
{
interactive_preload = true;
preload = false;

// XXX Test performance of new features //
_SuppressDocs = false;
_SuppressOverloads = false;
}

/// <summary>
Expand Down Expand Up @@ -82,15 +77,15 @@ public static void setPreload(bool preloadFlag)
//[ModuleProperty]
public static bool SuppressDocs
{
get { return _SuppressDocs; }
set { _SuppressDocs = value; }
get { return BindingManager.DefaultBindingOptions.SuppressDocs; }
set { BindingManager.DefaultBindingOptions.SuppressDocs = value; }
}

//[ModuleProperty]
public static bool SuppressOverloads
{
get { return _SuppressOverloads; }
set { _SuppressOverloads = value; }
get { return BindingManager.DefaultBindingOptions.SuppressOverloads; }
set { BindingManager.DefaultBindingOptions.SuppressOverloads = value; }
}

[ModuleFunction]
Expand Down
16 changes: 16 additions & 0 deletions src/testing/propertytest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
namespace Python.Test
{
public interface IInherited {
int InheritedProperty { get; }
}

/// <summary>
/// Supports units tests for property access.
/// </summary>
Expand Down Expand Up @@ -80,5 +84,17 @@ public ShortEnum EnumProperty
get { return _enum_property; }
set { _enum_property = value; }
}

public int InheritedProperty => 1;
}

public class PropertyTest2 : IInherited
{
int IInherited.InheritedProperty => 2;
}

public class PropertyTest3 : IInherited
{
int IInherited.InheritedProperty => 3;
}
}
12 changes: 11 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,17 @@ def pytest_configure(config):
import clr

sys.path.append(str(bin_path))
clr.AddReference("Python.Test")
python_test_module = clr.AddReference("Python.Test")
configure_custom_binding_options(python_test_module)


def configure_custom_binding_options(python_test_module):
from Python.Runtime import BindingManager, BindingOptions
binding_options = BindingOptions()
binding_options.AllowExplicitInterfaceImplementation = True
prop_test_3_type = [t for t in python_test_module.GetTypes() if "PropertyTest3" == t.Name][0]

BindingManager.SetBindingOptions(prop_test_3_type, binding_options)


def pytest_unconfigure(config):
Expand Down
11 changes: 10 additions & 1 deletion tests/test_property.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"""Test CLR property support."""

import pytest
from Python.Test import PropertyTest

from Python.Test import PropertyTest, PropertyTest2, PropertyTest3

def test_public_instance_property():
"""Test public instance properties."""
Expand Down Expand Up @@ -93,6 +93,15 @@ def test_private_property():
with pytest.raises(AttributeError):
_ = PropertyTest.PrivateStaticProperty

def test_inherited_property():
"""Test inherited properties."""

assert PropertyTest().InheritedProperty == 1
with pytest.raises(AttributeError):
_ = PropertyTest2().InheritedProperty

assert PropertyTest3().InheritedProperty == 3


def test_property_descriptor_get_set():
"""Test property descriptor get / set."""
Expand Down
Loading