Python loop to run after n minutes from start time - python
I am trying to create a while loop which will iterate between 2 time objects, while datetime.datetime.now().time() <= datetime.datetime.now() +relativedelta(hour=1): but on every n minutes or second interval. So if the starting time was 1:00 AM, the next iteration should begin at 1:05 AM with n being 5 mins. So the iteration should begin after 5 mins of the start time and not for example, from the end of an iteration, which is the case when using sleep. Could you please advise how this could be accomplished?
A possible solution to this was from here:
write python script that is executed every 5 minutes
import schedule
import time
def func():
print("this is python")
schedule.every(5).minutes.do(func)
while True:
schedule.run_pending()
time.sleep(1)
With this, the start time has to be 1 am. Secondly, what if the program needs to run say 5 min + 1. In that case a 6 min interval wont work.
Although schedule library has lots of capabilities, I think the following code will help you get what you want. you can simply change start_time, relativedelta and iteration_time
import time
import datetime
start_time = datetime.datetime(year=2022, month=4, day=5, hour=1, minute=00, second=00)
relativedelta = datetime.timedelta(hours=1)
iteration_time = datetime.timedelta(minutes=5)
end_time = start_time + relativedelta
last_run = None
def func():
print("this is python")
while True:
current_time = datetime.datetime.now()
if start_time <= current_time <= end_time:
if last_run:
if current_time >= last_run + iteration_time:
func()
last_run = current_time
else:
last_run = current_time
elif current_time > end_time:
break
time.sleep(1)
this code prints (this is python) each 5 minutes (iteration_time) from 4/5/2022 1:00:00AM (start_time) for 1 hour (relativedelta)
This can be achieved with a manual implementation.
Essentially, you would need to loop continuously until you reach the "active" time window. Once there, you basically execute your function and refuse to run again until the specified execution interval has passed. The main loop does not need to be executed as often as possible, but it is enough to run it once in a while as long as this is reasonably smaller than the execution interval. This effectively a way of limiting execution rate (throttling). Also, the execution time of the function func should be smaller than the interval, otherwise one or more executions are skipped.
import datetime
import time
def repeat_between(
start_dt,
stop_dt,
interval_td,
func,
func_args=None,
func_kws=None,
collect_results=True,
throttling_s=1):
# ensure valid `func_args` and `func_kws`
func_args = () if func_args is None else tuple(func_args)
func_kws = {} if func_kws is None else dict(func_kws)
# initialize current datetime and last run
curr_dt = datetime.datetime.now()
last_run = None
# ensure the start datetime is:
# - before the stop datetime
# - after the current datetime
if stop_dt < start_dt < curr_dt:
return
else:
# collect results here
result = []
# wait until reaching the start datetime
wait_td = (start_dt - curr_dt)
time.sleep(wait_td.total_seconds())
# loop until current datetime exceeds the stop datetime
while curr_dt <= stop_dt:
# if current time is
# - past the start datetime
# - near an interval timedelta
if curr_dt >= start_dt and \
(not last_run or curr_dt >= last_run + interval_td):
curr_result = func(*func_args, **func_kws)
if collect_results:
result.append(curr.result)
last_run = curr_dt
# wait some time before checking again
if throttling_s > 0:
time.sleep(throttling_s)
# update current time
curr_dt = datetime.datetime.now()
To test this, one could use for example:
r = repeat_between(
datetime.datetime.now() + datetime.timedelta(seconds=3),
datetime.datetime.now() + datetime.timedelta(seconds=10),
datetime.timedelta(seconds=2),
func=lambda: (datetime.datetime.now(), 'Hello!'),
throttling_s=0.1
)
print(r)
# [(datetime.datetime(2022, 4, 8, 15, 38, 21, 525347), 'Hello!'),
# (datetime.datetime(2022, 4, 8, 15, 38, 23, 530025), 'Hello!'),
# (datetime.datetime(2022, 4, 8, 15, 38, 25, 534628), 'Hello!'),
# (datetime.datetime(2022, 4, 8, 15, 38, 27, 539120), 'Hello!')]
I believe this could be considered an object-oriented "canonical" solution which creates a Thread subclass instance that will call a specified function repeatedly every datetime.timedelta units until canceled. The starting and how long it's left running are not details the class concerns itself with, and are left to the code making use of the class to determine.
Since most of the action occurs in a separate thread, the main thread could be doing other things concurrently, if desired.
import datetime
from threading import Thread, Event
import time
from typing import Callable
class TimedCalls(Thread):
"""Call function again every `interval` time duration after it's first run."""
def __init__(self, func: Callable, interval: datetime.timedelta) -> None:
super().__init__()
self.func = func
self.interval = interval
self.stopped = Event()
def cancel(self):
self.stopped.set()
def run(self):
next_call = time.time()
while not self.stopped.is_set():
self.func() # Target activity.
next_call = next_call + self.interval
# Block until beginning of next interval (unless canceled).
self.stopped.wait(next_call - time.time())
def my_function():
print(f"this is python: {time.strftime('%H:%M:%S', time.localtime())}")
# Start test a few secs from now.
start_time = datetime.datetime.now() + datetime.timedelta(seconds=5)
run_time = datetime.timedelta(minutes=2) # How long to iterate function.
end_time = start_time + run_time
assert start_time > datetime.datetime.now(), 'Start time must be in future'
timed_calls = TimedCalls(my_function, 10) # Thread to call function every 10 secs.
print(f'waiting until {start_time.strftime("%H:%M:%S")} to begin...')
wait_time = start_time - datetime.datetime.now()
time.sleep(wait_time.total_seconds())
print('starting')
timed_calls.start() # Start thread.
while datetime.datetime.now() < end_time:
time.sleep(1) # Twiddle thumbs while waiting.
print('done')
timed_calls.cancel()
Sample run:
waiting until 11:58:30 to begin...
starting
this is python: 11:58:30
this is python: 11:58:40
this is python: 11:58:50
this is python: 11:59:00
this is python: 11:59:10
this is python: 11:59:20
this is python: 11:59:30
this is python: 11:59:40
this is python: 11:59:50
this is python: 12:00:00
this is python: 12:00:10
this is python: 12:00:20
done
Did you try time.sleep(300)
where 300 is seconds.
if you want your program to run every 5 minuets you can use time.sleep
import time
while true:
#program
time.sleep(300)
if you want to iterate between dates use this template:
from datetime import timedelta
start_date = date_utils.parse('2021-01-01')
end_date = datetime.datetime.now()
while start_date <= end_date:
one_hour = timedelta(hours=1)
one_minute = timedelta(minutes=1)
start_date = start_date + datetime.timedelta(days=1)
Related
Problem with variable name 'start_time' is not defined
Import threading module import threading Import time module to implement sleep functionality import time Import datetime to get start time, end time and elapsed time from datetime import datetime #Function to loop 100000 times def loop_fun(name): for i in range(0, 10): time.sleep(0.0001) print(name) print("Loop executed!") #Function to execute loop_fun without multithreading num =0 def without_thread(num): starting_time = datetime.now() for i in range(0, num): loop_fun("Called from without_thread") ending_time = datetime.now() elapsed_time = (ending_time - starting_time).total_seconds() print("\n\nTime Elapsed without_thread: "+ str(elapsed_time)+"\n\n\n\n\n") #Function to execute loop_fun with multithreading" def with_thread(num): start_time = datetime.now() threads_lst = [] # Creating threads to call loop_fun for i in range(0, num): threads_lst.append(threading.Thread(target=loop_fun, args=("Called from with_thread",))) # Running threads for threads_ in threads_lst: threads_.start() # Waiting for completion of all threads for threads_ in threads_lst: threads_.join() end_time = datetime.now() elapsed_time = (end_time - start_time).total_seconds() print("\n\nTime Elapsed with_thread: "+ str(elapsed_time)+"\n\n\n\n\n") if __name__ == "__main__": without_thread(10) with_thread(10)
Your indentation is messed up. Copy/paste issue or does your code look like that ? Missing Indentation in Python makes it hard to guess what you are trying to achieve. Nevertheless I tried and came up with this code working w/o errors. Does it do what you want ? I frankly don't know. I added these lines, which got messed up by the forum I think from datetime import datetime import time import threading I "fixed" indentation #Function to loop 100000 times def loop_fun(name): for i in range(0, 10): time.sleep(0.0001) print(name) print("Loop executed!") #Function to execute loop_fun without multithreading num =0 def without_thread(num): starting_time = datetime.now() for i in range(0, num): loop_fun("Called from without_thread") ending_time = datetime.now() elapsed_time = (ending_time - starting_time).total_seconds() print("\n\nTime Elapsed without_thread: "+ str(elapsed_time)+"\n\n\n\n\n") #Function to execute loop_fun with multithreading" def with_thread(num): start_time = datetime.now() threads_lst = [] # Creating threads to call loop_fun for i in range(0, num): threads_lst.append(threading.Thread(target=loop_fun, args=("Called from with_thread",))) # Running threads for threads_ in threads_lst: threads_.start() # Waiting for completion of all threads for threads_ in threads_lst: threads_.join() end_time = datetime.now() elapsed_time = (end_time - start_time).total_seconds() print("\n\nTime Elapsed with_thread: "+ str(elapsed_time)+"\n\n\n\n\n") if __name__ == "__main__": without_thread(10) with_thread(10) It's printing stuff like that Loop executed! Called from without_thread Loop executed! Called from without_thread Loop executed! Time Elapsed without_thread: 6.378763
Speed up the execution time
I've got a task to speed up the execution time of this script. Target is below 12 secs. The code does the job but it's not really efficient. How can I improve it ? I suspect I'm not using the multiprocessing module the best way possible here: import multiprocessing, datetime from time import sleep def queue_time(customers, num_of_tills): if len(customers) == 0: return 0 start = datetime.datetime.now() for x in range(num_of_tills): for y in customers: process = multiprocessing.Process(target=sleep, args=(y,)) process.start() process.join() result = datetime.datetime.now() - start return result.seconds SUGGESTED EDIT: import multiprocessing, datetime from time import sleep def queue_time(customers, num_of_tills): if len(customers) == 0: return 0 start = datetime.datetime.now() for x in range(num_of_tills): for y in customers: process = multiprocessing.Process(target=sleep, args=(y,)) process.start() process.join() result = datetime.datetime.now() - start return result.seconds Please assume that the customers argument is a list of integers.
Problem getting function to run for a specific amount of time
I'm trying to run a function in python for a specific amount of time (say 100 sec), and then move on to run another function for a specific amount of time. I've tried creating a counter and using while counter < (some frame number). I've also tried using datetime by doing something like end_time = datetime.now() + timedelta(seconds=100) while datetime.now() < end_time: These things don't seem to be working and I don't know why. Here is my current version of the code: class FicTracAout90deg(object): def run(self, gain_yaw = 1): end_time = datetime.now() + timedelta(seconds=10) while datetime.now() < end_time: for item in self.pubsub.listen(): message = item['data'] try: data = json.loads(message) except TypeError: continue if data['type'] == 'reset': self.time_start = time.time() self.heading_rate_calc.reset(self.time_start) else: time_curr = time.time() heading = data['heading'] intx = data['intx'] inty = data['inty'] velx = data['velx'] vely = data['vely'] velheading = data['deltaheading'] self.accum_heading += velheading self.accum_x += velx self.accum_y += vely time_elapsed = time_curr - self.time_start I'm running this with the following code: from analogoutNoise import FicTracAoutNoise from analogout90deg import FicTracAout90deg import time #for a certain amount of time run block 1 #Block 1 for x in range(2): client = FicTracAout90deg() client.run(1) The 'run' function never seems to stop, and I don't understand why.
Likely the issue is that the line for item in self.pubsub.listen(): never returns a value and so it doesn't finish executing. If this statement doesn't finish execution then the rest of the code is not run and the outer loop is not checked.
Please post a SSCCE. In order to get your script to run, I had to add from datetime import datetime, timedelta. The following code does work, and is similar structurally to yours: from datetime import datetime, timedelta from time import sleep tBegin = datetime.now() tEnd = tBegin + timedelta(seconds=100) while datetime.now() < tEnd: print(datetime.now()) sleep(10) print(datetime.now()) Since this works, it seems to me that the problem is not with the time endpoints, but rather with the internals of your while loop. It can perform a very large number of iterations in 100 sec., which means it's pinning the processor for all that time and accumulating a lot of garbage. It's probably better to think up a different approach, as #iamchoosinganame suggested.
How to write logic for conditions occurring for x amount of time
I am trying to figure out how to do the following: If "some logic" occurs for more than 15 seconds: "execute some code" Im not quite sure how to ask python to do this. I am working with stock prices and my index column is dtype='datetime64[ns]. I tried searching other threads for hints on how to do this but i wasn't even quite sure what to search for. Sorry in advance if its a very elementary question.
try takinng the current time and comparing it every tick
One way to accomplish this is to use threads (multiprocessing) as they would be 2 tasks "some logic" and the "scheduler". This can be addressed easily with the following logic: import threading import time lock = threading.Lock() cond = threading.Condition(threading.Lock()) def waitLock(timeout): with cond: current_time = start_time = time.time() while current_time < start_time + timeout: if lock.acquire(False): return True else: cond.wait(timeout - current_time + start_time) current_time = time.time() return False Source: How to implement a Lock with a timeout in Python 2.7
import time def check(): if not some_logic(): return False start_time = time.time() while time.time() - start_time <= 15: if not some_logic(): return False return True if check(): ... # do stuff This runs some_logic as many times as possible to ensure that it returns True throughout the whole 15-second period.
Python running a cron job every 20 seconds
I am always using celery periodic task to run a cron but this time celery's crontab can not fulfill my cronjob. I need a cron that only runs on tuesday from 20:50 to 21:10 every 20 seconds. Can't seem to achieve this
Either you can add your python script to the system's crontab, or you can code it yourself. Just start a new thread that calculates the time until tuesday 20:50, then sleeps until then, then runs the job and sleeps for the remaining seconds and after 21:10 again calculates the time until tuesday 20:50. This can be easily done using the commands time.time() and time.sleep().
If ever anybody wants to enable running celery's crontab in seconds; Below is an example via extending the crontab from celery.schedules import crontab, cronfield from celery.utils.timeutils import ffwd CRON_REPR = '''\ <crontab: {0._orig_second} {0._orig_minute} {0._orig_hour} {0._orig_day_of_week} \ {0._orig_day_of_month} {0._orig_month_of_year} (s/m/h/d/dM/MY)>\ ''' class ExtendedCrontab(crontab): ''' custom crontab to support 'seconds' ''' def __init__(self, second=0, minute='*', hour='*', day_of_week='*', day_of_month='*', month_of_year='*', nowfun=None, app=None): super().__init__(second, minute, hour, day_of_week, day_of_month, month_of_year, nowfun, app) self._orig_second = cronfield(second) self.second = self._expand_cronspec(second, 60) def _delta_to_next(self, last_run_at, next_hour, next_minute, next_second): ''' Takes a datetime of last run, next minute and hour, and returns a relativedelta for the next scheduled day and time. Only called when day_of_month and/or month_of_year cronspec is specified to further limit scheduled task execution. ''' _ffwd = super()._delta_to_next(last_run_at, next_hour, next_minute) _ffwd.second = next_second return _ffwd def __repr__(self): return CRON_REPR.format(self) def __reduce__(self): return (self.__class__, (self._orig_second, self._orig_minute, self._orig_hour, self._orig_day_of_week, self._orig_day_of_month, self._orig_month_of_year), None) def remaining_delta(self, last_run_at, tz=None, ffwd=ffwd): tz = tz or self.tz last_run_at = self.maybe_make_aware(last_run_at) now = self.maybe_make_aware(self.now()) dow_num = last_run_at.isoweekday() % 7 # Sunday is day 0, not day 7 execute_this_date = (last_run_at.month in self.month_of_year and last_run_at.day in self.day_of_month and dow_num in self.day_of_week) execute_this_hour = (execute_this_date and last_run_at.day == now.day and last_run_at.month == now.month and last_run_at.year == now.year and last_run_at.hour in self.hour and last_run_at.minute < max(self.minute)) execute_this_minute = (last_run_at.minute in self.minute and last_run_at.second < max(self.second)) if execute_this_minute: next_second = min(second for second in self.second if second > last_run_at.second) delta = ffwd(second=next_second, microsecond=0) else: if execute_this_hour: next_minute = min(minute for minute in self.minute if minute > last_run_at.minute) next_second = min(self.second) delta = ffwd(minute=next_minute, second=next_second, microsecond=0) else: next_minute = min(self.minute) next_second = min(self.second) execute_today = (execute_this_date and last_run_at.hour < max(self.hour)) if execute_today: next_hour = min(hour for hour in self.hour if hour > last_run_at.hour) delta = ffwd(hour=next_hour, minute=next_minute, second=next_second, microsecond=0) else: next_hour = min(self.hour) all_dom_moy = (self._orig_day_of_month == '*' and self._orig_month_of_year == '*') if all_dom_moy: next_day = min([day for day in self.day_of_week if day > dow_num] or self.day_of_week) add_week = next_day == dow_num delta = ffwd(weeks=add_week and 1 or 0, weekday=(next_day - 1) % 7, hour=next_hour, minute=next_minute, second=next_second, microsecond=0) else: delta = self._delta_to_next(last_run_at, next_hour, next_minute, next_second) return self.to_local(last_run_at), delta, self.to_local(now) def __eq__(self, other): if isinstance(other, crontab): return (other.month_of_year == self.month_of_year and other.day_of_month == self.day_of_month and other.day_of_week == self.day_of_week and other.hour == self.hour and other.minute == self.minute and other.second == self.second) return NotImplemented ExtendedCrontab(second=second, minute=minute, hour=hour, day_of_week=day_of_week, day_of_month=day_of_month, month_of_year=month_of_year)