Commits

Jason R. Coombs committed 696215a

Refactored implementation of schedule classes. No longer do they override the datetime constructor, but now only provide suitable classmethods for construction in various forms.

Comments (0)

Files changed (4)

+8.0
+===
+
+* Refactored implementation of schedule classes. No longer do they override
+  the datetime constructor, but now only provide suitable classmethods for
+  construction in various forms.
+* Removed backward-compatible references from irc.client.
+
+Clients that reference the schedule classes from irc.client or that construct
+them from the basic constructor will need to update to use the new class
+methods::
+
+  - DelayedCommand -> DelayedCommand.after
+  - PeriodicCommand -> PeriodicCommand.after
+
 7.1
 ===
 
 # ERROR from the server triggers the error event and the disconnect event.
 # dropping of the connection triggers the disconnect event.
 
-# for backward-compatibility
-from .schedule import (PeriodicCommand, DelayedCommand,
-    PeriodicCommandFixedDelay)
-
 class IRCError(Exception):
     "An IRC exception"
 
         function -- Function to call.
         arguments -- Arguments to give the function.
         """
-        command = schedule.DelayedCommand(delay, function, arguments)
+        command = schedule.DelayedCommand.after(delay, function, arguments)
         self._schedule_command(command)
 
     def execute_every(self, period, function, arguments=()):
         function -- Function to call.
         arguments -- Arguments to give the function.
         """
-        command = schedule.PeriodicCommand(period, function, arguments)
+        command = schedule.PeriodicCommand.after(period, function, arguments)
         self._schedule_command(command)
 
     def _schedule_command(self, command):
     Clients may override .now() to have dates interpreted in a different
     manner, such as to use UTC or to have timezone-aware times.
     """
-    def __new__(cls, delay, function, arguments):
+    @classmethod
+    def now(self, tzinfo=None):
+        return datetime.datetime.now(tzinfo)
+
+    @classmethod
+    def from_datetime(cls, other):
+        return cls(other.year, other.month, other.day, other.hour,
+            other.minute, other.second, other.microsecond,
+            other.tzinfo)
+
+    @classmethod
+    def after(cls, delay, function, arguments):
         if not isinstance(delay, datetime.timedelta):
             delay = datetime.timedelta(seconds=delay)
-        at = cls.now() + delay
-        cmd = datetime.datetime.__new__(cls, at.year,
-            at.month, at.day, at.hour, at.minute, at.second,
-            at.microsecond, at.tzinfo)
+        due_time = cls.now() + delay
+        cmd = cls.from_datetime(due_time)
         cmd.delay = delay
         cmd.function = function
         cmd.arguments = arguments
         return cmd
 
     @classmethod
-    def now(self, tzinfo=None):
-        return datetime.datetime.now(tzinfo)
-
-    @classmethod
     def at_time(cls, at, function, arguments):
         """
         Construct a DelayedCommand to come due at `at`, where `at` may be
         """
         if isinstance(at, int):
             at = datetime.datetime.fromtimestamp(at)
-        delay = at - cls.now()
-        return cls(delay, function, arguments)
+        cmd = cls.from_datetime(at)
+        cmd.delay = at - cmd.now()
+        cmd.function = function
+        cmd.arguments = arguments
+        return cmd
 
     def due(self):
         return self.now() >= self
 
-class PeriodicCommandBase(DelayedCommand):
-    def next(self):
-        return PeriodicCommand(self.delay, self.function,
-            self.arguments)
-
-    def _check_delay(self):
-        if not self.delay > datetime.timedelta():
-            raise ValueError("A PeriodicCommand must have a positive, "
-                "non-zero delay.")
-
-class PeriodicCommand(PeriodicCommandBase):
+class PeriodicCommand(DelayedCommand):
     """
     Like a delayed command, but expect this command to run every delay
     seconds.
     """
-    def __init__(self, *args, **kwargs):
-        super(PeriodicCommand, self).__init__(*args, **kwargs)
-        self._check_delay()
+    def next(self):
+        cmd = self.__class__.from_datetime(self + self.delay)
+        cmd.delay = self.delay
+        cmd.function = self.function
+        cmd.arguments = self.arguments
+        return cmd
 
-class PeriodicCommandFixedDelay(PeriodicCommandBase):
+    def __setattr__(self, key, value):
+        if key == 'delay' and not value > datetime.timedelta():
+            raise ValueError("A PeriodicCommand must have a positive, "
+                "non-zero delay.")
+        super(PeriodicCommand, self).__setattr__(key, value)
+
+class PeriodicCommandFixedDelay(PeriodicCommand):
     """
     Like a periodic command, but don't calculate the delay based on
     the current time. Instead use a fixed delay following the initial
 
     @classmethod
     def at_time(cls, at, delay, function, arguments):
-        cmd = super(PeriodicCommandFixedDelay, cls).at_time(
-            at, function, arguments)
+        if isinstance(at, int):
+            at = datetime.datetime.fromtimestamp(at)
+        cmd = cls.from_datetime(at)
         if not isinstance(delay, datetime.timedelta):
             delay = datetime.timedelta(seconds=delay)
         cmd.delay = delay
-        cmd._check_delay()
+        cmd.function = function
+        cmd.arguments = arguments
         return cmd

irc/tests/test_schedule.py

 	null = lambda: None
 	delays = [random.randint(0, 99) for x in range(5)]
 	cmds = sorted([
-		schedule.DelayedCommand(delay, null, tuple())
+		schedule.DelayedCommand.after(delay, null, tuple())
 		for delay in delays
 	])
 	assert [c.delay.seconds for c in cmds] == sorted(delays)
 def test_periodic_command_delay():
 	"A PeriodicCommand must have a positive, non-zero delay."
 	with pytest.raises(ValueError) as exc_info:
-		schedule.PeriodicCommand(0, None, None)
+		schedule.PeriodicCommand.after(0, None, None)
 	assert str(exc_info.value) == test_periodic_command_delay.__doc__
 
 def test_periodic_command_fixed_delay():