From jython-checkins at python.org Mon Dec 2 21:31:00 2013 From: jython-checkins at python.org (jeff.allen) Date: Mon, 2 Dec 2013 21:31:00 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fixes_=232033_failing_test_?= =?utf-8?q?for_1_Mar_after_29_Feb=2E?= Message-ID: <3dYHyr70xJz7LjN@mail.python.org> http://hg.python.org/jython/rev/5f7a81d261c8 changeset: 7164:5f7a81d261c8 user: Santoso Wijaya date: Sun Dec 01 18:33:48 2013 +0000 summary: Fixes #2033 failing test for 1 Mar after 29 Feb. This was a test failure in test_strptime (test_mar1_comes_after_feb29_even_when_omitting_the_year). Solution falls back to the Python implementation for formats that include this case. files: NEWS | 1 + src/org/python/modules/time/Time.java | 31 +++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ - [ 2020 ] str.translate should delete characters in the second arg when table is None - [ 1753 ] zlib doesn't call end() on compress and decompress - [ 1860 ] test failures in test_array.py + - [ 2033 ] test_strptime fails: test_mar1_comes_after_feb29_even_when_omitting_the_year - [ 2046 ] sys.stdin.readline() hangs when used interactively (JLine, Windows) - [ 2060 ] Thread ident missing - [ 2082 ] Unexpected (Pdb) prompt during regression tests diff --git a/src/org/python/modules/time/Time.java b/src/org/python/modules/time/Time.java --- a/src/org/python/modules/time/Time.java +++ b/src/org/python/modules/time/Time.java @@ -750,14 +750,18 @@ /** - * @return the {@link SimpleDateFormat} format string equivalent to the - * strptime format string supplied as parameter, or null if there is - * no equivalent for SimpleDateFormat. + * Returns a {@link SimpleDateFormat} format string equivalent to the strptime + * format string supplied as parameter. If there is no reliable equivalent, it returns + * null, and the caller will use the Python implementation. + * + * @return format equivalent or null */ private static String py2java_format(String format) { StringBuilder builder = new StringBuilder(); boolean directive = false; boolean inQuote = false; + boolean containsYear = false; + boolean containsMonth = false; if (format.length() == 0) { return null; @@ -796,9 +800,30 @@ if (translated == null && notSupported.contains(charAt)) { return null; } + + switch (charAt) { + case 'c': + case 'x': + containsMonth = containsYear = true; + break; + case 'y': + case 'Y': + containsYear = true; + break; + case 'b': + case 'B': + case 'm': + containsMonth = true; + break; + } + builder.append(translated != null ? translated : charAt); directive = false; } + if (containsMonth && !containsYear) { + // Java differs from Python concerning 29 Feb with no year: safe choice is fall-back. + return null; + } if (inQuote) { builder.append("'"); } -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Dec 2 21:31:02 2013 From: jython-checkins at python.org (jeff.allen) Date: Mon, 2 Dec 2013 21:31:02 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fixes_=232071_datetime=2Eda?= =?utf-8?q?te=2Estrftime_support_for_=25f=2E?= Message-ID: <3dYHyt1tyfz7LjX@mail.python.org> http://hg.python.org/jython/rev/8b200f9ff1ea changeset: 7165:8b200f9ff1ea user: Santoso Wijaya date: Sun Dec 01 21:18:48 2013 +0000 summary: Fixes #2071 datetime.date.strftime support for %f. files: Lib/datetime.py | 11 +++++++++-- Lib/test/test_datetime.py | 1 - NEWS | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Lib/datetime.py b/Lib/datetime.py --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -182,7 +182,7 @@ return result # Correctly substitute for %z and %Z escapes in strftime formats. -def _wrap_strftime(object, format, timetuple): +def _wrap_strftime(object, format, timetuple, microsecond=0): year = timetuple[0] if year < 1900: raise ValueError("year=%d is before 1900; the datetime strftime() " @@ -225,6 +225,9 @@ # strftime is going to have at this: escape % Zreplace = s.replace('%', '%%') newformat.append(Zreplace) + elif ch == 'f': + us_string = '%.06d' % microsecond + newformat.append(us_string) else: push('%') push(ch) @@ -1269,7 +1272,7 @@ timetuple = (1900, 1, 1, self.__hour, self.__minute, self.__second, 0, 1, -1) - return _wrap_strftime(self, fmt, timetuple) + return _wrap_strftime(self, fmt, timetuple, self.microsecond) # Timezone functions @@ -1634,6 +1637,10 @@ "Convert to string, for str()." return self.isoformat(sep=' ') + def strftime(self, fmt): + "Format using strftime()." + return _wrap_strftime(self, fmt, self.timetuple(), self.microsecond) + def utcoffset(self): """Return the timezone offset in minutes east of UTC (negative west of UTC).""" diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py --- a/Lib/test/test_datetime.py +++ b/Lib/test/test_datetime.py @@ -3353,7 +3353,6 @@ if test_support.is_jython: del TestDate.test_format del TestDateTime.test_format - del TestDateTime.test_more_strftime del TestDateTime.test_strptime del TestTime.test_format diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -12,6 +12,7 @@ - [ 2033 ] test_strptime fails: test_mar1_comes_after_feb29_even_when_omitting_the_year - [ 2046 ] sys.stdin.readline() hangs when used interactively (JLine, Windows) - [ 2060 ] Thread ident missing + - [ 2071 ] datetime strftime %f does not work - [ 2082 ] Unexpected (Pdb) prompt during regression tests Jython 2.7b1 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Dec 2 21:31:04 2013 From: jython-checkins at python.org (jeff.allen) Date: Mon, 2 Dec 2013 21:31:04 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Update_test=5Fdatetime_from?= =?utf-8?q?_lib-python_and_add_FIXME-skips=2E?= Message-ID: <3dYHyw0Cjbz7Ljj@mail.python.org> http://hg.python.org/jython/rev/0ea46e98b2b2 changeset: 7166:0ea46e98b2b2 user: Jeff Allen date: Sun Dec 01 22:24:32 2013 +0000 summary: Update test_datetime from lib-python and add FIXME-skips. files: Lib/test/test_datetime.py | 422 +++++++++++++------------ 1 files changed, 214 insertions(+), 208 deletions(-) diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py --- a/Lib/test/test_datetime.py +++ b/Lib/test/test_datetime.py @@ -2,7 +2,7 @@ See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases """ - +from __future__ import division import sys import pickle import cPickle @@ -79,9 +79,9 @@ def __init__(self, offset, name): self.__offset = offset self.__name = name - self.failUnless(issubclass(NotEnough, tzinfo)) + self.assertTrue(issubclass(NotEnough, tzinfo)) ne = NotEnough(3, "NotByALongShot") - self.failUnless(isinstance(ne, tzinfo)) + self.assertIsInstance(ne, tzinfo) dt = datetime.now() self.assertRaises(NotImplementedError, ne.tzname, dt) @@ -90,7 +90,7 @@ def test_normal(self): fo = FixedOffset(3, "Three") - self.failUnless(isinstance(fo, tzinfo)) + self.assertIsInstance(fo, tzinfo) for dt in datetime.now(), None: self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3)) self.assertEqual(fo.tzname(dt), "Three") @@ -101,25 +101,25 @@ # carry no data), but they need to be picklable anyway else # concrete subclasses can't be pickled. orig = tzinfo.__new__(tzinfo) - self.failUnless(type(orig) is tzinfo) + self.assertTrue(type(orig) is tzinfo) for pickler, unpickler, proto in pickle_choices: green = pickler.dumps(orig, proto) derived = unpickler.loads(green) - self.failUnless(type(derived) is tzinfo) + self.assertTrue(type(derived) is tzinfo) def test_pickling_subclass(self): # Make sure we can pickle/unpickle an instance of a subclass. offset = timedelta(minutes=-300) orig = PicklableFixedOffset(offset, 'cookie') - self.failUnless(isinstance(orig, tzinfo)) - self.failUnless(type(orig) is PicklableFixedOffset) + self.assertIsInstance(orig, tzinfo) + self.assertTrue(type(orig) is PicklableFixedOffset) self.assertEqual(orig.utcoffset(None), offset) self.assertEqual(orig.tzname(None), 'cookie') for pickler, unpickler, proto in pickle_choices: green = pickler.dumps(orig, proto) derived = unpickler.loads(green) - self.failUnless(isinstance(derived, tzinfo)) - self.failUnless(type(derived) is PicklableFixedOffset) + self.assertIsInstance(derived, tzinfo) + self.assertTrue(type(derived) is PicklableFixedOffset) self.assertEqual(derived.utcoffset(None), offset) self.assertEqual(derived.tzname(None), 'cookie') @@ -136,16 +136,13 @@ def test_harmless_mixed_comparison(self): me = self.theclass(1, 1, 1) - self.failIf(me == ()) - self.failUnless(me != ()) - self.failIf(() == me) - self.failUnless(() != me) - - self.failUnless(me in [1, 20L, [], me]) - self.failIf(me not in [1, 20L, [], me]) - - self.failUnless([] in [me, 1, 20L, []]) - self.failIf([] not in [me, 1, 20L, []]) + self.assertFalse(me == ()) + self.assertTrue(me != ()) + self.assertFalse(() == me) + self.assertTrue(() != me) + + self.assertIn(me, [1, 20L, [], me]) + self.assertIn([], [me, 1, 20L, []]) def test_harmful_mixed_comparison(self): me = self.theclass(1, 1, 1) @@ -195,6 +192,7 @@ eq(td(seconds=0.001), td(milliseconds=1)) eq(td(milliseconds=0.001), td(microseconds=1)) + @unittest.skipIf(test_support.is_jython, "FIXME: overflow error on Jython") def test_computations(self): eq = self.assertEqual td = timedelta @@ -234,6 +232,13 @@ eq(a//10, td(0, 7*24*360)) eq(a//3600000, td(0, 0, 7*24*1000)) + # Issue #11576 + eq(td(999999999, 86399, 999999) - td(999999999, 86399, 999998), + td(0, 0, 1)) + eq(td(999999999, 1, 1) - td(999999999, 1, 0), + td(0, 0, 1)) + + def test_disallowed_computations(self): a = timedelta(42) @@ -266,6 +271,21 @@ self.assertEqual(td.seconds, seconds) self.assertEqual(td.microseconds, us) + @unittest.skipIf(test_support.is_jython, "FIXME: total_seconds() not supported") + def test_total_seconds(self): + td = timedelta(days=365) + self.assertEqual(td.total_seconds(), 31536000.0) + for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]: + td = timedelta(seconds=total_seconds) + self.assertEqual(td.total_seconds(), total_seconds) + # Issue8644: Test that td.total_seconds() has the same + # accuracy as td / timedelta(seconds=1). + for ms in [-1, -2, -123]: + td = timedelta(microseconds=ms) + self.assertEqual(td.total_seconds(), + ((24*3600*td.days + td.seconds)*10**6 + + td.microseconds)/10**6) + def test_carries(self): t1 = timedelta(days=100, weeks=-7, @@ -307,29 +327,29 @@ def test_compare(self): t1 = timedelta(2, 3, 4) t2 = timedelta(2, 3, 4) - self.failUnless(t1 == t2) - self.failUnless(t1 <= t2) - self.failUnless(t1 >= t2) - self.failUnless(not t1 != t2) - self.failUnless(not t1 < t2) - self.failUnless(not t1 > t2) + self.assertTrue(t1 == t2) + self.assertTrue(t1 <= t2) + self.assertTrue(t1 >= t2) + self.assertTrue(not t1 != t2) + self.assertTrue(not t1 < t2) + self.assertTrue(not t1 > t2) self.assertEqual(cmp(t1, t2), 0) self.assertEqual(cmp(t2, t1), 0) for args in (3, 3, 3), (2, 4, 4), (2, 3, 5): t2 = timedelta(*args) # this is larger than t1 - self.failUnless(t1 < t2) - self.failUnless(t2 > t1) - self.failUnless(t1 <= t2) - self.failUnless(t2 >= t1) - self.failUnless(t1 != t2) - self.failUnless(t2 != t1) - self.failUnless(not t1 == t2) - self.failUnless(not t2 == t1) - self.failUnless(not t1 > t2) - self.failUnless(not t2 < t1) - self.failUnless(not t1 >= t2) - self.failUnless(not t2 <= t1) + self.assertTrue(t1 < t2) + self.assertTrue(t2 > t1) + self.assertTrue(t1 <= t2) + self.assertTrue(t2 >= t1) + self.assertTrue(t1 != t2) + self.assertTrue(t2 != t1) + self.assertTrue(not t1 == t2) + self.assertTrue(not t2 == t1) + self.assertTrue(not t1 > t2) + self.assertTrue(not t2 < t1) + self.assertTrue(not t1 >= t2) + self.assertTrue(not t2 <= t1) self.assertEqual(cmp(t1, t2), -1) self.assertEqual(cmp(t2, t1), 1) @@ -377,7 +397,7 @@ # Verify td -> string -> td identity. s = repr(td) - self.failUnless(s.startswith('datetime.')) + self.assertTrue(s.startswith('datetime.')) s = s[9:] td2 = eval(s) self.assertEqual(td, td2) @@ -387,10 +407,10 @@ self.assertEqual(td, td2) def test_resolution_info(self): - self.assert_(isinstance(timedelta.min, timedelta)) - self.assert_(isinstance(timedelta.max, timedelta)) - self.assert_(isinstance(timedelta.resolution, timedelta)) - self.assert_(timedelta.max > timedelta.min) + self.assertIsInstance(timedelta.min, timedelta) + self.assertIsInstance(timedelta.max, timedelta) + self.assertIsInstance(timedelta.resolution, timedelta) + self.assertTrue(timedelta.max > timedelta.min) self.assertEqual(timedelta.min, timedelta(-999999999)) self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1)) self.assertEqual(timedelta.resolution, timedelta(0, 0, 1)) @@ -437,11 +457,11 @@ (-1, 24*3600-1, 999999)) def test_bool(self): - self.failUnless(timedelta(1)) - self.failUnless(timedelta(0, 1)) - self.failUnless(timedelta(0, 0, 1)) - self.failUnless(timedelta(microseconds=1)) - self.failUnless(not timedelta(0)) + self.assertTrue(timedelta(1)) + self.assertTrue(timedelta(0, 1)) + self.assertTrue(timedelta(0, 0, 1)) + self.assertTrue(timedelta(microseconds=1)) + self.assertTrue(not timedelta(0)) def test_subclass_timedelta(self): @@ -457,17 +477,17 @@ return round(sum) t1 = T(days=1) - self.assert_(type(t1) is T) + self.assertTrue(type(t1) is T) self.assertEqual(t1.as_hours(), 24) t2 = T(days=-1, seconds=-3600) - self.assert_(type(t2) is T) + self.assertTrue(type(t2) is T) self.assertEqual(t2.as_hours(), -25) t3 = t1 + t2 - self.assert_(type(t3) is timedelta) + self.assertTrue(type(t3) is timedelta) t4 = T.from_td(t3) - self.assert_(type(t4) is T) + self.assertTrue(type(t4) is T) self.assertEqual(t3.days, t4.days) self.assertEqual(t3.seconds, t4.seconds) self.assertEqual(t3.microseconds, t4.microseconds) @@ -530,7 +550,7 @@ self.theclass.today()): # Verify dt -> string -> date identity. s = repr(dt) - self.failUnless(s.startswith('datetime.')) + self.assertTrue(s.startswith('datetime.')) s = s[9:] dt2 = eval(s) self.assertEqual(dt, dt2) @@ -765,7 +785,7 @@ # It worked or it didn't. If it didn't, assume it's reason #2, and # let the test pass if they're within half a second of each other. - self.failUnless(today == todayagain or + self.assertTrue(today == todayagain or abs(todayagain - today) < timedelta(seconds=0.5)) def test_weekday(self): @@ -874,7 +894,7 @@ #check that this standard extension works t.strftime("%f") - + @unittest.skipIf(test_support.is_jython, "FIXME: some formats not accepted by Jython") def test_format(self): dt = self.theclass(2007, 9, 10) self.assertEqual(dt.__format__(''), str(dt)) @@ -902,10 +922,10 @@ self.assertEqual(b.__format__(fmt), 'B') def test_resolution_info(self): - self.assert_(isinstance(self.theclass.min, self.theclass)) - self.assert_(isinstance(self.theclass.max, self.theclass)) - self.assert_(isinstance(self.theclass.resolution, timedelta)) - self.assert_(self.theclass.max > self.theclass.min) + self.assertIsInstance(self.theclass.min, self.theclass) + self.assertIsInstance(self.theclass.max, self.theclass) + self.assertIsInstance(self.theclass.resolution, timedelta) + self.assertTrue(self.theclass.max > self.theclass.min) def test_extreme_timedelta(self): big = self.theclass.max - self.theclass.min @@ -953,29 +973,29 @@ def test_compare(self): t1 = self.theclass(2, 3, 4) t2 = self.theclass(2, 3, 4) - self.failUnless(t1 == t2) - self.failUnless(t1 <= t2) - self.failUnless(t1 >= t2) - self.failUnless(not t1 != t2) - self.failUnless(not t1 < t2) - self.failUnless(not t1 > t2) + self.assertTrue(t1 == t2) + self.assertTrue(t1 <= t2) + self.assertTrue(t1 >= t2) + self.assertTrue(not t1 != t2) + self.assertTrue(not t1 < t2) + self.assertTrue(not t1 > t2) self.assertEqual(cmp(t1, t2), 0) self.assertEqual(cmp(t2, t1), 0) for args in (3, 3, 3), (2, 4, 4), (2, 3, 5): t2 = self.theclass(*args) # this is larger than t1 - self.failUnless(t1 < t2) - self.failUnless(t2 > t1) - self.failUnless(t1 <= t2) - self.failUnless(t2 >= t1) - self.failUnless(t1 != t2) - self.failUnless(t2 != t1) - self.failUnless(not t1 == t2) - self.failUnless(not t2 == t1) - self.failUnless(not t1 > t2) - self.failUnless(not t2 < t1) - self.failUnless(not t1 >= t2) - self.failUnless(not t2 <= t1) + self.assertTrue(t1 < t2) + self.assertTrue(t2 > t1) + self.assertTrue(t1 <= t2) + self.assertTrue(t2 >= t1) + self.assertTrue(t1 != t2) + self.assertTrue(t2 != t1) + self.assertTrue(not t1 == t2) + self.assertTrue(not t2 == t1) + self.assertTrue(not t1 > t2) + self.assertTrue(not t2 < t1) + self.assertTrue(not t1 >= t2) + self.assertTrue(not t2 <= t1) self.assertEqual(cmp(t1, t2), -1) self.assertEqual(cmp(t2, t1), 1) @@ -1029,13 +1049,13 @@ their = Comparable() self.assertEqual(cmp(our, their), 0) self.assertEqual(cmp(their, our), 0) - self.failUnless(our == their) - self.failUnless(their == our) + self.assertTrue(our == their) + self.assertTrue(their == our) def test_bool(self): # All dates are considered true. - self.failUnless(self.theclass.min) - self.failUnless(self.theclass.max) + self.assertTrue(self.theclass.min) + self.assertTrue(self.theclass.max) def test_strftime_out_of_range(self): # For nasty technical reasons, we can't handle years before 1900. @@ -1158,7 +1178,7 @@ self.theclass.now()): # Verify dt -> string -> datetime identity. s = repr(dt) - self.failUnless(s.startswith('datetime.')) + self.assertTrue(s.startswith('datetime.')) s = s[9:] dt2 = eval(s) self.assertEqual(dt, dt2) @@ -1185,6 +1205,7 @@ # str is ISO format with the separator forced to a blank. self.assertEqual(str(t), "0002-03-02 00:00:00") + @unittest.skipIf(test_support.is_jython, "FIXME: some formats not accepted by Jython") def test_format(self): dt = self.theclass(2007, 9, 10, 4, 5, 1, 123) self.assertEqual(dt.__format__(''), str(dt)) @@ -1232,7 +1253,7 @@ dt2 = self.theclass(2002, 3, 1, 10, 0, 0) dt3 = self.theclass(2002, 3, 1, 9, 0, 0) self.assertEqual(dt1, dt3) - self.assert_(dt2 > dt3) + self.assertTrue(dt2 > dt3) # Make sure comparison doesn't forget microseconds, and isn't done # via comparing a float timestamp (an IEEE double doesn't have enough @@ -1243,7 +1264,7 @@ us = timedelta(microseconds=1) dt2 = dt1 + us self.assertEqual(dt2 - dt1, us) - self.assert_(dt1 < dt2) + self.assertTrue(dt1 < dt2) def test_strftime_with_bad_tzname_replace(self): # verify ok if tzinfo.tzname().replace() returns a non-string @@ -1423,12 +1444,12 @@ args = [2000, 11, 29, 20, 58, 16, 999998] t1 = self.theclass(*args) t2 = self.theclass(*args) - self.failUnless(t1 == t2) - self.failUnless(t1 <= t2) - self.failUnless(t1 >= t2) - self.failUnless(not t1 != t2) - self.failUnless(not t1 < t2) - self.failUnless(not t1 > t2) + self.assertTrue(t1 == t2) + self.assertTrue(t1 <= t2) + self.assertTrue(t1 >= t2) + self.assertTrue(not t1 != t2) + self.assertTrue(not t1 < t2) + self.assertTrue(not t1 > t2) self.assertEqual(cmp(t1, t2), 0) self.assertEqual(cmp(t2, t1), 0) @@ -1436,18 +1457,18 @@ newargs = args[:] newargs[i] = args[i] + 1 t2 = self.theclass(*newargs) # this is larger than t1 - self.failUnless(t1 < t2) - self.failUnless(t2 > t1) - self.failUnless(t1 <= t2) - self.failUnless(t2 >= t1) - self.failUnless(t1 != t2) - self.failUnless(t2 != t1) - self.failUnless(not t1 == t2) - self.failUnless(not t2 == t1) - self.failUnless(not t1 > t2) - self.failUnless(not t2 < t1) - self.failUnless(not t1 >= t2) - self.failUnless(not t2 <= t1) + self.assertTrue(t1 < t2) + self.assertTrue(t2 > t1) + self.assertTrue(t1 <= t2) + self.assertTrue(t2 >= t1) + self.assertTrue(t1 != t2) + self.assertTrue(t2 != t1) + self.assertTrue(not t1 == t2) + self.assertTrue(not t2 == t1) + self.assertTrue(not t1 > t2) + self.assertTrue(not t2 < t1) + self.assertTrue(not t1 >= t2) + self.assertTrue(not t2 <= t1) self.assertEqual(cmp(t1, t2), -1) self.assertEqual(cmp(t2, t1), 1) @@ -1480,8 +1501,8 @@ def test_microsecond_rounding(self): # Test whether fromtimestamp "rounds up" floats that are less # than one microsecond smaller than an integer. - self.assertEquals(self.theclass.fromtimestamp(0.9999999), - self.theclass.fromtimestamp(1)) + self.assertEqual(self.theclass.fromtimestamp(0.9999999), + self.theclass.fromtimestamp(1)) def test_insane_fromtimestamp(self): # It's possible that some platform maps time_t to double, @@ -1500,21 +1521,16 @@ for insane in -1e200, 1e200: self.assertRaises(ValueError, self.theclass.utcfromtimestamp, insane) - + @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps") def test_negative_float_fromtimestamp(self): - # Windows doesn't accept negative timestamps - if sys.platform == "win32": - return # The result is tz-dependent; at least test that this doesn't # fail (like it did before bug 1646728 was fixed). self.theclass.fromtimestamp(-1.05) + @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps") def test_negative_float_utcfromtimestamp(self): - # Windows doesn't accept negative timestamps - if sys.platform == "win32": - return d = self.theclass.utcfromtimestamp(-1.05) - self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000)) + self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000)) def test_utcnow(self): import time @@ -1528,8 +1544,9 @@ if abs(from_timestamp - from_now) <= tolerance: break # Else try again a few times. - self.failUnless(abs(from_timestamp - from_now) <= tolerance) - + self.assertTrue(abs(from_timestamp - from_now) <= tolerance) + + @unittest.skipIf(test_support.is_jython, "FIXME: %f not accepted") def test_strptime(self): import _strptime @@ -1697,7 +1714,7 @@ # Verify t -> string -> time identity. s = repr(t) - self.failUnless(s.startswith('datetime.')) + self.assertTrue(s.startswith('datetime.')) s = s[9:] t2 = eval(s) self.assertEqual(t, t2) @@ -1711,12 +1728,12 @@ args = [1, 2, 3, 4] t1 = self.theclass(*args) t2 = self.theclass(*args) - self.failUnless(t1 == t2) - self.failUnless(t1 <= t2) - self.failUnless(t1 >= t2) - self.failUnless(not t1 != t2) - self.failUnless(not t1 < t2) - self.failUnless(not t1 > t2) + self.assertTrue(t1 == t2) + self.assertTrue(t1 <= t2) + self.assertTrue(t1 >= t2) + self.assertTrue(not t1 != t2) + self.assertTrue(not t1 < t2) + self.assertTrue(not t1 > t2) self.assertEqual(cmp(t1, t2), 0) self.assertEqual(cmp(t2, t1), 0) @@ -1724,18 +1741,18 @@ newargs = args[:] newargs[i] = args[i] + 1 t2 = self.theclass(*newargs) # this is larger than t1 - self.failUnless(t1 < t2) - self.failUnless(t2 > t1) - self.failUnless(t1 <= t2) - self.failUnless(t2 >= t1) - self.failUnless(t1 != t2) - self.failUnless(t2 != t1) - self.failUnless(not t1 == t2) - self.failUnless(not t2 == t1) - self.failUnless(not t1 > t2) - self.failUnless(not t2 < t1) - self.failUnless(not t1 >= t2) - self.failUnless(not t2 <= t1) + self.assertTrue(t1 < t2) + self.assertTrue(t2 > t1) + self.assertTrue(t1 <= t2) + self.assertTrue(t2 >= t1) + self.assertTrue(t1 != t2) + self.assertTrue(t2 != t1) + self.assertTrue(not t1 == t2) + self.assertTrue(not t2 == t1) + self.assertTrue(not t1 > t2) + self.assertTrue(not t2 < t1) + self.assertTrue(not t1 >= t2) + self.assertTrue(not t2 <= t1) self.assertEqual(cmp(t1, t2), -1) self.assertEqual(cmp(t2, t1), 1) @@ -1843,6 +1860,7 @@ # A naive object replaces %z and %Z with empty strings. self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''") + @unittest.skipIf(test_support.is_jython, "FIXME: some formats not accepted") def test_format(self): t = self.theclass(1, 2, 3, 4) self.assertEqual(t.__format__(''), str(t)) @@ -1888,10 +1906,10 @@ "%s(23, 15)" % name) def test_resolution_info(self): - self.assert_(isinstance(self.theclass.min, self.theclass)) - self.assert_(isinstance(self.theclass.max, self.theclass)) - self.assert_(isinstance(self.theclass.resolution, timedelta)) - self.assert_(self.theclass.max > self.theclass.min) + self.assertIsInstance(self.theclass.min, self.theclass) + self.assertIsInstance(self.theclass.max, self.theclass) + self.assertIsInstance(self.theclass.resolution, timedelta) + self.assertTrue(self.theclass.max > self.theclass.min) def test_pickling(self): args = 20, 59, 16, 64**2 @@ -1911,12 +1929,12 @@ def test_bool(self): cls = self.theclass - self.failUnless(cls(1)) - self.failUnless(cls(0, 1)) - self.failUnless(cls(0, 0, 1)) - self.failUnless(cls(0, 0, 0, 1)) - self.failUnless(not cls(0)) - self.failUnless(not cls()) + self.assertTrue(cls(1)) + self.assertTrue(cls(0, 1)) + self.assertTrue(cls(0, 0, 1)) + self.assertTrue(cls(0, 0, 0, 1)) + self.assertTrue(not cls(0)) + self.assertTrue(not cls()) def test_replace(self): cls = self.theclass @@ -2013,7 +2031,7 @@ def utcoffset(self, dt): pass b = BetterTry() t = cls(1, 1, 1, tzinfo=b) - self.failUnless(t.tzinfo is b) + self.assertTrue(t.tzinfo is b) def test_utc_offset_out_of_bounds(self): class Edgy(tzinfo): @@ -2052,9 +2070,9 @@ for t in (cls(1, 1, 1), cls(1, 1, 1, tzinfo=None), cls(1, 1, 1, tzinfo=C1())): - self.failUnless(t.utcoffset() is None) - self.failUnless(t.dst() is None) - self.failUnless(t.tzname() is None) + self.assertTrue(t.utcoffset() is None) + self.assertTrue(t.dst() is None) + self.assertTrue(t.tzname() is None) class C3(tzinfo): def utcoffset(self, dt): return timedelta(minutes=-1439) @@ -2148,7 +2166,7 @@ self.assertEqual(t.minute, 0) self.assertEqual(t.second, 0) self.assertEqual(t.microsecond, 0) - self.failUnless(t.tzinfo is None) + self.assertTrue(t.tzinfo is None) def test_zones(self): est = FixedOffset(-300, "EST", 1) @@ -2163,25 +2181,25 @@ self.assertEqual(t1.tzinfo, est) self.assertEqual(t2.tzinfo, utc) self.assertEqual(t3.tzinfo, met) - self.failUnless(t4.tzinfo is None) + self.assertTrue(t4.tzinfo is None) self.assertEqual(t5.tzinfo, utc) self.assertEqual(t1.utcoffset(), timedelta(minutes=-300)) self.assertEqual(t2.utcoffset(), timedelta(minutes=0)) self.assertEqual(t3.utcoffset(), timedelta(minutes=60)) - self.failUnless(t4.utcoffset() is None) + self.assertTrue(t4.utcoffset() is None) self.assertRaises(TypeError, t1.utcoffset, "no args") self.assertEqual(t1.tzname(), "EST") self.assertEqual(t2.tzname(), "UTC") self.assertEqual(t3.tzname(), "MET") - self.failUnless(t4.tzname() is None) + self.assertTrue(t4.tzname() is None) self.assertRaises(TypeError, t1.tzname, "no args") self.assertEqual(t1.dst(), timedelta(minutes=1)) self.assertEqual(t2.dst(), timedelta(minutes=-2)) self.assertEqual(t3.dst(), timedelta(minutes=3)) - self.failUnless(t4.dst() is None) + self.assertTrue(t4.dst() is None) self.assertRaises(TypeError, t1.dst, "no args") self.assertEqual(hash(t1), hash(t2)) @@ -2257,7 +2275,7 @@ green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) - self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset)) + self.assertIsInstance(derived.tzinfo, PicklableFixedOffset) self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) self.assertEqual(derived.tzname(), 'cookie') @@ -2266,20 +2284,20 @@ cls = self.theclass t = cls(0, tzinfo=FixedOffset(-300, "")) - self.failUnless(t) + self.assertTrue(t) t = cls(5, tzinfo=FixedOffset(-300, "")) - self.failUnless(t) + self.assertTrue(t) t = cls(5, tzinfo=FixedOffset(300, "")) - self.failUnless(not t) + self.assertTrue(not t) t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, "")) - self.failUnless(not t) + self.assertTrue(not t) # Mostly ensuring this doesn't overflow internally. t = cls(0, tzinfo=FixedOffset(23*60 + 59, "")) - self.failUnless(t) + self.assertTrue(t) # But this should yield a value error -- the utcoffset is bogus. t = cls(0, tzinfo=FixedOffset(24*60, "")) @@ -2313,13 +2331,13 @@ # Ensure we can get rid of a tzinfo. self.assertEqual(base.tzname(), "+100") base2 = base.replace(tzinfo=None) - self.failUnless(base2.tzinfo is None) - self.failUnless(base2.tzname() is None) + self.assertTrue(base2.tzinfo is None) + self.assertTrue(base2.tzname() is None) # Ensure we can add one. base3 = base2.replace(tzinfo=z100) self.assertEqual(base, base3) - self.failUnless(base.tzinfo is base3.tzinfo) + self.assertTrue(base.tzinfo is base3.tzinfo) # Out of bounds. base = cls(1) @@ -2356,7 +2374,7 @@ # But if they're not identical, it isn't ignored. t2 = t2.replace(tzinfo=Varies()) - self.failUnless(t1 < t2) # t1's offset counter still going up + self.assertTrue(t1 < t2) # t1's offset counter still going up def test_subclass_timetz(self): @@ -2412,12 +2430,12 @@ tzinfo=FixedOffset(-1439, "")) # Make sure those compare correctly, and w/o overflow. - self.failUnless(t1 < t2) - self.failUnless(t1 != t2) - self.failUnless(t2 > t1) - - self.failUnless(t1 == t1) - self.failUnless(t2 == t2) + self.assertTrue(t1 < t2) + self.assertTrue(t1 != t2) + self.assertTrue(t2 > t1) + + self.assertTrue(t1 == t1) + self.assertTrue(t2 == t2) # Equal afer adjustment. t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, "")) @@ -2426,21 +2444,21 @@ # Change t1 not to subtract a minute, and t1 should be larger. t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, "")) - self.failUnless(t1 > t2) + self.assertTrue(t1 > t2) # Change t1 to subtract 2 minutes, and t1 should be smaller. t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, "")) - self.failUnless(t1 < t2) + self.assertTrue(t1 < t2) # Back to the original t1, but make seconds resolve it. t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""), second=1) - self.failUnless(t1 > t2) + self.assertTrue(t1 > t2) # Likewise, but make microseconds resolve it. t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""), microsecond=1) - self.failUnless(t1 > t2) + self.assertTrue(t1 > t2) # Make t2 naive and it should fail. t2 = self.theclass.min @@ -2484,8 +2502,7 @@ green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) - self.failUnless(isinstance(derived.tzinfo, - PicklableFixedOffset)) + self.assertIsInstance(derived.tzinfo, PicklableFixedOffset) self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) self.assertEqual(derived.tzname(), 'cookie') @@ -2555,7 +2572,7 @@ tz55 = FixedOffset(-330, "west 5:30") timeaware = now.time().replace(tzinfo=tz55) nowaware = self.theclass.combine(now.date(), timeaware) - self.failUnless(nowaware.tzinfo is tz55) + self.assertTrue(nowaware.tzinfo is tz55) self.assertEqual(nowaware.timetz(), timeaware) # Can't mix aware and non-aware. @@ -2574,15 +2591,15 @@ # Adding a delta should preserve tzinfo. delta = timedelta(weeks=1, minutes=12, microseconds=5678) nowawareplus = nowaware + delta - self.failUnless(nowaware.tzinfo is tz55) + self.assertTrue(nowaware.tzinfo is tz55) nowawareplus2 = delta + nowaware - self.failUnless(nowawareplus2.tzinfo is tz55) + self.assertTrue(nowawareplus2.tzinfo is tz55) self.assertEqual(nowawareplus, nowawareplus2) # that - delta should be what we started with, and that - what we # started with should be delta. diff = nowawareplus - delta - self.failUnless(diff.tzinfo is tz55) + self.assertTrue(diff.tzinfo is tz55) self.assertEqual(nowaware, diff) self.assertRaises(TypeError, lambda: delta - nowawareplus) self.assertEqual(nowawareplus - nowaware, delta) @@ -2591,7 +2608,7 @@ tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone") # Attach it to nowawareplus. nowawareplus = nowawareplus.replace(tzinfo=tzr) - self.failUnless(nowawareplus.tzinfo is tzr) + self.assertTrue(nowawareplus.tzinfo is tzr) # Make sure the difference takes the timezone adjustments into account. got = nowaware - nowawareplus # Expected: (nowaware base - nowaware offset) - @@ -2618,7 +2635,7 @@ off42 = FixedOffset(42, "42") another = meth(off42) again = meth(tz=off42) - self.failUnless(another.tzinfo is again.tzinfo) + self.assertTrue(another.tzinfo is again.tzinfo) self.assertEqual(another.utcoffset(), timedelta(minutes=42)) # Bad argument with and w/o naming the keyword. self.assertRaises(TypeError, meth, 16) @@ -2635,7 +2652,7 @@ utc = FixedOffset(0, "utc", 0) for dummy in range(3): now = datetime.now(weirdtz) - self.failUnless(now.tzinfo is weirdtz) + self.assertTrue(now.tzinfo is weirdtz) utcnow = datetime.utcnow().replace(tzinfo=utc) now2 = utcnow.astimezone(weirdtz) if abs(now - now2) < timedelta(seconds=30): @@ -2656,7 +2673,7 @@ off42 = FixedOffset(42, "42") another = meth(ts, off42) again = meth(ts, tz=off42) - self.failUnless(another.tzinfo is again.tzinfo) + self.assertTrue(another.tzinfo is again.tzinfo) self.assertEqual(another.utcoffset(), timedelta(minutes=42)) # Bad argument with and w/o naming the keyword. self.assertRaises(TypeError, meth, ts, 16) @@ -2850,13 +2867,13 @@ # Ensure we can get rid of a tzinfo. self.assertEqual(base.tzname(), "+100") base2 = base.replace(tzinfo=None) - self.failUnless(base2.tzinfo is None) - self.failUnless(base2.tzname() is None) + self.assertTrue(base2.tzinfo is None) + self.assertTrue(base2.tzname() is None) # Ensure we can add one. base3 = base2.replace(tzinfo=z100) self.assertEqual(base, base3) - self.failUnless(base.tzinfo is base3.tzinfo) + self.assertTrue(base.tzinfo is base3.tzinfo) # Out of bounds. base = cls(2000, 2, 29) @@ -2869,20 +2886,20 @@ fm5h = FixedOffset(-timedelta(hours=5), "m300") dt = self.theclass.now(tz=f44m) - self.failUnless(dt.tzinfo is f44m) + self.assertTrue(dt.tzinfo is f44m) # Replacing with degenerate tzinfo raises an exception. self.assertRaises(ValueError, dt.astimezone, fnone) # Ditto with None tz. self.assertRaises(TypeError, dt.astimezone, None) # Replacing with same tzinfo makes no change. x = dt.astimezone(dt.tzinfo) - self.failUnless(x.tzinfo is f44m) + self.assertTrue(x.tzinfo is f44m) self.assertEqual(x.date(), dt.date()) self.assertEqual(x.time(), dt.time()) # Replacing with different tzinfo does adjust. got = dt.astimezone(fm5h) - self.failUnless(got.tzinfo is fm5h) + self.assertTrue(got.tzinfo is fm5h) self.assertEqual(got.utcoffset(), timedelta(hours=-5)) expected = dt - dt.utcoffset() # in effect, convert to UTC expected += fm5h.utcoffset(dt) # and from there to local time @@ -2890,7 +2907,7 @@ self.assertEqual(got.date(), expected.date()) self.assertEqual(got.time(), expected.time()) self.assertEqual(got.timetz(), expected.timetz()) - self.failUnless(got.tzinfo is expected.tzinfo) + self.assertTrue(got.tzinfo is expected.tzinfo) self.assertEqual(got, expected) def test_aware_subtract(self): @@ -2965,7 +2982,7 @@ # But if they're not identical, it isn't ignored. t2 = t2.replace(tzinfo=Varies()) - self.failUnless(t1 < t2) # t1's offset counter still going up + self.assertTrue(t1 < t2) # t1's offset counter still going up def test_subclass_datetimetz(self): @@ -3103,7 +3120,7 @@ self.assertEqual(dt, there_and_back) # Because we have a redundant spelling when DST begins, there is - # (unforunately) an hour when DST ends that can't be spelled at all in + # (unfortunately) an hour when DST ends that can't be spelled at all in # local time. When DST ends, the clock jumps from 1:59 back to 1:00 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be @@ -3316,10 +3333,10 @@ # type comparison, despite that datetime is a subclass of date. as_date = date.today() as_datetime = datetime.combine(as_date, time()) - self.assert_(as_date != as_datetime) - self.assert_(as_datetime != as_date) - self.assert_(not as_date == as_datetime) - self.assert_(not as_datetime == as_date) + self.assertTrue(as_date != as_datetime) + self.assertTrue(as_datetime != as_date) + self.assertTrue(not as_date == as_datetime) + self.assertTrue(not as_datetime == as_date) self.assertRaises(TypeError, lambda: as_date < as_datetime) self.assertRaises(TypeError, lambda: as_datetime < as_date) self.assertRaises(TypeError, lambda: as_date <= as_datetime) @@ -3331,9 +3348,9 @@ # Neverthelss, comparison should work with the base-class (date) # projection if use of a date method is forced. - self.assert_(as_date.__eq__(as_datetime)) + self.assertTrue(as_date.__eq__(as_datetime)) different_day = (as_date.day + 1) % 20 + 1 - self.assert_(not as_date.__eq__(as_datetime.replace(day= + self.assertTrue(not as_date.__eq__(as_datetime.replace(day= different_day))) # And date should compare with other subclasses of date. If a @@ -3349,17 +3366,6 @@ self.assertEqual(datetime_sc, as_datetime) def test_main(): - # These deleted tests should be restored when we have better formating. - if test_support.is_jython: - del TestDate.test_format - del TestDateTime.test_format - del TestDateTime.test_strptime - del TestTime.test_format - - del TestDate.test_strftime - del TestTime.test_strftime - - test_support.run_unittest(__name__) if __name__ == "__main__": -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Dec 2 21:31:05 2013 From: jython-checkins at python.org (jeff.allen) Date: Mon, 2 Dec 2013 21:31:05 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fixes_=231964_time=2Estrpti?= =?utf-8?q?me=28=29_now_supports_=25f_in_format=2E?= Message-ID: <3dYHyx28Bwz7Ljf@mail.python.org> http://hg.python.org/jython/rev/b31e71644fa8 changeset: 7167:b31e71644fa8 user: Santoso Wijaya date: Mon Dec 02 08:25:56 2013 +0000 summary: Fixes #1964 time.strptime() now supports %f in format. Change to modules.Time to revert to Python implementation if %f seen, and to map IllegalArgumentException to ValueError along line suggested by arfrever. Unit tests for both. files: Lib/test/test_strptime_jy.py | 9 +++++++++ NEWS | 1 + src/org/python/modules/time/Time.java | 8 +++++++- 3 files changed, 17 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_strptime_jy.py b/Lib/test/test_strptime_jy.py --- a/Lib/test/test_strptime_jy.py +++ b/Lib/test/test_strptime_jy.py @@ -2,6 +2,7 @@ import unittest from datetime import datetime +from time import strptime from test import test_support @@ -13,6 +14,14 @@ # tests bug 1662 self.assertEqual(now, datetime.strptime(now.isoformat('T') + 'Z', "%Y-%m-%dT%H:%M:%SZ")) + def test_IllegalArgument_to_ValueError(self): + with self.assertRaises(ValueError): + d = strptime('', '%e') + + def test_issue1964(self): + d = strptime('0', '%f') + self.assertEqual(0, d[1]) + def test_main(): test_support.run_unittest( ParsingTests diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ - [ 2020 ] str.translate should delete characters in the second arg when table is None - [ 1753 ] zlib doesn't call end() on compress and decompress - [ 1860 ] test failures in test_array.py + - [ 1964 ] time.strptime() does not support %f in format - [ 2033 ] test_strptime fails: test_mar1_comes_after_feb29_even_when_omitting_the_year - [ 2046 ] sys.stdin.readline() hangs when used interactively (JLine, Windows) - [ 2060 ] Thread ident missing diff --git a/src/org/python/modules/time/Time.java b/src/org/python/modules/time/Time.java --- a/src/org/python/modules/time/Time.java +++ b/src/org/python/modules/time/Time.java @@ -704,7 +704,12 @@ // Format not translatable to java, fallback to _strptime return pystrptime(data_string, format); } - SimpleDateFormat d = new SimpleDateFormat(jformat); + SimpleDateFormat d = null; + try { + d = new SimpleDateFormat(jformat); + } catch (IllegalArgumentException e) { + throwValueError(e.getLocalizedMessage()); + } Calendar cal = Calendar.getInstance(); try { cal.setTime(d.parse(data_string)); @@ -746,6 +751,7 @@ // strptime formats not supported by SimpleDateFormat: private static final List notSupported = new ArrayList() {{ add('w'); + add('f'); }}; -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sat Dec 21 18:21:03 2013 From: jython-checkins at python.org (jim.baker) Date: Sat, 21 Dec 2013 18:21:03 +0100 (CET) Subject: [Jython-checkins] =?utf-8?b?anl0aG9uOiBTdXBwb3J0ICd0JyAodGV4dCkg?= =?utf-8?q?in_open_mode_-_this_was_a_recent_regression?= Message-ID: <3dmtrv62p1zS9S@mail.python.org> http://hg.python.org/jython/rev/0b2e1c866114 changeset: 7168:0b2e1c866114 user: Jim Baker date: Sat Dec 21 10:20:57 2013 -0700 summary: Support 't' (text) in open mode - this was a recent regression files: src/org/python/core/PyFile.java | 7 ++++++- 1 files changed, 6 insertions(+), 1 deletions(-) diff --git a/src/org/python/core/PyFile.java b/src/org/python/core/PyFile.java --- a/src/org/python/core/PyFile.java +++ b/src/org/python/core/PyFile.java @@ -206,7 +206,7 @@ private String parseMode(String mode) { String message = null; - boolean duplicate = false, invalid = false; + boolean duplicate = false, invalid = false, text_intent = false; int n = mode.length(); // Convert the letters to booleans, noticing duplicates @@ -234,6 +234,11 @@ duplicate = binary; binary = true; break; + case 't': + duplicate = text_intent; + text_intent = true; + binary = false; + break; case 'U': duplicate = universal; universal = true; -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 22 18:37:36 2013 From: jython-checkins at python.org (jeff.allen) Date: Sun, 22 Dec 2013 18:37:36 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fix_=232083=2C_to_prevent_o?= =?utf-8?q?s=2Eunlink=28=29_deleting_directory=2E?= Message-ID: <3dnW9X6yHbzQxh@mail.python.org> http://hg.python.org/jython/rev/dc9b1aa30f6d changeset: 7169:dc9b1aa30f6d parent: 7167:b31e71644fa8 user: Santoso Wijaya date: Sat Dec 21 20:18:51 2013 +0000 summary: Fix #2083, to prevent os.unlink() deleting directory. Also adds test to test_os_jy and a NEWS entry (JA). files: Lib/test/test_os_jy.py | 34 +++++++++- NEWS | 1 + src/org/python/modules/posix/PosixModule.java | 15 ++-- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/Lib/test/test_os_jy.py b/Lib/test/test_os_jy.py --- a/Lib/test/test_os_jy.py +++ b/Lib/test/test_os_jy.py @@ -6,7 +6,7 @@ import unittest from test import test_support -class OSTestCase(unittest.TestCase): +class OSFileTestCase(unittest.TestCase): def setUp(self): open(test_support.TESTFN, 'w').close() @@ -50,9 +50,37 @@ self.assertTrue(False) +class OSDirTestCase(unittest.TestCase): + + def setUp(self): + self.base = test_support.TESTFN + self.path = os.path.join(self.base, 'dir1', 'dir2', 'dir3') + os.makedirs(self.path) + + def test_rmdir(self): + # Remove end directory + os.rmdir(self.path) + # Fail to remove a chain of directories + self.assertRaises(OSError, os.rmdir, self.base) + + def test_issue2083(self): + # Should fail to remove/unlink directory + self.assertRaises(OSError, os.remove, self.path) + self.assertRaises(OSError, os.unlink, self.path) + + def tearDown(self): + # Some dirs may have been deleted. Find the longest that exists. + p = self.path + while not os.path.exists(p) and p != self.base: + p = os.path.dirname(p) + os.removedirs(p) + + def test_main(): - test_support.run_unittest(OSTestCase) - + test_support.run_unittest( + OSFileTestCase, + OSDirTestCase, + ) if __name__ == '__main__': test_main() diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -15,6 +15,7 @@ - [ 2060 ] Thread ident missing - [ 2071 ] datetime strftime %f does not work - [ 2082 ] Unexpected (Pdb) prompt during regression tests + - [ 2083 ] os.unlink() can delete directories Jython 2.7b1 Bugs Fixed diff --git a/src/org/python/modules/posix/PosixModule.java b/src/org/python/modules/posix/PosixModule.java --- a/src/org/python/modules/posix/PosixModule.java +++ b/src/org/python/modules/posix/PosixModule.java @@ -722,19 +722,18 @@ return posix.umask(mask); } - public static PyString __doc__unlink = new PyString( - "unlink(path)\n\n" + - "Remove a file (same as remove(path))."); + public static PyString __doc__unlink = new PyString("unlink(path)\n\n" + + "Remove a file (same as remove(path))."); + public static void unlink(PyObject path) { String absolutePath = absolutePath(path); File file = new File(absolutePath); - if (!file.delete()) { + if (file.isDirectory()) { + throw Py.OSError(Errno.EISDIR, path); + } else if (!file.delete()) { // Something went wrong, does stat raise an error? posix.stat(absolutePath); - // It exists, is it a directory, or do we not have permissions? - if (file.isDirectory()) { - throw Py.OSError(Errno.EISDIR, path); - } + // It exists, do we not have permissions? if (!file.canWrite()) { throw Py.OSError(Errno.EPERM, path); } -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 22 18:37:39 2013 From: jython-checkins at python.org (jeff.allen) Date: Sun, 22 Dec 2013 18:37:39 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fixes_hex_formatting_issues?= =?utf-8?q?_=232013_=28slow=29_and_=232075_=28incorrect_padding=29?= Message-ID: <3dnW9b2YW5zSqw@mail.python.org> http://hg.python.org/jython/rev/3bfef9e72808 changeset: 7170:3bfef9e72808 user: Santoso Wijaya date: Sun Dec 22 15:12:22 2013 +0000 summary: Fixes hex formatting issues #2013 (slow) and #2075 (incorrect padding) Special-cases binary, octal and hex formatting for speed and corrects alternate (#) formatting bug, allowing removal of several FIXMEs from test_types. files: Lib/test/test_str_jy.py | 5 + Lib/test/test_types.py | 48 ++-- NEWS | 12 +- src/org/python/core/PyInteger.java | 165 +++++++++++++++- src/org/python/core/PyLong.java | 4 +- 5 files changed, 183 insertions(+), 51 deletions(-) diff --git a/Lib/test/test_str_jy.py b/Lib/test/test_str_jy.py --- a/Lib/test/test_str_jy.py +++ b/Lib/test/test_str_jy.py @@ -53,6 +53,11 @@ self.assertEquals("%+f" % -5, "-5.000000") self.assertEquals("%+f" % 5, "+5.000000") + def test_format_issue2075(self): + self.assertEquals("%#018x" % 14, "0x000000000000000e") + self.assertEquals("{:#018x}".format(14), "0x000000000000000e") + self.assertEquals("{:+#018X}".format(14), "+0X00000000000000E") + self.assertEquals("{:#018X}".format(-14), "-0X00000000000000E") def test_argument_count_exception(self): "exception thrown when too many or too few arguments for format string" diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -365,54 +365,46 @@ test(0, "#b", '0b0') test(0, "-#b", '0b0') test(1, "-#b", '0b1') - #FIXME: not working. - #test(-1, "-#b", '-0b1') - #test(-1, "-#5b", ' -0b1') + test(-1, "-#b", '-0b1') + test(-1, "-#5b", ' -0b1') test(1, "+#5b", ' +0b1') test(100, "+#b", '+0b1100100') - #FIXME: not working. - #test(100, "#012b", '0b0001100100') - #test(-100, "#012b", '-0b001100100') + test(100, "#012b", '0b0001100100') + test(-100, "#012b", '-0b001100100') test(0, "#o", '0o0') test(0, "-#o", '0o0') test(1, "-#o", '0o1') - #FIXME: not working. - #test(-1, "-#o", '-0o1') - #test(-1, "-#5o", ' -0o1') + test(-1, "-#o", '-0o1') + test(-1, "-#5o", ' -0o1') test(1, "+#5o", ' +0o1') test(100, "+#o", '+0o144') - #FIXME: not working. - #test(100, "#012o", '0o0000000144') - #test(-100, "#012o", '-0o000000144') + test(100, "#012o", '0o0000000144') + test(-100, "#012o", '-0o000000144') test(0, "#x", '0x0') test(0, "-#x", '0x0') test(1, "-#x", '0x1') - #FIXME: not working. - #test(-1, "-#x", '-0x1') - #test(-1, "-#5x", ' -0x1') + test(-1, "-#x", '-0x1') + test(-1, "-#5x", ' -0x1') test(1, "+#5x", ' +0x1') test(100, "+#x", '+0x64') - #FIXME: not working. - #test(100, "#012x", '0x0000000064') - #test(-100, "#012x", '-0x000000064') - #test(123456, "#012x", '0x000001e240') - #test(-123456, "#012x", '-0x00001e240') + test(100, "#012x", '0x0000000064') + test(-100, "#012x", '-0x000000064') + test(123456, "#012x", '0x000001e240') + test(-123456, "#012x", '-0x00001e240') test(0, "#X", '0X0') test(0, "-#X", '0X0') test(1, "-#X", '0X1') - #FIXME: not working. - #test(-1, "-#X", '-0X1') - #test(-1, "-#5X", ' -0X1') + test(-1, "-#X", '-0X1') + test(-1, "-#5X", ' -0X1') test(1, "+#5X", ' +0X1') test(100, "+#X", '+0X64') - #FIXME: not working. - #test(100, "#012X", '0X0000000064') - #test(-100, "#012X", '-0X000000064') - #test(123456, "#012X", '0X000001E240') - #test(-123456, "#012X", '-0X00001E240') + test(100, "#012X", '0X0000000064') + test(-100, "#012X", '-0X000000064') + test(123456, "#012X", '0X000001E240') + test(-123456, "#012X", '-0X00001E240') # issue 5782, commas with no specifier type #FIXME: not working. diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -2,18 +2,20 @@ Jython 2.7b2 Bugs Fixed - - [ 1862 ] cStringIO does not support arrays as arguments - - [ 2027 ] Discrepancy in bin(-num) output - - [ 2005 ] threading.Event object's wait([timeout]) function returns null instead of True/False. - - [ 1926 ] Adjust MutableSet.pop test so we do not need to skip it - - [ 2020 ] str.translate should delete characters in the second arg when table is None - [ 1753 ] zlib doesn't call end() on compress and decompress - [ 1860 ] test failures in test_array.py + - [ 1862 ] cStringIO does not support arrays as arguments + - [ 1926 ] Adjust MutableSet.pop test so we do not need to skip it - [ 1964 ] time.strptime() does not support %f in format + - [ 2005 ] threading.Event object's wait([timeout]) function returns null instead of True/False. + - [ 2013 ] %x hex formatting takes O(N^2) time. + - [ 2020 ] str.translate should delete characters in the second arg when table is None + - [ 2027 ] Discrepancy in bin(-num) output - [ 2033 ] test_strptime fails: test_mar1_comes_after_feb29_even_when_omitting_the_year - [ 2046 ] sys.stdin.readline() hangs when used interactively (JLine, Windows) - [ 2060 ] Thread ident missing - [ 2071 ] datetime strftime %f does not work + - [ 2075 ] Incorrect padding for hex format strings - [ 2082 ] Unexpected (Pdb) prompt during regression tests - [ 2083 ] os.unlink() can delete directories diff --git a/src/org/python/core/PyInteger.java b/src/org/python/core/PyInteger.java --- a/src/org/python/core/PyInteger.java +++ b/src/org/python/core/PyInteger.java @@ -38,6 +38,8 @@ @Deprecated public static final BigInteger maxInt = MAX_INT; + private static final String LOOKUP = "0123456789abcdef"; + private final int value; public PyInteger(PyType subType, int v) { @@ -1047,6 +1049,7 @@ if (spec.precision != -1) { throw new IllegalArgumentException("Precision not allowed in integer format specifier"); } + int sign; if (value instanceof Integer) { int intValue = (Integer) value; @@ -1054,7 +1057,11 @@ } else { sign = ((BigInteger) value).signum(); } + String strValue; + String strPrefix = ""; + String strSign = ""; + if (spec.type == 'c') { if (spec.sign != '\0') { throw new IllegalArgumentException("Sign not allowed with integer format " @@ -1090,13 +1097,26 @@ format.setGroupingUsed(true); strValue = format.format(value); } else if (value instanceof BigInteger) { - strValue = ((BigInteger) value).toString(radix); + switch (radix) { + case 2: + strValue = toBinString((BigInteger) value); + break; + case 8: + strValue = toOctString((BigInteger) value); + break; + case 16: + strValue = toHexString((BigInteger) value); + break; + default: + // General case (v.slow in known implementations up to Java 7). + strValue = ((BigInteger) value).toString(radix); + break; + } } else { strValue = Integer.toString((Integer) value, radix); } if (spec.alternate) { - String strPrefix = ""; switch (radix) { case 2: strPrefix = "0b"; @@ -1109,32 +1129,145 @@ break; } - if (strValue.startsWith("-")) { - //assert (sign < 0); - if (!strPrefix.equals("")) - strValue = "-" + strPrefix + strValue.substring(1, strValue.length()); - } else { - strValue = strPrefix + strValue; + if (sign < 0) { + assert (strValue.startsWith("-")); + strSign = "-"; + strValue = strValue.substring(1); } } if (spec.type == 'X') { + strPrefix = strPrefix.toUpperCase(); strValue = strValue.toUpperCase(); } if (sign >= 0) { - if (spec.sign == '+') { - strValue = "+" + strValue; - } else if (spec.sign == ' ') { - strValue = " " + strValue; + switch (spec.sign) { + case '+': + case ' ': + strSign = Character.toString(spec.sign); + break; } } } - if (spec.align == '=' && (sign < 0 || spec.sign == '+' || spec.sign == ' ')) { - char signChar = strValue.charAt(0); - return signChar + spec.pad(strValue.substring(1), '>', 1); + + if (spec.align == '=' && (spec.sign == '-' || spec.sign == '+' || spec.sign == ' ')) { + assert (strSign.length() == 1); + return strSign + strPrefix + spec.pad(strValue, '>', 1 + strPrefix.length()); } - return spec.pad(strValue, '>', 0); + + if (spec.fill_char == 0) { + return spec.pad(strSign + strPrefix + strValue, '>', 0); + } + + return strSign + strPrefix + spec.pad(strValue, '>', strSign.length() + strPrefix.length()); + } + + /** + * A more efficient algorithm for generating a hexadecimal representation of a byte array. + * {@link BigInteger#toString(int)} is too slow because it generalizes to any radix and, + * consequently, is implemented using expensive mathematical operations. + * + * @param value the value to generate a hexadecimal string from + * @return the hexadecimal representation of value, with "-" sign prepended if necessary + */ + static final String toHexString(BigInteger value) { + int signum = value.signum(); + + // obvious shortcut + if (signum == 0) { + return "0"; + } + + // we want to work in absolute numeric value (negative sign is added afterward) + byte[] input = value.abs().toByteArray(); + StringBuilder sb = new StringBuilder(input.length * 2); + + int b; + for (int i = 0; i < input.length; i++) { + b = input[i] & 0xFF; + sb.append(LOOKUP.charAt(b >> 4)); + sb.append(LOOKUP.charAt(b & 0x0F)); + } + + // before returning the char array as string, remove leading zeroes, but not the last one + String result = sb.toString().replaceFirst("^0+(?!$)", ""); + return signum < 0 ? "-" + result : result; + } + + /** + * A more efficient algorithm for generating an octal representation of a byte array. + * {@link BigInteger#toString(int)} is too slow because it generalizes to any radix and, + * consequently, is implemented using expensive mathematical operations. + * + * @param value the value to generate an octal string from + * @return the octal representation of value, with "-" sign prepended if necessary + */ + static final String toOctString(BigInteger value) { + int signum = value.signum(); + + // obvious shortcut + if (signum == 0) { + return "0"; + } + + byte[] input = value.abs().toByteArray(); + if (input.length < 3) { + return value.toString(8); + } + + StringBuilder sb = new StringBuilder(input.length * 3); + + // working backwards, three bytes at a time + int threebytes; + int trip1, trip2, trip3; // most, middle, and least significant bytes in the triplet + for (int i = input.length - 1; i >= 0; i -= 3) { + trip3 = input[i] & 0xFF; + trip2 = ((i - 1) >= 0) ? (input[i - 1] & 0xFF) : 0x00; + trip1 = ((i - 2) >= 0) ? (input[i - 2] & 0xFF) : 0x00; + threebytes = trip3 | (trip2 << 8) | (trip1 << 16); + + // convert the three-byte value into an eight-character octal string + for (int j = 0; j < 8; j++) { + sb.append(LOOKUP.charAt((threebytes >> (j * 3)) & 0x000007)); + } + } + + String result = sb.reverse().toString().replaceFirst("^0+(?!%)", ""); + return signum < 0 ? "-" + result : result; + } + + /** + * A more efficient algorithm for generating a binary representation of a byte array. + * {@link BigInteger#toString(int)} is too slow because it generalizes to any radix and, + * consequently, is implemented using expensive mathematical operations. + * + * @param value the value to generate a binary string from + * @return the binary representation of value, with "-" sign prepended if necessary + */ + static final String toBinString(BigInteger value) { + int signum = value.signum(); + + // obvious shortcut + if (signum == 0) { + return "0"; + } + + // we want to work in absolute numeric value (negative sign is added afterward) + byte[] input = value.abs().toByteArray(); + StringBuilder sb = new StringBuilder(value.bitCount()); + + int b; + for (int i = 0; i < input.length; i++) { + b = input[i] & 0xFF; + for (int bit = 7; bit >= 0; bit--) { + sb.append(((b >> bit) & 0x1) > 0 ? "1" : "0"); + } + } + + // before returning the char array as string, remove leading zeroes, but not the last one + String result = sb.toString().replaceFirst("^0+(?!$)", ""); + return signum < 0 ? "-" + result : result; } @Override diff --git a/src/org/python/core/PyLong.java b/src/org/python/core/PyLong.java --- a/src/org/python/core/PyLong.java +++ b/src/org/python/core/PyLong.java @@ -977,7 +977,7 @@ @ExposedMethod(doc = BuiltinDocs.long___oct___doc) final PyString long___oct__() { - String s = getValue().toString(8); + String s = PyInteger.toOctString(getValue()); if (s.startsWith("-")) { return new PyString("-0" + s.substring(1, s.length()) + "L"); } else if (s.startsWith("0")) { @@ -994,7 +994,7 @@ @ExposedMethod(doc = BuiltinDocs.long___hex___doc) final PyString long___hex__() { - String s = getValue().toString(16); + String s = PyInteger.toHexString(getValue()); if (s.startsWith("-")) { return new PyString("-0x" + s.substring(1, s.length()) + "L"); } else { -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 22 18:37:40 2013 From: jython-checkins at python.org (jeff.allen) Date: Sun, 22 Dec 2013 18:37:40 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython=3A_=5BTrivial=5D_Remove_skip_f?= =?utf-8?q?rom_passing_test=5Fbuiltin=2E=2Etest=5Finput=5Fand=5Fraw=5Finpu?= =?utf-8?q?t=2E?= Message-ID: <3dnW9c4cNYz7Ljc@mail.python.org> http://hg.python.org/jython/rev/05192fad65cb changeset: 7171:05192fad65cb user: Jeff Allen date: Sun Dec 22 15:37:18 2013 +0000 summary: [Trivial] Remove skip from passing test_builtin..test_input_and_raw_input. Underlying bug was fixed as part of console changes at issue #2082. files: Lib/test/test_builtin.py | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1141,7 +1141,6 @@ self.assertRaises(TypeError, range, 1e100, 1e100, 1) self.assertRaises(TypeError, range, 1e100, 1e100, 1e100) - @unittest.skipIf(is_jython, "FIXME #1861: not working in Jython") def test_input_and_raw_input(self): self.write_testfile() fp = open(TESTFN, 'r') -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sun Dec 22 18:37:42 2013 From: jython-checkins at python.org (jeff.allen) Date: Sun, 22 Dec 2013 18:37:42 +0100 (CET) Subject: [Jython-checkins] =?utf-8?q?jython_=28merge_default_-=3E_default?= =?utf-8?q?=29=3A_Merge?= Message-ID: <3dnW9f2QNTz7Ljc@mail.python.org> http://hg.python.org/jython/rev/301a53702f95 changeset: 7172:301a53702f95 parent: 7168:0b2e1c866114 parent: 7171:05192fad65cb user: Jeff Allen date: Sun Dec 22 17:00:30 2013 +0000 summary: Merge files: Lib/test/test_builtin.py | 1 - Lib/test/test_os_jy.py | 34 +- Lib/test/test_str_jy.py | 5 + Lib/test/test_types.py | 48 +- NEWS | 13 +- src/org/python/core/PyInteger.java | 165 +++++++++- src/org/python/core/PyLong.java | 4 +- src/org/python/modules/posix/PosixModule.java | 15 +- 8 files changed, 222 insertions(+), 63 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1141,7 +1141,6 @@ self.assertRaises(TypeError, range, 1e100, 1e100, 1) self.assertRaises(TypeError, range, 1e100, 1e100, 1e100) - @unittest.skipIf(is_jython, "FIXME #1861: not working in Jython") def test_input_and_raw_input(self): self.write_testfile() fp = open(TESTFN, 'r') diff --git a/Lib/test/test_os_jy.py b/Lib/test/test_os_jy.py --- a/Lib/test/test_os_jy.py +++ b/Lib/test/test_os_jy.py @@ -6,7 +6,7 @@ import unittest from test import test_support -class OSTestCase(unittest.TestCase): +class OSFileTestCase(unittest.TestCase): def setUp(self): open(test_support.TESTFN, 'w').close() @@ -50,9 +50,37 @@ self.assertTrue(False) +class OSDirTestCase(unittest.TestCase): + + def setUp(self): + self.base = test_support.TESTFN + self.path = os.path.join(self.base, 'dir1', 'dir2', 'dir3') + os.makedirs(self.path) + + def test_rmdir(self): + # Remove end directory + os.rmdir(self.path) + # Fail to remove a chain of directories + self.assertRaises(OSError, os.rmdir, self.base) + + def test_issue2083(self): + # Should fail to remove/unlink directory + self.assertRaises(OSError, os.remove, self.path) + self.assertRaises(OSError, os.unlink, self.path) + + def tearDown(self): + # Some dirs may have been deleted. Find the longest that exists. + p = self.path + while not os.path.exists(p) and p != self.base: + p = os.path.dirname(p) + os.removedirs(p) + + def test_main(): - test_support.run_unittest(OSTestCase) - + test_support.run_unittest( + OSFileTestCase, + OSDirTestCase, + ) if __name__ == '__main__': test_main() diff --git a/Lib/test/test_str_jy.py b/Lib/test/test_str_jy.py --- a/Lib/test/test_str_jy.py +++ b/Lib/test/test_str_jy.py @@ -53,6 +53,11 @@ self.assertEquals("%+f" % -5, "-5.000000") self.assertEquals("%+f" % 5, "+5.000000") + def test_format_issue2075(self): + self.assertEquals("%#018x" % 14, "0x000000000000000e") + self.assertEquals("{:#018x}".format(14), "0x000000000000000e") + self.assertEquals("{:+#018X}".format(14), "+0X00000000000000E") + self.assertEquals("{:#018X}".format(-14), "-0X00000000000000E") def test_argument_count_exception(self): "exception thrown when too many or too few arguments for format string" diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -365,54 +365,46 @@ test(0, "#b", '0b0') test(0, "-#b", '0b0') test(1, "-#b", '0b1') - #FIXME: not working. - #test(-1, "-#b", '-0b1') - #test(-1, "-#5b", ' -0b1') + test(-1, "-#b", '-0b1') + test(-1, "-#5b", ' -0b1') test(1, "+#5b", ' +0b1') test(100, "+#b", '+0b1100100') - #FIXME: not working. - #test(100, "#012b", '0b0001100100') - #test(-100, "#012b", '-0b001100100') + test(100, "#012b", '0b0001100100') + test(-100, "#012b", '-0b001100100') test(0, "#o", '0o0') test(0, "-#o", '0o0') test(1, "-#o", '0o1') - #FIXME: not working. - #test(-1, "-#o", '-0o1') - #test(-1, "-#5o", ' -0o1') + test(-1, "-#o", '-0o1') + test(-1, "-#5o", ' -0o1') test(1, "+#5o", ' +0o1') test(100, "+#o", '+0o144') - #FIXME: not working. - #test(100, "#012o", '0o0000000144') - #test(-100, "#012o", '-0o000000144') + test(100, "#012o", '0o0000000144') + test(-100, "#012o", '-0o000000144') test(0, "#x", '0x0') test(0, "-#x", '0x0') test(1, "-#x", '0x1') - #FIXME: not working. - #test(-1, "-#x", '-0x1') - #test(-1, "-#5x", ' -0x1') + test(-1, "-#x", '-0x1') + test(-1, "-#5x", ' -0x1') test(1, "+#5x", ' +0x1') test(100, "+#x", '+0x64') - #FIXME: not working. - #test(100, "#012x", '0x0000000064') - #test(-100, "#012x", '-0x000000064') - #test(123456, "#012x", '0x000001e240') - #test(-123456, "#012x", '-0x00001e240') + test(100, "#012x", '0x0000000064') + test(-100, "#012x", '-0x000000064') + test(123456, "#012x", '0x000001e240') + test(-123456, "#012x", '-0x00001e240') test(0, "#X", '0X0') test(0, "-#X", '0X0') test(1, "-#X", '0X1') - #FIXME: not working. - #test(-1, "-#X", '-0X1') - #test(-1, "-#5X", ' -0X1') + test(-1, "-#X", '-0X1') + test(-1, "-#5X", ' -0X1') test(1, "+#5X", ' +0X1') test(100, "+#X", '+0X64') - #FIXME: not working. - #test(100, "#012X", '0X0000000064') - #test(-100, "#012X", '-0X000000064') - #test(123456, "#012X", '0X000001E240') - #test(-123456, "#012X", '-0X00001E240') + test(100, "#012X", '0X0000000064') + test(-100, "#012X", '-0X000000064') + test(123456, "#012X", '0X000001E240') + test(-123456, "#012X", '-0X00001E240') # issue 5782, commas with no specifier type #FIXME: not working. diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -2,19 +2,22 @@ Jython 2.7b2 Bugs Fixed - - [ 1862 ] cStringIO does not support arrays as arguments - - [ 2027 ] Discrepancy in bin(-num) output - - [ 2005 ] threading.Event object's wait([timeout]) function returns null instead of True/False. - - [ 1926 ] Adjust MutableSet.pop test so we do not need to skip it - - [ 2020 ] str.translate should delete characters in the second arg when table is None - [ 1753 ] zlib doesn't call end() on compress and decompress - [ 1860 ] test failures in test_array.py + - [ 1862 ] cStringIO does not support arrays as arguments + - [ 1926 ] Adjust MutableSet.pop test so we do not need to skip it - [ 1964 ] time.strptime() does not support %f in format + - [ 2005 ] threading.Event object's wait([timeout]) function returns null instead of True/False. + - [ 2013 ] %x hex formatting takes O(N^2) time. + - [ 2020 ] str.translate should delete characters in the second arg when table is None + - [ 2027 ] Discrepancy in bin(-num) output - [ 2033 ] test_strptime fails: test_mar1_comes_after_feb29_even_when_omitting_the_year - [ 2046 ] sys.stdin.readline() hangs when used interactively (JLine, Windows) - [ 2060 ] Thread ident missing - [ 2071 ] datetime strftime %f does not work + - [ 2075 ] Incorrect padding for hex format strings - [ 2082 ] Unexpected (Pdb) prompt during regression tests + - [ 2083 ] os.unlink() can delete directories Jython 2.7b1 Bugs Fixed diff --git a/src/org/python/core/PyInteger.java b/src/org/python/core/PyInteger.java --- a/src/org/python/core/PyInteger.java +++ b/src/org/python/core/PyInteger.java @@ -38,6 +38,8 @@ @Deprecated public static final BigInteger maxInt = MAX_INT; + private static final String LOOKUP = "0123456789abcdef"; + private final int value; public PyInteger(PyType subType, int v) { @@ -1047,6 +1049,7 @@ if (spec.precision != -1) { throw new IllegalArgumentException("Precision not allowed in integer format specifier"); } + int sign; if (value instanceof Integer) { int intValue = (Integer) value; @@ -1054,7 +1057,11 @@ } else { sign = ((BigInteger) value).signum(); } + String strValue; + String strPrefix = ""; + String strSign = ""; + if (spec.type == 'c') { if (spec.sign != '\0') { throw new IllegalArgumentException("Sign not allowed with integer format " @@ -1090,13 +1097,26 @@ format.setGroupingUsed(true); strValue = format.format(value); } else if (value instanceof BigInteger) { - strValue = ((BigInteger) value).toString(radix); + switch (radix) { + case 2: + strValue = toBinString((BigInteger) value); + break; + case 8: + strValue = toOctString((BigInteger) value); + break; + case 16: + strValue = toHexString((BigInteger) value); + break; + default: + // General case (v.slow in known implementations up to Java 7). + strValue = ((BigInteger) value).toString(radix); + break; + } } else { strValue = Integer.toString((Integer) value, radix); } if (spec.alternate) { - String strPrefix = ""; switch (radix) { case 2: strPrefix = "0b"; @@ -1109,32 +1129,145 @@ break; } - if (strValue.startsWith("-")) { - //assert (sign < 0); - if (!strPrefix.equals("")) - strValue = "-" + strPrefix + strValue.substring(1, strValue.length()); - } else { - strValue = strPrefix + strValue; + if (sign < 0) { + assert (strValue.startsWith("-")); + strSign = "-"; + strValue = strValue.substring(1); } } if (spec.type == 'X') { + strPrefix = strPrefix.toUpperCase(); strValue = strValue.toUpperCase(); } if (sign >= 0) { - if (spec.sign == '+') { - strValue = "+" + strValue; - } else if (spec.sign == ' ') { - strValue = " " + strValue; + switch (spec.sign) { + case '+': + case ' ': + strSign = Character.toString(spec.sign); + break; } } } - if (spec.align == '=' && (sign < 0 || spec.sign == '+' || spec.sign == ' ')) { - char signChar = strValue.charAt(0); - return signChar + spec.pad(strValue.substring(1), '>', 1); + + if (spec.align == '=' && (spec.sign == '-' || spec.sign == '+' || spec.sign == ' ')) { + assert (strSign.length() == 1); + return strSign + strPrefix + spec.pad(strValue, '>', 1 + strPrefix.length()); } - return spec.pad(strValue, '>', 0); + + if (spec.fill_char == 0) { + return spec.pad(strSign + strPrefix + strValue, '>', 0); + } + + return strSign + strPrefix + spec.pad(strValue, '>', strSign.length() + strPrefix.length()); + } + + /** + * A more efficient algorithm for generating a hexadecimal representation of a byte array. + * {@link BigInteger#toString(int)} is too slow because it generalizes to any radix and, + * consequently, is implemented using expensive mathematical operations. + * + * @param value the value to generate a hexadecimal string from + * @return the hexadecimal representation of value, with "-" sign prepended if necessary + */ + static final String toHexString(BigInteger value) { + int signum = value.signum(); + + // obvious shortcut + if (signum == 0) { + return "0"; + } + + // we want to work in absolute numeric value (negative sign is added afterward) + byte[] input = value.abs().toByteArray(); + StringBuilder sb = new StringBuilder(input.length * 2); + + int b; + for (int i = 0; i < input.length; i++) { + b = input[i] & 0xFF; + sb.append(LOOKUP.charAt(b >> 4)); + sb.append(LOOKUP.charAt(b & 0x0F)); + } + + // before returning the char array as string, remove leading zeroes, but not the last one + String result = sb.toString().replaceFirst("^0+(?!$)", ""); + return signum < 0 ? "-" + result : result; + } + + /** + * A more efficient algorithm for generating an octal representation of a byte array. + * {@link BigInteger#toString(int)} is too slow because it generalizes to any radix and, + * consequently, is implemented using expensive mathematical operations. + * + * @param value the value to generate an octal string from + * @return the octal representation of value, with "-" sign prepended if necessary + */ + static final String toOctString(BigInteger value) { + int signum = value.signum(); + + // obvious shortcut + if (signum == 0) { + return "0"; + } + + byte[] input = value.abs().toByteArray(); + if (input.length < 3) { + return value.toString(8); + } + + StringBuilder sb = new StringBuilder(input.length * 3); + + // working backwards, three bytes at a time + int threebytes; + int trip1, trip2, trip3; // most, middle, and least significant bytes in the triplet + for (int i = input.length - 1; i >= 0; i -= 3) { + trip3 = input[i] & 0xFF; + trip2 = ((i - 1) >= 0) ? (input[i - 1] & 0xFF) : 0x00; + trip1 = ((i - 2) >= 0) ? (input[i - 2] & 0xFF) : 0x00; + threebytes = trip3 | (trip2 << 8) | (trip1 << 16); + + // convert the three-byte value into an eight-character octal string + for (int j = 0; j < 8; j++) { + sb.append(LOOKUP.charAt((threebytes >> (j * 3)) & 0x000007)); + } + } + + String result = sb.reverse().toString().replaceFirst("^0+(?!%)", ""); + return signum < 0 ? "-" + result : result; + } + + /** + * A more efficient algorithm for generating a binary representation of a byte array. + * {@link BigInteger#toString(int)} is too slow because it generalizes to any radix and, + * consequently, is implemented using expensive mathematical operations. + * + * @param value the value to generate a binary string from + * @return the binary representation of value, with "-" sign prepended if necessary + */ + static final String toBinString(BigInteger value) { + int signum = value.signum(); + + // obvious shortcut + if (signum == 0) { + return "0"; + } + + // we want to work in absolute numeric value (negative sign is added afterward) + byte[] input = value.abs().toByteArray(); + StringBuilder sb = new StringBuilder(value.bitCount()); + + int b; + for (int i = 0; i < input.length; i++) { + b = input[i] & 0xFF; + for (int bit = 7; bit >= 0; bit--) { + sb.append(((b >> bit) & 0x1) > 0 ? "1" : "0"); + } + } + + // before returning the char array as string, remove leading zeroes, but not the last one + String result = sb.toString().replaceFirst("^0+(?!$)", ""); + return signum < 0 ? "-" + result : result; } @Override diff --git a/src/org/python/core/PyLong.java b/src/org/python/core/PyLong.java --- a/src/org/python/core/PyLong.java +++ b/src/org/python/core/PyLong.java @@ -977,7 +977,7 @@ @ExposedMethod(doc = BuiltinDocs.long___oct___doc) final PyString long___oct__() { - String s = getValue().toString(8); + String s = PyInteger.toOctString(getValue()); if (s.startsWith("-")) { return new PyString("-0" + s.substring(1, s.length()) + "L"); } else if (s.startsWith("0")) { @@ -994,7 +994,7 @@ @ExposedMethod(doc = BuiltinDocs.long___hex___doc) final PyString long___hex__() { - String s = getValue().toString(16); + String s = PyInteger.toHexString(getValue()); if (s.startsWith("-")) { return new PyString("-0x" + s.substring(1, s.length()) + "L"); } else { diff --git a/src/org/python/modules/posix/PosixModule.java b/src/org/python/modules/posix/PosixModule.java --- a/src/org/python/modules/posix/PosixModule.java +++ b/src/org/python/modules/posix/PosixModule.java @@ -722,19 +722,18 @@ return posix.umask(mask); } - public static PyString __doc__unlink = new PyString( - "unlink(path)\n\n" + - "Remove a file (same as remove(path))."); + public static PyString __doc__unlink = new PyString("unlink(path)\n\n" + + "Remove a file (same as remove(path))."); + public static void unlink(PyObject path) { String absolutePath = absolutePath(path); File file = new File(absolutePath); - if (!file.delete()) { + if (file.isDirectory()) { + throw Py.OSError(Errno.EISDIR, path); + } else if (!file.delete()) { // Something went wrong, does stat raise an error? posix.stat(absolutePath); - // It exists, is it a directory, or do we not have permissions? - if (file.isDirectory()) { - throw Py.OSError(Errno.EISDIR, path); - } + // It exists, do we not have permissions? if (!file.canWrite()) { throw Py.OSError(Errno.EPERM, path); } -- Repository URL: http://hg.python.org/jython