Commits

Jason R. Coombs  committed 638d799 Merge

Merge fix from 0.6.3dev

  • Participants
  • Parent commits e7f28ca, d21ca62

Comments (0)

Files changed (4)

 support that interface through all versions of irc 1.x, so if you've made
 these changes, you can safely depend on `irc >= 0.7, <2.0dev`.
 
+0.6.3
+=====
+
+* Fixed failing test where DelayedCommands weren't being sorted properly.
+  DelayedCommand a now subclass of the DateTime object, where the command's
+  due time is the datetime. Fixed issue #3518508.
+
 0.6.2
 =====
 

File irc/client.py

 import time
 import types
 import ssl as ssl_mod
+import datetime
 
 try:
     import pkg_resources
     def _schedule_command(self, command):
         bisect.insort(self.delayed_commands, command)
         if self.fn_to_add_timeout:
-            self.fn_to_add_timeout(command.delay)
+            self.fn_to_add_timeout(total_seconds(command.delay))
 
     def dcc(self, dcctype="chat"):
         """Creates and returns a DCCConnection object.
         if self.fn_to_remove_socket:
             self.fn_to_remove_socket(connection._get_socket())
 
-class DelayedCommand(object):
+class DelayedCommand(datetime.datetime):
     """
-    A three-tuple describing a command to be executed after delay seconds.
+    A command to be executed after some delay (seconds or timedelta).
     """
-    def __init__(self, delay, function, arguments):
-        self.at = time.time() + delay
-        self.delay = delay
-        self.function = function
-        self.arguments = arguments
+    def __new__(cls, delay, function, arguments):
+        if not isinstance(delay, datetime.timedelta):
+            delay = datetime.timedelta(seconds=delay)
+        at = datetime.datetime.utcnow() + delay
+        cmd = datetime.datetime.__new__(DelayedCommand, at.year,
+            at.month, at.day, at.hour, at.minute, at.second,
+            at.microsecond, at.tzinfo)
+        cmd.delay = delay
+        cmd.function = function
+        cmd.arguments = arguments
+        return cmd
 
     def at_time(cls, at, function, arguments):
         """
-        Construct a DelayedCommand to come due at `at`.
+        Construct a DelayedCommand to come due at `at`, where `at` may be
+        a datetime or timestamp.
         """
-        delay = at - time.time()
+        if isinstance(at, int):
+            at = datetime.datetime.utcfromtimestamp(at)
+        delay = at - datetime.datetime.utcnow()
         return cls(delay, function, arguments)
     at_time = classmethod(at_time)
 
     def due(self):
-        return time.time() >= self.at
+        return datetime.datetime.utcnow() >= self
 
 class PeriodicCommand(DelayedCommand):
     """
     seconds.
     """
     def next(self):
-        return PeriodicCommand(self.at + self.delay, self.function,
+        return PeriodicCommand(self.delay, self.function,
             self.arguments)
 
 _rfc_1459_command_regexp = re.compile("^(:(?P<prefix>[^ ]+) +)?(?P<command>[^ ]+)( *(?P<argument> .+))?")
 # for compatibility
 def irc_lower(str):
     return IRCFoldedCase(str).lower()
+
+def total_seconds(td):
+    """
+    Python 2.7 adds a total_seconds method to timedelta objects.
+    See http://docs.python.org/library/datetime.html#datetime.timedelta.total_seconds
+    """
+    try:
+        result = td.total_seconds()
+    except AttributeError:
+        result = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6
+    return result

File irc/tests/test_irclib.py

+import random
+
 import irc.client
 
 def test_version():
 	assert 'VERSION' in vars(irc.client)
 	assert isinstance(irc.client.VERSION, tuple)
 	assert irc.client.VERSION, "No VERSION detected."
+
+def test_delayed_command_order():
+	"""
+	delayed commands should be sorted by delay time
+	"""
+	null = lambda: None
+	delays = [random.randint(0, 99) for x in xrange(5)]
+	cmds = sorted([
+		irc.client.DelayedCommand(delay, null, tuple())
+		for delay in delays
+	])
+	assert [c.delay.seconds for c in cmds] == sorted(delays)

File lib/irclib.py

File contents unchanged.