From 085f75a8510000b38da641d5183a8a701c69d6d6 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sat, 23 Feb 2008 16:23:05 +0000 Subject: [PATCH] #1330538: Improve comparison of xmlrpclib.DateTime and datetime instances. Remove automatic handling of datetime.date and datetime.time. This breaks backward compatibility, but python-dev discussion was strongly against this automatic conversion; see the bug for a link. --- Doc/library/xmlrpclib.rst | 22 ++++-------- Doc/whatsnew/2.6.rst | 9 +++++ Lib/test/test_xmlrpc.py | 55 +++++------------------------ Lib/xmlrpclib.py | 73 +++++++++++++++++++++++++++------------ 4 files changed, 74 insertions(+), 85 deletions(-) diff --git a/Doc/library/xmlrpclib.rst b/Doc/library/xmlrpclib.rst index 1c50b799ab8..9dd6c0e3b60 100644 --- a/Doc/library/xmlrpclib.rst +++ b/Doc/library/xmlrpclib.rst @@ -34,10 +34,7 @@ between conformable Python objects and XML on the wire. all clients and servers; see http://ontosys.com/xml-rpc/extensions.php for a description. The *use_datetime* flag can be used to cause date/time values to be presented as :class:`datetime.datetime` objects; this is false by default. - :class:`datetime.datetime`, :class:`datetime.date` and :class:`datetime.time` - objects may be passed to calls. :class:`datetime.date` objects are converted - with a time of "00:00:00". :class:`datetime.time` objects are converted using - today's date. + :class:`datetime.datetime` objects may be passed to calls. Both the HTTP and HTTPS transports support the URL syntax extension for HTTP Basic Authentication: ``http://user:pass@host:port/path``. The ``user:pass`` @@ -81,9 +78,7 @@ between conformable Python objects and XML on the wire. +---------------------------------+---------------------------------------------+ | :const:`dates` | in seconds since the epoch (pass in an | | | instance of the :class:`DateTime` class) or | - | | a :class:`datetime.datetime`, | - | | :class:`datetime.date` or | - | | :class:`datetime.time` instance | + | | a :class:`datetime.datetime` instance. | +---------------------------------+---------------------------------------------+ | :const:`binary data` | pass in an instance of the :class:`Binary` | | | wrapper class | @@ -221,10 +216,10 @@ The client code for the preceding server:: DateTime Objects ---------------- -This class may be initialized with seconds since the epoch, a time tuple, an ISO -8601 time/date string, or a :class:`datetime.datetime`, :class:`datetime.date` -or :class:`datetime.time` instance. It has the following methods, supported -mainly for internal use by the marshalling/unmarshalling code: +This class may be initialized with seconds since the epoch, a time +tuple, an ISO 8601 time/date string, or a :class:`datetime.datetime` +instance. It has the following methods, supported mainly for internal +use by the marshalling/unmarshalling code: .. method:: DateTime.decode(string) @@ -507,10 +502,7 @@ Convenience Functions ``None`` if no method name is present in the packet. If the XML-RPC packet represents a fault condition, this function will raise a :exc:`Fault` exception. The *use_datetime* flag can be used to cause date/time values to be presented as - :class:`datetime.datetime` objects; this is false by default. Note that even if - you call an XML-RPC method with :class:`datetime.date` or :class:`datetime.time` - objects, they are converted to :class:`DateTime` objects internally, so only - :class:`datetime.datetime` objects will be returned. + :class:`datetime.datetime` objects; this is false by default. .. versionchanged:: 2.5 The *use_datetime* flag was added. diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index 3f89c03fe8c..a0ec071e281 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -1511,6 +1511,15 @@ code: .. Issue 1706815 +* The :mod:`xmlrpclib` module no longer automatically converts + :class:`datetime.date` and :class:`datetime.time` to the + :class:`xmlrpclib.DateTime` type; the conversion semantics were + not necessarily correct for all applications. Code using + :mod:`xmlrpclib` should convert :class:`date` and :class:`time` + instances. + + .. Issue 1330538 + .. ====================================================================== diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 4ed48cfa55f..b9e7692af57 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -33,10 +33,6 @@ alist = [{'astring': 'foo@bar.baz.spam', (2005, 02, 10, 11, 41, 23, 0, 1, -1)), 'datetime3': xmlrpclib.DateTime( datetime.datetime(2005, 02, 10, 11, 41, 23)), - 'datetime4': xmlrpclib.DateTime( - datetime.date(2005, 02, 10)), - 'datetime5': xmlrpclib.DateTime( - datetime.time(11, 41, 23)), }] class XMLRPCTestCase(unittest.TestCase): @@ -59,34 +55,14 @@ class XMLRPCTestCase(unittest.TestCase): (newdt,), m = xmlrpclib.loads(s, use_datetime=0) self.assertEquals(newdt, xmlrpclib.DateTime('20050210T11:41:23')) - def test_dump_bare_date(self): - # This checks that an unwrapped datetime.date object can be handled - # by the marshalling code. This can't be done via test_dump_load() - # since the unmarshaller produces a datetime object - d = datetime.datetime(2005, 02, 10, 11, 41, 23).date() - s = xmlrpclib.dumps((d,)) - (newd,), m = xmlrpclib.loads(s, use_datetime=1) - self.assertEquals(newd.date(), d) - self.assertEquals(newd.time(), datetime.time(0, 0, 0)) - self.assertEquals(m, None) - - (newdt,), m = xmlrpclib.loads(s, use_datetime=0) - self.assertEquals(newdt, xmlrpclib.DateTime('20050210T00:00:00')) - - def test_dump_bare_time(self): - # This checks that an unwrapped datetime.time object can be handled - # by the marshalling code. This can't be done via test_dump_load() - # since the unmarshaller produces a datetime object - t = datetime.datetime(2005, 02, 10, 11, 41, 23).time() - s = xmlrpclib.dumps((t,)) - (newt,), m = xmlrpclib.loads(s, use_datetime=1) - today = datetime.datetime.now().date().strftime("%Y%m%d") - self.assertEquals(newt.time(), t) - self.assertEquals(newt.date(), datetime.datetime.now().date()) - self.assertEquals(m, None) - - (newdt,), m = xmlrpclib.loads(s, use_datetime=0) - self.assertEquals(newdt, xmlrpclib.DateTime('%sT11:41:23'%today)) + def test_cmp_datetime_DateTime(self): + now = datetime.datetime.now() + dt = xmlrpclib.DateTime(now.timetuple()) + self.assert_(dt == now) + self.assert_(now == dt) + then = now + datetime.timedelta(seconds=4) + self.assert_(then >= dt) + self.assert_(dt < then) def test_bug_1164912 (self): d = xmlrpclib.DateTime() @@ -242,21 +218,6 @@ class DateTimeTestCase(unittest.TestCase): t = xmlrpclib.DateTime(d) self.assertEqual(str(t), '20070102T03:04:05') - def test_datetime_date(self): - d = datetime.date(2007,9,8) - t = xmlrpclib.DateTime(d) - self.assertEqual(str(t), '20070908T00:00:00') - - def test_datetime_time(self): - d = datetime.time(13,17,19) - # allow for date rollover by checking today's or tomorrow's dates - dd1 = datetime.datetime.now().date() - dd2 = dd1 + datetime.timedelta(days=1) - vals = (dd1.strftime('%Y%m%dT13:17:19'), - dd2.strftime('%Y%m%dT13:17:19')) - t = xmlrpclib.DateTime(d) - self.assertEqual(str(t) in vals, True) - def test_repr(self): d = datetime.datetime(2007,1,2,3,4,5) t = xmlrpclib.DateTime(d) diff --git a/Lib/xmlrpclib.py b/Lib/xmlrpclib.py index 3864dadc0b2..d99f5d7600e 100644 --- a/Lib/xmlrpclib.py +++ b/Lib/xmlrpclib.py @@ -357,13 +357,6 @@ class DateTime: if datetime and isinstance(value, datetime.datetime): self.value = value.strftime("%Y%m%dT%H:%M:%S") return - if datetime and isinstance(value, datetime.date): - self.value = value.strftime("%Y%m%dT%H:%M:%S") - return - if datetime and isinstance(value, datetime.time): - today = datetime.datetime.now().strftime("%Y%m%d") - self.value = value.strftime(today+"T%H:%M:%S") - return if not isinstance(value, (TupleType, time.struct_time)): if value == 0: value = time.time() @@ -371,10 +364,57 @@ class DateTime: value = time.strftime("%Y%m%dT%H:%M:%S", value) self.value = value - def __cmp__(self, other): + def make_comparable(self, other): if isinstance(other, DateTime): - other = other.value - return cmp(self.value, other) + s = self.value + o = other.value + elif datetime and isinstance(other, datetime.datetime): + s = self.value + o = other.strftime("%Y%m%dT%H:%M:%S") + elif isinstance(other, (str, unicode)): + s = self.value + o = other + elif hasattr(other, "timetuple"): + s = self.timetuple() + o = other.timetuple() + else: + otype = (hasattr(other, "__class__") + and other.__class__.__name__ + or type(other)) + raise TypeError("Can't compare %s and %s" % + (self.__class__.__name__, otype)) + return s, o + + def __lt__(self, other): + s, o = self.make_comparable(other) + return s < o + + def __le__(self, other): + s, o = self.make_comparable(other) + return s <= o + + def __gt__(self, other): + s, o = self.make_comparable(other) + return s > o + + def __ge__(self, other): + s, o = self.make_comparable(other) + return s >= o + + def __eq__(self, other): + s, o = self.make_comparable(other) + return s == o + + def __ne__(self, other): + s, o = self.make_comparable(other) + return s != o + + def timetuple(self): + return time.strptime(self.value, "%Y%m%dT%H:%M:%S") + + def __cmp__(self, other): + s, o = self.make_comparable(other) + return cmp(s, o) ## # Get date/time value. @@ -736,19 +776,6 @@ class Marshaller: write("\n") dispatch[datetime.datetime] = dump_datetime - def dump_date(self, value, write): - write("") - write(value.strftime("%Y%m%dT00:00:00")) - write("\n") - dispatch[datetime.date] = dump_date - - def dump_time(self, value, write): - write("") - write(datetime.datetime.now().date().strftime("%Y%m%dT")) - write(value.strftime("%H:%M:%S")) - write("\n") - dispatch[datetime.time] = dump_time - def dump_instance(self, value, write): # check for special wrappers if value.__class__ in WRAPPERS: