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.

  • Participants
  • Parent commits 44409ca

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

File irc/client.py

 # 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):

File irc/schedule.py

     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

File 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():