#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.
This commit is contained in:
Andrew M. Kuchling 2008-02-23 16:23:05 +00:00
parent 8328bbc57d
commit 085f75a851
4 changed files with 74 additions and 85 deletions

View File

@ -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.

View File

@ -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
.. ======================================================================

View File

@ -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)

View File

@ -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("</dateTime.iso8601></value>\n")
dispatch[datetime.datetime] = dump_datetime
def dump_date(self, value, write):
write("<value><dateTime.iso8601>")
write(value.strftime("%Y%m%dT00:00:00"))
write("</dateTime.iso8601></value>\n")
dispatch[datetime.date] = dump_date
def dump_time(self, value, write):
write("<value><dateTime.iso8601>")
write(datetime.datetime.now().date().strftime("%Y%m%dT"))
write(value.strftime("%H:%M:%S"))
write("</dateTime.iso8601></value>\n")
dispatch[datetime.time] = dump_time
def dump_instance(self, value, write):
# check for special wrappers
if value.__class__ in WRAPPERS: