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)

Categories

Resources