From jython-checkins at python.org Mon Jul 9 09:13:11 2018 From: jython-checkins at python.org (stefan.richthofer) Date: Mon, 09 Jul 2018 13:13:11 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fixes_=232672_by_applying_?= =?utf-8?q?github_PR_=23102_by_James_Mudd_with_minor_edits=2E?= Message-ID: <20180709131311.1.652285F7FF2F0880@mg.python.org> https://hg.python.org/jython/rev/123a2956db3f changeset: 8168:123a2956db3f user: James Mudd date: Mon Jul 09 15:12:49 2018 +0200 summary: Fixes #2672 by applying github PR #102 by James Mudd with minor edits. files: Lib/test/test_format_jy.py | 25 ++++++++++ Lib/test/test_list_jy.py | 2 +- NEWS | 3 +- src/org/python/core/stringlib/IntegerFormatter.java | 5 +- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_format_jy.py b/Lib/test/test_format_jy.py --- a/Lib/test/test_format_jy.py +++ b/Lib/test/test_format_jy.py @@ -22,6 +22,29 @@ def __float__(self): return self. x self.assertEqual('1.0', '%.1f' % Foo(1.0)) + def test_formatting_int_min_value_as_decimal(self): + # Test for http://bugs.jython.org/issue2672 + x = int(-(1<<31)) + self.assertEqual('-2147483648', '%d' % x) + self.assertEqual('-2147483648', '%i' % x) + + def test_formatting_int_min_value_as_hex(self): + # Test for http://bugs.jython.org/issue2672 + x = int(-(1<<31)) + self.assertEqual('-80000000', '%x' % x) + self.assertEqual('-80000000', '%X' % x) + + def test_formatting_int_min_value_as_octal(self): + # Test for http://bugs.jython.org/issue2672 + x = int(-(1<<31)) + self.assertEqual('-20000000000', '%o' % x) + + def test_formatting_int_min_value_as_binary(self): + # Test for http://bugs.jython.org/issue2672 + x = int(-(1<<31)) + self.assertEqual('-10000000000000000000000000000000', '{0:b}'.format(x)) + + class FormatUnicodeBase(unittest.TestCase): # Test padding non-BMP result @@ -29,6 +52,7 @@ self.padcheck(u"architect") self.padcheck(u'a\U00010001cde') + class FormatUnicodeClassic(FormatUnicodeBase): # Check using %-formatting @@ -39,6 +63,7 @@ self.assertEqual(u' '*6 + s[0:4], '%010.4s' % s) self.assertEqual(s[0:3] + u' '*5, '%-8.3s' % s) + class FormatUnicodeModern(FormatUnicodeBase): # Check using __format__ diff --git a/Lib/test/test_list_jy.py b/Lib/test/test_list_jy.py --- a/Lib/test/test_list_jy.py +++ b/Lib/test/test_list_jy.py @@ -257,7 +257,7 @@ self.assertEqual(jl, b_to_z_by_2) def test_concat(self): - # See http://bugs.jython.org/issue2688 + # See http://bugs.jython.org/issue2688 lst = ArrayList([1, 2, 3]) lst2 = [4, 5, 6] self.assertEquals(lst+lst2, [1, 2, 3, 4, 5, 6]) diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -2,8 +2,9 @@ For more details, please see https://hg.python.org/jython -Developement tip +Development tip Bugs fixed + - [ 2672 ] Integer formatting emits two minus signs with -2^31 - [ 2688 ] ClassCastException when adding list of non-PyObjects - [ 2659 ] Determine console encoding without access violation (Java 9) - [ 2662 ] IllegalAccessException accessing public abstract method via PyReflectedFunction diff --git a/src/org/python/core/stringlib/IntegerFormatter.java b/src/org/python/core/stringlib/IntegerFormatter.java --- a/src/org/python/core/stringlib/IntegerFormatter.java +++ b/src/org/python/core/stringlib/IntegerFormatter.java @@ -327,7 +327,10 @@ if (value < 0) { // Negative value: deal with sign and base, and convert magnitude. negativeSign(null); - number = Integer.toString(-value); + // Here there is a special case for int min value due to wrapping, to avoid a double + // negative sign being added see http://bugs.jython.org/issue2672 + // The string constant here is -Integer.MIN_VALUE + number = value == Integer.MIN_VALUE ? "2147483648" : Integer.toString(-value); } else { // Positive value: deal with sign, base and magnitude. positiveSign(null); -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Wed Jul 11 14:38:44 2018 From: jython-checkins at python.org (jeff.allen) Date: Wed, 11 Jul 2018 18:38:44 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Correct_result_of_!=3D_com?= =?utf-8?q?parison_against_List=2C_Set=2C_Map_=28fixes_=232639=29?= Message-ID: <20180711183844.1.CF8C95EB6A6B7419@mg.python.org> https://hg.python.org/jython/rev/e546ad3d85bd changeset: 8169:e546ad3d85bd user: James Mudd date: Wed Jul 11 16:24:29 2018 +0100 summary: Correct result of != comparison against List, Set, Map (fixes #2639) Adds __ne__ method to JavaProxy{List, Set, Map}. Changes BaseSet __eq__ and __ne__ methods to use item by item comparison where needed. Some minor improvements e.g. use entrySet for map comparison. Also refactors methods for testing equality and inequality to be very similar across JavaProxy{List, Set, Map}. Adds test coverage of != comparisons to Java {List, Set, Map}, and tests with more Java types, the comparions in both directions. files: Lib/test/test_dict_jy.py | 125 +++++++++++- Lib/test/test_list_jy.py | 113 ++++++++++- Lib/test/test_set_jy.py | 79 +++++++- NEWS | 1 + src/org/python/core/BaseSet.java | 88 +++++++- src/org/python/core/JavaProxyList.java | 109 ++++++++--- src/org/python/core/JavaProxyMap.java | 85 +++++++- src/org/python/core/JavaProxySet.java | 78 +++++-- 8 files changed, 561 insertions(+), 117 deletions(-) diff --git a/Lib/test/test_dict_jy.py b/Lib/test/test_dict_jy.py --- a/Lib/test/test_dict_jy.py +++ b/Lib/test/test_dict_jy.py @@ -1,10 +1,14 @@ from test import test_support -from java.util import HashMap, Hashtable import unittest from collections import defaultdict import test_dict +from java.util import HashMap, LinkedHashMap, Hashtable +from java.util.concurrent import ConcurrentHashMap + + class DictInitTest(unittest.TestCase): + def testInternalSetitemInInit(self): """Test for http://jython.org/bugs/1816134 @@ -41,14 +45,17 @@ else: self.fail("dict as dict key should raise TypeError") + class DictCmpTest(unittest.TestCase): "Test for http://bugs.jython.org/issue1031" + def testDictCmp(self): # 'Implicit' comparision of dicts against other types instances # shouldn't raise exception: self.assertNotEqual({}, '') # The same, but explicitly calling __cmp__ should raise TypeError: self.assertRaises(TypeError, {}.__cmp__, '') + def testDictDerivedCmp(self): # With derived classes that doesn't override __cmp__, the behaviour # should be the same that with dicts: @@ -86,7 +93,9 @@ self.assertEqual(derived_dict_with_custom_cmp(), '') self.assertEqual(yet_another_dict(), '') + class DictMiscTest(unittest.TestCase): + def test_pop_key_error(self): # tests http://bugs.jython.org/issue2247 with self.assertRaisesRegexp(KeyError, r"^1$"): @@ -96,8 +105,10 @@ with self.assertRaisesRegexp(KeyError, r"^frozenset\(\[\]\)$"): {}.pop(frozenset()) + class DerivedDictTest(unittest.TestCase): "Tests for derived dict behaviour" + def test_raising_custom_key_error(self): class CustomKeyError(KeyError): pass @@ -105,7 +116,7 @@ def __getitem__(self, key): raise CustomKeyError("custom message") self.assertRaises(CustomKeyError, lambda: DerivedDict()['foo']) - + def test_issue1676(self): #See http://bugs.jython.org/issue1676 x=defaultdict() @@ -123,8 +134,11 @@ class JavaIntegrationTest(unittest.TestCase): "Tests for instantiating dicts from Java maps and hashtables" - def test_hashmap(self): - x = HashMap() + + type2test = HashMap + + def test_map(self): + x = self.type2test() x.put('a', 1) x.put('b', 2) x.put('c', 3) @@ -132,8 +146,8 @@ y = dict(x) self.assertEqual(set(y.items()), set([('a', 1), ('b', 2), ('c', 3), ((1,2), "xyz")])) - def test_hashmap_builtin_pymethods(self): - x = HashMap() + def test_map_builtin_pymethods(self): + x = self.type2test() x['a'] = 1 x[(1, 2)] = 'xyz' self.assertEqual({tup for tup in x.iteritems()}, {('a', 1), ((1, 2), 'xyz')}) @@ -142,26 +156,96 @@ self.assertEqual(str(x), repr(x)) self.assertEqual(type(str(x)), type(repr(x))) - def test_hashtable_equal(self): + def test_equal(self): for d in ({}, {1:2}): - x = Hashtable(d) + x = self.type2test(d) self.assertEqual(x, d) self.assertEqual(d, x) self.assertEqual(x, HashMap(d)) - def test_hashtable_remove(self): - x = Hashtable({}) + def test_remove(self): + x = self.type2test({'a': 1}) + del x['a'] + self.assertEqual(x, {}) + + x = self.type2test({}) with self.assertRaises(KeyError): del x[0] - def test_hashtable(self): - x = Hashtable() - x.put('a', 1) - x.put('b', 2) - x.put('c', 3) - x.put((1,2), "xyz") - y = dict(x) - self.assertEqual(set(y.items()), set([('a', 1), ('b', 2), ('c', 3), ((1,2), "xyz")])) + def test_equality_empty_dict(self): + jmap = self.type2test() + self.assertTrue(jmap == {}) + self.assertTrue({} == jmap) + + def test_equality_simple_dict(self): + jmap = self.type2test() + self.assertFalse({'a': 1} == jmap) + self.assertFalse(jmap == {'a': 1}) + + def test_equality_mixed_types_dict(self): + ref = {False:0, 'a':1, u'b':2L, 3:"3"} + alt = {0:False, u'a':True, 'b':2, 3:"3"} + self.assertEqual(ref, alt) # test assumption + jref = self.type2test(ref) + for v in [ref, alt, jref]: + self.assertTrue(jref == v) + self.assertTrue(v == jref) + self.assertTrue(jref == self.type2test(v)) + self.assertTrue(self.type2test(v) == jref) + + alt1 = ref.copy(); alt1['a'] = 2; + alt2 = ref.copy(); del alt2['a']; + alt3 = ref.copy(); alt3['c'] = []; + for v in [alt1, alt2, alt3, {}]: + self.assertFalse(jref == v) + self.assertFalse(v == jref) + self.assertFalse(jref == self.type2test(v)) + self.assertFalse(self.type2test(v) == jref) + + # Test for http://bugs.jython.org/issue2639 + # This is to test the != comparisons between Java and Python maps/dict + def test_inequality_empty_dict(self): + jmap = self.type2test() + self.assertFalse(jmap != {}) + self.assertFalse({} != jmap) + + def test_inequality_simple_dict(self): + jmap = self.type2test() + self.assertTrue(jmap != {'a': 1}) + self.assertTrue({'a': 1} != jmap) + + def test_inequality_mixed_types_dict(self): + ref = {False:0, 'a':1, u'b':2L, 3:"3"} + alt = {0:False, u'a':True, 'b':2, 3:"3"} + self.assertEqual(ref, alt) # test assumption + jref = self.type2test(ref) + for v in [ref, alt, jref]: + self.assertFalse(jref != v) + self.assertFalse(v != jref) + self.assertFalse(jref != self.type2test(v)) + self.assertFalse(self.type2test(v) != jref) + + alt1 = ref.copy(); alt1['a'] = 2; + alt2 = ref.copy(); del alt2['a']; + alt3 = ref.copy(); alt3['c'] = []; + for v in [alt1, alt2, alt3, {}]: + self.assertTrue(jref != v) + self.assertTrue(v != jref) + self.assertTrue(jref != self.type2test(v)) + self.assertTrue(self.type2test(v) != jref) + + +class JavaHashMapTest(JavaIntegrationTest): + type2test = HashMap + +class JavaLinkedHashMapTest(JavaIntegrationTest): + type2test = LinkedHashMap + +class JavaHashtableTest(JavaIntegrationTest): + type2test = Hashtable + +class JavaConcurrentHashMapTest(JavaIntegrationTest): + type2test = HashMap class JavaDictTest(test_dict.DictTest): @@ -265,7 +349,10 @@ DictCmpTest, DictMiscTest, DerivedDictTest, - JavaIntegrationTest, + JavaHashMapTest, + JavaLinkedHashMapTest, + JavaConcurrentHashMapTest, + JavaHashtableTest, JavaDictTest, PyStringMapTest) diff --git a/Lib/test/test_list_jy.py b/Lib/test/test_list_jy.py --- a/Lib/test/test_list_jy.py +++ b/Lib/test/test_list_jy.py @@ -6,9 +6,10 @@ import test_list if test_support.is_jython: - from java.util import ArrayList + from java.util import ArrayList, LinkedList, Vector from java.lang import Integer, String + class ListTestCase(unittest.TestCase): def test_recursive_list_slices(self): @@ -60,6 +61,7 @@ self.assertEqual(len(lst), 64000) self.assertEqual(sum(lst), 2047968000) + class ThreadSafetyTestCase(unittest.TestCase): def run_threads(self, f, num=10): @@ -112,6 +114,7 @@ self.assert_(lst[1] in (1,10)) self.run_threads(tester) + class ExtendedSliceTestCase(unittest.TestCase): '''Really thrash extended slice operations on list.''' type2test = list @@ -228,17 +231,17 @@ self.assertEqual(a, b) def test_extend_java_ArrayList(self): - jl = ArrayList([]) + jl = self.type2test([]) jl.extend([1,2]) - self.assertEqual(jl, ArrayList([1,2])) - jl.extend(ArrayList([3,4])) + self.assertEqual(jl, self.type2test([1,2])) + jl.extend(self.type2test([3,4])) self.assertEqual(jl, [1,2,3,4]) - + def test_remove(self): # Verifies that overloaded java.util.List#remove(int) method can still be used, but with Python index semantics # http://bugs.jython.org/issue2456 - jl = ArrayList(xrange(10, -1, -1)) # 10 .. 0, inclusive - jl.remove(0) # removes jl[-1] (=0) + jl = self.type2test(xrange(10, -1, -1)) # 10 .. 0, inclusive + jl.remove(0) # removes jl[-1] (=0) self.assertEqual(jl, range(10, 0, -1)) # 10 .. 1 self.assertRaises(ValueError, jl.remove, Integer(0)) # j.l.Integer does not support __index__ - maybe it should! jl.remove(0) # removes jl[0] (=10) @@ -251,7 +254,7 @@ a_to_z = list(chr(i) for i in xrange(ord('a'), ord('z') + 1)) b_to_z_by_2 = list(chr(i) for i in xrange(ord('b'), ord('z') + 1, 2)) - jl = ArrayList(a_to_z) + jl = self.type2test(a_to_z) for i in xrange(13): jl.remove(i) self.assertEqual(jl, b_to_z_by_2) @@ -263,6 +266,90 @@ self.assertEquals(lst+lst2, [1, 2, 3, 4, 5, 6]) self.assertEquals(lst2+lst, [4, 5, 6, 1, 2, 3]) + def test_equality_empty_list(self): + jl = self.type2test() + self.assertTrue(jl == []) + self.assertTrue([] == jl) + + def test_equality_simple_list(self): + jl = self.type2test() + self.assertFalse(jl == [1]) + self.assertFalse([1] == jl) + + def test_equality_mixed_types_list(self): + ref = [False, 1, 3**9, "3"] + alt = [0, True, 3L**9, u"3"] + self.assertEqual(ref, alt) # test assumption + jref = self.type2test(ref) + + for v in [ref, alt, jref]: + self.assertTrue(jref == v) + self.assertTrue(v == jref) + self.assertTrue(jref == self.type2test(v)) + + alt = [False, 1, 2e4, "3"] + for v in [alt, ref[:-1], ref+[{}], []]: + self.assertFalse(jref == v) + self.assertFalse(v == jref) + self.assertFalse(jref == self.type2test(v)) + + # Test for http://bugs.jython.org/issue2639 + # This is to test the != comparisons between Java and Python lists + def test_inequality_empty_list(self): + jl = self.type2test() + self.assertFalse(jl != []) + self.assertFalse([] != jl) + + def test_inequality_simple_list(self): + jl = self.type2test() + self.assertTrue(jl != [1]) + self.assertTrue([1] != jl) + + def test_inequality_mixed_types_list(self): + ref = [False, 1, 3**9, "3"] + alt = [0, True, 3L**9, u"3"] + self.assertEqual(ref, alt) # test assumption + jref = self.type2test(ref) + + for v in [ref, alt, jref]: + self.assertFalse(jref != v) + self.assertFalse(v != jref) + self.assertFalse(jref != self.type2test(v)) + + alt = [False, 1, 2e4, "3"] + for v in [alt, ref[:-1], ref+[{}], []]: + self.assertTrue(jref != v) + self.assertTrue(v != jref) + self.assertTrue(jref != self.type2test(v)) + + +class JavaArrayListTestCase(JavaListTestCase): + type2test = ArrayList + + +class JavaLinkedListTestCase(JavaListTestCase): + type2test = LinkedList + + @unittest.skip("Fails for LinkedList see http://bugs.jython.org/issue2645") + def test_pop(self): + pass + + @unittest.skip("Seems to hang for linked list") + def test_bigrepeat(self): + pass + + @unittest.skip("Takes a long time and can cause OutOfMemory") + def test_overflow(self): + pass + + +class JavaVectorTestCase(JavaListTestCase): + type2test = Vector + + @unittest.skip("Takes a long time and can cause OutOfMemory") + def test_bigrepeat(self): + pass + class ListSubclassTestCase(unittest.TestCase): @@ -305,11 +392,13 @@ def test_main(): - test_support.run_unittest(ListSubclassTestCase, - ListTestCase, + test_support.run_unittest(ListTestCase, ThreadSafetyTestCase, ExtendedSliceTestCase, - JavaListTestCase) - + JavaArrayListTestCase, + JavaLinkedListTestCase, + JavaVectorTestCase, + ListSubclassTestCase) + if __name__ == "__main__": test_main() diff --git a/Lib/test/test_set_jy.py b/Lib/test/test_set_jy.py --- a/Lib/test/test_set_jy.py +++ b/Lib/test/test_set_jy.py @@ -84,7 +84,7 @@ class TestJavaSet(test_set.TestSet): - thetype = HashSet + type2test = HashSet def test_init(self): # Instances of Java types cannot be re-initialized @@ -102,18 +102,83 @@ dup = pickle.loads(p) self.assertEqual(self.s, dup, "%s != %s" % (self.s, dup)) + def test_equality_empty_set(self): + js = self.type2test() + self.assertTrue(js == set()) + self.assertTrue(set() == js) + + def test_equality_simple_set(self): + js = self.type2test() + self.assertFalse(js == set([1])) + self.assertFalse(set([1]) == js) + + def test_equality_mixed_types_set(self): + ref = {False, 1, 3**9, "3"} + alt = {0, True, 3L**9, u"3"} + self.assertEqual(ref, alt) # test assumption + jref = self.type2test(ref) + for v in [ref, alt, jref]: + self.assertTrue(jref == v) + self.assertTrue(v == jref) + self.assertTrue(jref == self.type2test(v)) + self.assertTrue(self.type2test(v) == jref) + + alt1 = {False, 1.01, 3**9, "3"} + alt2 = {False, 1, "3"}; + alt3 = {False, 1, 3**9, "3", ""}; + for v in [alt1, alt2, alt3, set()]: + self.assertFalse(jref == v) + self.assertFalse(v == jref) + self.assertFalse(jref == self.type2test(v)) + self.assertFalse(self.type2test(v) == jref) + + # Test for http://bugs.jython.org/issue2639 + # This is to test the != comparisons between Java and Python sets + def test_inequality_empty_set(self): + js = self.type2test() + self.assertFalse(js != set()) + self.assertFalse(set() != js) + + def test_inequality_simple_set(self): + js = self.type2test() + self.assertTrue(js != set([1])) + self.assertTrue(set([1]) != js) + + def test_inequality_mixed_types_set(self): + ref = {False, 1, 3**9, "3"} + alt = {0, True, 3L**9, u"3"} + self.assertEqual(ref, alt) # test assumption + jref = self.type2test(ref) + + for v in [ref, alt, jref]: + self.assertFalse(jref != v) + self.assertFalse(v != jref) + self.assertFalse(jref != self.type2test(v)) + self.assertFalse(self.type2test(v) != jref) + + alt1 = {False, 1.01, 3**9, "3"} + alt2 = {False, 1, "3"}; + alt3 = {False, 1, 3**9, "3", ""}; + for v in [alt1, alt2, alt3, set()]: + self.assertTrue(jref != v) + self.assertTrue(v != jref) + self.assertTrue(jref != self.type2test(v)) + self.assertTrue(self.type2test(v) != jref) + class TestJavaHashSet(TestJavaSet): - thetype = HashSet + type2test = HashSet + class TestJavaLinkedHashSet(TestJavaSet): - thetype = LinkedHashSet + type2test = LinkedHashSet + class SetSubclassCallsSuperMethods(set): # Used to verify all call paths where there is more than one way # to call the super method, such as (union, __or__), etc - + def _valid_op_args(f): def _screener(*args): if len(args) != 2: @@ -132,12 +197,12 @@ def issubset(self, other): return super(SetSubclassCallsSuperMethods, self).issubset(other) - + __le__ = issubset def issuperset(self, other): return super(SetSubclassCallsSuperMethods, self).issuperset(other) - + __ge__ = issuperset def union(self, *others): @@ -166,7 +231,7 @@ update = _call_for_side_effects(_update) __ior__ = _update - + def _difference_update(self, *others): super(SetSubclassCallsSuperMethods, self).difference_update(*others) return self diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ Development tip Bugs fixed + - [ 2639 ] Incorrect result when using != comparison against Java {List, Set, Map} - [ 2672 ] Integer formatting emits two minus signs with -2^31 - [ 2688 ] ClassCastException when adding list of non-PyObjects - [ 2659 ] Determine console encoding without access violation (Java 9) diff --git a/src/org/python/core/BaseSet.java b/src/org/python/core/BaseSet.java --- a/src/org/python/core/BaseSet.java +++ b/src/org/python/core/BaseSet.java @@ -31,7 +31,7 @@ protected void _update(PyObject [] args) { _update(_set, args); } - + /** * Update the underlying set with the contents of the iterable. */ @@ -70,6 +70,7 @@ * @param other A BaseSet instance. * @return The union of the two sets as a new set. */ + @Override public PyObject __or__(PyObject other) { return baseset___or__(other); } @@ -90,6 +91,7 @@ * @param other A BaseSet instance. * @return The intersection of the two sets as a new set. */ + @Override public PyObject __and__(PyObject other) { return baseset___and__(other); } @@ -110,6 +112,7 @@ * @param other A BaseSet instance. * @return The difference of the two sets as a new set. */ + @Override public PyObject __sub__(PyObject other) { return baseset___sub__(other); } @@ -124,16 +127,16 @@ public PyObject difference(PyObject other) { return baseset_difference(other); } - + final PyObject baseset_difference(PyObject other) { return baseset_difference(new PyObject[] {other}); } - + final PyObject baseset_difference(PyObject [] args) { if (args.length == 0) { return BaseSet.makeNewSet(getType(), this); } - + BaseSet o = BaseSet.makeNewSet(getType(), this); for (PyObject item: args) { BaseSet bs = args[0] instanceof BaseSet ? (BaseSet)item : new PySet(item); @@ -157,6 +160,7 @@ * @param other A BaseSet instance. * @return The symmetric difference of the two sets as a new set. */ + @Override public PyObject __xor__(PyObject other) { return baseset___xor__(other); } @@ -193,6 +197,7 @@ * * @return The hashCode of the set. */ + @Override public abstract int hashCode(); /** @@ -200,6 +205,7 @@ * * @return The length of the set. */ + @Override public int __len__() { return baseset___len__(); } @@ -214,6 +220,7 @@ * * @return true if the set is not empty, false otherwise */ + @Override public boolean __nonzero__() { return !_set.isEmpty(); } @@ -223,6 +230,7 @@ * * @return An iteration of the set. */ + @Override public PyObject __iter__() { return baseset___iter__(); } @@ -243,6 +251,7 @@ }; } + @Override public boolean __contains__(PyObject other) { return baseset___contains__(other); } @@ -256,6 +265,7 @@ } } + @Override public int __cmp__(PyObject other) { return baseset___cmp__(other); } @@ -264,28 +274,61 @@ throw Py.TypeError("cannot compare sets using cmp()"); } + @Override public PyObject __eq__(PyObject other) { return baseset___eq__(other); } final PyObject baseset___eq__(PyObject other) { - if (other instanceof BaseSet) { - return Py.newBoolean(_set.equals(((BaseSet)other)._set)); + // jobj might be Py.NoConversion if other is not a Set + Object jobj = other.__tojava__(Set.class); + if (jobj instanceof Set) { + final Set jSet = (Set) jobj; + // If the sizes differ must be not equal + if (jSet.size() != size()) { + return Py.False; + } + // Now need to perform element comparison + for (Object otherItem : jSet) { + if (!contains(otherItem)) { + return Py.False; // If any item is not contained then they are not equal + } + } + // All items are contained and the lentgh is the same so we are equal + return Py.True; } + // other wasn't a set so not equal return Py.False; } + @Override public PyObject __ne__(PyObject other) { return baseset___ne__(other); } final PyObject baseset___ne__(PyObject other) { - if (other instanceof BaseSet) { - return Py.newBoolean(!_set.equals(((BaseSet)other)._set)); + // jobj might be Py.NoConversion if other is not a Set + Object jobj = other.__tojava__(Set.class); + if (jobj instanceof Set) { + final Set jSet = (Set) jobj; + // If the sizes differ must be not equal + if (jSet.size() != size()) { + return Py.True; + } + // Now need to perform element comparison + for (Object otherItem : jSet) { + if (!contains(otherItem)) { + return Py.True; // If any item is not contained then they are not equal + } + } + // All items are contained and the lentgh is the same so we are equal + return Py.False; } + // other wasn't a set so not equal return Py.True; } + @Override public PyObject __le__(PyObject other) { return baseset___le__(other); } @@ -294,6 +337,7 @@ return baseset_issubset(asBaseSet(other)); } + @Override public PyObject __ge__(PyObject other) { return baseset___ge__(other); } @@ -302,6 +346,7 @@ return baseset_issuperset(asBaseSet(other)); } + @Override public PyObject __lt__(PyObject other) { return baseset___lt__(other); } @@ -311,6 +356,7 @@ return Py.newBoolean(size() < bs.size() && baseset_issubset(other).__nonzero__()); } + @Override public PyObject __gt__(PyObject other) { return baseset___gt__(other); } @@ -326,6 +372,7 @@ * * @return a tuple of (constructor, (elements)) */ + @Override public PyObject __reduce__() { return baseset___reduce__(); } @@ -344,7 +391,7 @@ result._update(other); return result; } - + final PyObject baseset_union(PyObject [] args) { BaseSet result = BaseSet.makeNewSet(getType(), this); for (PyObject item: args) { @@ -370,13 +417,13 @@ PyObject common = __builtin__.filter(big.__getattr__("__contains__"), little); return BaseSet.makeNewSet(getType(), common); } - + final PyObject baseset_intersection(PyObject [] args) { BaseSet result = BaseSet.makeNewSet(getType(), this); if (args.length == 0) { return result; } - + for (PyObject other: args) { result = (BaseSet)result.baseset_intersection(other); } @@ -405,12 +452,13 @@ BaseSet bs = other instanceof BaseSet ? (BaseSet)other : new PySet(other); return bs.baseset_issubset(this); } - + final PyObject baseset_isdisjoint(PyObject other) { BaseSet bs = other instanceof BaseSet ? (BaseSet)other : new PySet(other); return Collections.disjoint(_set, bs._set) ? Py.True : Py.False; } + @Override public String toString() { return baseset_toString(); } @@ -502,30 +550,37 @@ return so; } + @Override public int size() { return _set.size(); } + @Override public void clear() { _set.clear(); } + @Override public boolean isEmpty() { return _set.isEmpty(); } + @Override public boolean add(Object o) { return _set.add(Py.java2py(o)); } + @Override public boolean contains(Object o) { return _set.contains(Py.java2py(o)); } + @Override public boolean remove(Object o) { return _set.remove(Py.java2py(o)); } + @Override public boolean addAll(Collection c) { boolean added = false; for (Object object : c) { @@ -534,6 +589,7 @@ return added; } + @Override public boolean containsAll(Collection c) { for (Object object : c) { if (!_set.contains(Py.java2py(object))) { @@ -543,6 +599,7 @@ return true; } + @Override public boolean removeAll(Collection c) { boolean removed = false; for (Object object : c) { @@ -551,6 +608,7 @@ return removed; } + @Override public boolean retainAll(Collection c) { boolean modified = false; Iterator e = iterator(); @@ -563,28 +621,34 @@ return modified; } + @Override public Iterator iterator() { return new Iterator() { Iterator real = _set.iterator(); + @Override public boolean hasNext() { return real.hasNext(); } + @Override public Object next() { return Py.tojava(real.next(), Object.class); } + @Override public void remove() { real.remove(); } }; } + @Override public Object[] toArray() { return toArray(new Object[size()]); } + @Override public Object[] toArray(Object a[]) { int size = size(); if (a.length < size) { diff --git a/src/org/python/core/JavaProxyList.java b/src/org/python/core/JavaProxyList.java --- a/src/org/python/core/JavaProxyList.java +++ b/src/org/python/core/JavaProxyList.java @@ -42,6 +42,67 @@ throw Py.JavaError(e); } } + + /** + * Compares this object with other to check for equality. Used to implement __eq __ and + * __ne__. May return null if the other object cannot be compared i.e. is not a Python list + * or Java List. + * + * @param other The object to compare to this + * @return true is equal, false if not equal and null if we can't compare + */ + protected PyBoolean isEqual(PyObject other) { + if (isPyList(other)) { + // Being compared to a Python list + PyList oList = (PyList) other; + List jList = asList(); + if (jList.size() != oList.size()) { + // Size mismatched so not equal + return Py.False; + } + for (int i = 0; i < jList.size(); i++) { + // Do element by element comparison, if any elements are not equal return false + if (!Py.java2py(jList.get(i))._eq(oList.pyget(i)).__nonzero__()) { + return Py.False; + } + } + // All elements are equal so the lists are equal + return Py.True; + } else { + // Being compared to something that is not a Python list + Object oj = other.getJavaProxy(); + if (oj instanceof List) { + // Being compared to a Java List + List oList = (List) oj; + List jList = asList(); + if (jList.size() != oList.size()) { + // Size mismatched so not equal + return Py.False; + } + for (int i = 0; i < jList.size(); i++) { + /* + * Do element by element comparison, if any elements are not equal return + * false. + */ + if (!Py.java2py(jList.get(i))._eq(Py.java2py(oList.get(i))).__nonzero__()) { + return Py.False; + } + } + // All elements are equal so the lists are equal + return Py.True; + } else { + /* + * other is not a Python or Java list, so we don't know if were equal therefore + * return null. + */ + return null; + } + } + } + + private boolean isPyList(PyObject object) { + return object.getType().isSubType(PyList.TYPE); + } } protected static class ListIndexDelegate extends SequenceIndexDelegate { @@ -257,6 +318,7 @@ this.cmp = cmp; } + @Override public int compare(KV o1, KV o2) { int result; if (cmp != null && cmp != Py.None) { @@ -274,6 +336,7 @@ return result; } + @Override public boolean equals(Object o) { if (o == this) { return true; @@ -339,38 +402,23 @@ private static final PyBuiltinMethodNarrow listEqProxy = new ListMethod("__eq__", 1) { @Override public PyObject __call__(PyObject other) { - List jList = asList(); - if (other.getType().isSubType(PyList.TYPE)) { - PyList oList = (PyList) other; - if (jList.size() != oList.size()) { - return Py.False; - } - for (int i = 0; i < jList.size(); i++) { - if (!Py.java2py(jList.get(i))._eq(oList.pyget(i)).__nonzero__()) { - return Py.False; - } - } - return Py.True; - } else { - Object oj = other.getJavaProxy(); - if (oj instanceof List) { - List oList = (List) oj; - if (jList.size() != oList.size()) { - return Py.False; - } - for (int i = 0; i < jList.size(); i++) { - if (!Py.java2py(jList.get(i))._eq( - Py.java2py(oList.get(i))).__nonzero__()) { - return Py.False; - } - } - return Py.True; - } else { - return null; - } - } + return isEqual(other); } }; + + private static final PyBuiltinMethodNarrow listNeProxy = new ListMethod("__ne__", 1) { + @Override + public PyObject __call__(PyObject other) { + // isEqual may return null if we don't know how to compare to other. + PyBoolean equal = isEqual(other); + if (equal != null) { + // implement NOT equal by the inverse of equal + return equal.__not__(); + } + return null; + } + }; + private static final PyBuiltinMethodNarrow listAppendProxy = new ListMethod("append", 1) { @Override public PyObject __call__(PyObject value) { @@ -618,6 +666,7 @@ listGetProxy, listSetProxy, listEqProxy, + listNeProxy, listRemoveProxy, listAppendProxy, listExtendProxy, diff --git a/src/org/python/core/JavaProxyMap.java b/src/org/python/core/JavaProxyMap.java --- a/src/org/python/core/JavaProxyMap.java +++ b/src/org/python/core/JavaProxyMap.java @@ -1,7 +1,9 @@ package org.python.core; +import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; /** @@ -36,37 +38,68 @@ } } - private static PyObject mapEq(PyObject self, PyObject other) { - Map selfMap = ((Map) self.getJavaProxy()); - if (other.getType().isSubType(PyDictionary.TYPE)) { + /** + * Compares this object with other to check for equality. Used to implement __eq __ and __ne__. + * May return null if the other object cannot be compared i.e. is not a Python dict or Java Map. + * + * @param other The object to compare to this + * @return true is equal, false if not equal and null if we can't compare + */ + private static PyBoolean mapEq(PyObject self, PyObject other) { + if (isPyDict(other)) { + // Being compared to Python dict PyDictionary oDict = (PyDictionary) other; + Map selfMap = (Map) self.getJavaProxy(); if (selfMap.size() != oDict.size()) { + // Map/dict are different sizes therefore not equal return Py.False; } - for (Object jkey : selfMap.keySet()) { - Object jval = selfMap.get(jkey); + // Loop through all entries checking the keys and values are matched + for (Entry entry : selfMap.entrySet()) { + Object jkey = entry.getKey(); + Object jval = entry.getValue(); PyObject oVal = oDict.__finditem__(Py.java2py(jkey)); if (oVal == null) { + // No value for this key in oDict, therefore not equal return Py.False; } if (!Py.java2py(jval)._eq(oVal).__nonzero__()) { + // The values for this key differ therefore not equal return Py.False; } } + // All keys and values are equal therefore map/dict are equal return Py.True; } else { + // Being compared to something that is not a Python dict Object oj = other.getJavaProxy(); if (oj instanceof Map) { - Map oMap = (Map) oj; - return Py.newBoolean(selfMap.equals(oMap)); + // Being compared to a Java Map convert to Python + Map jMap = (Map) oj; + final Map pyMap = new HashMap<>(); + for (Entry el : jMap.entrySet()) { + pyMap.put(Py.java2py(el.getKey()), Py.java2py(el.getValue())); + } + // Compare again this time after conversion to Python dict + return mapEq(self, new PyDictionary(pyMap)); } else { + /* + * other is not a Python dict or Java Map, so we don't know if were equal therefore + * return null + */ return null; } } } - // Map ordering comparisons (lt, le, gt, ge) are based on the key sets; - // we just define mapLe + mapEq for total ordering of such key sets + private static boolean isPyDict(PyObject object) { + return object.getType().isSubType(PyDictionary.TYPE); + } + + /* + * Map ordering comparisons (lt, le, gt, ge) are based on the key sets; we just define mapLe + + * mapEq for total ordering of such key sets + */ private static PyObject mapLe(PyObject self, PyObject other) { Set selfKeys = ((Map) self.getJavaProxy()).keySet(); if (other.getType().isSubType(PyDictionary.TYPE)) { @@ -121,6 +154,18 @@ return mapEq(self, other); } }; + private static final PyBuiltinMethodNarrow mapNeProxy = new MapMethod("__ne__", 1) { + @Override + public PyObject __call__(PyObject other) { + // mapEq may return null if we don't know how to compare to other. + PyBoolean equal = mapEq(self, other); + if (equal != null) { + // implement NOT equal by the inverse of equal + return equal.__not__(); + } + return null; + } + }; private static final PyBuiltinMethodNarrow mapLeProxy = new MapMethod("__le__", 1) { @Override public PyObject __call__(PyObject other) { @@ -158,9 +203,13 @@ return asMap().containsKey(other) ? Py.True : Py.False; } }; - // "get" needs to override java.util.Map#get() in its subclasses, too, so this needs to be injected last - // (i.e. when HashMap is loaded not when it is recursively loading its super-type Map) + /* + * "get" needs to override java.util.Map#get() in its subclasses, too, so this needs to be + * injected last (i.e. when HashMap is loaded not when it is recursively loading its super-type + * Map). + */ private static final PyBuiltinMethodNarrow mapGetProxy = new MapMethod("get", 1, 2) { + @Override public PyObject __call__(PyObject key) { return __call__(key, Py.None); @@ -371,9 +420,11 @@ @Override public PyObject __call__(PyObject other) { - // `other` is either another dict-like object, or an iterable of key/value pairs (as tuples - // or other iterables of length two) - return __call__(new PyObject[]{other}, new String[]{}); + /* + * `other` is either another dict-like object, or an iterable of key/value pairs (as + * tuples or other iterables of length two) + */ + return __call__(new PyObject[] {other}, new String[] {}); } @Override @@ -459,7 +510,10 @@ Object defobj = _default == null ? Py.None : Py.tojava(_default, Object.class); Class theClass = asClass(); try { - // always injected to java.util.Map, so we know the class object we get from asClass is subtype of java.util.Map + /* + * always injected to java.util.Map, so we know the class object we get from + * asClass is subtype of java.util.Map + */ Map theMap = (Map) theClass.newInstance(); for (PyObject key : keys.asIterable()) { theMap.put(Py.tojava(key, Object.class), defobj); @@ -481,6 +535,7 @@ mapIterProxy, mapReprProxy, mapEqProxy, + mapNeProxy, mapLeProxy, mapLtProxy, mapGeProxy, diff --git a/src/org/python/core/JavaProxySet.java b/src/org/python/core/JavaProxySet.java --- a/src/org/python/core/JavaProxySet.java +++ b/src/org/python/core/JavaProxySet.java @@ -40,29 +40,45 @@ return newPySet; } - public boolean isEqual(PyObject other) { - Set selfSet = asSet(); - Object oj = other.getJavaProxy(); - if (oj != null && oj instanceof Set) { - @SuppressWarnings("unchecked") - Set otherSet = (Set) oj; - if (selfSet.size() != otherSet.size()) { - return false; + /** + * Compares this object with other to check for equality. Used to implement __eq __ and + * __ne__. May return null if the other object cannot be compared i.e. is not a Python or + * Java set. + * + * @param other The object to compare to this + * @return true is equal, false if not equal and null if we can't compare + */ + protected PyBoolean isEqual(PyObject other) { + if (isPySet(other)) { + // Being compared to a Python set + final Set otherPySet = ((BaseSet) other).getSet(); + final Set selfSet = asSet(); + if (selfSet.size() != otherPySet.size()) { + // Sets are different sizes therefore not equal + return Py.False; } - return selfSet.containsAll(otherSet); - } else if (isPySet(other)) { - Set otherPySet = ((BaseSet) other).getSet(); - if (selfSet.size() != otherPySet.size()) { - return false; - } - for (PyObject pyobj : otherPySet) { - if (!selfSet.contains(pyobj.__tojava__(Object.class))) { - return false; + // Do element by element comparison, if any elements are not contained return false + for (Object obj : selfSet) { + if (!otherPySet.contains(Py.java2py(obj))) { + return Py.False; } } - return true; + // All elements are equal so the sets are equal + return Py.True; } - return false; + else { + // Being compared to something that is not a Python set + final Object oj = other.getJavaProxy(); + if (oj instanceof Set) { + // Being compared to Java Set convert to Python set and call recursively + final PySet otherPySet = new PySet(Py.javas2pys(((Set) oj).toArray())); + return isEqual(otherPySet); + } else { + // other is not a Python or Java set, so we don't know if + // were equal therefore return null + return null; + } + } } public boolean isSuperset(PyObject other) { @@ -164,22 +180,27 @@ super(name, 0, -1); } + @Override public PyObject __call__() { return __call__(Py.EmptyObjects); } + @Override public PyObject __call__(PyObject obj) { return __call__(new PyObject[]{obj}); } + @Override public PyObject __call__(PyObject obj1, PyObject obj2) { return __call__(new PyObject[]{obj1, obj2}); } + @Override public PyObject __call__(PyObject obj1, PyObject obj2, PyObject obj3) { return __call__(new PyObject[]{obj1, obj2, obj3}); } + @Override public PyObject __call__(PyObject obj1, PyObject obj2, PyObject obj3, PyObject obj4) { return __call__(new PyObject[]{obj1, obj2, obj3, obj4}); } @@ -286,13 +307,25 @@ private static final SetMethod eqProxy = new SetMethod("__eq__", 1) { @Override public PyObject __call__(PyObject other) { - return Py.newBoolean(isEqual(other)); + return isEqual(other); + } + }; + private static final SetMethod neProxy = new SetMethod("__ne__", 1) { + @Override + public PyObject __call__(PyObject other) { + // isEqual may return null if we don't know how to compare to other. + PyBoolean equal = isEqual(other); + if (equal != null) { + // implement NOT equal by the inverse of equal + return isEqual(other).__not__(); + } + return null; } }; private static final SetMethod ltProxy = new SetMethod("__lt__", 1) { @Override public PyObject __call__(PyObject other) { - return Py.newBoolean(!isEqual(other) && isSubset(other)); + return isEqual(other).__not__().__and__(Py.newBoolean(isSubset(other))); } }; @@ -327,7 +360,7 @@ private static final SetMethod gtProxy = new SetMethod("__gt__", 1) { @Override public PyObject __call__(PyObject other) { - return Py.newBoolean(!isEqual(other) && isSuperset(other)); + return isEqual(other).__not__().__and__(Py.newBoolean(isSuperset(other))); } }; @@ -549,6 +582,7 @@ return new PyBuiltinMethod[]{ cmpProxy, eqProxy, + neProxy, ltProxy, new IsSubsetMethod("__le__"), new IsSubsetMethod("issubset"), -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Wed Jul 11 14:53:19 2018 From: jython-checkins at python.org (jeff.allen) Date: Wed, 11 Jul 2018 18:53:19 +0000 Subject: [Jython-checkins] =?utf-8?q?jython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_Merge_Gradle_build_script_addition_to_trunk=2E?= Message-ID: <20180711185319.1.FA57679347394958@mg.python.org> https://hg.python.org/jython/rev/ebd8868b8323 changeset: 8171:ebd8868b8323 parent: 8169:e546ad3d85bd parent: 8170:921c66481876 user: Jeff Allen date: Wed Jul 11 19:46:40 2018 +0100 summary: Merge Gradle build script addition to trunk. files: .gitignore | 6 + .hgignore | 6 + build.gradle | 755 ++++++++++ gradle/wrapper/gradle-wrapper.jar | Bin gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 172 ++ gradlew.bat | 84 + settings.gradle | 5 + tests/java/org/python/core/BaseBytesTest.java | 12 +- tests/java/org/python/core/PyBufferTest.java | 2 +- tests/java/org/python/core/PyByteArrayTest.java | 27 +- 11 files changed, 1057 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ *.swp \#* *~ + +.gradle + # IntelliJ files *.eml *.ipr @@ -32,8 +35,11 @@ __pycache__ ant.properties bin + build +build2 cachedir dist target + profile.txt diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -8,6 +8,9 @@ *.swp \#* *~ + +.gradle + # IntelliJ files *.eml *.ipr @@ -27,8 +30,11 @@ __pycache__ ant.properties bin + build +build2 cachedir dist + profile.txt out diff --git a/build.gradle b/build.gradle new file mode 100644 --- /dev/null +++ b/build.gradle @@ -0,0 +1,755 @@ +/* + * Gradle build for Jython. See also settings.gradle. + * + * This is an attempt to build a distributable JAR using Gradle that could be + * cited as a dependency by other Gradle or Maven projects, when they use the + * Jython interpreter from Java (under JSR-223 or directly). + * + * At present, the build is additional to the Ant build that remains the + * primary and reliable support for development, for test, and to build the + * Jython installers. + * + * The delivered jar should contain only Jython project material (Java classes + * and the Python library) while the many JARs Jython depends on will be cited + * in the accompanying metadata as dependencies. + * + * The Jython source structure does not follow the standard convention for + * Gradle. This script deals with that without changing it, but it uses a build + * directory (build2) entirely separate from Ant's, in which generated and + * compiled material is posted conformant with Gradle conventions. This means + * that the later tasks Gradle provides (test and jar) do not need so much + * customisation. + */ + +plugins { + id 'java-library' + id 'antlr' + // XXX Add maven and use it. +} + +import java.text.SimpleDateFormat + +// ---------------- Determine the version of Jython ---------------------------- + +/* + * This one string will be used to name the generated JAR and version-stamp the + * application. It should be all you have to edit to version a release built + * here. But of course you have to do it the hard way too (see build.xml) as + * long as Ant is also used. + */ +// . ( . )? ( )? ([-+] ? )? + +version = '2.7.2a1+' + +// Valid examples (please preserve in comments): +//version = '2.7.2a2' +//version = '2.7.2b1' +//version = '2.7.2rc1' +//version = '2.7.2' + + +// ---------------- Miscellaneous configuration -------------------------------- + + +// Separate the Gradle build from that of Ant +buildDir = file('build2') +ext { + buildDate = new Date() + // Java source generated by ANTLR + antlrGenDir = "$buildDir/gensrc/org/python/antlr" + // This is where we assemble the standard library pre-JAR + buildLibDir = "$buildDir/resources/main/Lib/" + compiledLibDir = "$buildDir/classes/main/Lib/" +} + + +repositories { + /* + * Jython is distributed through Maven Central. Get our dependencies there + * too. + */ + mavenCentral() +} + +sourceSets { + + main { // Non-standard locations must be specified explicitly + + antlr { + srcDirs = ['grammar'] + exclude 'Base.g' // Not used (and produces errors) + } + + java { + srcDirs = ['src', project.ext.antlrGenDir] + exclude 'com/**' // for now + } + + // output.resourcesDir = project.ext.assemblyDir + + resources { + // Resources in project root, but this invites an explosion: + // srcDirs = [''] + // ... so claim no sources: + srcDirs = [] + // and fix it in task processResources + } + } + + test { // Non-standard locations must be specified explicitly + + java { + srcDirs = ['tests/java'] + exclude 'com/**' // XXX for now + } + } +} + +dependencies { + /* + * These must correspond fairly exactly with the external libraries (JARs) + * mentioned in the Ant build.xml. + */ + + // Using a version available from repo (not 'extlibs/servlet-api-2.5' as in build.xml) + implementation group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0' + + // implementation 'mysql-connector-java-5.1.42-bin' + // implementation 'postgresql-42.1.1.jre7' + + // pin to Antlr 3.1.3 until we upgrade parsing + antlr 'org.antlr:antlr:3.1.3' // use ANTLR version 3 + implementation 'org.antlr:antlr-runtime:3.1.3' + implementation 'org.antlr:stringtemplate:3.2.1' + + implementation group: 'org.apache.commons', name: 'commons-compress', version: '1.14' + + // pin to ASM 5.2 until we upgrade compilation + implementation group: 'org.ow2.asm', name: 'asm', version: '5.2' + implementation group: 'org.ow2.asm', name: 'asm-commons', version: '5.2' + implementation group: 'org.ow2.asm', name: 'asm-util', version: '5.2' + + implementation group: 'com.google.guava', name: 'guava', version: '22.0-android' + implementation group: 'com.ibm.icu', name: 'icu4j', version: '59.1' + + implementation group: 'com.carrotsearch', name: 'java-sizeof', version: '0.0.5' + + implementation group: 'com.github.jnr', name: 'jffi', version: '1.2.16' + implementation group: 'com.github.jnr', name: 'jnr-netdb', version: '1.1.6' + implementation group: 'com.github.jnr', name: 'jnr-ffi', version: '2.1.7' + implementation group: 'com.github.jnr', name: 'jnr-posix', version: '3.0.44' + implementation group: 'com.github.jnr', name: 'jnr-constants', version: '0.9.9' + + implementation group: 'jline', name: 'jline', version: '2.14.5' + + implementation group: 'io.netty', name: 'netty-buffer', version: '4.1.24.Final' + implementation group: 'io.netty', name: 'netty-codec', version: '4.1.24.Final' + implementation group: 'io.netty', name: 'netty-common', version: '4.1.24.Final' + implementation group: 'io.netty', name: 'netty-handler', version: '4.1.24.Final' + implementation group: 'io.netty', name: 'netty-resolver', version: '4.1.24.Final' + implementation group: 'io.netty', name: 'netty-transport', version: '4.1.24.Final' + + // Used implicitly in the Ant build, must be explicit here + implementation group: 'org.apache.ant', name: 'ant', version: '1.9.7' + + testImplementation group: 'junit', name: 'junit', version: '4.10' + +} + +// ---------------- Resource Processing ---------------------------------------- + +/* + * Jython brings several files we could treat as resources, but they do not sit + * in the Gradle-conventional 'main/resources' directory, rather are in the + * project root or rub shoulders with the java source. Pick them individually. + */ +processResources { + from(file('.')) { + include 'LICENSE.txt' + } + from(file('src')) { + include 'META-INF/**' + include 'org/python/modules/ucnhash.dat' + } +} + +// ---------------- ANTLR Task ------------------------------------------------- + +generateGrammarSource { + maxHeapSize = "512m" + outputDirectory = file(project.ext.antlrGenDir) +} + +// ---------------- Expose Task ------------------------------------------------ + +/* + * The exposer operates between two (somewhat fixed) directories. We follow + * the Gradle-conventional directory structure (not the legacy one). + */ +ext { + compileDir = "$buildDir/classes/java/main/" + exposedDir = "$buildDir/classes/exposed/main/" +} + +configurations { + expose.extendsFrom(implementation) +} + +dependencies { + // Put our compiled classes on the path of the expose (Ant) task + expose files("$buildDir/classes/java/main") +} + +// A (Gradle) task to run the Ant task 'expose'. +task expose (group: 'Custom', dependsOn: compileJava) { + + description = 'Expose Java types to Python using their annotations.' + + // Allow Gradle to infer the need to regenreate the outputs + inputs.files(fileTree("${project.ext.compileDir}/org/python")) + outputs.dir(project.ext.exposedDir) + + doLast { + /* + * Define an Ant task called 'expose' in the project's AntBuilder. + * We can't define it until ExposeTask has been compiled. + */ + ant.taskdef( + name: 'expose', + classname: 'org.python.expose.generate.ExposeTask', + classpath: configurations.expose.asPath + ) + + // Use the Gradle-conventional directory structure (not the legacy one). + ant.expose( + srcdir: file(project.ext.compileDir), + destdir: mkdir(file(project.ext.exposedDir)), + includesfile: file('CoreExposed.includes') + ) + } +} + +// ---------------- Version-related file generation ---------------------------- + +/* + * Write the information that org.python.Version reads from + * org/python/version.properties in the class file structure. The inputs to + * this are: information from Mercurial (hg command required); project.version; + * and project.ext.buildDate. The task works quite hard to decode + * project.version, which must have the correct form, to deduce whether you + * really intend this to be a release. If anything comes after the release + * number, typically it's a '+' sign, the version becomes a snapshot. + */ +task generateVersionInfo( + type: WriteProperties, + description: 'Write the version information as properties') { + + outputFile = file("${processResources.destinationDir}/org/python/version.properties") + comment = ' Jython version information (from build.gradle)' + + // Create the properties when the task runs. But do it before the write! + doFirst { + /* + * Query Mercurial for version and tagging. + */ + String hgOutput = 'hg identify -ibt'.execute().text + hgOutput = hgOutput.split('\n', 2)[0] + + // (+)? + String[] parts = hgOutput.split(/\s+/, 3) + if (parts.length != 3) { + throw new IllegalArgumentException( + "Cannot split Mercurial output '$hgOutput' into 3") + } + def (ident, branch, tag) = parts + property('jython.build.hg_branch', branch) + property('jython.build.hg_tag', tag) + property('jython.build.hg_version', ident) + + /* + * Decompose the version string into elements for Jython to access as + * properties. (The Ant build.xml requires them to be set in parts, but + * we can work it out from the .) + */ + // .(.)()?([-+])? + def versionRegex = /(\d+)\.(\d+)(\.(\d+))?((a|b|rc)(\d+))?([-+]\w*)?/ + def versionMatcher = project.version =~ versionRegex + if (versionMatcher.count != 1) { + throw new IllegalArgumentException( + "Cannot parse project version string '${project.version}'") + } + // In principle it could match more than once: take the leftmost + def versionResult = versionMatcher[0] + + + // Useful debug + /* + for (int i=0; i < versionResult.size(); i++) { + String part = versionResult[i] + println "versionResult[$i] = $part" + } */ + + // . means ..0 + String major = versionResult[1] + String minor = versionResult[2] + String micro = versionResult[3] ? versionResult[4] : '0' + + // Convert the optional to numbers + int SNAPSHOT = 0xaa + int level = 0, serial = 0 + if (versionResult[5]) { + // This is some kind of pre-final release (unless snapshaot) + serial = versionResult[7] as int + switch (versionResult[6]) { + case 'a': level = 0xa; break // ALPHA release + case 'b': level = 0xb; break // BETA release + case 'rc': level = 0xc; break // release candidate + } + } else { + // Not marked as a/b/rc so ostensibly a final release. + level = 0xf + } + + // Except, if there was something after the version we decoded ... + if (versionResult[8]) { + level = SNAPSHOT // it's snapshot or dev version of some kind + serial = 0 + } + + property('jython.version', project.version) + property('jython.major_version', major) + property('jython.minor_version', minor) + property('jython.micro_version', micro) + property('jython.release_level', level) + property('jython.release_serial', serial) + + /* + * Time-stamp the build. In the time part, the ':' gets escaped to + * '\:', consistent with Properties.store(), unlike the Ant build. + */ + property('jython.build.time', + (new SimpleDateFormat('HH:mm:ss')) + .format(project.ext.buildDate)) + property('jython.build.date', + (new SimpleDateFormat('MMM d yyyy')) + .format(project.ext.buildDate)) + + /* + * Remind the developer about tagging it if it looks like a release, + * or to adjust project.version if we're moving on from the release. + */ + if (level != SNAPSHOT) { + def L = [0:'', 10:'a', 11:'b', 12:'rc', 15:''] + String release = "$major.$minor.$micro${L[level]}${serial?:''}" + println "This build is versioned for distribution as $release" + if (tag == 'tip' || ident.endsWith('+')) { + println "If this really is distributable, " + + "don't forget to tag it in the repository.\n" + + "Alternatively, add a suffix to version = " + + "'${project.version}' in build.gradle to shut this up." + } + } + } +} + +// Attach this task to processResources +processResources.dependsOn(generateVersionInfo) + + +// ---------------- Copy Python Library ---------------------------------------- + +/* + * The default behaviour of the Java plug-in is to make a JAR of the classes in + * the "main" source set. We need a more complex assembly that provides users + * with exposed classes instead of their plain counterparts, and also various + * configuration files and the Python library. + * + * These copies include the tests, so we can test things :), but a subsequent + * JarTask of the build should exclude them as necessary. + */ + +ext { + libPython = 'lib-python/2.7' + libJython = 'Lib' +} + + +/* + * Copy the Python standard library. We take this from a distribution of + * CPython, but take only the files specified in CPythonLib.includes in the + * project root. + */ +task copyLib( + type: Copy, + description: 'Merge lib-python and Jython Lib during assembly') { + + into "${project.ext.buildLibDir}" + + // Copy Jython Lib, with precedence over CPython files of the same name + from file(project.ext.libJython) + exclude 'test/' + exclude '**/*.class' + duplicatesStrategy = DuplicatesStrategy.INCLUDE + + // Allow Gradle to infer the need to regenerate the outputs + inputs.dir project.ext.libJython + inputs.dir project.ext.libPython + inputs.file file('CPythonLib.includes') + outputs.dir project.ext.buildLibDir + + doFirst { + // Select the CPython stdlib files by making a list. + def cPythonLibIncludes = [] + // Read list from CPythonLib.includes, stripping comments and spaces. + file('CPythonLib.includes').eachLine { line -> + def trimmed = line.split('#', 2)[0].trim() + if (trimmed.length() > 0) { + cPythonLibIncludes << trimmed + } + } + // Copy the subset as specified by the list + project.copy { + from file(project.ext.libPython) + include cPythonLibIncludes + exclude '**/*.pyc', '**/*.pyd' + into project.ext.buildLibDir + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + } + + /* + + + + + + + + + */ +} + + + + +// ---------------- Jython-Compile Python -------------------------------------- + +/* + * Compile the Python modules to .class files for the JAR. Whereas Jython runs + * happily with a concrete Lib folder, creating and caching the .class files, + * when Jython is supplied as a JAR, we prefer to compile the class files once + * in advance. + */ + +configurations { + pycompile.extendsFrom(implementation) +} + +dependencies { + // Jython as built so far should be on the path of the jycompile (Ant) task + pycompile files("$buildDir/classes/exposed/main") + pycompile files("$buildDir/classes/java/main") + pycompile files("$buildDir/resources/main") +} + +// A (Gradle) task to run the Ant task 'jycompile' (not pycompile). +task pycompile( + group: 'Custom', + description: 'Compile the Python modules to .class files for the JAR') { + + dependsOn compileJava + dependsOn expose + dependsOn copyLib + + // Allow Gradle to infer the need to regenerate the outputs + inputs.dir project.ext.buildLibDir + outputs.dir project.ext.compiledLibDir + + doFirst { + /* + * Define an Ant task called 'jycompile' in the project's AntBuilder. + * We can't define it until JythoncAntTask has been compiled, so this + * must happen during the execution of the task (early). + */ + ant.taskdef( + name: 'jycompile', + classname: 'org.python.util.JycompileAntTask', + classpath: configurations.pycompile.asPath + ) + } + + doLast { + /* + * Now use the 'jycompile' Ant task tocompile the Python source we + * supply to users. The exclusions are copied from build.xml, as also + * is this comment: + + */ + def exclusions = ['test/**', 'lib2to3/tests/**', + 'lib2to3/tests/data/myfixes/**'] + ant.jycompile( + srcdir: project.ext.buildLibDir, + destdir: project.ext.compiledLibDir, + excludes: exclusions.join(',') // Yes, it's that way round :o + ) + } + +} + + +// ---------------- Building the JAR ------------------------------------------- + +/* + * The default behaviour of the Java plug-in is to make a JAR of the classes in + * the "main" source set. We need a more complex operation that provides users + * with exposed classes instead of their plain counterparts, and also various + * configuration files and the Python library. + * + * Much of the assembly has taken place already in selective copying to the + * build directory by tasks processResources and copyLib. + */ + +task jar(type: Jar, overwrite: true) { + + dependsOn pycompile + + /* + * This is a custom replacement Jar task so that we may control the + * order of adding classes. Exposed versions of identically-named classes + * supersede (by arriving first) those directly from compilation. + */ + exclude 'org/python/expose/generate/**' // The expose tool itself + + // Add the exposed classes to the JAR first + from expose + + // Add other compiled classes but without overwriting the exposed ones + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from compileJava + + // Add the resources + from processResources + + // Add Python library + preserveFileTimestamps = true + from file(project.ext.buildLibDir).parent + from file(project.ext.compiledLibDir).parent + + manifest { + // These attribute values are based on inspecting the ant build + attributes ([ + 'Main-Class': 'org.python.util.jython', + 'Built-By': 'Mistake', + ]) + + attributes( [ // Build-Info section + 'version': project.version, + 'build-compiler': 'modern', + 'jdk-target-version': '1.7', + 'debug': true, + ], 'Build-Info' ) + } + +} + + +// ---------------- Java unit tests -------------------------------------------- + +ext { + //distDir = relativePath("$buildDir/assembly") + testSourceDir = relativePath('tests/java') +} + + +dependencies { + // Put the exposed classes on the path of the test tasks + testImplementation files(expose) +} + +// Ensure exposed classes are ahead of standard path +sourceSets.test { + compileClasspath = files(expose.outputs) + compileClasspath + runtimeClasspath = files(expose.outputs) + runtimeClasspath + // println "runtimeClasspath = ${runtimeClasspath.asPath}" +} + +compileTestJava { + dependsOn expose +} + +test { + + dependsOn copyLib + + // Stop on first test failure + failFast = true + + // Properties as defined in Ant target javatest-basepath + //systemProperty 'python.home', project.ext.distDir + // XXX Not sure of all that python.home is used for in tests. + systemProperty 'python.home', file(copyLib.destinationDir).parent + systemProperty 'python.test.source.dir', project.ext.testSourceDir + // Place cache outside the targets for jar task + systemProperty 'python.cachedir', "${project.buildDir}/cachedir" + + // Include/exclude based on Ant target javatest-basepath + include '**/*Test*' + exclude '**/InterpTestCase' + exclude '**/jythonTest*' // Must run interactively + exclude 'org/python/antlr/**' + exclude 'org/python/tests/imp/**' // See build.xml:importest + + // Some additional exclusions or else the task fails + // FIXME: leaves stdin/out/err as PyFileWriter that has no fileno() + // causing _ioTest to fail. + exclude '**/jsr223/*' + // FIXME: Tests that hard-code directory paths (use a symbol): + exclude 'org/python/compiler/custom_proxymaker/**' + exclude 'org/python/compiler/JavaMakerSmokeTest.class' + // FIXME: Failing test finds project root from test class location + exclude 'org/python/core/PySystemState_registry_Test.class' + // FIXME: Fails as sys._jy_console not set when run under Gradle + exclude 'org/python/util/InterpreterTest.class' + // FIXME: Failing test, see issue 2410 + exclude 'org/python/core/PySystemStateTest.class' + + doFirst { + println "systemProperties = $systemProperties" + } + + /* From build.xml : + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */ +} + +// ---------------- Miscellaneous fettling of the prepare phase ---------------- + +// Source is globally UTF-8 (well, nearly). +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" +} + + + +// ---------------- Tasks for debugging ---------------------------------------- + + +task dumpCP { + doLast { + //println('configurations.testCompile:') + //configurations.testCompile.each { println it } + //println('configurations.testRuntime:') + //configurations.testRuntime.each { println it } + println('configurations.expose:') + configurations.expose.each { println it } + println('configurations.pycompile:') + configurations.pycompile.each { println it } + } +} + + +task dumpex { + doLast { + int n = project.ext.exposedDir.length() + Set exposedClasses = new TreeSet() + //println "*** files in ${project.ext.exposedDir}:" + for (f in fileTree(project.ext.exposedDir)) { + //println project.relativePath(f).substring(n) + exposedClasses.add( project.relativePath(f).substring(n) ) + } + //for (f in exposedClasses) { println f } + + println "${fileTree(project.ext.compileDir).size()} compiled classes." + + n = project.ext.compileDir.length() + int countx = 0 + for (f in fileTree(project.ext.compileDir)) { + //println project.relativePath(f).substring(n) + String name = project.relativePath(f).substring(n) + if (name in exposedClasses) { countx += 1 } + } + println "${exposedClasses.size()} classes from ${countx} exposed." + + def compiledToJar = fileTree(project.ext.compileDir).filter({ + File f -> !(project.relativePath(f).substring(n) in exposedClasses) + }) + + println "${compiledToJar.size()} to be jarred (after filtering)." + + int counti = 0 + for (f in fileTree(project.ext.compileDir)) { + String name = project.relativePath(f).substring(n) + String exposed = (name in exposedClasses) ? "EXPOSED" : "" + // println "${name} ${exposed}" + if (exposed) { counti += 1 } + } + + println "${counti} in overlap." + } +} + + +task dump { + doLast { + // Debug + println '*** source sets ***' + for ( sourceSet in sourceSets ) { + println " ${sourceSet}" + // for ( name in sourceSet.asMap.keys ) { + // sourceDirectorySet = sourceSet.asMap[name] + // println " $name = $sourceDirectorySet" + // } + } + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..91ca28c8b802289c3a438766657a5e98f20eff03 GIT binary patch [stripped] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ + at if "%DEBUG%" == "" @echo off + at rem ########################################################################## + at rem + at rem Gradle startup script for Windows + at rem + at rem ########################################################################## + + at rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + + at rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + + at rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init + at rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args + at rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute + at rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + at rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end + at rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,5 @@ +/* + * Gradle settings for Jython. See also build.gradle. + */ + +rootProject.name = 'jython' diff --git a/tests/java/org/python/core/BaseBytesTest.java b/tests/java/org/python/core/BaseBytesTest.java --- a/tests/java/org/python/core/BaseBytesTest.java +++ b/tests/java/org/python/core/BaseBytesTest.java @@ -217,7 +217,7 @@ // Local constructor from byte[] int[] aRef = toInts("Chaque coquillage incrust?"); BaseBytes a = getInstance(aRef); - System.out.println(toString(a)); + // System.out.println(toString(a)); assertEquals(aRef.length, a.size()); // init(int) at various sizes @@ -237,7 +237,7 @@ BaseBytes a = getInstance(aRef); // Copy constructor b = bytes(a) BaseBytes b = getInstance(a); - System.out.println(toString(b)); + // System.out.println(toString(b)); assertEquals(a.size(), b.size()); // assertEquals(a.storage, b.storage); // Supposed to share? // Check we got the same bytes @@ -254,7 +254,7 @@ // Make an Iterable of that Iterable ia = iterableBytes(aRef); BaseBytes a = getInstance(ia); - System.out.println(toString(a)); + // System.out.println(toString(a)); assertEquals(aRef.length, a.size()); checkInts(aRef, a); @@ -319,7 +319,7 @@ PyObject aRef = boobyPrize[dip]; try { BaseBytes a = getInstance(brantub[dip]); - System.out.println(toString(a)); + // System.out.println(toString(a)); fail("Exception not thrown for " + brantub[dip]); } catch (PyException pye) { // System.out.println(pye); @@ -452,7 +452,7 @@ try { a.pyset(start, x); - System.out.println(toString(a)); + // System.out.println(toString(a)); fail(String.format("Exception not thrown for pyset(%d,%s)", start, x)); } catch (PyException pye) { // System.out.println(pye); @@ -476,7 +476,7 @@ try { a.setslice(start, stop, step, x); - System.out.println(toString(a)); + // System.out.println(toString(a)); fail(String.format("Exception not thrown for setslice(%d,%d,%d,%s)", start, stop, step, x)); } catch (PyException pye) { diff --git a/tests/java/org/python/core/PyBufferTest.java b/tests/java/org/python/core/PyBufferTest.java --- a/tests/java/org/python/core/PyBufferTest.java +++ b/tests/java/org/python/core/PyBufferTest.java @@ -60,7 +60,7 @@ protected int verbosity = 0; /** Print a list of the test material. (From JUnit 4.12 use Parameters(name)). */ - protected static final boolean PRINT_KEY = true; + protected static final boolean PRINT_KEY = false; /** Size of some large arrays. */ static final int LONG = 1000; diff --git a/tests/java/org/python/core/PyByteArrayTest.java b/tests/java/org/python/core/PyByteArrayTest.java --- a/tests/java/org/python/core/PyByteArrayTest.java +++ b/tests/java/org/python/core/PyByteArrayTest.java @@ -12,6 +12,9 @@ */ public class PyByteArrayTest extends BaseBytesTest { + /** This program may be used to display operation time, but this is not necessary for tests. */ + final static boolean SHOW_OPERATION_TIMES = false; + /** * Constructor required by JUnit. * @@ -963,11 +966,13 @@ * slice to replace is simple and contiguous (2-argument slice). */ public void testSetsliceTime() { - int verbose = 1; - timeSetslice(50, 100, SMALL, 2 * SMALL, verbose); - timeSetslice(50, 100, MEDIUM, MEDIUM, verbose); - timeSetslice(500, 20, LARGE, LARGE / 5, verbose); - // timeSetslice(1000, 4, HUGE, HUGE/5, verbose); + if (SHOW_OPERATION_TIMES) { + int verbose = 1; + timeSetslice(50, 100, SMALL, 2 * SMALL, verbose); + timeSetslice(50, 100, MEDIUM, MEDIUM, verbose); + timeSetslice(500, 20, LARGE, LARGE / 5, verbose); + timeSetslice(1000, 4, HUGE, HUGE/5, verbose); + } } /** @@ -1388,11 +1393,13 @@ * slice to replace is extended (3-argument slice). */ public void testDelsliceTime3() { - int verbose = 1; - timeDelslice3(50, 100, SMALL, verbose); - timeDelslice3(50, 100, MEDIUM, verbose); - timeDelslice3(20, 4, LARGE, verbose); - // timeDelslice3(10, 1, HUGE, verbose); + if (SHOW_OPERATION_TIMES) { + int verbose = 1; + timeDelslice3(50, 100, SMALL, verbose); + timeDelslice3(50, 100, MEDIUM, verbose); + timeDelslice3(20, 4, LARGE, verbose); + timeDelslice3(10, 1, HUGE, verbose); + } } /** -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Wed Jul 11 14:53:19 2018 From: jython-checkins at python.org (jeff.allen) Date: Wed, 11 Jul 2018 18:53:19 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Provide_Gradle_build_scrip?= =?utf-8?q?t_with_jar_and_test_targets=2E?= Message-ID: <20180711185319.1.890807F33E7A0EBA@mg.python.org> https://hg.python.org/jython/rev/921c66481876 changeset: 8170:921c66481876 parent: 8166:b8ce85eacfda user: Jeff Allen date: Wed Jul 11 14:57:10 2018 +0100 summary: Provide Gradle build script with jar and test targets. This does not yet go as far as publishing an artefact. It answers several questions about what should be in the JAR and how to create it with Gradle, so a good place to commit something. files: .gitignore | 6 + .hgignore | 6 + build.gradle | 755 ++++++++++ gradle/wrapper/gradle-wrapper.jar | Bin gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 172 ++ gradlew.bat | 84 + settings.gradle | 5 + tests/java/org/python/core/BaseBytesTest.java | 12 +- tests/java/org/python/core/PyBufferTest.java | 2 +- tests/java/org/python/core/PyByteArrayTest.java | 27 +- 11 files changed, 1057 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ *.swp \#* *~ + +.gradle + # IntelliJ files *.eml *.ipr @@ -32,8 +35,11 @@ __pycache__ ant.properties bin + build +build2 cachedir dist target + profile.txt diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -8,6 +8,9 @@ *.swp \#* *~ + +.gradle + # IntelliJ files *.eml *.ipr @@ -27,8 +30,11 @@ __pycache__ ant.properties bin + build +build2 cachedir dist + profile.txt out diff --git a/build.gradle b/build.gradle new file mode 100644 --- /dev/null +++ b/build.gradle @@ -0,0 +1,755 @@ +/* + * Gradle build for Jython. See also settings.gradle. + * + * This is an attempt to build a distributable JAR using Gradle that could be + * cited as a dependency by other Gradle or Maven projects, when they use the + * Jython interpreter from Java (under JSR-223 or directly). + * + * At present, the build is additional to the Ant build that remains the + * primary and reliable support for development, for test, and to build the + * Jython installers. + * + * The delivered jar should contain only Jython project material (Java classes + * and the Python library) while the many JARs Jython depends on will be cited + * in the accompanying metadata as dependencies. + * + * The Jython source structure does not follow the standard convention for + * Gradle. This script deals with that without changing it, but it uses a build + * directory (build2) entirely separate from Ant's, in which generated and + * compiled material is posted conformant with Gradle conventions. This means + * that the later tasks Gradle provides (test and jar) do not need so much + * customisation. + */ + +plugins { + id 'java-library' + id 'antlr' + // XXX Add maven and use it. +} + +import java.text.SimpleDateFormat + +// ---------------- Determine the version of Jython ---------------------------- + +/* + * This one string will be used to name the generated JAR and version-stamp the + * application. It should be all you have to edit to version a release built + * here. But of course you have to do it the hard way too (see build.xml) as + * long as Ant is also used. + */ +// . ( . )? ( )? ([-+] ? )? + +version = '2.7.2a1+' + +// Valid examples (please preserve in comments): +//version = '2.7.2a2' +//version = '2.7.2b1' +//version = '2.7.2rc1' +//version = '2.7.2' + + +// ---------------- Miscellaneous configuration -------------------------------- + + +// Separate the Gradle build from that of Ant +buildDir = file('build2') +ext { + buildDate = new Date() + // Java source generated by ANTLR + antlrGenDir = "$buildDir/gensrc/org/python/antlr" + // This is where we assemble the standard library pre-JAR + buildLibDir = "$buildDir/resources/main/Lib/" + compiledLibDir = "$buildDir/classes/main/Lib/" +} + + +repositories { + /* + * Jython is distributed through Maven Central. Get our dependencies there + * too. + */ + mavenCentral() +} + +sourceSets { + + main { // Non-standard locations must be specified explicitly + + antlr { + srcDirs = ['grammar'] + exclude 'Base.g' // Not used (and produces errors) + } + + java { + srcDirs = ['src', project.ext.antlrGenDir] + exclude 'com/**' // for now + } + + // output.resourcesDir = project.ext.assemblyDir + + resources { + // Resources in project root, but this invites an explosion: + // srcDirs = [''] + // ... so claim no sources: + srcDirs = [] + // and fix it in task processResources + } + } + + test { // Non-standard locations must be specified explicitly + + java { + srcDirs = ['tests/java'] + exclude 'com/**' // XXX for now + } + } +} + +dependencies { + /* + * These must correspond fairly exactly with the external libraries (JARs) + * mentioned in the Ant build.xml. + */ + + // Using a version available from repo (not 'extlibs/servlet-api-2.5' as in build.xml) + implementation group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0' + + // implementation 'mysql-connector-java-5.1.42-bin' + // implementation 'postgresql-42.1.1.jre7' + + // pin to Antlr 3.1.3 until we upgrade parsing + antlr 'org.antlr:antlr:3.1.3' // use ANTLR version 3 + implementation 'org.antlr:antlr-runtime:3.1.3' + implementation 'org.antlr:stringtemplate:3.2.1' + + implementation group: 'org.apache.commons', name: 'commons-compress', version: '1.14' + + // pin to ASM 5.2 until we upgrade compilation + implementation group: 'org.ow2.asm', name: 'asm', version: '5.2' + implementation group: 'org.ow2.asm', name: 'asm-commons', version: '5.2' + implementation group: 'org.ow2.asm', name: 'asm-util', version: '5.2' + + implementation group: 'com.google.guava', name: 'guava', version: '22.0-android' + implementation group: 'com.ibm.icu', name: 'icu4j', version: '59.1' + + implementation group: 'com.carrotsearch', name: 'java-sizeof', version: '0.0.5' + + implementation group: 'com.github.jnr', name: 'jffi', version: '1.2.16' + implementation group: 'com.github.jnr', name: 'jnr-netdb', version: '1.1.6' + implementation group: 'com.github.jnr', name: 'jnr-ffi', version: '2.1.7' + implementation group: 'com.github.jnr', name: 'jnr-posix', version: '3.0.44' + implementation group: 'com.github.jnr', name: 'jnr-constants', version: '0.9.9' + + implementation group: 'jline', name: 'jline', version: '2.14.5' + + implementation group: 'io.netty', name: 'netty-buffer', version: '4.1.24.Final' + implementation group: 'io.netty', name: 'netty-codec', version: '4.1.24.Final' + implementation group: 'io.netty', name: 'netty-common', version: '4.1.24.Final' + implementation group: 'io.netty', name: 'netty-handler', version: '4.1.24.Final' + implementation group: 'io.netty', name: 'netty-resolver', version: '4.1.24.Final' + implementation group: 'io.netty', name: 'netty-transport', version: '4.1.24.Final' + + // Used implicitly in the Ant build, must be explicit here + implementation group: 'org.apache.ant', name: 'ant', version: '1.9.7' + + testImplementation group: 'junit', name: 'junit', version: '4.10' + +} + +// ---------------- Resource Processing ---------------------------------------- + +/* + * Jython brings several files we could treat as resources, but they do not sit + * in the Gradle-conventional 'main/resources' directory, rather are in the + * project root or rub shoulders with the java source. Pick them individually. + */ +processResources { + from(file('.')) { + include 'LICENSE.txt' + } + from(file('src')) { + include 'META-INF/**' + include 'org/python/modules/ucnhash.dat' + } +} + +// ---------------- ANTLR Task ------------------------------------------------- + +generateGrammarSource { + maxHeapSize = "512m" + outputDirectory = file(project.ext.antlrGenDir) +} + +// ---------------- Expose Task ------------------------------------------------ + +/* + * The exposer operates between two (somewhat fixed) directories. We follow + * the Gradle-conventional directory structure (not the legacy one). + */ +ext { + compileDir = "$buildDir/classes/java/main/" + exposedDir = "$buildDir/classes/exposed/main/" +} + +configurations { + expose.extendsFrom(implementation) +} + +dependencies { + // Put our compiled classes on the path of the expose (Ant) task + expose files("$buildDir/classes/java/main") +} + +// A (Gradle) task to run the Ant task 'expose'. +task expose (group: 'Custom', dependsOn: compileJava) { + + description = 'Expose Java types to Python using their annotations.' + + // Allow Gradle to infer the need to regenreate the outputs + inputs.files(fileTree("${project.ext.compileDir}/org/python")) + outputs.dir(project.ext.exposedDir) + + doLast { + /* + * Define an Ant task called 'expose' in the project's AntBuilder. + * We can't define it until ExposeTask has been compiled. + */ + ant.taskdef( + name: 'expose', + classname: 'org.python.expose.generate.ExposeTask', + classpath: configurations.expose.asPath + ) + + // Use the Gradle-conventional directory structure (not the legacy one). + ant.expose( + srcdir: file(project.ext.compileDir), + destdir: mkdir(file(project.ext.exposedDir)), + includesfile: file('CoreExposed.includes') + ) + } +} + +// ---------------- Version-related file generation ---------------------------- + +/* + * Write the information that org.python.Version reads from + * org/python/version.properties in the class file structure. The inputs to + * this are: information from Mercurial (hg command required); project.version; + * and project.ext.buildDate. The task works quite hard to decode + * project.version, which must have the correct form, to deduce whether you + * really intend this to be a release. If anything comes after the release + * number, typically it's a '+' sign, the version becomes a snapshot. + */ +task generateVersionInfo( + type: WriteProperties, + description: 'Write the version information as properties') { + + outputFile = file("${processResources.destinationDir}/org/python/version.properties") + comment = ' Jython version information (from build.gradle)' + + // Create the properties when the task runs. But do it before the write! + doFirst { + /* + * Query Mercurial for version and tagging. + */ + String hgOutput = 'hg identify -ibt'.execute().text + hgOutput = hgOutput.split('\n', 2)[0] + + // (+)? + String[] parts = hgOutput.split(/\s+/, 3) + if (parts.length != 3) { + throw new IllegalArgumentException( + "Cannot split Mercurial output '$hgOutput' into 3") + } + def (ident, branch, tag) = parts + property('jython.build.hg_branch', branch) + property('jython.build.hg_tag', tag) + property('jython.build.hg_version', ident) + + /* + * Decompose the version string into elements for Jython to access as + * properties. (The Ant build.xml requires them to be set in parts, but + * we can work it out from the .) + */ + // .(.)()?([-+])? + def versionRegex = /(\d+)\.(\d+)(\.(\d+))?((a|b|rc)(\d+))?([-+]\w*)?/ + def versionMatcher = project.version =~ versionRegex + if (versionMatcher.count != 1) { + throw new IllegalArgumentException( + "Cannot parse project version string '${project.version}'") + } + // In principle it could match more than once: take the leftmost + def versionResult = versionMatcher[0] + + + // Useful debug + /* + for (int i=0; i < versionResult.size(); i++) { + String part = versionResult[i] + println "versionResult[$i] = $part" + } */ + + // . means ..0 + String major = versionResult[1] + String minor = versionResult[2] + String micro = versionResult[3] ? versionResult[4] : '0' + + // Convert the optional to numbers + int SNAPSHOT = 0xaa + int level = 0, serial = 0 + if (versionResult[5]) { + // This is some kind of pre-final release (unless snapshaot) + serial = versionResult[7] as int + switch (versionResult[6]) { + case 'a': level = 0xa; break // ALPHA release + case 'b': level = 0xb; break // BETA release + case 'rc': level = 0xc; break // release candidate + } + } else { + // Not marked as a/b/rc so ostensibly a final release. + level = 0xf + } + + // Except, if there was something after the version we decoded ... + if (versionResult[8]) { + level = SNAPSHOT // it's snapshot or dev version of some kind + serial = 0 + } + + property('jython.version', project.version) + property('jython.major_version', major) + property('jython.minor_version', minor) + property('jython.micro_version', micro) + property('jython.release_level', level) + property('jython.release_serial', serial) + + /* + * Time-stamp the build. In the time part, the ':' gets escaped to + * '\:', consistent with Properties.store(), unlike the Ant build. + */ + property('jython.build.time', + (new SimpleDateFormat('HH:mm:ss')) + .format(project.ext.buildDate)) + property('jython.build.date', + (new SimpleDateFormat('MMM d yyyy')) + .format(project.ext.buildDate)) + + /* + * Remind the developer about tagging it if it looks like a release, + * or to adjust project.version if we're moving on from the release. + */ + if (level != SNAPSHOT) { + def L = [0:'', 10:'a', 11:'b', 12:'rc', 15:''] + String release = "$major.$minor.$micro${L[level]}${serial?:''}" + println "This build is versioned for distribution as $release" + if (tag == 'tip' || ident.endsWith('+')) { + println "If this really is distributable, " + + "don't forget to tag it in the repository.\n" + + "Alternatively, add a suffix to version = " + + "'${project.version}' in build.gradle to shut this up." + } + } + } +} + +// Attach this task to processResources +processResources.dependsOn(generateVersionInfo) + + +// ---------------- Copy Python Library ---------------------------------------- + +/* + * The default behaviour of the Java plug-in is to make a JAR of the classes in + * the "main" source set. We need a more complex assembly that provides users + * with exposed classes instead of their plain counterparts, and also various + * configuration files and the Python library. + * + * These copies include the tests, so we can test things :), but a subsequent + * JarTask of the build should exclude them as necessary. + */ + +ext { + libPython = 'lib-python/2.7' + libJython = 'Lib' +} + + +/* + * Copy the Python standard library. We take this from a distribution of + * CPython, but take only the files specified in CPythonLib.includes in the + * project root. + */ +task copyLib( + type: Copy, + description: 'Merge lib-python and Jython Lib during assembly') { + + into "${project.ext.buildLibDir}" + + // Copy Jython Lib, with precedence over CPython files of the same name + from file(project.ext.libJython) + exclude 'test/' + exclude '**/*.class' + duplicatesStrategy = DuplicatesStrategy.INCLUDE + + // Allow Gradle to infer the need to regenerate the outputs + inputs.dir project.ext.libJython + inputs.dir project.ext.libPython + inputs.file file('CPythonLib.includes') + outputs.dir project.ext.buildLibDir + + doFirst { + // Select the CPython stdlib files by making a list. + def cPythonLibIncludes = [] + // Read list from CPythonLib.includes, stripping comments and spaces. + file('CPythonLib.includes').eachLine { line -> + def trimmed = line.split('#', 2)[0].trim() + if (trimmed.length() > 0) { + cPythonLibIncludes << trimmed + } + } + // Copy the subset as specified by the list + project.copy { + from file(project.ext.libPython) + include cPythonLibIncludes + exclude '**/*.pyc', '**/*.pyd' + into project.ext.buildLibDir + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + } + + /* + + + + + + + + + */ +} + + + + +// ---------------- Jython-Compile Python -------------------------------------- + +/* + * Compile the Python modules to .class files for the JAR. Whereas Jython runs + * happily with a concrete Lib folder, creating and caching the .class files, + * when Jython is supplied as a JAR, we prefer to compile the class files once + * in advance. + */ + +configurations { + pycompile.extendsFrom(implementation) +} + +dependencies { + // Jython as built so far should be on the path of the jycompile (Ant) task + pycompile files("$buildDir/classes/exposed/main") + pycompile files("$buildDir/classes/java/main") + pycompile files("$buildDir/resources/main") +} + +// A (Gradle) task to run the Ant task 'jycompile' (not pycompile). +task pycompile( + group: 'Custom', + description: 'Compile the Python modules to .class files for the JAR') { + + dependsOn compileJava + dependsOn expose + dependsOn copyLib + + // Allow Gradle to infer the need to regenerate the outputs + inputs.dir project.ext.buildLibDir + outputs.dir project.ext.compiledLibDir + + doFirst { + /* + * Define an Ant task called 'jycompile' in the project's AntBuilder. + * We can't define it until JythoncAntTask has been compiled, so this + * must happen during the execution of the task (early). + */ + ant.taskdef( + name: 'jycompile', + classname: 'org.python.util.JycompileAntTask', + classpath: configurations.pycompile.asPath + ) + } + + doLast { + /* + * Now use the 'jycompile' Ant task tocompile the Python source we + * supply to users. The exclusions are copied from build.xml, as also + * is this comment: + + */ + def exclusions = ['test/**', 'lib2to3/tests/**', + 'lib2to3/tests/data/myfixes/**'] + ant.jycompile( + srcdir: project.ext.buildLibDir, + destdir: project.ext.compiledLibDir, + excludes: exclusions.join(',') // Yes, it's that way round :o + ) + } + +} + + +// ---------------- Building the JAR ------------------------------------------- + +/* + * The default behaviour of the Java plug-in is to make a JAR of the classes in + * the "main" source set. We need a more complex operation that provides users + * with exposed classes instead of their plain counterparts, and also various + * configuration files and the Python library. + * + * Much of the assembly has taken place already in selective copying to the + * build directory by tasks processResources and copyLib. + */ + +task jar(type: Jar, overwrite: true) { + + dependsOn pycompile + + /* + * This is a custom replacement Jar task so that we may control the + * order of adding classes. Exposed versions of identically-named classes + * supersede (by arriving first) those directly from compilation. + */ + exclude 'org/python/expose/generate/**' // The expose tool itself + + // Add the exposed classes to the JAR first + from expose + + // Add other compiled classes but without overwriting the exposed ones + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from compileJava + + // Add the resources + from processResources + + // Add Python library + preserveFileTimestamps = true + from file(project.ext.buildLibDir).parent + from file(project.ext.compiledLibDir).parent + + manifest { + // These attribute values are based on inspecting the ant build + attributes ([ + 'Main-Class': 'org.python.util.jython', + 'Built-By': 'Mistake', + ]) + + attributes( [ // Build-Info section + 'version': project.version, + 'build-compiler': 'modern', + 'jdk-target-version': '1.7', + 'debug': true, + ], 'Build-Info' ) + } + +} + + +// ---------------- Java unit tests -------------------------------------------- + +ext { + //distDir = relativePath("$buildDir/assembly") + testSourceDir = relativePath('tests/java') +} + + +dependencies { + // Put the exposed classes on the path of the test tasks + testImplementation files(expose) +} + +// Ensure exposed classes are ahead of standard path +sourceSets.test { + compileClasspath = files(expose.outputs) + compileClasspath + runtimeClasspath = files(expose.outputs) + runtimeClasspath + // println "runtimeClasspath = ${runtimeClasspath.asPath}" +} + +compileTestJava { + dependsOn expose +} + +test { + + dependsOn copyLib + + // Stop on first test failure + failFast = true + + // Properties as defined in Ant target javatest-basepath + //systemProperty 'python.home', project.ext.distDir + // XXX Not sure of all that python.home is used for in tests. + systemProperty 'python.home', file(copyLib.destinationDir).parent + systemProperty 'python.test.source.dir', project.ext.testSourceDir + // Place cache outside the targets for jar task + systemProperty 'python.cachedir', "${project.buildDir}/cachedir" + + // Include/exclude based on Ant target javatest-basepath + include '**/*Test*' + exclude '**/InterpTestCase' + exclude '**/jythonTest*' // Must run interactively + exclude 'org/python/antlr/**' + exclude 'org/python/tests/imp/**' // See build.xml:importest + + // Some additional exclusions or else the task fails + // FIXME: leaves stdin/out/err as PyFileWriter that has no fileno() + // causing _ioTest to fail. + exclude '**/jsr223/*' + // FIXME: Tests that hard-code directory paths (use a symbol): + exclude 'org/python/compiler/custom_proxymaker/**' + exclude 'org/python/compiler/JavaMakerSmokeTest.class' + // FIXME: Failing test finds project root from test class location + exclude 'org/python/core/PySystemState_registry_Test.class' + // FIXME: Fails as sys._jy_console not set when run under Gradle + exclude 'org/python/util/InterpreterTest.class' + // FIXME: Failing test, see issue 2410 + exclude 'org/python/core/PySystemStateTest.class' + + doFirst { + println "systemProperties = $systemProperties" + } + + /* From build.xml : + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */ +} + +// ---------------- Miscellaneous fettling of the prepare phase ---------------- + +// Source is globally UTF-8 (well, nearly). +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" +} + + + +// ---------------- Tasks for debugging ---------------------------------------- + + +task dumpCP { + doLast { + //println('configurations.testCompile:') + //configurations.testCompile.each { println it } + //println('configurations.testRuntime:') + //configurations.testRuntime.each { println it } + println('configurations.expose:') + configurations.expose.each { println it } + println('configurations.pycompile:') + configurations.pycompile.each { println it } + } +} + + +task dumpex { + doLast { + int n = project.ext.exposedDir.length() + Set exposedClasses = new TreeSet() + //println "*** files in ${project.ext.exposedDir}:" + for (f in fileTree(project.ext.exposedDir)) { + //println project.relativePath(f).substring(n) + exposedClasses.add( project.relativePath(f).substring(n) ) + } + //for (f in exposedClasses) { println f } + + println "${fileTree(project.ext.compileDir).size()} compiled classes." + + n = project.ext.compileDir.length() + int countx = 0 + for (f in fileTree(project.ext.compileDir)) { + //println project.relativePath(f).substring(n) + String name = project.relativePath(f).substring(n) + if (name in exposedClasses) { countx += 1 } + } + println "${exposedClasses.size()} classes from ${countx} exposed." + + def compiledToJar = fileTree(project.ext.compileDir).filter({ + File f -> !(project.relativePath(f).substring(n) in exposedClasses) + }) + + println "${compiledToJar.size()} to be jarred (after filtering)." + + int counti = 0 + for (f in fileTree(project.ext.compileDir)) { + String name = project.relativePath(f).substring(n) + String exposed = (name in exposedClasses) ? "EXPOSED" : "" + // println "${name} ${exposed}" + if (exposed) { counti += 1 } + } + + println "${counti} in overlap." + } +} + + +task dump { + doLast { + // Debug + println '*** source sets ***' + for ( sourceSet in sourceSets ) { + println " ${sourceSet}" + // for ( name in sourceSet.asMap.keys ) { + // sourceDirectorySet = sourceSet.asMap[name] + // println " $name = $sourceDirectorySet" + // } + } + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..91ca28c8b802289c3a438766657a5e98f20eff03 GIT binary patch [stripped] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ + at if "%DEBUG%" == "" @echo off + at rem ########################################################################## + at rem + at rem Gradle startup script for Windows + at rem + at rem ########################################################################## + + at rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + + at rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + + at rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init + at rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args + at rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute + at rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + at rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end + at rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,5 @@ +/* + * Gradle settings for Jython. See also build.gradle. + */ + +rootProject.name = 'jython' diff --git a/tests/java/org/python/core/BaseBytesTest.java b/tests/java/org/python/core/BaseBytesTest.java --- a/tests/java/org/python/core/BaseBytesTest.java +++ b/tests/java/org/python/core/BaseBytesTest.java @@ -217,7 +217,7 @@ // Local constructor from byte[] int[] aRef = toInts("Chaque coquillage incrust?"); BaseBytes a = getInstance(aRef); - System.out.println(toString(a)); + // System.out.println(toString(a)); assertEquals(aRef.length, a.size()); // init(int) at various sizes @@ -237,7 +237,7 @@ BaseBytes a = getInstance(aRef); // Copy constructor b = bytes(a) BaseBytes b = getInstance(a); - System.out.println(toString(b)); + // System.out.println(toString(b)); assertEquals(a.size(), b.size()); // assertEquals(a.storage, b.storage); // Supposed to share? // Check we got the same bytes @@ -254,7 +254,7 @@ // Make an Iterable of that Iterable ia = iterableBytes(aRef); BaseBytes a = getInstance(ia); - System.out.println(toString(a)); + // System.out.println(toString(a)); assertEquals(aRef.length, a.size()); checkInts(aRef, a); @@ -319,7 +319,7 @@ PyObject aRef = boobyPrize[dip]; try { BaseBytes a = getInstance(brantub[dip]); - System.out.println(toString(a)); + // System.out.println(toString(a)); fail("Exception not thrown for " + brantub[dip]); } catch (PyException pye) { // System.out.println(pye); @@ -452,7 +452,7 @@ try { a.pyset(start, x); - System.out.println(toString(a)); + // System.out.println(toString(a)); fail(String.format("Exception not thrown for pyset(%d,%s)", start, x)); } catch (PyException pye) { // System.out.println(pye); @@ -476,7 +476,7 @@ try { a.setslice(start, stop, step, x); - System.out.println(toString(a)); + // System.out.println(toString(a)); fail(String.format("Exception not thrown for setslice(%d,%d,%d,%s)", start, stop, step, x)); } catch (PyException pye) { diff --git a/tests/java/org/python/core/PyBufferTest.java b/tests/java/org/python/core/PyBufferTest.java --- a/tests/java/org/python/core/PyBufferTest.java +++ b/tests/java/org/python/core/PyBufferTest.java @@ -60,7 +60,7 @@ protected int verbosity = 0; /** Print a list of the test material. (From JUnit 4.12 use Parameters(name)). */ - protected static final boolean PRINT_KEY = true; + protected static final boolean PRINT_KEY = false; /** Size of some large arrays. */ static final int LONG = 1000; diff --git a/tests/java/org/python/core/PyByteArrayTest.java b/tests/java/org/python/core/PyByteArrayTest.java --- a/tests/java/org/python/core/PyByteArrayTest.java +++ b/tests/java/org/python/core/PyByteArrayTest.java @@ -12,6 +12,9 @@ */ public class PyByteArrayTest extends BaseBytesTest { + /** This program may be used to display operation time, but this is not necessary for tests. */ + final static boolean SHOW_OPERATION_TIMES = false; + /** * Constructor required by JUnit. * @@ -963,11 +966,13 @@ * slice to replace is simple and contiguous (2-argument slice). */ public void testSetsliceTime() { - int verbose = 1; - timeSetslice(50, 100, SMALL, 2 * SMALL, verbose); - timeSetslice(50, 100, MEDIUM, MEDIUM, verbose); - timeSetslice(500, 20, LARGE, LARGE / 5, verbose); - // timeSetslice(1000, 4, HUGE, HUGE/5, verbose); + if (SHOW_OPERATION_TIMES) { + int verbose = 1; + timeSetslice(50, 100, SMALL, 2 * SMALL, verbose); + timeSetslice(50, 100, MEDIUM, MEDIUM, verbose); + timeSetslice(500, 20, LARGE, LARGE / 5, verbose); + timeSetslice(1000, 4, HUGE, HUGE/5, verbose); + } } /** @@ -1388,11 +1393,13 @@ * slice to replace is extended (3-argument slice). */ public void testDelsliceTime3() { - int verbose = 1; - timeDelslice3(50, 100, SMALL, verbose); - timeDelslice3(50, 100, MEDIUM, verbose); - timeDelslice3(20, 4, LARGE, verbose); - // timeDelslice3(10, 1, HUGE, verbose); + if (SHOW_OPERATION_TIMES) { + int verbose = 1; + timeDelslice3(50, 100, SMALL, verbose); + timeDelslice3(50, 100, MEDIUM, verbose); + timeDelslice3(20, 4, LARGE, verbose); + timeDelslice3(10, 1, HUGE, verbose); + } } /** -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Thu Jul 12 06:41:17 2018 From: jython-checkins at python.org (jeff.allen) Date: Thu, 12 Jul 2018 10:41:17 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Clean_up_Demo_directory_Py?= =?utf-8?q?thon_content_for_readability?= Message-ID: <20180712104117.1.8ADF875DEC449E5A@mg.python.org> https://hg.python.org/jython/rev/c532fa336c22 changeset: 8172:c532fa336c22 user: Richie Bendall date: Thu Jul 12 11:30:49 2018 +0100 summary: Clean up Demo directory Python content for readability White-space changes (hard tabs and other cruft) to improve readability using AutoPEP8 and Autoflake. files: ACKNOWLEDGMENTS | 1 + Demo/applet/deprecated/ButtonDemo.py | 27 +- Demo/applet/deprecated/ButtonFontDemo.py | 29 +- Demo/applet/deprecated/CheckboxDemo.py | 42 +- Demo/applet/deprecated/ChoiceDemo.py | 23 +- Demo/applet/deprecated/Converter.py | 112 +- Demo/applet/deprecated/CoordinatesDemo.py | 55 +- Demo/applet/deprecated/HelloApplet.py | 11 +- Demo/applet/deprecated/HelloWorld.py | 3 +- Demo/applet/deprecated/LabelDemo.py | 11 +- Demo/applet/deprecated/ListDemo.py | 59 +- Demo/awt/Colors.py | 15 +- Demo/awt/Graph.py | 110 +- Demo/awt/simple.py | 2 + Demo/bean/TempConverter.py | 54 +- Demo/javaclasses/deprecated/Graph.py | 116 +- Demo/modjy_webapp/demo_app.py | 21 +- Demo/swing/JythonConsole/Action.py | 63 +- Demo/swing/JythonConsole/Console.py | 336 +++++---- Demo/swing/JythonConsole/Keymap.py | 131 ++- Demo/swing/JythonConsole/Styles.py | 51 +- Demo/swing/ListDemo.py | 1 - Demo/swing/ObjectTree.py | 23 +- Demo/swing/TreeDemo.py | 58 +- Demo/swing/simple.py | 2 + 25 files changed, 700 insertions(+), 656 deletions(-) diff --git a/ACKNOWLEDGMENTS b/ACKNOWLEDGMENTS --- a/ACKNOWLEDGMENTS +++ b/ACKNOWLEDGMENTS @@ -180,6 +180,7 @@ Mat Booth Alex Gaman Tom Bech + Richie Bendall Local Variables: mode: indented-text diff --git a/Demo/applet/deprecated/ButtonDemo.py b/Demo/applet/deprecated/ButtonDemo.py --- a/Demo/applet/deprecated/ButtonDemo.py +++ b/Demo/applet/deprecated/ButtonDemo.py @@ -6,27 +6,28 @@ from java import awt, applet + class ButtonDemo(applet.Applet): def init(self): - self.b1 = awt.Button('Disable middle button', - actionPerformed=self.disable) - self.b2 = awt.Button('Middle button') - self.b3 = awt.Button('Enable middle button', - enabled=0, actionPerformed=self.enable) + self.b1 = awt.Button('Disable middle button', + actionPerformed=self.disable) + self.b2 = awt.Button('Middle button') + self.b3 = awt.Button('Enable middle button', + enabled=0, actionPerformed=self.enable) - self.add(self.b1) - self.add(self.b2) - self.add(self.b3) + self.add(self.b1) + self.add(self.b2) + self.add(self.b3) def enable(self, event): - self.b1.enabled = self.b2.enabled = 1 - self.b3.enabled = 0 + self.b1.enabled = self.b2.enabled = 1 + self.b3.enabled = 0 def disable(self, event): - self.b1.enabled = self.b2.enabled = 0 - self.b3.enabled = 1 + self.b1.enabled = self.b2.enabled = 0 + self.b3.enabled = 1 -if __name__ == '__main__': +if __name__ == '__main__': import pawt pawt.test(ButtonDemo()) diff --git a/Demo/applet/deprecated/ButtonFontDemo.py b/Demo/applet/deprecated/ButtonFontDemo.py --- a/Demo/applet/deprecated/ButtonFontDemo.py +++ b/Demo/applet/deprecated/ButtonFontDemo.py @@ -6,28 +6,29 @@ from java import awt, applet + class ButtonFontDemo(applet.Applet): def init(self): - self.font = awt.Font('Serif', 0, 24) - self.b1 = awt.Button('Disable middle button', - actionPerformed=self.disable) - self.b2 = awt.Button('Middle button') - self.b3 = awt.Button('Enable middle button', - enabled=0, actionPerformed=self.enable) + self.font = awt.Font('Serif', 0, 24) + self.b1 = awt.Button('Disable middle button', + actionPerformed=self.disable) + self.b2 = awt.Button('Middle button') + self.b3 = awt.Button('Enable middle button', + enabled=0, actionPerformed=self.enable) - self.add(self.b1) - self.add(self.b2) - self.add(self.b3) + self.add(self.b1) + self.add(self.b2) + self.add(self.b3) def enable(self, event): - self.b1.enabled = self.b2.enabled = 1 - self.b3.enabled = 0 + self.b1.enabled = self.b2.enabled = 1 + self.b3.enabled = 0 def disable(self, event): - self.b1.enabled = self.b2.enabled = 0 - self.b3.enabled = 1 + self.b1.enabled = self.b2.enabled = 0 + self.b3.enabled = 1 -if __name__ == '__main__': +if __name__ == '__main__': import pawt pawt.test(ButtonFontDemo()) diff --git a/Demo/applet/deprecated/CheckboxDemo.py b/Demo/applet/deprecated/CheckboxDemo.py --- a/Demo/applet/deprecated/CheckboxDemo.py +++ b/Demo/applet/deprecated/CheckboxDemo.py @@ -6,36 +6,36 @@ from java import awt, applet + class CheckboxDemo(applet.Applet): def init(self): - cb1 = awt.Checkbox('Checkbox 1') - cb2 = awt.Checkbox('Checkbox 2') - cb3 = awt.Checkbox('Checkbox 3', state=1) + cb1 = awt.Checkbox('Checkbox 1') + cb2 = awt.Checkbox('Checkbox 2') + cb3 = awt.Checkbox('Checkbox 3', state=1) - p1 = awt.Panel(layout=awt.FlowLayout()) + p1 = awt.Panel(layout=awt.FlowLayout()) - p1.add(cb1) - p1.add(cb2) - p1.add(cb3) + p1.add(cb1) + p1.add(cb2) + p1.add(cb3) - cbg = awt.CheckboxGroup() - cb4 = awt.Checkbox('Checkbox 4', cbg, 0) - cb5 = awt.Checkbox('Checkbox 5', cbg, 0) - cb6 = awt.Checkbox('Checkbox 6', cbg, 0) + cbg = awt.CheckboxGroup() + cb4 = awt.Checkbox('Checkbox 4', cbg, 0) + cb5 = awt.Checkbox('Checkbox 5', cbg, 0) + cb6 = awt.Checkbox('Checkbox 6', cbg, 0) - p2 = awt.Panel(layout=awt.FlowLayout()) - p2.add(cb4) - p2.add(cb5) - p2.add(cb6) + p2 = awt.Panel(layout=awt.FlowLayout()) + p2.add(cb4) + p2.add(cb5) + p2.add(cb6) - self.setLayout(awt.GridLayout(0, 2)) - self.add(p1) - self.add(p2) + self.setLayout(awt.GridLayout(0, 2)) + self.add(p1) + self.add(p2) - self.validate() + self.validate() -if __name__ == '__main__': +if __name__ == '__main__': import pawt pawt.test(CheckboxDemo()) - diff --git a/Demo/applet/deprecated/ChoiceDemo.py b/Demo/applet/deprecated/ChoiceDemo.py --- a/Demo/applet/deprecated/ChoiceDemo.py +++ b/Demo/applet/deprecated/ChoiceDemo.py @@ -6,21 +6,22 @@ from java import awt, applet -class ChoiceDemo(applet.Applet): - def init(self): - self.choices = awt.Choice(itemStateChanged = self.change) - for item in ['ichi', 'ni', 'san', 'yon']: - self.choices.addItem(item) - self.label = awt.Label() - self.change() +class ChoiceDemo(applet.Applet): + def init(self): + self.choices = awt.Choice(itemStateChanged=self.change) + for item in ['ichi', 'ni', 'san', 'yon']: + self.choices.addItem(item) - self.add(self.choices) - self.add(self.label) + self.label = awt.Label() + self.change() + + self.add(self.choices) + self.add(self.label) def change(self, event=None): - selection = self.choices.selectedIndex, self.choices.selectedItem - self.label.text = 'Item #%d selected. Text = "%s".' % selection + selection = self.choices.selectedIndex, self.choices.selectedItem + self.label.text = 'Item #%d selected. Text = "%s".' % selection if __name__ == '__main__': diff --git a/Demo/applet/deprecated/Converter.py b/Demo/applet/deprecated/Converter.py --- a/Demo/applet/deprecated/Converter.py +++ b/Demo/applet/deprecated/Converter.py @@ -10,102 +10,98 @@ from pawt import GridBag basicUnits = [['Metric System', [('Centimeters', 0.01), - ('Meters', 1.0), - ('Kilometers', 1000.0)]], - ['U.S. System', [('Inches', 0.0254), - ('Feet', 0.305), - ('Yards', 0.914), - ('Miles', 1613.0)]] - ] - + ('Meters', 1.0), + ('Kilometers', 1000.0)]], + ['U.S. System', [('Inches', 0.0254), + ('Feet', 0.305), + ('Yards', 0.914), + ('Miles', 1613.0)]] + ] class SimpleBorder: def paint(self, g): - g.drawRect(0,0,self.size.width-1, self.size.height-1) + g.drawRect(0, 0, self.size.width-1, self.size.height-1) def getInsets(self): - return awt.Insets(5,5,5,5) - + return awt.Insets(5, 5, 5, 5) class Converter(Applet, SimpleBorder): def init(self, unitSets=basicUnits): - self.setLayout(awt.GridLayout(2,0,5,5)) - self.panels = [] - for name, units in unitSets: - panel = ConversionPanel(name, units, self) - self.panels.append(panel) - self.add(panel) + self.setLayout(awt.GridLayout(2, 0, 5, 5)) + self.panels = [] + for name, units in unitSets: + panel = ConversionPanel(name, units, self) + self.panels.append(panel) + self.add(panel) def convert(self, master): - value = master.getValue() - multiplier = master.getMultiplier() + value = master.getValue() + multiplier = master.getMultiplier() - for panel in self.panels: - if panel is not master: - panel.setValue(multiplier/panel.getMultiplier()*value) - + for panel in self.panels: + if panel is not master: + panel.setValue(multiplier/panel.getMultiplier()*value) class ConversionPanel(awt.Panel, SimpleBorder, - ActionListener, AdjustmentListener, ItemListener): + ActionListener, AdjustmentListener, ItemListener): max, block = 10000, 100 def __init__(self, title, units, controller): - self.units = units - self.controller = controller + self.units = units + self.controller = controller - bag = GridBag(self, fill='HORIZONTAL') + bag = GridBag(self, fill='HORIZONTAL') - label = awt.Label(title, awt.Label.CENTER) - bag.addRow(label) + label = awt.Label(title, awt.Label.CENTER) + bag.addRow(label) - self.text = awt.TextField('0', 10, actionListener=self) - bag.add(self.text, weightx=1.0) + self.text = awt.TextField('0', 10, actionListener=self) + bag.add(self.text, weightx=1.0) - self.chooser = awt.Choice(itemListener=self) - for name, multiplier in units: - self.chooser.add(name) - bag.addRow(self.chooser) + self.chooser = awt.Choice(itemListener=self) + for name, multiplier in units: + self.chooser.add(name) + bag.addRow(self.chooser) - self.slider = awt.Scrollbar(awt.Scrollbar.HORIZONTAL, - maximum=self.max+10, - blockIncrement=self.block, - adjustmentListener=self) - bag.add(self.slider) + self.slider = awt.Scrollbar(awt.Scrollbar.HORIZONTAL, + maximum=self.max+10, + blockIncrement=self.block, + adjustmentListener=self) + bag.add(self.slider) def getMultiplier(self): - return self.units[self.chooser.selectedIndex][1] + return self.units[self.chooser.selectedIndex][1] def getValue(self): - try: - return float(self.text.getText()) - except: - return 0.0 + try: + return float(self.text.getText()) + except: + return 0.0 def actionPerformed(self, e): - self.setSlider(self.getValue()) - self.controller.convert(self) + self.setSlider(self.getValue()) + self.controller.convert(self) def itemStateChanged(self, e): - self.controller.convert(self) + self.controller.convert(self) def adjustmentValueChanged(self, e): - self.text.setText(str(e.getValue())) - self.controller.convert(self) + self.text.setText(str(e.getValue())) + self.controller.convert(self) def setValue(self, v): - self.text.setText(str(v)) - self.setSlider(v) + self.text.setText(str(v)) + self.setSlider(v) def setSlider(self, f): - if f > self.max: - f = self.max - if f < 0: - f = 0 - self.slider.value = int(f) - + if f > self.max: + f = self.max + if f < 0: + f = 0 + self.slider.value = int(f) if __name__ == '__main__': diff --git a/Demo/applet/deprecated/CoordinatesDemo.py b/Demo/applet/deprecated/CoordinatesDemo.py --- a/Demo/applet/deprecated/CoordinatesDemo.py +++ b/Demo/applet/deprecated/CoordinatesDemo.py @@ -10,58 +10,55 @@ class CoordinatesDemo(applet.Applet): def init(self): - bag = GridBag(self) + bag = GridBag(self) - self.framedArea = FramedArea(self) - bag.addRow(self.framedArea, weighty=1.0, fill='BOTH') + self.framedArea = FramedArea(self) + bag.addRow(self.framedArea, weighty=1.0, fill='BOTH') - self.label = awt.Label('Click within the framed area') - bag.addRow(self.label, weightx=1.0, weighty=0.0, fill='HORIZONTAL') + self.label = awt.Label('Click within the framed area') + bag.addRow(self.label, weightx=1.0, weighty=0.0, fill='HORIZONTAL') def updateLabel(self, point): - text = 'Click occurred at coordinate (%d, %d).' - self.label.text = text % (point.x, point.y) - + text = 'Click occurred at coordinate (%d, %d).' + self.label.text = text % (point.x, point.y) class FramedArea(awt.Panel): def __init__(self, controller): - self.background = awt.Color.lightGray - self.setLayout(awt.GridLayout(1,0)) + self.background = awt.Color.lightGray + self.setLayout(awt.GridLayout(1, 0)) - self.add(CoordinateArea(controller)) + self.add(CoordinateArea(controller)) def getInsets(self): - return awt.Insets(4,4,5,5) + return awt.Insets(4, 4, 5, 5) def paint(self, g): - d = self.size + d = self.size - g.color = self.background - g.draw3DRect(0, 0, d.width-1, d.height-1, 1) - g.draw3DRect(3, 3, d.width-7, d.height-7, 1) - + g.color = self.background + g.draw3DRect(0, 0, d.width-1, d.height-1, 1) + g.draw3DRect(3, 3, d.width-7, d.height-7, 1) class CoordinateArea(awt.Canvas): def __init__(self, controller): - self.mousePressed = self.push - self.controller = controller + self.mousePressed = self.push + self.controller = controller def push(self, e): - try: - self.point.x = e.x - self.point.y = e.y - except AttributeError: - self.point = awt.Point(e.x, e.y) + try: + self.point.x = e.x + self.point.y = e.y + except AttributeError: + self.point = awt.Point(e.x, e.y) - self.repaint() + self.repaint() def paint(self, g): - if hasattr(self, 'point'): - self.controller.updateLabel(self.point) - g.fillRect(self.point.x-1, self.point.y-1, 2, 2) - + if hasattr(self, 'point'): + self.controller.updateLabel(self.point) + g.fillRect(self.point.x-1, self.point.y-1, 2, 2) if __name__ == '__main__': diff --git a/Demo/applet/deprecated/HelloApplet.py b/Demo/applet/deprecated/HelloApplet.py --- a/Demo/applet/deprecated/HelloApplet.py +++ b/Demo/applet/deprecated/HelloApplet.py @@ -19,10 +19,11 @@ from java import awt, applet + class HelloApplet(applet.Applet): def paint(self, g): - g.setColor(awt.Color.black) - g.fill3DRect(5,5,590,100,0) - g.setFont(awt.Font('Arial', 0, 80)) - g.setColor(awt.Color.blue) - g.drawString('Hello World', 90, 80) + g.setColor(awt.Color.black) + g.fill3DRect(5, 5, 590, 100, 0) + g.setFont(awt.Font('Arial', 0, 80)) + g.setColor(awt.Color.blue) + g.drawString('Hello World', 90, 80) diff --git a/Demo/applet/deprecated/HelloWorld.py b/Demo/applet/deprecated/HelloWorld.py --- a/Demo/applet/deprecated/HelloWorld.py +++ b/Demo/applet/deprecated/HelloWorld.py @@ -1,9 +1,10 @@ from java.applet import Applet import sys + class HelloWorld(Applet): def paint(self, g): - g.drawString("Hello from Jython %s!" % sys.version, 20, 30) + g.drawString("Hello from Jython %s!" % sys.version, 20, 30) if __name__ == '__main__': diff --git a/Demo/applet/deprecated/LabelDemo.py b/Demo/applet/deprecated/LabelDemo.py --- a/Demo/applet/deprecated/LabelDemo.py +++ b/Demo/applet/deprecated/LabelDemo.py @@ -7,12 +7,13 @@ from java import applet from java.awt import Label, GridLayout -class LabelDemo(applet.Applet): + +class LabelDemo(applet.Applet): def init(self): - self.setLayout(GridLayout(0,1)) - self.add(Label('Left')) - self.add(Label('Center', Label.CENTER)) - self.add(Label('Right', Label.RIGHT)) + self.setLayout(GridLayout(0, 1)) + self.add(Label('Left')) + self.add(Label('Center', Label.CENTER)) + self.add(Label('Right', Label.RIGHT)) if __name__ == '__main__': diff --git a/Demo/applet/deprecated/ListDemo.py b/Demo/applet/deprecated/ListDemo.py --- a/Demo/applet/deprecated/ListDemo.py +++ b/Demo/applet/deprecated/ListDemo.py @@ -8,49 +8,50 @@ from java.awt.event import ItemEvent from pawt import GridBag + class ListDemo(applet.Applet): def fillList(self, list, names): - list.actionPerformed=self.action - list.itemStateChanged=self.change + list.actionPerformed = self.action + list.itemStateChanged = self.change - for name in names: - list.add(name) + for name in names: + list.add(name) def init(self): - self.spanish = awt.List(4, 1) - self.fillList(self.spanish, ['uno', 'dos', 'tres', 'cuatro', - 'cinco', 'seis', 'siete']) - self.italian = awt.List() - self.fillList(self.italian, ['uno', 'due', 'tre', 'quattro', - 'cinque', 'sei', 'sette']) + self.spanish = awt.List(4, 1) + self.fillList(self.spanish, ['uno', 'dos', 'tres', 'cuatro', + 'cinco', 'seis', 'siete']) + self.italian = awt.List() + self.fillList(self.italian, ['uno', 'due', 'tre', 'quattro', + 'cinque', 'sei', 'sette']) - self.output = awt.TextArea(10, 40, editable=0) + self.output = awt.TextArea(10, 40, editable=0) - bag = GridBag(self) - bag.add(self.output, - fill='BOTH', weightx=1.0, weighty=1.0, - gridheight=2) + bag = GridBag(self) + bag.add(self.output, + fill='BOTH', weightx=1.0, weighty=1.0, + gridheight=2) - bag.addRow(self.spanish, fill='VERTICAL') - bag.addRow(self.italian, fill='VERTICAL') + bag.addRow(self.spanish, fill='VERTICAL') + bag.addRow(self.italian, fill='VERTICAL') - self.language = {self.spanish:'Spanish', self.italian:'Italian'} + self.language = {self.spanish: 'Spanish', self.italian: 'Italian'} def action(self, e): - list = e.source - text = 'Action event occurred on "%s" in %s.\n' - self.output.append(text % (list.selectedItem, self.language[list])) + list = e.source + text = 'Action event occurred on "%s" in %s.\n' + self.output.append(text % (list.selectedItem, self.language[list])) def change(self, e): - list = e.source - if e.stateChange == ItemEvent.SELECTED: - select = 'Select' - else: - select = 'Deselect' + list = e.source + if e.stateChange == ItemEvent.SELECTED: + select = 'Select' + else: + select = 'Deselect' - text = '%s event occurred on item #%d (%s) in %s.\n' - params = (select, e.item, list.getItem(e.item), self.language[list]) - self.output.append(text % params) + text = '%s event occurred on item #%d (%s) in %s.\n' + params = (select, e.item, list.getItem(e.item), self.language[list]) + self.output.append(text % params) if __name__ == '__main__': diff --git a/Demo/awt/Colors.py b/Demo/awt/Colors.py --- a/Demo/awt/Colors.py +++ b/Demo/awt/Colors.py @@ -9,11 +9,12 @@ p = awt.Panel() for name in dir(colors): - color = getattr(colors, name) - if isinstance(color, awt.Color): - l = awt.Label(name, awt.Label.CENTER, background=color) - intensity = sqrt(color.red**2 + color.green**2 + color.blue**2)/3 - if intensity < 90: l.foreground = colors.white - p.add(l) + color = getattr(colors, name) + if isinstance(color, awt.Color): + l = awt.Label(name, awt.Label.CENTER, background=color) + intensity = sqrt(color.red**2 + color.green**2 + color.blue**2)/3 + if intensity < 90: + l.foreground = colors.white + p.add(l) -test(p, size=(700,500)) \ No newline at end of file +test(p, size=(700, 500)) diff --git a/Demo/awt/Graph.py b/Demo/awt/Graph.py --- a/Demo/awt/Graph.py +++ b/Demo/awt/Graph.py @@ -2,62 +2,64 @@ from math import * from jarray import array + class Graph(awt.Canvas): - def __init__(self): - self.function = None + def __init__(self): + self.function = None + + def paint(self, g): + if self.function is None: + return self.error(g) + + sz = self.size + xs = range(0, sz.width, 2) + + xscale = 4*pi/sz.width + xoffset = -2*pi + + yscale = -sz.height/2. + yoffset = sz.height/2. - def paint(self, g): - if self.function is None: - return self.error(g) - - sz = self.size - xs = range(0, sz.width, 2) - - xscale = 4*pi/sz.width - xoffset = -2*pi - - yscale = -sz.height/2. - yoffset = sz.height/2. - - ys = [] - for x in xs: - x = xscale*x + xoffset - y = int(yscale*self.function(x)+yoffset) - ys.append(y) - g.drawPolyline(array(xs, 'i'), array(ys, 'i'), len(xs)) + ys = [] + for x in xs: + x = xscale*x + xoffset + y = int(yscale*self.function(x)+yoffset) + ys.append(y) + g.drawPolyline(array(xs, 'i'), array(ys, 'i'), len(xs)) + + def error(self, g): + message = "Invalid Expression" + g.font = awt.Font('Serif', awt.Font.BOLD, 20) + width = g.fontMetrics.stringWidth(message) - def error(self, g): - message = "Invalid Expression" - g.font = awt.Font('Serif', awt.Font.BOLD, 20) - width = g.fontMetrics.stringWidth(message) - - x = (self.size.width-width)/2 - y = (self.size.height+g.fontMetrics.height)/2 - g.drawString("Invalid Expression", x, y) - - def setExpression(self, e): - "@sig public void setExpression(java.lang.String e)" - try: - self.function = eval('lambda x: '+e) - except: - self.function = None - self.repaint() - + x = (self.size.width-width)/2 + y = (self.size.height+g.fontMetrics.height)/2 + g.drawString("Invalid Expression", x, y) + + def setExpression(self, e): + "@sig public void setExpression(java.lang.String e)" + try: + self.function = eval('lambda x: '+e) + except: + self.function = None + self.repaint() + if __name__ == '__main__': - def enter(e): - graph.setExpression(expression.text) - expression.caretPosition=0 - expression.selectAll() - - p = awt.Panel(layout=awt.BorderLayout()) - graph = Graph() - p.add(graph, 'Center') - - expression = awt.TextField(text='(sin(3*x)+cos(x))/2', actionPerformed=enter) - p.add(expression, 'South') - - import pawt - pawt.test(p, size=(300,300)) - - enter(None) + def enter(e): + graph.setExpression(expression.text) + expression.caretPosition = 0 + expression.selectAll() + + p = awt.Panel(layout=awt.BorderLayout()) + graph = Graph() + p.add(graph, 'Center') + + expression = awt.TextField( + text='(sin(3*x)+cos(x))/2', actionPerformed=enter) + p.add(expression, 'South') + + import pawt + pawt.test(p, size=(300, 300)) + + enter(None) diff --git a/Demo/awt/simple.py b/Demo/awt/simple.py --- a/Demo/awt/simple.py +++ b/Demo/awt/simple.py @@ -9,8 +9,10 @@ import java from java import awt + def exit(e): java.lang.System.exit(0) + frame = awt.Frame('AWT Example', visible=1) button = awt.Button('Close Me!', actionPerformed=exit) frame.add(button, 'Center') diff --git a/Demo/bean/TempConverter.py b/Demo/bean/TempConverter.py --- a/Demo/bean/TempConverter.py +++ b/Demo/bean/TempConverter.py @@ -1,36 +1,38 @@ import java + class TempConverter(java.lang.Object): - def __init__(self): - self.setFahrenheit(0.0) + def __init__(self): + self.setFahrenheit(0.0) - def setFahrenheit(self, degrees): - "@sig public void setFahrenheit(double degrees)" - self.f = degrees - self.c = (degrees-32.)/1.8 + def setFahrenheit(self, degrees): + "@sig public void setFahrenheit(double degrees)" + self.f = degrees + self.c = (degrees-32.)/1.8 - def getFahrenheit(self): - "@sig public double getFahrenheit()" - return self.f + def getFahrenheit(self): + "@sig public double getFahrenheit()" + return self.f - def setCelsius(self, degrees): - "@sig public void setCelsius(double degrees)" - self.c = degrees - self.f = degrees*1.8+32. + def setCelsius(self, degrees): + "@sig public void setCelsius(double degrees)" + self.c = degrees + self.f = degrees*1.8+32. - def getCelsius(self): - "@sig public double getCelsius()" - return self.c + def getCelsius(self): + "@sig public double getCelsius()" + return self.c - def __repr__(self): - return '<%.2g degrees fahrenheit == %.2g celsius>' % (self.f, self.c) + def __repr__(self): + return '<%.2g degrees fahrenheit == %.2g celsius>' % (self.f, self.c) + if __name__ == '__main__': - c = TempConverter() - print c - c.setCelsius(100) - print c - c.setCelsius(0) - print c - c.setFahrenheit(212) - print c \ No newline at end of file + c = TempConverter() + print c + c.setCelsius(100) + print c + c.setCelsius(0) + print c + c.setFahrenheit(212) + print c diff --git a/Demo/javaclasses/deprecated/Graph.py b/Demo/javaclasses/deprecated/Graph.py --- a/Demo/javaclasses/deprecated/Graph.py +++ b/Demo/javaclasses/deprecated/Graph.py @@ -2,65 +2,67 @@ from math import * from jarray import array + class Graph(awt.Canvas): - def __init__(self, initialExpression=None): - "@sig public Graph(java.lang.String initialExpression)" - self.function = None - if initialExpression is not None: - self.setExpression(initialExpression) + def __init__(self, initialExpression=None): + "@sig public Graph(java.lang.String initialExpression)" + self.function = None + if initialExpression is not None: + self.setExpression(initialExpression) + + def paint(self, g): + if self.function is None: + return self.error(g) + + sz = self.size + xs = range(0, sz.width, 2) + + xscale = 4*pi/sz.width + xoffset = -2*pi + + yscale = -sz.height/2. + yoffset = sz.height/2. - def paint(self, g): - if self.function is None: - return self.error(g) - - sz = self.size - xs = range(0, sz.width, 2) - - xscale = 4*pi/sz.width - xoffset = -2*pi - - yscale = -sz.height/2. - yoffset = sz.height/2. - - ys = [] - for x in xs: - x = xscale*x + xoffset - y = int(yscale*self.function(x)+yoffset) - ys.append(y) - g.drawPolyline(array(xs, 'i'), array(ys, 'i'), len(xs)) + ys = [] + for x in xs: + x = xscale*x + xoffset + y = int(yscale*self.function(x)+yoffset) + ys.append(y) + g.drawPolyline(array(xs, 'i'), array(ys, 'i'), len(xs)) + + def error(self, g): + message = "Invalid Expression" + g.font = awt.Font('Serif', awt.Font.BOLD, 20) + width = g.fontMetrics.stringWidth(message) - def error(self, g): - message = "Invalid Expression" - g.font = awt.Font('Serif', awt.Font.BOLD, 20) - width = g.fontMetrics.stringWidth(message) - - x = (self.size.width-width)/2 - y = (self.size.height+g.fontMetrics.height)/2 - g.drawString("Invalid Expression", x, y) - - def setExpression(self, e): - "@sig public void setExpression(java.lang.String e)" - try: - self.function = eval('lambda x: '+e) - except: - self.function = None - self.repaint() - + x = (self.size.width-width)/2 + y = (self.size.height+g.fontMetrics.height)/2 + g.drawString("Invalid Expression", x, y) + + def setExpression(self, e): + "@sig public void setExpression(java.lang.String e)" + try: + self.function = eval('lambda x: '+e) + except: + self.function = None + self.repaint() + if __name__ == '__main__': - def enter(e): - graph.setExpression(expression.text) - expression.caretPosition=0 - expression.selectAll() - - p = awt.Panel(layout=awt.BorderLayout()) - graph = Graph() - p.add(graph, 'Center') - - expression = awt.TextField(text='(sin(3*x)+cos(x))/2', actionPerformed=enter) - p.add(expression, 'South') - - import pawt - pawt.test(p, size=(300,300)) - - enter(None) + def enter(e): + graph.setExpression(expression.text) + expression.caretPosition = 0 + expression.selectAll() + + p = awt.Panel(layout=awt.BorderLayout()) + graph = Graph() + p.add(graph, 'Center') + + expression = awt.TextField( + text='(sin(3*x)+cos(x))/2', actionPerformed=enter) + p.add(expression, 'South') + + import pawt + pawt.test(p, size=(300, 300)) + + enter(None) diff --git a/Demo/modjy_webapp/demo_app.py b/Demo/modjy_webapp/demo_app.py --- a/Demo/modjy_webapp/demo_app.py +++ b/Demo/modjy_webapp/demo_app.py @@ -1,30 +1,37 @@ import sys -def escape_html(s): return s.replace('&', '&').replace('<', '<').replace('>', '>') + +def escape_html(s): return s.replace( + '&', '&').replace('<', '<').replace('>', '>') + def cutoff(s, n=100): - if len(s) > n: return s[:n]+ '.. cut ..' + if len(s) > n: + return s[:n] + '.. cut ..' return s + def handler(environ, start_response): - writer = start_response("200 OK", [ ('content-type', 'text/html') ]) + writer = start_response("200 OK", [('content-type', 'text/html')]) response_parts = [] response_parts.append("") response_parts.append("") response_parts.append("Modjy demo application") response_parts.append("") response_parts.append("") - response_parts.append("

Modjy servlet running correctly: jython %s on %s:

" % (sys.version, sys.platform)) + response_parts.append( + "

Modjy servlet running correctly: jython %s on %s:

" % (sys.version, sys.platform)) response_parts.append("

Hello WSGI World!

") - response_parts.append("

Here are the contents of the WSGI environment

") + response_parts.append( + "

Here are the contents of the WSGI environment

") environ_str = "" keys = environ.keys() keys.sort() for ix, name in enumerate(keys): if ix % 2: - background='#ffffff' + background = '#ffffff' else: - background='#eeeeee' + background = '#eeeeee' style = " style='background-color:%s;'" % background value = escape_html(cutoff(str(environ[name]))) or ' ' environ_str = "%s\n%s%s" % \ diff --git a/Demo/swing/JythonConsole/Action.py b/Demo/swing/JythonConsole/Action.py --- a/Demo/swing/JythonConsole/Action.py +++ b/Demo/swing/JythonConsole/Action.py @@ -1,40 +1,43 @@ # I don't really like the design of this one... from pawt import swing + class Action(swing.AbstractAction): - def __init__(self, name, action=None, icon=None, description=None, needEvent=0): - if action is None: - action = name - name = action.__name__ + def __init__(self, name, action=None, icon=None, description=None, needEvent=0): + if action is None: + action = name + name = action.__name__ - #swing.AbstractAction.__init__(self, name) - self.name = name - self.icon = icon - if icon: - self.setIcon(swing.Action.SMALL_ICON, icon) - if description: - self.setText(swing.Action.SHORT_DESCRIPTION, description) - self.description = description - else: - self.description = name - self.action = action + #swing.AbstractAction.__init__(self, name) + self.name = name + self.icon = icon + if icon: + self.setIcon(swing.Action.SMALL_ICON, icon) + if description: + self.setText(swing.Action.SHORT_DESCRIPTION, description) + self.description = description + else: + self.description = name + self.action = action - self.enabled = 1 - self.needEvent = needEvent + self.enabled = 1 + self.needEvent = needEvent - def actionPerformed(self, event): - if self.needEvent: - self.action(event) - else: - self.action() + def actionPerformed(self, event): + if self.needEvent: + self.action(event) + else: + self.action() - def createMenuItem(self): - mi = swing.JMenuItem(self.name, actionListener=self, enabled=self.enabled) - return mi + def createMenuItem(self): + mi = swing.JMenuItem( + self.name, actionListener=self, enabled=self.enabled) + return mi + class TargetAction(Action): - def actionPerformed(self, event): - if self.needEvent: - self.action(self.getTarget(), event) - else: - self.action(self.getTarget()) \ No newline at end of file + def actionPerformed(self, event): + if self.needEvent: + self.action(self.getTarget(), event) + else: + self.action(self.getTarget()) diff --git a/Demo/swing/JythonConsole/Console.py b/Demo/swing/JythonConsole/Console.py --- a/Demo/swing/JythonConsole/Console.py +++ b/Demo/swing/JythonConsole/Console.py @@ -18,194 +18,204 @@ from java.awt.event import ActionEvent from java.lang import Thread, System from code import compile_command -import string, sys, re +import string +import sys +import re + class OutputBuffer: - def __init__(self, console, stylename): - self.console = console - self.stylename = stylename - - def flush(self): - pass - - def write(self, text): - self.console.write(text, self.stylename) + def __init__(self, console, stylename): + self.console = console + self.stylename = stylename + + def flush(self): + pass + + def write(self, text): + self.console.write(text, self.stylename) + class Console: - def __init__(self, styles=None, keymap=None): - if styles is None: - styles = Styles() - basic = styles.add('normal', tabsize=3, fontSize=12, fontFamily="Courier") - styles.add('error', parent=basic, foreground=colors.red) - styles.add('output', parent=basic, foreground=colors.blue) - styles.add('input', parent=basic, foreground=colors.black) - styles.add('prompt', parent=basic, foreground=colors.purple) - self.styles = styles - - # This is a hack to get at an inner class - # This will not be required in JPython-1.1 - ForegroundAction = getattr(swing.text, 'StyledEditorKit$ForegroundAction') - self.inputAction = ForegroundAction("start input", colors.black) + def __init__(self, styles=None, keymap=None): + if styles is None: + styles = Styles() + basic = styles.add('normal', tabsize=3, + fontSize=12, fontFamily="Courier") + styles.add('error', parent=basic, foreground=colors.red) + styles.add('output', parent=basic, foreground=colors.blue) + styles.add('input', parent=basic, foreground=colors.black) + styles.add('prompt', parent=basic, foreground=colors.purple) + self.styles = styles - if keymap is None: - keymap = Keymap() - keymap.bind('enter', self.enter) - keymap.bind('tab', self.tab) - keymap.bind('escape', self.escape) - keymap.bind('up', self.uphistory) - keymap.bind('down', self.downhistory) - - self.keymap = keymap - - self.document = swing.text.DefaultStyledDocument(self.styles) - self.document.setLogicalStyle(0, self.styles.get('normal')) + # This is a hack to get at an inner class + # This will not be required in JPython-1.1 + ForegroundAction = getattr( + swing.text, 'StyledEditorKit$ForegroundAction') + self.inputAction = ForegroundAction("start input", colors.black) + + if keymap is None: + keymap = Keymap() + keymap.bind('enter', self.enter) + keymap.bind('tab', self.tab) + keymap.bind('escape', self.escape) + keymap.bind('up', self.uphistory) + keymap.bind('down', self.downhistory) + + self.keymap = keymap + + self.document = swing.text.DefaultStyledDocument(self.styles) + self.document.setLogicalStyle(0, self.styles.get('normal')) + + self.textpane = swing.JTextPane(self.document) + self.textpane.keymap = self.keymap - self.textpane = swing.JTextPane(self.document) - self.textpane.keymap = self.keymap - - self.history = [] - self.oldHistoryLength = 0 - self.historyPosition = 0 - - self.command = [] - self.locals = {} + self.history = [] + self.oldHistoryLength = 0 + self.historyPosition = 0 + + self.command = [] + self.locals = {} + + def write(self, text, stylename='normal'): + style = self.styles.get(stylename) + self.document.insertString(self.document.length, text, style) + + def beep(self): + self.textpane.toolkit.beep() - def write(self, text, stylename='normal'): - style = self.styles.get(stylename) - self.document.insertString(self.document.length, text, style) - - def beep(self): - self.textpane.toolkit.beep() + def startUserInput(self, prompt=None): + if prompt is not None: + self.write(prompt, 'prompt') + self.startInput = self.document.createPosition(self.document.length-1) + #self.document.setCharacterAttributes(self.document.length-1, 1, self.styles.get('input'), 1) + self.textpane.caretPosition = self.document.length + ae = ActionEvent( + self.textpane, ActionEvent.ACTION_PERFORMED, 'start input') + self.inputAction.actionPerformed(ae) - def startUserInput(self, prompt=None): - if prompt is not None: - self.write(prompt, 'prompt') - self.startInput = self.document.createPosition(self.document.length-1) - #self.document.setCharacterAttributes(self.document.length-1, 1, self.styles.get('input'), 1) - self.textpane.caretPosition = self.document.length - ae = ActionEvent(self.textpane, ActionEvent.ACTION_PERFORMED, 'start input') - self.inputAction.actionPerformed(ae) + def getinput(self): + offset = self.startInput.offset + line = self.document.getText(offset+1, self.document.length-offset) + return string.rstrip(line) - def getinput(self): - offset = self.startInput.offset - line = self.document.getText(offset+1, self.document.length-offset) - return string.rstrip(line) + def replaceinput(self, text): + offset = self.startInput.offset + 1 + self.document.remove(offset, self.document.length-offset) + self.write(text, 'input') - def replaceinput(self, text): - offset = self.startInput.offset + 1 - self.document.remove(offset, self.document.length-offset) - self.write(text, 'input') - - def enter(self): - line = self.getinput() - self.write('\n', 'input') - - self.history.append(line) - self.handleLine(line) - - def gethistory(self, direction): - historyLength = len(self.history) - if self.oldHistoryLength < historyLength: - # new line was entered after last call - self.oldHistoryLength = historyLength - if self.history[self.historyPosition] != self.history[-1]: - self.historyPosition = historyLength + def enter(self): + line = self.getinput() + self.write('\n', 'input') + + self.history.append(line) + self.handleLine(line) + + def gethistory(self, direction): + historyLength = len(self.history) + if self.oldHistoryLength < historyLength: + # new line was entered after last call + self.oldHistoryLength = historyLength + if self.history[self.historyPosition] != self.history[-1]: + self.historyPosition = historyLength - pos = self.historyPosition + direction + pos = self.historyPosition + direction + + if 0 <= pos < historyLength: + self.historyPosition = pos + self.replaceinput(self.history[pos]) + else: + self.beep() - if 0 <= pos < historyLength: - self.historyPosition = pos - self.replaceinput(self.history[pos]) - else: - self.beep() + def uphistory(self): + self.gethistory(-1) - def uphistory(self): - self.gethistory(-1) + def downhistory(self): + self.gethistory(1) - def downhistory(self): - self.gethistory(1) + def tab(self): + self.write('\t', 'input') - def tab(self): - self.write('\t', 'input') - - def escape(self): - if (not hasattr(self, 'pythonThread') or self.pythonThread is None or - not self.pythonThread.alive): - self.beep() - return - - self.pythonThread.stopPython() + def escape(self): + if (not hasattr(self, 'pythonThread') or self.pythonThread is None or + not self.pythonThread.alive): + self.beep() + return + + self.pythonThread.stopPython() - def capturePythonOutput(self, stdoutStyle='output', stderrStyle='error'): - import sys - sys.stdout = OutputBuffer(self, stdoutStyle) - sys.stderr = OutputBuffer(self, stderrStyle) + def capturePythonOutput(self, stdoutStyle='output', stderrStyle='error'): + import sys + sys.stdout = OutputBuffer(self, stdoutStyle) + sys.stderr = OutputBuffer(self, stderrStyle) + + def handleLine(self, text): + self.command.append(text) - def handleLine(self, text): - self.command.append(text) - - try: - code = compile_command(string.join(self.command, '\n')) - except SyntaxError: - traceback.print_exc(0) - self.command = [] - self.startUserInput(str(sys.ps1)+'\t') - return + try: + code = compile_command(string.join(self.command, '\n')) + except SyntaxError: + traceback.print_exc(0) + self.command = [] + self.startUserInput(str(sys.ps1)+'\t') + return - if code is None: - self.startUserInput(str(sys.ps2)+'\t') - return - - self.command = [] - - pt = PythonThread(code, self) - self.pythonThread = pt - pt.start() - - def newInput(self): - self.startUserInput(str(sys.ps1)+'\t') - + if code is None: + self.startUserInput(str(sys.ps2)+'\t') + return + + self.command = [] + + pt = PythonThread(code, self) + self.pythonThread = pt + pt.start() + + def newInput(self): + self.startUserInput(str(sys.ps1)+'\t') + + import traceback + class PythonThread(Thread): - def __init__(self, code, console): - self.code = code - self.console = console - self.locals = console.locals - - def run(self): - try: - exec self.code in self.locals - - #Include these lines to actually exit on a sys.exit() call - #except SystemExit, value: - # raise SystemExit, value - - except: - exc_type, exc_value, exc_traceback = sys.exc_info() - l = len(traceback.extract_tb(sys.exc_traceback)) - try: - 1/0 - except: - m = len(traceback.extract_tb(sys.exc_traceback)) - traceback.print_exception(exc_type, exc_value, exc_traceback, l-m) - - self.console.newInput() + def __init__(self, code, console): + self.code = code + self.console = console + self.locals = console.locals + + def run(self): + try: + exec self.code in self.locals + + # Include these lines to actually exit on a sys.exit() call + # except SystemExit, value: + # raise SystemExit, value - def stopPython(self): - #Should spend 2 seconds trying to kill thread in nice Python style first... - self.stop() + except: + exc_type, exc_value, exc_traceback = sys.exc_info() + l = len(traceback.extract_tb(sys.exc_traceback)) + try: + 1/0 + except: + m = len(traceback.extract_tb(sys.exc_traceback)) + traceback.print_exception(exc_type, exc_value, exc_traceback, l-m) + + self.console.newInput() + + def stopPython(self): + # Should spend 2 seconds trying to kill thread in nice Python style first... + self.stop() + header = """\ JPython %(version)s on %(platform)s %(copyright)s -""" % {'version':sys.version, 'platform':sys.platform, 'copyright':sys.copyright} +""" % {'version': sys.version, 'platform': sys.platform, 'copyright': sys.copyright} if __name__ == '__main__': - c = Console() - pane = swing.JScrollPane(c.textpane) - swing.test(pane, size=(500,400), name='JPython Console') - c.write(header, 'output') - c.capturePythonOutput() - c.textpane.requestFocus() - c.newInput() \ No newline at end of file + c = Console() + pane = swing.JScrollPane(c.textpane) + swing.test(pane, size=(500, 400), name='JPython Console') + c.write(header, 'output') + c.capturePythonOutput() + c.textpane.requestFocus() + c.newInput() diff --git a/Demo/swing/JythonConsole/Keymap.py b/Demo/swing/JythonConsole/Keymap.py --- a/Demo/swing/JythonConsole/Keymap.py +++ b/Demo/swing/JythonConsole/Keymap.py @@ -4,84 +4,93 @@ from Action import Action _keynames = {} -def getKeyStroke(key): - if len(_keynames) == 0: - for name in dir(KeyEvent): - if name[:3] == 'VK_': - _keynames[string.lower(name[3:])] = getattr(KeyEvent, name) + - if key is None: - return KeyStroke.getKeyStroke(KeyEvent.CHAR_UNDEFINED) +def getKeyStroke(key): + if len(_keynames) == 0: + for name in dir(KeyEvent): + if name[:3] == 'VK_': + _keynames[string.lower(name[3:])] = getattr(KeyEvent, name) - if len(key) == 1: - return KeyStroke.getKeyStroke(key) + if key is None: + return KeyStroke.getKeyStroke(KeyEvent.CHAR_UNDEFINED) - fields = string.split(key, '-') - key = fields[-1] - mods = fields[:-1] + if len(key) == 1: + return KeyStroke.getKeyStroke(key) - modifiers = 0 - for mod in mods: - if mod == 'C': - modifiers = modifiers | InputEvent.CTRL_MASK - elif mod == 'S': - modifiers = modifiers | InputEvent.SHIFT_MASK - #Meta and Alt don't currently work right - elif mod == 'M': - modifiers = modifiers | InputEvent.META_MASK - elif mod == 'A': - modifiers = modifiers | InputEvent.ALT_MASK - else: - raise ValueError, 'Invalid modifier in '+key + fields = string.split(key, '-') + key = fields[-1] + mods = fields[:-1] - return KeyStroke.getKeyStroke(_keynames[key], modifiers) + modifiers = 0 + for mod in mods: + if mod == 'C': + modifiers = modifiers | InputEvent.CTRL_MASK + elif mod == 'S': + modifiers = modifiers | InputEvent.SHIFT_MASK + # Meta and Alt don't currently work right + elif mod == 'M': + modifiers = modifiers | InputEvent.META_MASK + elif mod == 'A': + modifiers = modifiers | InputEvent.ALT_MASK + else: + raise ValueError, 'Invalid modifier in '+key + + return KeyStroke.getKeyStroke(_keynames[key], modifiers) def makeAction(o): - if isinstance(o, Action): return o - if callable(o): return Action(o) + if isinstance(o, Action): + return o + if callable(o): + return Action(o) + class Keymap: - __keynames = {} - __defaultKeymap = text.JTextComponent.getKeymap(text.JTextComponent.DEFAULT_KEYMAP) + __keynames = {} + __defaultKeymap = text.JTextComponent.getKeymap( + text.JTextComponent.DEFAULT_KEYMAP) + + def __init__(self, bindings={}, parent=__defaultKeymap): + self.keymap = text.JTextComponent.addKeymap(None, parent) + for key, action in bindings.items(): + self.bind(key, action) - def __init__(self, bindings={}, parent=__defaultKeymap): - self.keymap = text.JTextComponent.addKeymap(None, parent) - for key, action in bindings.items(): - self.bind(key, action) + def bind(self, key, action): + self.keymap.addActionForKeyStroke( + getKeyStroke(key), makeAction(action)) - def bind(self, key, action): - self.keymap.addActionForKeyStroke(getKeyStroke(key), makeAction(action)) + def __tojava__(self, c): + if isinstance(self.keymap, c): + return self.keymap - def __tojava__(self, c): - if isinstance(self.keymap, c): - return self.keymap if __name__ == '__main__': - km = Keymap() - class T: - def __init__(self, message): - self.message = message - self.__name__ = message - def __call__(self): - print self.message + km = Keymap() + + class T: + def __init__(self, message): + self.message = message + self.__name__ = message + + def __call__(self): + print self.message - km.bind('x', T('x')) - km.bind('C-x', T('C-x')) - km.bind('A-x', T('A-x')) - km.bind('up', T('up')) - km.bind('enter', T('enter')) - km.bind('tab', T('tab')) - km.bind('S-tab', T('S-tab')) - + km.bind('x', T('x')) + km.bind('C-x', T('C-x')) + km.bind('A-x', T('A-x')) + km.bind('up', T('up')) + km.bind('enter', T('enter')) + km.bind('tab', T('tab')) + km.bind('S-tab', T('S-tab')) - text = "hello\nworld" + text = "hello\nworld" - from pawt import swing, test + from pawt import swing, test - doc = swing.text.DefaultStyledDocument() - doc.insertString(0, text, None) - edit = swing.JTextPane(doc) - edit.keymap = km + doc = swing.text.DefaultStyledDocument() + doc.insertString(0, text, None) + edit = swing.JTextPane(doc) + edit.keymap = km - test(edit, size=(150,80)) + test(edit, size=(150, 80)) diff --git a/Demo/swing/JythonConsole/Styles.py b/Demo/swing/JythonConsole/Styles.py --- a/Demo/swing/JythonConsole/Styles.py +++ b/Demo/swing/JythonConsole/Styles.py @@ -1,34 +1,35 @@ from pawt.swing.text import StyleContext, StyleConstants, TabSet, TabStop import string + class Styles: - def __init__(self, context=None): - if context is None: - context = StyleContext() - self.context = context - self.default = self.context.getStyle(StyleContext.DEFAULT_STYLE) + def __init__(self, context=None): + if context is None: + context = StyleContext() + self.context = context + self.default = self.context.getStyle(StyleContext.DEFAULT_STYLE) - def add(self, name, parent=None, tabsize=None, **keywords): - if parent is None: - parent = self.default - style = self.context.addStyle(name, parent) + def add(self, name, parent=None, tabsize=None, **keywords): + if parent is None: + parent = self.default + style = self.context.addStyle(name, parent) - for key, value in keywords.items(): - key = string.upper(key[0])+key[1:] - meth = getattr(StyleConstants, "set"+key) - meth(style, value) + for key, value in keywords.items(): + key = string.upper(key[0])+key[1:] + meth = getattr(StyleConstants, "set"+key) + meth(style, value) - if tabsize is not None: - charWidth=StyleConstants.getFontSize(style) - tabs = [] - for i in range(20): - tabs.append(TabStop(i*tabsize*charWidth)) - StyleConstants.setTabSet(style, TabSet(tabs)) - return style + if tabsize is not None: + charWidth = StyleConstants.getFontSize(style) + tabs = [] + for i in range(20): + tabs.append(TabStop(i*tabsize*charWidth)) + StyleConstants.setTabSet(style, TabSet(tabs)) + return style - def get(self, stylename): - return self.context.getStyle(stylename) + def get(self, stylename): + return self.context.getStyle(stylename) - def __tojava__(self, c): - if isinstance(self.context, c): - return self.context \ No newline at end of file + def __tojava__(self, c): + if isinstance(self.context, c): + return self.context diff --git a/Demo/swing/ListDemo.py b/Demo/swing/ListDemo.py --- a/Demo/swing/ListDemo.py +++ b/Demo/swing/ListDemo.py @@ -3,4 +3,3 @@ lst = swing.JList(['a', 'b', 'c']) swing.test(lst) - diff --git a/Demo/swing/ObjectTree.py b/Demo/swing/ObjectTree.py --- a/Demo/swing/ObjectTree.py +++ b/Demo/swing/ObjectTree.py @@ -9,8 +9,7 @@ import java leaves = (None, TypeType, IntType, StringType, FloatType, NoneType, - BuiltinFunctionType, BuiltinMethodType) - + BuiltinFunctionType, BuiltinMethodType) class PyEnumeration(java.util.Enumeration): @@ -26,13 +25,13 @@ return self.seq[self.index-1] - def classattrs(c, attrs): for base in c.__bases__: classattrs(base, attrs) for name in c.__dict__.keys(): attrs[name] = 1 + def mydir(obj): attrs = {} if hasattr(obj, '__class__'): @@ -44,6 +43,7 @@ ret.sort() return ret + def shortrepr(obj): r = repr(obj) if len(r) > 80: @@ -51,7 +51,6 @@ return r - class ObjectNode(swing.tree.TreeNode): def __init__(self, parent, name, object): self.myparent = parent @@ -60,7 +59,7 @@ def getChildren(self): if hasattr(self, 'mychildren'): - return self.mychildren + return self.mychildren if self.isLeaf(): self.mychildren = None @@ -69,10 +68,10 @@ children = [] for name in mydir(self.object): if name[:2] == '__': - continue + continue try: children.append(ObjectNode(self, name, - getattr(self.object, name))) + getattr(self.object, name))) except TypeError: print 'type error on', name, self.object self.mychildren = children @@ -102,7 +101,7 @@ index = 0 for child in self.getChildren(): if child == node: - return index + return index index = index+1 return -1 @@ -113,13 +112,13 @@ return self.name+' = '+shortrepr(self.object) - if __name__ == '__main__': class foo: - bar=99 - eggs='hello' + bar = 99 + eggs = 'hello' + class baz: - x,y,z=1,2,3 + x, y, z = 1, 2, 3 func = range import __main__ diff --git a/Demo/swing/TreeDemo.py b/Demo/swing/TreeDemo.py --- a/Demo/swing/TreeDemo.py +++ b/Demo/swing/TreeDemo.py @@ -4,47 +4,51 @@ """ data = { - 'PyObject': { - 'PyInteger':None, - 'PyFloat':None, - 'PyComplex':None, + 'PyObject': { + 'PyInteger': None, + 'PyFloat': None, + 'PyComplex': None, 'PySequence': { - 'PyArray':None, - 'PyList':None, - 'PyTuple':None, - 'PyString':None, + 'PyArray': None, + 'PyList': None, + 'PyTuple': None, + 'PyString': None, }, 'PyClass': { - 'PyJavaClass':None, + 'PyJavaClass': None, }, - }, - 'sys':None, - 'Py':None, - 'PyException':None, - '__builtin__':None, - 'ThreadState':None, + }, + 'sys': None, + 'Py': None, + 'PyException': None, + '__builtin__': None, + 'ThreadState': None, } from pawt import swing Node = swing.tree.DefaultMutableTreeNode + def addNode(tree, key, value): - node = Node(key) - tree.add(node) - if value is not None: - addLeaves(node, value.items()) + node = Node(key) + tree.add(node) + if value is not None: + addLeaves(node, value.items()) + def addLeaves(node, items): - items.sort() - for key, value in items: - addNode(node, key, value) + items.sort() + for key, value in items: + addNode(node, key, value) + def makeTree(name, data): - tree = Node('A Few JPython Classes') - addLeaves(tree, data.items()) - return tree + tree = Node('A Few JPython Classes') + addLeaves(tree, data.items()) + return tree + if __name__ == '__main__': - tree = makeTree('Some JPython Classes', data) - swing.test(swing.JScrollPane(swing.JTree(tree))) + tree = makeTree('Some JPython Classes', data) + swing.test(swing.JScrollPane(swing.JTree(tree))) diff --git a/Demo/swing/simple.py b/Demo/swing/simple.py --- a/Demo/swing/simple.py +++ b/Demo/swing/simple.py @@ -7,8 +7,10 @@ from pawt import swing import java + def exit(e): java.lang.System.exit(0) + frame = swing.JFrame('Swing Example', visible=1) button = swing.JButton('Close Me!', actionPerformed=exit) frame.contentPane.add(button) -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Sat Jul 14 18:13:17 2018 From: jython-checkins at python.org (jeff.allen) Date: Sat, 14 Jul 2018 22:13:17 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Re-work_Py=2EgetJarFileNam?= =?utf-8?q?eFromURL_to_fix_=232410=2E?= Message-ID: <20180714221317.1.5830AA53DFAFA210@mg.python.org> https://hg.python.org/jython/rev/d83a36fe9f8e changeset: 8173:d83a36fe9f8e user: Richie Bendall date: Sat Jul 14 22:25:20 2018 +0100 summary: Re-work Py.getJarFileNameFromURL to fix #2410. This version uses java.net methods instead of ad hoc string manipulation because these are likely to be correct for both files and network paths. As a side effect, the file separator is now correct for the platform, so compensating transformations of the result are removed, and the test adjusted to expect this. files: src/org/python/core/Py.java | 172 ++++----- tests/java/org/python/core/PySystemStateTest.java | 84 +++- 2 files changed, 139 insertions(+), 117 deletions(-) diff --git a/src/org/python/core/Py.java b/src/org/python/core/Py.java --- a/src/org/python/core/Py.java +++ b/src/org/python/core/Py.java @@ -16,7 +16,11 @@ import java.io.StreamCorruptedException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.net.JarURLConnection; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; +import java.net.URLConnection; import java.net.URLDecoder; import java.sql.Date; import java.sql.Time; @@ -24,13 +28,11 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.List; -import java.util.Set; import org.python.antlr.base.mod; import org.python.core.adapter.ClassicPyObjectAdapter; import org.python.core.adapter.ExtensiblePyObjectAdapter; import org.python.modules.posix.PosixModule; -import org.python.util.Generic; import com.google.common.base.CharMatcher; @@ -2520,48 +2522,36 @@ } /** - * Infers the usual Jython executable name from the position of the - * jar-file returned by {@link #getJarFileName()} by replacing the - * file name with "bin/jython". This is intended as an easy fallback - * for cases where {@code sys.executable} is {@code None} due to - * direct launching via the java executable.
- * Note that this does not necessarily return the actual executable, - * but instead infers the place where it is usually expected to be. - * Use {@code sys.executable} to get the actual executable (may be - * {@code None}. - * - * In contrast to {@link #getJarFileName()} and - * {@link #getJarFileNameFromURL(java.net.URL)} this method returns - * the path using system-specific separator characters. + * Infers the usual Jython executable name from the position of the jar-file returned by + * {@link #getJarFileName()} by replacing the file name with "bin/jython". This is intended as + * an easy fallback for cases where {@code sys.executable} is {@code None} due to direct + * launching via the java executable. + *

+ * Note that this does not necessarily return the actual executable, but instead infers the + * place where it is usually expected to be. Use {@code sys.executable} to get the actual + * executable (may be {@code None}. * * @return usual Jython-executable as absolute path */ public static String getDefaultExecutableName() { - return getDefaultBinDir()+File.separator+( - Platform.IS_WINDOWS ? "jython.exe" : "jython"); + return getDefaultBinDir() + File.separator + + (Platform.IS_WINDOWS ? "jython.exe" : "jython"); } /** - * Infers the usual Jython bin-dir from the position of the jar-file - * returned by {@link #getJarFileName()} byr replacing the file name - * with "bin". This is intended as an easy fallback for cases where - * {@code sys.executable} is {@code null} due to direct launching via - * the java executable.
- * Note that this does not necessarily return the actual bin-directory, - * but instead infers the place where it is usually expected to be. - * - * In contrast to {@link #getJarFileName()} and - * {@link #getJarFileNameFromURL(java.net.URL)} this method returns - * the path using system-specific separator characters. + * Infers the usual Jython bin-dir from the position of the jar-file returned by + * {@link #getJarFileName()} byr replacing the file name with "bin". This is intended as an easy + * fallback for cases where {@code sys.executable} is {@code null} due to direct launching via + * the java executable. + *

+ * Note that this does not necessarily return the actual bin-directory, but instead infers the + * place where it is usually expected to be. * * @return usual Jython bin-dir as absolute path */ public static String getDefaultBinDir() { String jar = _getJarFileName(); - if (File.separatorChar != '/') { - jar = jar.replace('/', File.separatorChar); - } - return jar.substring(0, jar.lastIndexOf(File.separatorChar)+1)+"bin"; + return jar.substring(0, jar.lastIndexOf(File.separatorChar) + 1) + "bin"; } /** @@ -2569,14 +2559,11 @@ * jython-jar-file. Usually this is jython.jar, but can also be jython-dev.jar or * jython-standalone.jar or something custom. * - * @return the full name of the jar file containing this class, null - * if not available. + * @return the full name of the jar file containing this class, null if not + * available. */ public static String getJarFileName() { String jar = _getJarFileName(); - if (File.separatorChar != '/') { - jar = jar.replace('/', File.separatorChar); - } return jar; } @@ -2585,10 +2572,8 @@ * jython-jar-file. Usually this is jython.jar, but can also be jython-dev.jar or * jython-standalone.jar or something custom. * - * Note that it does not use system-specific seperator-chars, but always '/'. - * - * @return the full name of the jar file containing this class, null - * if not available. + * @return the full name of the jar file containing this class, null if not + * available. */ public static String _getJarFileName() { Class thisClass = Py.class; @@ -2598,71 +2583,58 @@ return getJarFileNameFromURL(url); } - /**exclusively used by {@link #getJarFileNameFromURL(java.net.URL)}.*/ - private static final String JAR_URL_PREFIX = "jar:file:"; - /**exclusively used by {@link #getJarFileNameFromURL(java.net.URL)}.*/ - private static final String JAR_SEPARATOR = "!"; - /**exclusively used by {@link #getJarFileNameFromURL(java.net.URL)}.*/ - private static final String VFSZIP_PREFIX = "vfszip:"; - /**exclusively used by {@link #getJarFileNameFromURL(java.net.URL)}.*/ - private static final String VFS_PREFIX = "vfs:"; - /** - * Converts a url that points to a jar-file to the actual jar-file name. - * Note that it does not use system-specific seperator-chars, but always '/'. + * Return the path in the file system (as a string) of a JAR located by a URL. Three protocols + * are supported, Java JAR-file protocol, and two JBoss protocols "vfs" and "vfszip". + *

+ * The JAR-file protocol URL, which must be a {@code jar:file:} reference to a contained element + * (that is, it has a "!/" part) is able to identify an actual JAR in a file system that may + * then be opened using {@code jarFile = new JarFile(jarFileName)}. The path to the JAR is + * returned. If the JAR is accessed by another mechanism ({@code http:} say) this will fail. + *

+ * The JBoss URL must be a reference to exactly + * {@code vfs:/org/python/core/PySystemState.class}, or the same thing using the + * {@code vfszip:} protocol, where <JAR> stands for the absolute path to the Jython JAR in + * VFS. There is no "!/" marker: in JBoss VFS a JAR is treated just like a directory and can no + * longer be opened as a JAR. The method essentially just swaps a VFS protocol for the Java + * {@code file:} protocol. The path returned will be correct only if this naive swap is valid. + * + * @param url into the JAR + * @return the file path or {@code null} in the event of a detectable error */ public static String getJarFileNameFromURL(URL url) { - String jarFileName = null; - if (url != null) { - try { - // escape plus signs, since the URLDecoder would turn them into spaces - final String plus = "\\+"; - final String escapedPlus = "__ppluss__"; - String rawUrl = url.toString(); - rawUrl = rawUrl.replaceAll(plus, escapedPlus); - String urlString = URLDecoder.decode(rawUrl, "UTF-8"); - urlString = urlString.replaceAll(escapedPlus, plus); - int jarSeparatorIndex = urlString.lastIndexOf(JAR_SEPARATOR); - if (urlString.startsWith(JAR_URL_PREFIX) && jarSeparatorIndex > 0) { - // jar:file:/install_dir/jython.jar!/org/python/core/PySystemState.class - int start = JAR_URL_PREFIX.length(); - if (Platform.IS_WINDOWS && urlString.charAt(start+1) != '/') { - // The check for urlString.charAt(start+1) != '/' is done to preserve network paths. - start++; - } - jarFileName = urlString.substring(start, jarSeparatorIndex); - } else if (urlString.startsWith(VFSZIP_PREFIX)) { - // vfszip:/some/path/jython.jar/org/python/core/PySystemState.class - final String path = Py.class.getName().replace('.', '/'); - int jarIndex = urlString.indexOf(".jar/".concat(path)); + URI fileURI = null; + try { + switch (url == null ? "" : url.getProtocol()) { + + case "jar": + // url is jar:file:/some/path/some.jar!/package/with/A.class + URLConnection c = url.openConnection(); + fileURI = ((JarURLConnection) c).getJarFileURL().toURI(); + break; + + case "vfs": + case "vfszip": + // path is /some/path/some-jython.jar/org/python/core/PySystemState.class + String path = url.getPath(); + final String target = ".jar/" + Py.class.getName().replace('.', '/'); + int jarIndex = path.indexOf(target); if (jarIndex > 0) { - jarIndex += 4; - int start = VFSZIP_PREFIX.length(); - if (Platform.IS_WINDOWS && urlString.charAt(start+1) != '/') { - // The check for urlString.charAt(start+1) != '/' is done to preserve network paths. - // vfszip:/C:/some/path/jython.jar/org/python/core/PySystemState.class - start++; - } - jarFileName = urlString.substring(start, jarIndex); + // path contains the target class in a JAR, so make a file URL for it + fileURI = new URL("file:" + path.substring(0, jarIndex + 4)).toURI(); } - } else if (urlString.startsWith(VFS_PREFIX)) { - // vfs:/some/path/jython.jar/org/python/core/PySystemState.class - final String path = Py.class.getName().replace('.', '/'); - int jarIndex = urlString.indexOf(".jar/".concat(path)); - if (jarIndex > 0) { - jarIndex += 4; - int start = VFS_PREFIX.length(); - if (Platform.IS_WINDOWS && urlString.charAt(start+1) != '/') { - // The check for urlString.charAt(start+1) != '/' is done to preserve network paths. - // vfs:/C:/some/path/jython.jar/org/python/core/PySystemState.class - start++; - } - jarFileName = urlString.substring(start, jarIndex); - } - } - } catch (Exception e) {} + break; + + default: + // Unknown protocol or url==null: fileURI = null + break; + } + } catch (IOException | URISyntaxException e) { + // Handler cannot open connection or URL is malformed some way: fileURI = null } - return jarFileName; + + // The JAR file is now identified in fileURI but needs decoding to a file + return fileURI == null ? null : new File(fileURI).toString(); } //------------------------contructor-section--------------------------- diff --git a/tests/java/org/python/core/PySystemStateTest.java b/tests/java/org/python/core/PySystemStateTest.java --- a/tests/java/org/python/core/PySystemStateTest.java +++ b/tests/java/org/python/core/PySystemStateTest.java @@ -1,34 +1,78 @@ package org.python.core; +import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; -import junit.framework.TestCase; +import org.python.util.PythonInterpreter; import jnr.posix.util.Platform; -import org.python.util.PythonInterpreter; +import junit.framework.TestCase; public class PySystemStateTest extends TestCase { + /** + * A class to hold examples of URLs (just the path and class noise) and the reference answer. + * Provide the reference answer like a Un*x path (forward slash). + */ + private static class JarExample { + + final String urlJarPath; + final String urlClassPath; + final String filePath; + + JarExample(String urlJarPath, String urlClassPath, String unixPath) { + this.urlJarPath = urlJarPath; + this.urlClassPath = urlClassPath; + if (Platform.IS_WINDOWS) { + this.filePath = new File(unixPath).toString(); + } else { + this.filePath = unixPath; + } + } + } + + /** + * Examples of URLs (just the path and class noise) and the reference answer. Provide the + * reference answer like a Un*x path (forward slash). + */ + private static JarExample[] jarExamples = { // + // simple jar-file url + new JarExample("/some_dir/some.jar", "a/package/with/A.class", "/some_dir/some.jar"), + // jar-file url to decode + new JarExample("/some%20dir/some.jar", "a/package/with/A.class", "/some dir/some.jar"), + // In an old implementation using URLDecoder "+" needed special treatment + new JarExample("/some+dir/some.jar", "a/package/with/A.class", "/some+dir/some.jar"), + // Some characters should be encoded in the URL, but emerge as themselves in the path. + new JarExample("/n%c3%a5gon/katalog/r%c3%a4tt.jar", "en/f%c3%b6rpackning/med/En.class", + "/n?gon/katalog/r?tt.jar") // + }; + + /** + * Test case for finding the path in the local file system of the file located by a JAR-file + * URL. A URL is a sequence of characters (from a limited set) that encodes a sequence of octets + * that may (if the protocol intends it) represent characters in some encoding. In the case of a + * JAR-file URL, these octets encode the file path elements in UTF-8. + */ public void testGetJarFileNameFromURL() throws Exception { // null assertNull(Py.getJarFileNameFromURL(null)); - // plain jar url - String urlString = "jar:file:/some_dir/some.jar!/a/package/with/A.class"; - URL url = new URL(urlString); - assertEquals("/some_dir/some.jar", Py.getJarFileNameFromURL(url)); - // jar url to decode - urlString = "jar:file:/some%20dir/some.jar!/a/package/with/A.class"; - url = new URL(urlString); - assertEquals("/some dir/some.jar", Py.getJarFileNameFromURL(url)); - // jar url with + signs to escape - urlString = "jar:file:/some+dir/some.jar!/a/package/with/A.class"; - url = new URL(urlString); - assertEquals("/some+dir/some.jar", Py.getJarFileNameFromURL(url)); + // Examples from the table + for (JarExample ex : jarExamples) { + // Something like jar:file:/some_dir/some.jar!/a/package/with/A.class + URL url = new URL("jar:file:" + ex.urlJarPath + "!/" + ex.urlClassPath); + assertEquals(ex.filePath, Py.getJarFileNameFromURL(url)); + } } + /** + * Test case for finding the path in the local file system of the file located by a JBoss vfszip + * URL. This is problematic as an objective because a path in the VFS does not necessarily have + * a counterpart in the local file system. However, the implementation and test are based on + * behaviour observed when this is the case. + */ public void testGetJarFileNameFromURL_jboss() throws Exception { final String protocol = "vfszip"; final String host = ""; @@ -42,17 +86,17 @@ url = new URL(protocol, host, port, file, handler); // tests with jboss on windows gave URL's like this: assertEquals("vfszip:/C:/some_dir/some.jar/org/python/core/PySystemState.class", url.toString()); - assertEquals("C:/some_dir/some.jar", Py.getJarFileNameFromURL(url)); + assertEquals("C:\\some_dir\\some.jar", Py.getJarFileNameFromURL(url)); // jboss url to decode file = "/C:/some%20dir/some.jar/org/python/core/PySystemState.class"; url = new URL(protocol, host, port, file, handler); assertEquals("vfszip:/C:/some%20dir/some.jar/org/python/core/PySystemState.class", url.toString()); - assertEquals("C:/some dir/some.jar", Py.getJarFileNameFromURL(url)); + assertEquals("C:\\some dir\\some.jar", Py.getJarFileNameFromURL(url)); // jboss url with + to escape file = "/C:/some+dir/some.jar/org/python/core/PySystemState.class"; url = new URL(protocol, host, port, file, handler); assertEquals("vfszip:/C:/some+dir/some.jar/org/python/core/PySystemState.class", url.toString()); - assertEquals("C:/some+dir/some.jar", Py.getJarFileNameFromURL(url)); + assertEquals("C:\\some+dir\\some.jar", Py.getJarFileNameFromURL(url)); } else { // plain jboss url file = "/some_dir/some.jar/org/python/core/PySystemState.class"; @@ -85,6 +129,12 @@ } } + /** + * A URL handler that emulates the behaviour (as far as we're concerned) of + * {@code org.jboss.virtual.protocol.vfs.Handler}, that we can use to make URLs that behave the + * same way as JBoss ones. + * + */ protected static class TestJBossURLStreamHandler extends URLStreamHandler { @Override -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Sat Jul 14 19:05:43 2018 From: jython-checkins at python.org (jeff.allen) Date: Sat, 14 Jul 2018 23:05:43 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Enable_PySystemStateTest_i?= =?utf-8?q?n_Gradle_build_following_fix_for_=232410=2E?= Message-ID: <20180714230543.1.DAECAFAF712294AE@mg.python.org> https://hg.python.org/jython/rev/10fa6ab4745f changeset: 8174:10fa6ab4745f user: Jeff Allen date: Sat Jul 14 23:58:12 2018 +0100 summary: Enable PySystemStateTest in Gradle build following fix for #2410. (Note previous set d83a36fe9f8e is wrongly-attributed. Those mistakes are mine.) files: build.gradle | 6 ++++-- 1 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle --- a/build.gradle +++ b/build.gradle @@ -613,18 +613,20 @@ exclude 'org/python/tests/imp/**' // See build.xml:importest // Some additional exclusions or else the task fails + // FIXME: leaves stdin/out/err as PyFileWriter that has no fileno() // causing _ioTest to fail. exclude '**/jsr223/*' + // FIXME: Tests that hard-code directory paths (use a symbol): exclude 'org/python/compiler/custom_proxymaker/**' exclude 'org/python/compiler/JavaMakerSmokeTest.class' + // FIXME: Failing test finds project root from test class location exclude 'org/python/core/PySystemState_registry_Test.class' + // FIXME: Fails as sys._jy_console not set when run under Gradle exclude 'org/python/util/InterpreterTest.class' - // FIXME: Failing test, see issue 2410 - exclude 'org/python/core/PySystemStateTest.class' doFirst { println "systemProperties = $systemProperties" -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Wed Jul 18 04:28:26 2018 From: jython-checkins at python.org (jeff.allen) Date: Wed, 18 Jul 2018 08:28:26 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Improve_support_for_Window?= =?utf-8?q?s_UNC_paths_in_getJarFileNameFromURL=2E?= Message-ID: <20180718082826.1.6401D66E7DDC1EF1@mg.python.org> https://hg.python.org/jython/rev/c0b0894950ff changeset: 8175:c0b0894950ff user: Jeff Allen date: Wed Jul 18 08:07:34 2018 +0100 summary: Improve support for Windows UNC paths in getJarFileNameFromURL. This adds to the fix for issue #2410. Tests now include drive-letter and UNC paths when the OS platform is Windows. We work around some strange behaviour wrt UNC paths that may be a Java conformance issue. files: NEWS | 1 + src/org/python/core/Py.java | 37 ++++++- tests/java/org/python/core/PySystemStateTest.java | 51 ++++++++- 3 files changed, 80 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ Development tip Bugs fixed + - [ 2410 ] Regression in PySystemStateTest (leading slash) - [ 2639 ] Incorrect result when using != comparison against Java {List, Set, Map} - [ 2672 ] Integer formatting emits two minus signs with -2^31 - [ 2688 ] ClassCastException when adding list of non-PyObjects diff --git a/src/org/python/core/Py.java b/src/org/python/core/Py.java --- a/src/org/python/core/Py.java +++ b/src/org/python/core/Py.java @@ -17,11 +17,11 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.JarURLConnection; +import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; -import java.net.URLDecoder; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; @@ -2609,6 +2609,11 @@ case "jar": // url is jar:file:/some/path/some.jar!/package/with/A.class + if (Platform.IS_WINDOWS) { + // ... or jar:file://host/some/path/some.jar!/package/with/A.class + // ... or jar:file:////host/some/path/some.jar!/package/with/A.class + url = tweakWindowsFileURL(url); + } URLConnection c = url.openConnection(); fileURI = ((JarURLConnection) c).getJarFileURL().toURI(); break; @@ -2629,7 +2634,7 @@ // Unknown protocol or url==null: fileURI = null break; } - } catch (IOException | URISyntaxException e) { + } catch (IOException | URISyntaxException | IllegalArgumentException e) { // Handler cannot open connection or URL is malformed some way: fileURI = null } @@ -2637,6 +2642,34 @@ return fileURI == null ? null : new File(fileURI).toString(); } + /** + * If the argument is a {@code jar:file:} or {@code file:} URL, compensate for a bug in Java's + * construction of URLs affecting {@code java.io.File} and {@code java.net.URLConnection} on + * Windows. This is a helper for {@link #getJarFileNameFromURL(URL)}. + *

+ * This bug bites when a JAR file is at a (Windows) UNC location, and a {@code jar:file:} URL is + * derived from {@code Class.getResource()} as it is in {@link #_getJarFileName()}. When URL is + * supplied to {@link #getJarFileNameFromURL(URL)}, the bug leads to a URI that falsely treats a + * server as an "authority". It subsequently causes an {@code IllegalArgumentException} with the + * message "URI has an authority component" when we try to construct a File. See + * {@link https://bugs.java.com/view_bug.do?bug_id=6360233} ("won't fix"). + * + * @param url Possibly malformed URL + * @return corrected URL + */ + private static URL tweakWindowsFileURL(URL url) throws MalformedURLException { + String urlstr = url.toString(); + int fileIndex = urlstr.indexOf("file://"); // 7 chars + if (fileIndex >= 0) { + // Intended UNC path. If there is no slash following these two, insert "/" here: + int insert = fileIndex + 7; + if (urlstr.length() > insert && urlstr.charAt(insert) != '/') { + url = new URL(urlstr.substring(0, insert) + "//" + urlstr.substring(insert)); + } + } + return url; + } + //------------------------contructor-section--------------------------- static class py2JyClassCacheItem { List> interfaces; diff --git a/tests/java/org/python/core/PySystemStateTest.java b/tests/java/org/python/core/PySystemStateTest.java --- a/tests/java/org/python/core/PySystemStateTest.java +++ b/tests/java/org/python/core/PySystemStateTest.java @@ -5,6 +5,9 @@ import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.python.util.PythonInterpreter; @@ -23,14 +26,17 @@ final String urlClassPath; final String filePath; + /** This constructor adapts unixPath to Windows when on Windows. */ JarExample(String urlJarPath, String urlClassPath, String unixPath) { + this(urlJarPath, urlClassPath, + Platform.IS_WINDOWS ? new File(unixPath).toString() : unixPath, true); + } + + /** This constructor accepts filePath exactly as given. */ + JarExample(String urlJarPath, String urlClassPath, String filePath, boolean ignored) { this.urlJarPath = urlJarPath; this.urlClassPath = urlClassPath; - if (Platform.IS_WINDOWS) { - this.filePath = new File(unixPath).toString(); - } else { - this.filePath = unixPath; - } + this.filePath = filePath; } } @@ -38,7 +44,7 @@ * Examples of URLs (just the path and class noise) and the reference answer. Provide the * reference answer like a Un*x path (forward slash). */ - private static JarExample[] jarExamples = { // + private static List jarExamples = Arrays.asList(// // simple jar-file url new JarExample("/some_dir/some.jar", "a/package/with/A.class", "/some_dir/some.jar"), // jar-file url to decode @@ -48,7 +54,38 @@ // Some characters should be encoded in the URL, but emerge as themselves in the path. new JarExample("/n%c3%a5gon/katalog/r%c3%a4tt.jar", "en/f%c3%b6rpackning/med/En.class", "/n?gon/katalog/r?tt.jar") // - }; + ); + + /* Check drive-letter and UNC path handling if on Windows. */ + static { + if (Platform.IS_WINDOWS) { + // Add some examples to the list (must be made mutable for that). + jarExamples = new ArrayList(jarExamples); + + // Drive-letter examples + jarExamples.add(new JarExample("/C:/some_dir/some.jar", "a/package/with/A.class", + "C:\\some_dir\\some.jar", true)); + jarExamples.add(new JarExample("/E:/n%c3%a5gon/katalog/r%c3%a4tt.jar", "med/En.class", + "E:\\n?gon\\katalog\\r?tt.jar", true)); + + // Simple network file path (UNC path without controversial characters) + String p = "/org/python/version.properies"; + String r = "\\\\localhost\\shared\\jython-dev.jar"; + // JAR UNC file resource URL as produced by File.getURL or getURI + jarExamples.add(new JarExample("////localhost/shared/jython-dev.jar", p, r, true)); + // JAR UNC file resource URL as produced by URLClassLoader.getResource + jarExamples.add(new JarExample("//localhost/shared/jython-dev.jar", p, r, true)); + + // Network file path (UNC path with a controversial characters) + r = "\\\\localhost\\shared\\jy thon%dev.jar"; + // JAR UNC file resource URL based on (deprecated) File.getURL is invalid + // jarExamples.add(new JarExample("//localhost/shared/jy thon%dev.jar", p, r, true)); + // JAR UNC file resource URL based on File.getURI + jarExamples.add(new JarExample("////localhost/shared/jy%20thon%25dev.jar", p, r, true)); + // JAR UNC file resource URL as produced by URLClassLoader.getResource + jarExamples.add(new JarExample("//localhost/shared/jy%20thon%25dev.jar", p, r, true)); + } + } /** * Test case for finding the path in the local file system of the file located by a JAR-file -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Sat Jul 21 05:34:32 2018 From: jython-checkins at python.org (jeff.allen) Date: Sat, 21 Jul 2018 09:34:32 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Build_a_distributable_JAR_?= =?utf-8?q?with_Gradle_=28=2Epom_needs_work=29=2E?= Message-ID: <20180721093432.1.355A72C407F93115@mg.python.org> https://hg.python.org/jython/rev/86b859301121 changeset: 8176:86b859301121 user: Jeff Allen date: Sat Jul 21 10:23:24 2018 +0100 summary: Build a distributable JAR with Gradle (.pom needs work). The task publishToMavenLocal produces a useful JAR although the POM lacks the correct dependencies. files: build.gradle | 113 ++++++++++++++++++++++++++++++++++---- 1 files changed, 101 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ plugins { id 'java-library' id 'antlr' - // XXX Add maven and use it. + id 'maven-publish' } import java.text.SimpleDateFormat @@ -47,6 +47,8 @@ //version = '2.7.2rc1' //version = '2.7.2' +group = 'org.python' + // ---------------- Miscellaneous configuration -------------------------------- @@ -365,7 +367,7 @@ * configuration files and the Python library. * * These copies include the tests, so we can test things :), but a subsequent - * JarTask of the build should exclude them as necessary. + * JarTask of the build should exclude them as necessary. (Not yet implemented.) */ ext { @@ -521,7 +523,6 @@ * Much of the assembly has taken place already in selective copying to the * build directory by tasks processResources and copyLib. */ - task jar(type: Jar, overwrite: true) { dependsOn pycompile @@ -533,26 +534,26 @@ */ exclude 'org/python/expose/generate/**' // The expose tool itself - // Add the exposed classes to the JAR first + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + preserveFileTimestamps = true + + // Add the exposed classes to the JAR from expose - // Add other compiled classes but without overwriting the exposed ones - duplicatesStrategy = DuplicatesStrategy.EXCLUDE + // Add other compiled classes from compileJava - // Add the resources + // Add the resources (includes merged Python library) from processResources - // Add Python library - preserveFileTimestamps = true - from file(project.ext.buildLibDir).parent + // Add compiled Python library from file(project.ext.compiledLibDir).parent manifest { // These attribute values are based on inspecting the ant build attributes ([ 'Main-Class': 'org.python.util.jython', - 'Built-By': 'Mistake', + 'Built-By': 'build.gradle', ]) attributes( [ // Build-Info section @@ -566,6 +567,70 @@ } +// ---------------- Documentation ---------------------------------------------- + +/* + * JavaDoc, anyway. + */ + +javadoc { + options.encoding = 'UTF-8' +} + +// ---------------- Publication ------------------------------------------------ + +/* + * Post the JAR we built to a public repository. We provide secondary -source + * and -javadoc JARs too (supporting 'main'). + * + * How do we test the artifact actually published is correct? The 'test' task + * tests Jython laid out in the build directory, not the JAR we propose to + * distribute. + * + * Maybe have a second JAR that contains the additional material necessary to + * run integration tests (regression tests and others). Python tests are + * (incorrectly) in the main JAR at present. + */ + +task sourcesJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.allJava +} + +task javadocJar(type: Jar) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +publishing { + + publications { + // The production JAR we expect to be cited as a dependency by users + main(MavenPublication) { + + // Correctly-constructed jar but a .pom without dependencies: + artifact jar + + // Nice .pom but the jar is the default built by the Java plugin: + //from components.java + + // Also provide the source. + artifact sourcesJar + // Also provide the docs. (Some javadoc errors currntly.) + artifact javadocJar + } + } + + repositories { + // Dummy one for test: need sonatype here and credentials from user + maven { + name = 'myRepo' + url = "file://${buildDir}/repo" + } + } +} + + // ---------------- Java unit tests -------------------------------------------- ext { @@ -675,6 +740,7 @@ */ } + // ---------------- Miscellaneous fettling of the prepare phase ---------------- // Source is globally UTF-8 (well, nearly). @@ -700,8 +766,31 @@ } } +task dumpCompo { + doLast { + println('components:') + components.each { println it } + println('components.java:') + components.java.each { println it } + } +} + +task dumpArt { + doLast { + println('artifacts:') + artifacts.each { println it } + } +} + +task dumpArch { + doLast { + println('archives:') + configurations.archives.each { println it } + } +} task dumpex { + // Legacy approach to obtaining files to JAR doLast { int n = project.ext.exposedDir.length() Set exposedClasses = new TreeSet() @@ -742,7 +831,7 @@ } -task dump { +task dumpSS { doLast { // Debug println '*** source sets ***' -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Mon Jul 30 01:23:12 2018 From: jython-checkins at python.org (jeff.allen) Date: Mon, 30 Jul 2018 05:23:12 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Rework_Gradle_build_to_obt?= =?utf-8?q?ain_distributable_JAR_and_POM=2C_both_correct=2E?= Message-ID: <20180730052312.1.7ADCF6368A1993B1@mg.python.org> https://hg.python.org/jython/rev/121e7ca91b4a changeset: 8177:121e7ca91b4a user: Jeff Allen date: Sun Jul 29 23:11:43 2018 +0100 summary: Rework Gradle build to obtain distributable JAR and POM, both correct. Command "./gradlew publishToMavenLocal" will post a JAR and POM locally that may be used experimentally in Maven-based projects. files: build.gradle | 253 ++++++++++++++++---------------------- 1 files changed, 104 insertions(+), 149 deletions(-) diff --git a/build.gradle b/build.gradle --- a/build.gradle +++ b/build.gradle @@ -52,16 +52,26 @@ // ---------------- Miscellaneous configuration -------------------------------- +// Support Java 7 onwards +sourceCompatibility = '1.7' +targetCompatibility = '1.7' // Separate the Gradle build from that of Ant buildDir = file('build2') ext { buildDate = new Date() + /* + * The directory structure supporting the build has separate locations for + * several intermediate stages. + */ // Java source generated by ANTLR antlrGenDir = "$buildDir/gensrc/org/python/antlr" - // This is where we assemble the standard library pre-JAR + // Intermediate locations for compiled classes + unexposedDir = "$buildDir/unexposed" + exposedDir = "$buildDir/exposed" + // The standard library may safely be assembled in-place as a resource buildLibDir = "$buildDir/resources/main/Lib/" - compiledLibDir = "$buildDir/classes/main/Lib/" + compiledLibDir = "$buildDir/resources/main/Lib/" } @@ -87,11 +97,8 @@ exclude 'com/**' // for now } - // output.resourcesDir = project.ext.assemblyDir - resources { - // Resources in project root, but this invites an explosion: - // srcDirs = [''] + // Resources in project root, but this invites an explosion. // ... so claim no sources: srcDirs = [] // and fix it in task processResources @@ -131,7 +138,7 @@ implementation group: 'org.ow2.asm', name: 'asm-commons', version: '5.2' implementation group: 'org.ow2.asm', name: 'asm-util', version: '5.2' - implementation group: 'com.google.guava', name: 'guava', version: '22.0-android' + api group: 'com.google.guava', name: 'guava', version: '22.0-android' implementation group: 'com.ibm.icu', name: 'icu4j', version: '59.1' implementation group: 'com.carrotsearch', name: 'java-sizeof', version: '0.0.5' @@ -164,6 +171,11 @@ * Jython brings several files we could treat as resources, but they do not sit * in the Gradle-conventional 'main/resources' directory, rather are in the * project root or rub shoulders with the java source. Pick them individually. + * + * Several tasks defined below declare that processResources depends on them, + * with the objective that at the end of processResources all generated + * resources and the stdlib (but not the compiled stdlib) should be in place + * in $buildDir/resources/main. */ processResources { from(file('.')) { @@ -182,24 +194,29 @@ outputDirectory = file(project.ext.antlrGenDir) } +// ---------------- compleJava Task ------------------------------------------------- + +compileJava { + // Divert compiled classes to intermediate location pre-exposure. + destinationDir = file(project.ext.unexposedDir) + println "compileJava.destinationDir = $destinationDir}" +} + // ---------------- Expose Task ------------------------------------------------ /* - * The exposer operates between two (somewhat fixed) directories. We follow - * the Gradle-conventional directory structure (not the legacy one). + * The exposer operates between the output of compileJava (unexposed directory) + * and a second intermediate location (exposed directory). These two the + * mergeExposed task will finally combine in the Gradle-standard classes + * directory used as input by the jar task. */ -ext { - compileDir = "$buildDir/classes/java/main/" - exposedDir = "$buildDir/classes/exposed/main/" -} - configurations { expose.extendsFrom(implementation) } dependencies { - // Put our compiled classes on the path of the expose (Ant) task - expose files("$buildDir/classes/java/main") + // The expose (Ant) task depends on classes compiled to here: + expose files(project.ext.unexposedDir) } // A (Gradle) task to run the Ant task 'expose'. @@ -208,7 +225,7 @@ description = 'Expose Java types to Python using their annotations.' // Allow Gradle to infer the need to regenreate the outputs - inputs.files(fileTree("${project.ext.compileDir}/org/python")) + inputs.files(fileTree("${project.ext.unexposedDir}/org/python")) outputs.dir(project.ext.exposedDir) doLast { @@ -224,13 +241,27 @@ // Use the Gradle-conventional directory structure (not the legacy one). ant.expose( - srcdir: file(project.ext.compileDir), + srcdir: file(project.ext.unexposedDir), destdir: mkdir(file(project.ext.exposedDir)), includesfile: file('CoreExposed.includes') ) } } +// Task to merge the exposed and unexposed classes +task mergeExposed(group: 'Custom', type:Copy, dependsOn: expose) { + description = 'Copy exposed Java types to classes.' + // Exposed version will take precedence + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from file(exposedDir) + from file(unexposedDir) + into sourceSets.main.output.classesDirs.singleFile +} + +// Attach to the classes task the placing of all compiled and exposed classes. +classes.dependsOn(mergeExposed) + + // ---------------- Version-related file generation ---------------------------- /* @@ -375,11 +406,12 @@ libJython = 'Lib' } - /* * Copy the Python standard library. We take this from a distribution of - * CPython, but take only the files specified in CPythonLib.includes in the - * project root. + * CPython, but take only the files specified in CPythonLib.includes. + * The Jython version of the standard library will be copied to the same place. + * Files from the Jython library having the same name (relative path) as one + * in CPythonLib.includes thereby take precedence. */ task copyLib( type: Copy, @@ -411,28 +443,17 @@ } // Copy the subset as specified by the list project.copy { + into project.ext.buildLibDir from file(project.ext.libPython) include cPythonLibIncludes exclude '**/*.pyc', '**/*.pyd' - into project.ext.buildLibDir duplicatesStrategy = DuplicatesStrategy.EXCLUDE } } - - /* - - - - - - - - - */ } - +// Attach this task to processResources +processResources.dependsOn(copyLib) // ---------------- Jython-Compile Python -------------------------------------- @@ -450,7 +471,6 @@ dependencies { // Jython as built so far should be on the path of the jycompile (Ant) task - pycompile files("$buildDir/classes/exposed/main") pycompile files("$buildDir/classes/java/main") pycompile files("$buildDir/resources/main") } @@ -460,9 +480,9 @@ group: 'Custom', description: 'Compile the Python modules to .class files for the JAR') { - dependsOn compileJava - dependsOn expose - dependsOn copyLib + // Compiler depends on rest of Jython being fully assembled in 'classes' + dependsOn classes + // Note that classes depends on processResources (Java plug-in). // Allow Gradle to infer the need to regenerate the outputs inputs.dir project.ext.buildLibDir @@ -516,39 +536,32 @@ /* * The default behaviour of the Java plug-in is to make a JAR of the classes in - * the "main" source set. We need a more complex operation that provides users - * with exposed classes instead of their plain counterparts, and also various - * configuration files and the Python library. - * - * Much of the assembly has taken place already in selective copying to the - * build directory by tasks processResources and copyLib. + * the "main" source set and its resources. Having carefully substituted/added + * exposed classes in the assembled classes directory, and having prepared the + * (compiled) stdlib as a resource, this is close to what we need, with a few + * adjustments as noted. */ -task jar(type: Jar, overwrite: true) { +jar { + // Ensure that compiled stdlib is part of the resources to JAR. dependsOn pycompile - /* - * This is a custom replacement Jar task so that we may control the - * order of adding classes. Exposed versions of identically-named classes - * supersede (by arriving first) those directly from compilation. - */ - exclude 'org/python/expose/generate/**' // The expose tool itself - - duplicatesStrategy = DuplicatesStrategy.EXCLUDE + // It is important for import that X$py.class be newer than X.py preserveFileTimestamps = true - // Add the exposed classes to the JAR - from expose - - // Add other compiled classes - from compileJava + // We don't JAR the expose tool itself + exclude 'org/python/expose/generate/**' - // Add the resources (includes merged Python library) - from processResources + // Exclude tests and test material from the main JAR + exclude 'Lib/distutils/tests/' + exclude 'Lib/email/test/' + exclude 'Lib/json/tests/' + exclude 'Lib/lib2to3/tests/' + exclude 'Lib/unittest/tests/' + exclude 'Lib/test/' + // XXX is this not more naturally done by having separate test resources? - // Add compiled Python library - from file(project.ext.compiledLibDir).parent - + // Build a custom manifest manifest { // These attribute values are based on inspecting the ant build attributes ([ @@ -559,11 +572,15 @@ attributes( [ // Build-Info section 'version': project.version, 'build-compiler': 'modern', - 'jdk-target-version': '1.7', + 'jdk-target-version': project.targetCompatibility, 'debug': true, ], 'Build-Info' ) } - + + doFirst { + println "jar: for ${archiveName}" + } + } @@ -607,17 +624,13 @@ publications { // The production JAR we expect to be cited as a dependency by users main(MavenPublication) { - - // Correctly-constructed jar but a .pom without dependencies: - artifact jar - // Nice .pom but the jar is the default built by the Java plugin: - //from components.java + from components.java // Also provide the source. artifact sourcesJar - // Also provide the docs. (Some javadoc errors currntly.) - artifact javadocJar + // Also provide the docs. (Some javadoc errors currently.) + //artifact javadocJar } } @@ -663,7 +676,6 @@ failFast = true // Properties as defined in Ant target javatest-basepath - //systemProperty 'python.home', project.ext.distDir // XXX Not sure of all that python.home is used for in tests. systemProperty 'python.home', file(copyLib.destinationDir).parent systemProperty 'python.test.source.dir', project.ext.testSourceDir @@ -766,81 +778,24 @@ } } -task dumpCompo { +task dumpSS { doLast { - println('components:') - components.each { println it } - println('components.java:') - components.java.each { println it } - } -} - -task dumpArt { - doLast { - println('artifacts:') - artifacts.each { println it } - } -} - -task dumpArch { - doLast { - println('archives:') - configurations.archives.each { println it } + println '*** source sets ***' + for (ss in sourceSets) { + String name = ss.name + println ss + println " ${name}.compileConfigurationName = ${ss.compileConfigurationName}" + println " ${name}.implementationConfigurationName = ${ss.implementationConfigurationName}" + println " ${name}.runtimeConfigurationName = ${ss.runtimeConfigurationName}" + println " ${name}.java.srcDirs = ${ss.java.srcDirs}" + println " ${name}.antlr.srcDirs = ${ss.antlr.srcDirs}" + println " ${name}.resources.srcDirs = ${ss.resources.srcDirs}" + println " ${name}.output.dirs = ${ss.output.dirs.files}" + println " ${name}.output.classesDirs = ${ss.output.classesDirs.files}" + println " ${name}.output.resourcesDir = ${ss.output.resourcesDir}" + println " ${name}.classesTaskName = ${ss.classesTaskName}" + println " ${name}.compileJavaTaskName = ${ss.compileJavaTaskName}" + println " ${name}.jarTaskName = ${ss.jarTaskName}" + } } } - -task dumpex { - // Legacy approach to obtaining files to JAR - doLast { - int n = project.ext.exposedDir.length() - Set exposedClasses = new TreeSet() - //println "*** files in ${project.ext.exposedDir}:" - for (f in fileTree(project.ext.exposedDir)) { - //println project.relativePath(f).substring(n) - exposedClasses.add( project.relativePath(f).substring(n) ) - } - //for (f in exposedClasses) { println f } - - println "${fileTree(project.ext.compileDir).size()} compiled classes." - - n = project.ext.compileDir.length() - int countx = 0 - for (f in fileTree(project.ext.compileDir)) { - //println project.relativePath(f).substring(n) - String name = project.relativePath(f).substring(n) - if (name in exposedClasses) { countx += 1 } - } - println "${exposedClasses.size()} classes from ${countx} exposed." - - def compiledToJar = fileTree(project.ext.compileDir).filter({ - File f -> !(project.relativePath(f).substring(n) in exposedClasses) - }) - - println "${compiledToJar.size()} to be jarred (after filtering)." - - int counti = 0 - for (f in fileTree(project.ext.compileDir)) { - String name = project.relativePath(f).substring(n) - String exposed = (name in exposedClasses) ? "EXPOSED" : "" - // println "${name} ${exposed}" - if (exposed) { counti += 1 } - } - - println "${counti} in overlap." - } -} - - -task dumpSS { - doLast { - // Debug - println '*** source sets ***' - for ( sourceSet in sourceSets ) { - println " ${sourceSet}" - // for ( name in sourceSet.asMap.keys ) { - // sourceDirectorySet = sourceSet.asMap[name] - // println " $name = $sourceDirectorySet" - // } - } - } -} \ No newline at end of file -- Repository URL: https://hg.python.org/jython