Kaydet (Commit) 085f75a8 authored tarafından Andrew M. Kuchling's avatar Andrew M. Kuchling

#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.
üst 8328bbc5
...@@ -34,10 +34,7 @@ between conformable Python objects and XML on the wire. ...@@ -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 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 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. be presented as :class:`datetime.datetime` objects; this is false by default.
:class:`datetime.datetime`, :class:`datetime.date` and :class:`datetime.time` :class:`datetime.datetime` objects may be passed to calls.
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.
Both the HTTP and HTTPS transports support the URL syntax extension for HTTP Both the HTTP and HTTPS transports support the URL syntax extension for HTTP
Basic Authentication: ``http://user:pass@host:port/path``. The ``user:pass`` Basic Authentication: ``http://user:pass@host:port/path``. The ``user:pass``
...@@ -81,9 +78,7 @@ between conformable Python objects and XML on the wire. ...@@ -81,9 +78,7 @@ between conformable Python objects and XML on the wire.
+---------------------------------+---------------------------------------------+ +---------------------------------+---------------------------------------------+
| :const:`dates` | in seconds since the epoch (pass in an | | :const:`dates` | in seconds since the epoch (pass in an |
| | instance of the :class:`DateTime` class) or | | | instance of the :class:`DateTime` class) or |
| | a :class:`datetime.datetime`, | | | a :class:`datetime.datetime` instance. |
| | :class:`datetime.date` or |
| | :class:`datetime.time` instance |
+---------------------------------+---------------------------------------------+ +---------------------------------+---------------------------------------------+
| :const:`binary data` | pass in an instance of the :class:`Binary` | | :const:`binary data` | pass in an instance of the :class:`Binary` |
| | wrapper class | | | wrapper class |
...@@ -221,10 +216,10 @@ The client code for the preceding server:: ...@@ -221,10 +216,10 @@ The client code for the preceding server::
DateTime Objects DateTime Objects
---------------- ----------------
This class may be initialized with seconds since the epoch, a time tuple, an ISO This class may be initialized with seconds since the epoch, a time
8601 time/date string, or a :class:`datetime.datetime`, :class:`datetime.date` tuple, an ISO 8601 time/date string, or a :class:`datetime.datetime`
or :class:`datetime.time` instance. It has the following methods, supported instance. It has the following methods, supported mainly for internal
mainly for internal use by the marshalling/unmarshalling code: use by the marshalling/unmarshalling code:
.. method:: DateTime.decode(string) .. method:: DateTime.decode(string)
...@@ -507,10 +502,7 @@ Convenience Functions ...@@ -507,10 +502,7 @@ Convenience Functions
``None`` if no method name is present in the packet. If the XML-RPC packet ``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. 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 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 :class:`datetime.datetime` objects; this is false by default.
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.
.. versionchanged:: 2.5 .. versionchanged:: 2.5
The *use_datetime* flag was added. The *use_datetime* flag was added.
......
...@@ -1511,6 +1511,15 @@ code: ...@@ -1511,6 +1511,15 @@ code:
.. Issue 1706815 .. 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
.. ====================================================================== .. ======================================================================
......
...@@ -33,10 +33,6 @@ alist = [{'astring': 'foo@bar.baz.spam', ...@@ -33,10 +33,6 @@ alist = [{'astring': 'foo@bar.baz.spam',
(2005, 02, 10, 11, 41, 23, 0, 1, -1)), (2005, 02, 10, 11, 41, 23, 0, 1, -1)),
'datetime3': xmlrpclib.DateTime( 'datetime3': xmlrpclib.DateTime(
datetime.datetime(2005, 02, 10, 11, 41, 23)), 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): class XMLRPCTestCase(unittest.TestCase):
...@@ -59,34 +55,14 @@ class XMLRPCTestCase(unittest.TestCase): ...@@ -59,34 +55,14 @@ class XMLRPCTestCase(unittest.TestCase):
(newdt,), m = xmlrpclib.loads(s, use_datetime=0) (newdt,), m = xmlrpclib.loads(s, use_datetime=0)
self.assertEquals(newdt, xmlrpclib.DateTime('20050210T11:41:23')) self.assertEquals(newdt, xmlrpclib.DateTime('20050210T11:41:23'))
def test_dump_bare_date(self): def test_cmp_datetime_DateTime(self):
# This checks that an unwrapped datetime.date object can be handled now = datetime.datetime.now()
# by the marshalling code. This can't be done via test_dump_load() dt = xmlrpclib.DateTime(now.timetuple())
# since the unmarshaller produces a datetime object self.assert_(dt == now)
d = datetime.datetime(2005, 02, 10, 11, 41, 23).date() self.assert_(now == dt)
s = xmlrpclib.dumps((d,)) then = now + datetime.timedelta(seconds=4)
(newd,), m = xmlrpclib.loads(s, use_datetime=1) self.assert_(then >= dt)
self.assertEquals(newd.date(), d) self.assert_(dt < then)
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_bug_1164912 (self): def test_bug_1164912 (self):
d = xmlrpclib.DateTime() d = xmlrpclib.DateTime()
...@@ -242,21 +218,6 @@ class DateTimeTestCase(unittest.TestCase): ...@@ -242,21 +218,6 @@ class DateTimeTestCase(unittest.TestCase):
t = xmlrpclib.DateTime(d) t = xmlrpclib.DateTime(d)
self.assertEqual(str(t), '20070102T03:04:05') 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): def test_repr(self):
d = datetime.datetime(2007,1,2,3,4,5) d = datetime.datetime(2007,1,2,3,4,5)
t = xmlrpclib.DateTime(d) t = xmlrpclib.DateTime(d)
......
...@@ -357,13 +357,6 @@ class DateTime: ...@@ -357,13 +357,6 @@ class DateTime:
if datetime and isinstance(value, datetime.datetime): if datetime and isinstance(value, datetime.datetime):
self.value = value.strftime("%Y%m%dT%H:%M:%S") self.value = value.strftime("%Y%m%dT%H:%M:%S")
return 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 not isinstance(value, (TupleType, time.struct_time)):
if value == 0: if value == 0:
value = time.time() value = time.time()
...@@ -371,10 +364,57 @@ class DateTime: ...@@ -371,10 +364,57 @@ class DateTime:
value = time.strftime("%Y%m%dT%H:%M:%S", value) value = time.strftime("%Y%m%dT%H:%M:%S", value)
self.value = value self.value = value
def __cmp__(self, other): def make_comparable(self, other):
if isinstance(other, DateTime): if isinstance(other, DateTime):
other = other.value s = self.value
return cmp(self.value, other) 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. # Get date/time value.
...@@ -736,19 +776,6 @@ class Marshaller: ...@@ -736,19 +776,6 @@ class Marshaller:
write("</dateTime.iso8601></value>\n") write("</dateTime.iso8601></value>\n")
dispatch[datetime.datetime] = dump_datetime 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): def dump_instance(self, value, write):
# check for special wrappers # check for special wrappers
if value.__class__ in WRAPPERS: if value.__class__ in WRAPPERS:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment