From 0a59aec62f8d7bf9f8d1fbe42947b7aed183324a Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Tue, 14 Jan 2025 10:06:38 -0600 Subject: [PATCH 1/2] add pep8 binding option --- src/runtime/BindingOptions.cs | 7 ++++++ src/runtime/ClassManager.cs | 42 +++++++++++++++++++++++++++++++++++ src/testing/methodtest.cs | 1 - src/testing/pep8test.cs | 16 +++++++++++++ tests/conftest.py | 17 ++++++++++++-- tests/test_pep8.py | 31 ++++++++++++++++++++++++++ 6 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 src/testing/pep8test.cs create mode 100644 tests/test_pep8.py diff --git a/src/runtime/BindingOptions.cs b/src/runtime/BindingOptions.cs index 474128e8..25935a50 100644 --- a/src/runtime/BindingOptions.cs +++ b/src/runtime/BindingOptions.cs @@ -9,6 +9,7 @@ public class BindingOptions private bool _SuppressDocs = false; private bool _SuppressOverloads = false; private bool _AllowExplicitInterfaceImplementation = false; + private bool _Pep8Aliases = false; //[ModuleProperty] public bool SuppressDocs @@ -29,6 +30,12 @@ public bool AllowExplicitInterfaceImplementation get { return _AllowExplicitInterfaceImplementation; } set { _AllowExplicitInterfaceImplementation = value; } } + + public bool Pep8Aliases + { + get { return _Pep8Aliases; } + set { _Pep8Aliases = value; } + } } public class BindingManager diff --git a/src/runtime/ClassManager.cs b/src/runtime/ClassManager.cs index 59624664..35329456 100644 --- a/src/runtime/ClassManager.cs +++ b/src/runtime/ClassManager.cs @@ -353,6 +353,29 @@ private static string SanitizeName(string name) return name; } + private static string ToSnakeCase(string str) + { + var strToModify = str; + var prefix = ""; + if (str.StartsWith("get_")) + { + prefix = "get_"; + strToModify = str.Substring(4); + } + if (str.StartsWith("set_")) + { + prefix = "set_"; + strToModify = str.Substring(4); + } + + var result = string.Concat( + strToModify.Select((x, i) => + i > 0 && char.IsUpper(x) && (char.IsLower(strToModify[i - 1]) || i < strToModify.Length - 1 && char.IsLower(strToModify[i + 1])) + ? "_" + x + : x.ToString())).ToLowerInvariant(); + return prefix + result; + } + private static ClassInfo GetClassInfo(Type type, ClassBase impl, BindingOptions bindingOptions) { var typeName = type.Name; @@ -517,6 +540,12 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl, BindingOptions var propertyName = SanitizeName(pi.Name); ob = new PropertyObject(pi); ci.members[propertyName] = ob.AllocObject(); + if (bindingOptions.Pep8Aliases) + { + string pep8PropertyName = ToSnakeCase(propertyName); + if (pep8PropertyName != null && pep8PropertyName != propertyName) + ci.members[pep8PropertyName] = ob.AllocObject(); + } continue; case MemberTypes.Field: @@ -527,6 +556,12 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl, BindingOptions } ob = new FieldObject(fi); ci.members[mi.Name] = ob.AllocObject(); + if (bindingOptions.Pep8Aliases) + { + string pep8FieldName = ToSnakeCase(mi.Name); + if (pep8FieldName != null && pep8FieldName != mi.Name) + ci.members[pep8FieldName] = ob.AllocObject(); + } continue; case MemberTypes.Event: @@ -563,6 +598,13 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl, BindingOptions ob = new MethodObject(type, name, mlist); ci.members[name] = ob.AllocObject(); + if (bindingOptions.Pep8Aliases) + { + string pep8Name = ToSnakeCase(name); + if (pep8Name != null && pep8Name != name) + ci.members[pep8Name] = ob.AllocObject(); + } + if (mlist.Any(OperatorMethod.IsOperatorMethod)) { string pyName = OperatorMethod.GetPyMethodName(name); diff --git a/src/testing/methodtest.cs b/src/testing/methodtest.cs index ec05fef7..d4729292 100644 --- a/src/testing/methodtest.cs +++ b/src/testing/methodtest.cs @@ -736,7 +736,6 @@ public static void PointerArray(int*[] array) } } - public class MethodTestSub : MethodTest { public MethodTestSub() : base() diff --git a/src/testing/pep8test.cs b/src/testing/pep8test.cs new file mode 100644 index 00000000..8633d008 --- /dev/null +++ b/src/testing/pep8test.cs @@ -0,0 +1,16 @@ +namespace Python.Test +{ + public class Pep8Test + { + public string Foo() => "hello"; + + private int _bar = 1; + public int Bar + { + get { return _bar; } + set { _bar = value; } + } + + public double BazPi = 3.14; + } +} \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index 7226dd98..7787fab9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -90,15 +90,28 @@ def pytest_configure(config): python_test_module = clr.AddReference("Python.Test") configure_custom_binding_options(python_test_module) + launch_debugger() + def configure_custom_binding_options(python_test_module): from Python.Runtime import BindingManager, BindingOptions + module_types = python_test_module.GetTypes() + binding_options = BindingOptions() binding_options.AllowExplicitInterfaceImplementation = True - prop_test_3_type = [t for t in python_test_module.GetTypes() if "PropertyTest3" == t.Name][0] - + prop_test_3_type = [t for t in module_types if "PropertyTest3" == t.Name][0] BindingManager.SetBindingOptions(prop_test_3_type, binding_options) + binding_options = BindingOptions() + binding_options.Pep8Aliases = True + method_test_pep8_type = [t for t in module_types if "Pep8Test" == t.Name][0] + BindingManager.SetBindingOptions(method_test_pep8_type, binding_options) + + +def launch_debugger(): + import System + System.Diagnostics.Debugger.Launch() + def pytest_unconfigure(config): global bin_path diff --git a/tests/test_pep8.py b/tests/test_pep8.py new file mode 100644 index 00000000..bedec059 --- /dev/null +++ b/tests/test_pep8.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +"""Test CLR method support.""" + +import System +import pytest +from Python.Test import Pep8Test + +def test_pep8_method(): + """Test PEP8-styled alias of method.""" + assert "hello" == Pep8Test().Foo() + assert "hello" == Pep8Test().foo() + + +def test_pep8_property(): + """Test PEP8-styled alias of property.""" + pep8_test = Pep8Test() + assert 1 == pep8_test.Bar + pep8_test.Bar = 2 + assert 2 == pep8_test.bar + pep8_test.bar = 3 + assert 3 == pep8_test.bar + + +def test_pep8_field(): + """Test PEP8-styled alias of field.""" + pep8_test = Pep8Test() + assert 3.14 == pep8_test.BazPi + assert 3.14 == pep8_test.baz_pi + pep8_test.baz_pi = 3.1415 + assert 3.1415 == pep8_test.BazPi From 8b16d1d6bfa569f54b0eea0a62695bbf4555a498 Mon Sep 17 00:00:00 2001 From: Mohamed Koubaa Date: Tue, 14 Jan 2025 10:09:32 -0600 Subject: [PATCH 2/2] readme and remove debugger block --- README.rst | 1 + tests/conftest.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index bebf53fb..05d10ecd 100644 --- a/README.rst +++ b/README.rst @@ -7,3 +7,4 @@ Changes relative to pythonnet: * Revert of `#1240 `_. * Opt-into explicit interface wrapping, `#19 `_. This opts into the behavior that became the default in #1240 if ToPythonAs is explicitly used * Option to bind explicit interface implementations, `#23 `_. This provides a runtime option to expose C# explicit interface implementations to Python. +* Option to bind pep8 aliases `#24 `_. \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index 7787fab9..f077b86b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -90,7 +90,7 @@ def pytest_configure(config): python_test_module = clr.AddReference("Python.Test") configure_custom_binding_options(python_test_module) - launch_debugger() + #launch_debugger() def configure_custom_binding_options(python_test_module):