[Jython-checkins] jython: Improve complex MRO handling. (Fixes bjo #2445.)

jeff.allen jython-checkins at python.org
Mon Mar 18 02:21:14 EDT 2019


https://hg.python.org/jython/rev/3c4f2c14b808
changeset:   8226:3c4f2c14b808
user:        Jim Baker <jim.baker at python.org>
date:        Sun Mar 17 20:57:18 2019 +0000
summary:
  Improve complex MRO handling. (Fixes bjo #2445.)

This change fixes a defect in MRO processing (as posted by Jim). It also adds a
test class (adapted by Jeff) with complex inheritance between its inner classes,
and a regression test that would fail before Jim's patch was applied.

files:
  Lib/test/test_java_integration.py               |  25 +++-
  NEWS                                            |   1 +
  src/org/python/core/PyJavaType.java             |  25 +++-
  tests/java/org/python/tests/mro/TortureMRO.java |  51 ++++++++++
  4 files changed, 89 insertions(+), 13 deletions(-)


diff --git a/Lib/test/test_java_integration.py b/Lib/test/test_java_integration.py
--- a/Lib/test/test_java_integration.py
+++ b/Lib/test/test_java_integration.py
@@ -234,6 +234,7 @@
 Keywords.with = lambda self: "with"
 Keywords.yield = lambda self: "yield"
 
+
 class PyReservedNamesTest(unittest.TestCase):
     "Access to reserved words"
 
@@ -330,6 +331,7 @@
     def test_yield(self):
         self.assertEquals(self.kws.yield(), "yield")
 
+
 class ImportTest(unittest.TestCase):
     def test_bad_input_exception(self):
         self.assertRaises(ValueError, __import__, '')
@@ -378,6 +380,13 @@
         x = String('test')
         self.assertRaises(TypeError, list, x)
 
+    def test_null_tostring(self):
+        # http://bugs.jython.org/issue1819
+        nts = NullToString()
+        self.assertEqual(repr(nts), '')
+        self.assertEqual(str(nts), '')
+        self.assertEqual(unicode(nts), '')
+
 class JavaDelegationTest(unittest.TestCase):
     def test_list_delegation(self):
         for c in ArrayList, Vector:
@@ -531,6 +540,9 @@
         self.assertEquals(7, m.initial)
         self.assertEquals(None, m.nonexistent, "Nonexistent fields should be passed on to the Map")
 
+
+class JavaMROTest(unittest.TestCase):
+
     def test_adding_on_interface(self):
         GetitemAdder.addPredefined()
         class UsesInterfaceMethod(FirstPredefinedGetitem):
@@ -543,13 +555,6 @@
         self.assertRaises(TypeError, __import__, "org.python.tests.mro.ConfusedOnImport")
         self.assertRaises(TypeError, GetitemAdder.addPostdefined)
 
-    def test_null_tostring(self):
-        # http://bugs.jython.org/issue1819
-        nts = NullToString()
-        self.assertEqual(repr(nts), '')
-        self.assertEqual(str(nts), '')
-        self.assertEqual(unicode(nts), '')
-
     def test_diamond_inheritance_of_iterable_and_map(self):
         """Test deeply nested diamond inheritance of Iterable and Map, as see in some Clojure classes"""
         # http://bugs.jython.org/issue1878
@@ -570,6 +575,11 @@
         self.assertEqual(set(m), set(["abc", "xyz"]))
         self.assertEqual(m["abc"], 42)
 
+    def test_diamond_lattice_inheritance(self):
+        # http://bugs.jython.org/issue2445
+        from org.python.tests.mro import TortureMRO
+
+
 def roundtrip_serialization(obj):
     """Returns a deep copy of an object, via serializing it
  
@@ -946,6 +956,7 @@
         JavaDelegationTest,
         JavaReservedNamesTest,
         JavaStringTest,
+        JavaMROTest,
         JavaWrapperCustomizationTest,
         PyReservedNamesTest,
         SecurityManagerTest,
diff --git a/NEWS b/NEWS
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,7 @@
 
 Development tip
   Bugs fixed
+    - [ 2445 ] Eclipse's DelegatingFeatureMap has MRO conflict
     - [ 2732 ] Regression in large module support for pip
     - [ GH-131 ] Support Unicode in zxJDBC exceptions.
     - [ 2654 ] Imported modules allow access to submodules
diff --git a/src/org/python/core/PyJavaType.java b/src/org/python/core/PyJavaType.java
--- a/src/org/python/core/PyJavaType.java
+++ b/src/org/python/core/PyJavaType.java
@@ -257,7 +257,17 @@
                     PyList types = new PyList();
                     Set<Class<?>> proxySet = Generic.set();
                     for (PyJavaType othertype : conflictedAttributes) {
-                        if (othertype.modified != null && othertype.modified.contains(method)) {
+                        /*
+                         * Ignore any pairings of types that are in a superclass/superinterface
+                         * relationship with each other. This problem is a false positive that
+                         * happens because of the automatic addition of methods so that Java classes
+                         * behave more like their corresponding Python types, such as adding sort or
+                         * remove. See http://bugs.jython.org/issue2445
+                         */
+                        if (othertype.modified != null && othertype.modified.contains(method)
+                                && !(othertype.getProxyType().isAssignableFrom(type.getProxyType())
+                                        || type.getProxyType()
+                                                .isAssignableFrom(othertype.getProxyType()))) {
                             types.add(othertype);
                             proxySet.add(othertype.getProxyType());
                         }
@@ -269,13 +279,16 @@
                      * __iter__. Annoying but necessary logic. See http://bugs.jython.org/issue1878
                      */
                     if (method.equals("__iter__")
-                            && proxySet.equals(Generic.set(Iterable.class, Map.class))) {
+                            && Generic.set(Iterable.class, Map.class).containsAll(proxySet)) {
                         continue;
                     }
-                    throw Py.TypeError(String.format(
-                            "Supertypes that share a modified attribute "
-                                    + "have an MRO conflict[attribute=%s, supertypes=%s, type=%s]",
-                            method, types, this.getName()));
+
+                    if (types.size() > 0) {
+                        throw Py.TypeError(String.format(
+                                "Supertypes that share a modified attribute "
+                                        + "have an MRO conflict[attribute=%s, supertypes=%s, type=%s]",
+                                method, types, this.getName()));
+                    }
                 }
             }
         }
diff --git a/tests/java/org/python/tests/mro/TortureMRO.java b/tests/java/org/python/tests/mro/TortureMRO.java
new file mode 100644
--- /dev/null
+++ b/tests/java/org/python/tests/mro/TortureMRO.java
@@ -0,0 +1,51 @@
+// Copyright (c) Jython Developers.
+// Licensed to the Python Software Foundation under a Contributor Agreement.
+
+package org.python.tests.mro;
+
+/**
+ * A class providing interface and abstract class relationships that approximate the structure of
+ * org.eclipse.emf.ecore.util.DelegatingFeatureMap, in order to exercise b.j.o issue 2445. The
+ * complex inheritance (more complex than diamond inheritance) confused PyJavaType handling of the
+ * MRO. This class is imported by
+ * {@code test_java_integration.JavaMROTest.test_diamond_lattice_inheritance} as a test.
+ * <p>
+ * An invocation at the prompt (for debugging use), and output before the fix, is:
+ *
+ * <pre>
+ * PS > dist\bin\jython -S -c"from org.python.tests.mro import TortureMRO"
+ * Traceback (most recent call last):
+ *   File "<string>", line 1, in <module>
+ * TypeError: Supertypes that share a modified attribute have an MRO
+ * conflict[attribute=sort, supertypes=[<type 'java.util.List'>,
+ * <type 'java.util.AbstractList'>], type=TortureMRO$Target]
+ * </pre>
+ */
+public class TortureMRO {
+
+    interface Entry {} // Was FeatureMap.Entry
+
+    interface Thing {} // Was EStructuralFeature.Setting
+
+    interface EList<E> extends java.util.List<E> {}
+
+    interface IEList<E> extends EList<E> {}
+
+    interface FList extends EList<Entry> {} // Was FeatureMap
+
+    interface FIEListThing extends FList, IEList<Entry>, Thing {}  // Was 2 FeatureMap.Internal
+
+    abstract static class AbstractEList<E> extends java.util.AbstractList<E> implements EList<E> {}
+
+    abstract static class DEList<E> extends AbstractEList<E> {}
+
+    interface NList<E> extends EList<E> {}
+
+    abstract static class DNListImpl<E> extends DEList<E> implements NList<E> {}
+
+    abstract static class DNIEListImpl<E> extends DNListImpl<E> implements IEList<E> {}
+
+    abstract static class DNIEListThing<E> extends DNIEListImpl<E> implements Thing {}
+
+    public abstract static class Target extends DNIEListThing<Entry> implements FIEListThing {}
+}

-- 
Repository URL: https://hg.python.org/jython


More information about the Jython-checkins mailing list