Event scheduler library already exists and can be "tweaked" to become
periodic. - https://docs.python.org/3/library/sched.html

Best Regards,
Ronie

On Sun, Jun 9, 2019, 2:35 AM <killer...@gmail.com> wrote:

> Hi,
>
> I recently needed to run a function every half a second and the first
> option was threading.Timer, but it just tick once.
> Based on https://stackoverflow.com/a/48741004 I derived a class from
> Timer to run endlessly, but there are two things I do not like:
> - The function, args and kwargs attributes of Timer are just the same as
> the _target, _args and _kwargs from Thread.
> - It feels better that Timer can "tick" a given number of times.
>
> So I changed Timer to be more inline with the Thread class and make it
> possible to run the function more than once:
>
> class Timer(Thread):
>     FOREVER = Ellipsis
>
>     def __init__(self, interval, function, args=None, kwargs=None,
>                  name=None, *, ticks=1, daemon=None):
>         # Keep compatibility with the old Timer, where the default
>         # args and kwargs where mutable.
>         if args is None:
>             args = []
>         if kwargs is None:
>             kwargs = {}
>
>         Thread.__init__(self, target=function,
>                         args=args, kwargs=kwargs,
>                         name=name, daemon=daemon)
>         self.interval = interval
>         self.ticks = ticks
>         self.finished = Event()
>
>     def cancel(self):
>         """Stop the timer if it hasn't finished yet."""
>         self.finished.set()
>
>     def run(self):
>         try:
>             while not self.finished.wait(self.interval):
>                 if self._target and not self.finished.is_set():
>                     self._target(*self._args, **self._kwargs)
>
>                 if self.ticks is self.FOREVER:
>                     continue
>
>                 self.ticks -= 1
>                 if self.ticks <= 0:
>                     break
>         finally:
>             # Remove the reference to the function for the same
>             # reason it is done in Thread.run
>             del self._target
>             # However, for backwards compatibility do not remove
>             # _args and _kwargs as is done in Thread.run
>             # del self._args, self._kwargs
>
>         self.finished.set()
>
>     def join(self, timeout=None):
>         # If the timer was set to repeat endlessly
>         # it will never join if it is not cancelled before.
>         if self.ticks is self.FOREVER:
>             self.cancel()
>         super().join(timeout=timeout)
>
>     @property
>     def function(self):
>         return getattr(self, '_target', None)
>
>     @function.setter
>     def function(self, value):
>         self._target = value
>
>     @property
>     def args(self):
>         return self._args
>
>     @args.setter
>     def args(self, value):
>         self._args = value
>
>     @property
>     def kwargs(self):
>         return self._kwargs
>
>     @kwargs.setter
>     def kwargs(self, value):
>         self._kwargs = value
>
> The default option for ticks is to run once, just like the current Timer.
>
> With this version it is possible to do things like:
> >>> t = Timer(10.0, f, ticks=3)
> >>> t.start()
> # Will run f three times, with 10 seconds between each call.
>
> >>> t = Timer(10.0, f, ticks=Timer.FOREVER, daemon=True)
> >>> t.start()
> # Will run f every 10 seconds until the end of the program
> # It also stops the thread if the program is interrupted.
>
> >>> t = Timer(10.0, f, ticks=..., daemon=True)
> >>> t.start()
> # Same as above, but I like the usage of Ellipsis here :)
>
>
> Other option is to leave Timer as it is and create a new class, so it is
> not necessary to keep the compatibility with the function, args and kwargs
> attributes:
>
> class Ticker(Thread):
>     FOREVER = Ellipsis
>
>     def __init__(self, interval, target=None, args=(), kwargs=None,
>                  group=None, name=None, *, ticks=1, daemon=None):
>         super().__init__(group=group, target=target,
>                         args=args, kwargs=kwargs,
>                         name=name, daemon=daemon)
>         self.interval = interval
>         self.ticks = ticks
>         self.finished = Event()
>
>     def cancel(self):
>         """Stop the timer if it hasn't finished yet."""
>         self.finished.set()
>
>     def evaluate(self):
>         if self._target and not self.finished.is_set():
>             self._target(*self._args, **self._kwargs)
>
>     def run(self):
>         try:
>             while not self.finished.wait(self.interval):
>                 self.evaluate()
>
>                 if self.ticks is self.FOREVER:
>                     continue
>
>                 self.ticks -= 1
>                 if self.ticks <= 0:
>                     break
>         finally:
>             # Remove the reference to the function for the same
>             # reason it is done in Thread.run
>             del self._target, self._args, self._kwargs
>
>         self.finished.set()
>
>     def join(self, timeout=None):
>         # If the timer was set to repeat endlessly
>         # it will never join if it is not cancelled before.
>         if self.ticks is self.FOREVER:
>             self.cancel()
>         super().join(timeout=timeout)
>
>
> Do you think it is useful to try to clean up and add this code to
> threading or it is just something too specific to consider changing the
> standard library?
>
> Regards,
>
> Andrés
> _______________________________________________
> Python-ideas mailing list -- python-ideas@python.org
> To unsubscribe send an email to python-ideas-le...@python.org
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at
> https://mail.python.org/archives/list/python-ideas@python.org/message/33JL6L64UKHLO3ZRIHEQOWRCEFMAL5O7/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/I5GEL4PH4YNFTRM2NI4SJHICLCNSN773/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to