Skip to content

Commit 1621bbe

Browse files
authored
Hide .NET methods in Int32 until CLR visible (#1396)
* Hide .NET methods in Int32 until CLR visible * Hide .NET fields in Int32 until CLR visible * Make .NET members unaccessible in Int32 until CLR visible * Introduce Python-supporting-type category * Add tests * Add ore tests * Use IsPythonSupportingType in more places * Rename PythonBinder.IsExtendedType to IsExtendedPythonType * Fix merge conflict
1 parent 541c7b0 commit 1621bbe

5 files changed

Lines changed: 85 additions & 18 deletions

File tree

Src/IronPython/Runtime/Binding/PythonBinder.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -805,18 +805,39 @@ internal static string GetTypeNameInternal(Type t) {
805805
return t.Name;
806806
}
807807

808-
public static bool IsExtendedType(Type/*!*/ t) {
808+
/// <summary>
809+
/// Tests if a given type is a Python type (<see cref="IsPythonType(Type)"/>)
810+
/// that is also an extended type
811+
/// (i.e. a type with extension members defined in an extension type, like one of the Ops clases).
812+
/// </summary>
813+
/// <remarks>
814+
/// There exist Python types that are not extended types (e.g. <see cref="Bytes"/>),
815+
/// and types that are extended types but not Python types (e.g. <see cref="Array"/>).
816+
/// In some places in <see cref="PythonBinder"/> an extended Python type is called a system type.
817+
/// </remarks>
818+
public static bool IsExtendedPythonType(Type/*!*/ t) {
809819
Debug.Assert(t != null);
810820

811821
return _sysTypes.ContainsKey(t);
812822
}
813823

824+
/// <summary>
825+
/// Tests if a given type is used to implement one of the types in the Python language.
826+
/// </summary>
814827
public static bool IsPythonType(Type/*!*/ t) {
815828
Debug.Assert(t != null);
816829

817830
return _sysTypes.ContainsKey(t) || t.IsDefined(typeof(PythonTypeAttribute), false);
818831
}
819832

833+
/// <summary>
834+
/// Tests if a given type is a non-Python type but which instances are used
835+
/// to support one of the Python types.
836+
/// </summary>
837+
public static bool IsPythonSupportingType(Type t) {
838+
return t == typeof(int); // GH #52: Int32 instances used to support Python's int (== BigInteger)
839+
}
840+
820841
/// <summary>
821842
/// Event handler for when our domain manager has an assembly loaded by the user hosting the script
822843
/// runtime. Here we can gather any information regarding extension methods.

Src/IronPython/Runtime/Binding/PythonGetMemberBinder.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -579,13 +579,13 @@ internal static DynamicMetaObject FallbackWorker(PythonContext context, DynamicM
579579
bool isNoThrow = ((options & GetMemberOptions.IsNoThrow) != 0) ? true : false;
580580
Type limitType = self.GetLimitType();
581581

582-
if (limitType == typeof(DynamicNull) || PythonBinder.IsPythonType(limitType)) {
582+
if (limitType == typeof(DynamicNull) || PythonBinder.IsPythonType(limitType) || PythonBinder.IsPythonSupportingType(limitType)) {
583583
// look up in the PythonType so that we can
584-
// get our custom method names (e.g. string.startswith)
584+
// get our custom method names (e.g. string.startswith)
585585
PythonType argType = DynamicHelpers.GetPythonTypeFromType(limitType);
586586

587587
// if the name is defined in the CLS context but not the normal context then
588-
// we will hide it.
588+
// we will hide it.
589589
if (argType.IsHiddenMember(name)) {
590590
DynamicMetaObject baseRes = PythonContext.GetPythonContext(action).Binder.GetMember(
591591
name,
@@ -599,7 +599,7 @@ internal static DynamicMetaObject FallbackWorker(PythonContext context, DynamicM
599599
return BindingHelpers.FilterShowCls(codeContext, action, baseRes, failure);
600600
}
601601
}
602-
602+
603603
var res = context.Binder.GetMember(name, self, resolverFactory, isNoThrow, errorSuggestion);
604604
if (res is ErrorMetaObject) {
605605
// see if we can bind to any extension methods...

Src/IronPython/Runtime/Operations/PythonTypeOps.cs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ internal static ReflectedEvent GetReflectedEvent(EventTracker tracker) {
385385
ReflectedEvent res;
386386
lock (_eventCache) {
387387
if (!_eventCache.TryGetValue(tracker, out res)) {
388-
if (PythonBinder.IsExtendedType(tracker.DeclaringType)) {
388+
if (PythonBinder.IsExtendedPythonType(tracker.DeclaringType)) {
389389
_eventCache[tracker] = res = new ReflectedEvent(tracker, true);
390390
} else {
391391
_eventCache[tracker] = res = new ReflectedEvent(tracker, false);
@@ -640,8 +640,8 @@ private static bool IsMethodAlwaysVisible(Type/*!*/ type, MemberInfo/*!*/[]/*!*/
640640
if (PythonBinder.IsPythonType(type)) {
641641
// only show methods defined outside of the system types (object, string)
642642
foreach (MethodInfo mi in methods) {
643-
if (PythonBinder.IsExtendedType(mi.DeclaringType) ||
644-
PythonBinder.IsExtendedType(mi.GetBaseDefinition().DeclaringType) ||
643+
if (PythonBinder.IsExtendedPythonType(mi.DeclaringType) ||
644+
PythonBinder.IsExtendedPythonType(mi.GetBaseDefinition().DeclaringType) ||
645645
PythonHiddenAttribute.IsHidden(mi)) {
646646
alwaysVisible = false;
647647
break;
@@ -651,12 +651,22 @@ private static bool IsMethodAlwaysVisible(Type/*!*/ type, MemberInfo/*!*/[]/*!*/
651651
// check if this is a virtual override helper, if so we
652652
// may need to filter it out.
653653
foreach (MethodInfo mi in methods) {
654-
if (PythonBinder.IsExtendedType(mi.DeclaringType)) {
654+
if (PythonBinder.IsExtendedPythonType(mi.DeclaringType)) {
655+
alwaysVisible = false;
656+
break;
657+
}
658+
}
659+
} else if (type.IsAssignableFrom(typeof(int))) { // GH #52
660+
// only show methods defined outside of int
661+
foreach (MethodInfo mi in methods) {
662+
Debug.Assert(!mi.DeclaringType.IsInterface || typeof(int).GetInterfaces().Contains(mi.DeclaringType));
663+
if (PythonBinder.IsPythonSupportingType(mi.DeclaringType) ||
664+
mi.DeclaringType.IsInterface ||
665+
PythonHiddenAttribute.IsHidden(mi)) {
655666
alwaysVisible = false;
656667
break;
657668
}
658669
}
659-
660670
}
661671
return alwaysVisible;
662672
}
@@ -675,7 +685,8 @@ internal static PythonTypeSlot GetReflectedField(FieldInfo info) {
675685
PythonTypeSlot res;
676686

677687
NameType nt = NameType.Field;
678-
if (!PythonBinder.IsExtendedType(info.DeclaringType) &&
688+
if (!PythonBinder.IsExtendedPythonType(info.DeclaringType) &&
689+
!PythonBinder.IsPythonSupportingType(info.DeclaringType) &&
679690
!PythonHiddenAttribute.IsHidden(info)) {
680691
nt |= NameType.PythonField;
681692
}
@@ -758,7 +769,7 @@ internal static ReflectedGetterSetter GetReflectedProperty(PropertyTracker pt, M
758769
}
759770

760771
if (pt is ReflectedPropertyTracker rpt) {
761-
if (PythonBinder.IsExtendedType(pt.DeclaringType) ||
772+
if (PythonBinder.IsExtendedPythonType(pt.DeclaringType) ||
762773
PythonHiddenAttribute.IsHidden(rpt.Property, true)) {
763774
nt = NameType.Property;
764775
}

Tests/test_class.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1474,13 +1474,22 @@ def test_method_correct_name(self):
14741474
# report the proper name in __str__
14751475
self.assertTrue(repr(BaseException.__str__).find('__str__') != -1)
14761476

1477-
#TODO: @skip("multiple_execute")
14781477
def test_no_clr_attributes_sanity(self):
1479-
self.assertEqual(hasattr(int, 'MaxValue'), False)
1480-
self.assertEqual(hasattr(int, 'MinValue'), False)
1481-
self.assertEqual(hasattr(int, 'Abs'), False)
1482-
self.assertEqual(hasattr(int, 'BitwiseOr'), False)
1483-
self.assertEqual(hasattr(int, 'Equals'), False)
1478+
for o in [int, 1, big(1)]:
1479+
with self.subTest(object=o):
1480+
self.assertFalse(hasattr(o, 'MaxValue'))
1481+
self.assertFalse(hasattr(o, 'MinValue'))
1482+
self.assertFalse(hasattr(o, 'Abs'))
1483+
self.assertFalse(hasattr(o, 'BitwiseOr'))
1484+
self.assertFalse(hasattr(o, 'Log10'))
1485+
self.assertFalse(hasattr(o, 'ToByteArray'))
1486+
self.assertFalse(hasattr(o, 'IsEven'))
1487+
self.assertFalse(hasattr(o, 'Zero'))
1488+
self.assertFalse(hasattr(o, 'Equals'))
1489+
self.assertFalse(hasattr(o, 'CompareTo'))
1490+
self.assertFalse(hasattr(o, 'GetTypeCode'))
1491+
self.assertFalse(hasattr(o, 'ToString'))
1492+
self.assertFalse(hasattr(o, 'TryFormat'))
14841493

14851494
self.assertEqual(hasattr(str, 'Empty'), False)
14861495
self.assertEqual(hasattr(str, 'Compare'), False)

Tests/test_int.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,36 @@
33
# See the LICENSE file in the project root for more information.
44

55
import sys
6+
import unittest
67

78
from iptest import IronPythonTestCase, is_cli, big, myint, skipUnlessIronPython, run_test
89

10+
class IntNoClrTest(IronPythonTestCase):
11+
"""Must be run before IntTest because it depends on CLR API not being visible."""
12+
13+
def test_instance_set(self):
14+
i = 1
15+
j = big(1)
16+
self.assertSetEqual(set(dir(i)), set(dir(j)))
17+
918
class IntTest(IronPythonTestCase):
19+
def test_instance_set(self):
20+
i = 1
21+
j = big(1)
22+
from System import Int32
23+
24+
self.assertSetEqual(set(dir(i)) - set(dir(j)), {'MaxValue', 'MinValue'})
25+
self.assertSetEqual(set(dir(Int32)) - set(dir(int)), {'MaxValue', 'MinValue'})
26+
27+
@unittest.expectedFailure
28+
def test_instance_set_todo(self):
29+
i = 1
30+
j = big(1)
31+
from System import Int32
32+
33+
self.assertSetEqual(set(dir(j)) - set(dir(i)), set())
34+
self.assertSetEqual(set(dir(int)) - set(dir(Int32)), set())
35+
1036
def test_from_bytes(self):
1137
self.assertEqual(type(int.from_bytes(b"abc", "big")), int)
1238
self.assertEqual(type(myint.from_bytes(b"abc", "big")), myint) # https://github.com/IronLanguages/ironpython3/pull/973

0 commit comments

Comments
 (0)