Skip to content

Commit f763078

Browse files
koubaaMohamed Koubaa
andauthored
add pep8 alias binding option (#24)
### What does this implement/fix? Explain your changes. C# naming conventions are different from python naming conventions. CamelCase is the usual convention for properties and methods in C#, while snake_case is preferred in python (pep8). When using ansys-pythonnet to bind C# to python, python users will use the C# APIs with the C# conventions. This breaks expectations for python users. This PR adds an option to enable pep8 aliases for python bindings, allowing python code to use pep8 even if the C# code does not. --------- Co-authored-by: Mohamed Koubaa <[email protected]>
1 parent c5f9793 commit f763078

File tree

7 files changed

+112
-3
lines changed

7 files changed

+112
-3
lines changed

README.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ Changes relative to pythonnet:
77
* Revert of `#1240 <https://github.com/pythonnet/pythonnet/pull/1240>`_.
88
* 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
99
* 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.
10+
* Option to bind pep8 aliases `#24 <https://github.com/ansys/ansys-pythonnet/pull/24>`_.

src/runtime/BindingOptions.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class BindingOptions
99
private bool _SuppressDocs = false;
1010
private bool _SuppressOverloads = false;
1111
private bool _AllowExplicitInterfaceImplementation = false;
12+
private bool _Pep8Aliases = false;
1213

1314
//[ModuleProperty]
1415
public bool SuppressDocs
@@ -29,6 +30,12 @@ public bool AllowExplicitInterfaceImplementation
2930
get { return _AllowExplicitInterfaceImplementation; }
3031
set { _AllowExplicitInterfaceImplementation = value; }
3132
}
33+
34+
public bool Pep8Aliases
35+
{
36+
get { return _Pep8Aliases; }
37+
set { _Pep8Aliases = value; }
38+
}
3239
}
3340

3441
public class BindingManager

src/runtime/ClassManager.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,29 @@ private static string SanitizeName(string name)
353353
return name;
354354
}
355355

356+
private static string ToSnakeCase(string str)
357+
{
358+
var strToModify = str;
359+
var prefix = "";
360+
if (str.StartsWith("get_"))
361+
{
362+
prefix = "get_";
363+
strToModify = str.Substring(4);
364+
}
365+
if (str.StartsWith("set_"))
366+
{
367+
prefix = "set_";
368+
strToModify = str.Substring(4);
369+
}
370+
371+
var result = string.Concat(
372+
strToModify.Select((x, i) =>
373+
i > 0 && char.IsUpper(x) && (char.IsLower(strToModify[i - 1]) || i < strToModify.Length - 1 && char.IsLower(strToModify[i + 1]))
374+
? "_" + x
375+
: x.ToString())).ToLowerInvariant();
376+
return prefix + result;
377+
}
378+
356379
private static ClassInfo GetClassInfo(Type type, ClassBase impl, BindingOptions bindingOptions)
357380
{
358381
var typeName = type.Name;
@@ -517,6 +540,12 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl, BindingOptions
517540
var propertyName = SanitizeName(pi.Name);
518541
ob = new PropertyObject(pi);
519542
ci.members[propertyName] = ob.AllocObject();
543+
if (bindingOptions.Pep8Aliases)
544+
{
545+
string pep8PropertyName = ToSnakeCase(propertyName);
546+
if (pep8PropertyName != null && pep8PropertyName != propertyName)
547+
ci.members[pep8PropertyName] = ob.AllocObject();
548+
}
520549
continue;
521550

522551
case MemberTypes.Field:
@@ -527,6 +556,12 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl, BindingOptions
527556
}
528557
ob = new FieldObject(fi);
529558
ci.members[mi.Name] = ob.AllocObject();
559+
if (bindingOptions.Pep8Aliases)
560+
{
561+
string pep8FieldName = ToSnakeCase(mi.Name);
562+
if (pep8FieldName != null && pep8FieldName != mi.Name)
563+
ci.members[pep8FieldName] = ob.AllocObject();
564+
}
530565
continue;
531566

532567
case MemberTypes.Event:
@@ -563,6 +598,13 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl, BindingOptions
563598

564599
ob = new MethodObject(type, name, mlist);
565600
ci.members[name] = ob.AllocObject();
601+
if (bindingOptions.Pep8Aliases)
602+
{
603+
string pep8Name = ToSnakeCase(name);
604+
if (pep8Name != null && pep8Name != name)
605+
ci.members[pep8Name] = ob.AllocObject();
606+
}
607+
566608
if (mlist.Any(OperatorMethod.IsOperatorMethod))
567609
{
568610
string pyName = OperatorMethod.GetPyMethodName(name);

src/testing/methodtest.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,6 @@ public static void PointerArray(int*[] array)
736736
}
737737
}
738738

739-
740739
public class MethodTestSub : MethodTest
741740
{
742741
public MethodTestSub() : base()

src/testing/pep8test.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace Python.Test
2+
{
3+
public class Pep8Test
4+
{
5+
public string Foo() => "hello";
6+
7+
private int _bar = 1;
8+
public int Bar
9+
{
10+
get { return _bar; }
11+
set { _bar = value; }
12+
}
13+
14+
public double BazPi = 3.14;
15+
}
16+
}

tests/conftest.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,28 @@ def pytest_configure(config):
9090
python_test_module = clr.AddReference("Python.Test")
9191
configure_custom_binding_options(python_test_module)
9292

93+
#launch_debugger()
94+
9395

9496
def configure_custom_binding_options(python_test_module):
9597
from Python.Runtime import BindingManager, BindingOptions
98+
module_types = python_test_module.GetTypes()
99+
96100
binding_options = BindingOptions()
97101
binding_options.AllowExplicitInterfaceImplementation = True
98-
prop_test_3_type = [t for t in python_test_module.GetTypes() if "PropertyTest3" == t.Name][0]
99-
102+
prop_test_3_type = [t for t in module_types if "PropertyTest3" == t.Name][0]
100103
BindingManager.SetBindingOptions(prop_test_3_type, binding_options)
101104

105+
binding_options = BindingOptions()
106+
binding_options.Pep8Aliases = True
107+
method_test_pep8_type = [t for t in module_types if "Pep8Test" == t.Name][0]
108+
BindingManager.SetBindingOptions(method_test_pep8_type, binding_options)
109+
110+
111+
def launch_debugger():
112+
import System
113+
System.Diagnostics.Debugger.Launch()
114+
102115

103116
def pytest_unconfigure(config):
104117
global bin_path

tests/test_pep8.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""Test CLR method support."""
4+
5+
import System
6+
import pytest
7+
from Python.Test import Pep8Test
8+
9+
def test_pep8_method():
10+
"""Test PEP8-styled alias of method."""
11+
assert "hello" == Pep8Test().Foo()
12+
assert "hello" == Pep8Test().foo()
13+
14+
15+
def test_pep8_property():
16+
"""Test PEP8-styled alias of property."""
17+
pep8_test = Pep8Test()
18+
assert 1 == pep8_test.Bar
19+
pep8_test.Bar = 2
20+
assert 2 == pep8_test.bar
21+
pep8_test.bar = 3
22+
assert 3 == pep8_test.bar
23+
24+
25+
def test_pep8_field():
26+
"""Test PEP8-styled alias of field."""
27+
pep8_test = Pep8Test()
28+
assert 3.14 == pep8_test.BazPi
29+
assert 3.14 == pep8_test.baz_pi
30+
pep8_test.baz_pi = 3.1415
31+
assert 3.1415 == pep8_test.BazPi

0 commit comments

Comments
 (0)