Time as stopping condition of infinite loop - python

# run infinitly
while(True):
done = False;
while(not done):
#Do Stuff
#Main Program
#stopping condition of inner while loop
if datetime.datetime.now().minute == 10:
done = True
if datetime.datetime.now().minute == 10:
time.sleep(60-datetime.datetime.now().second)
I can't figure out why this will not work for me. The entire script is supposed to run on an infinite loop, and then the inner loop is supposed to go through until the stopping condition is met every 10 minutes.
It was my understanding, and maybe I am wrong, that using datetime.datetime.now().minute like I did in an if loop like that would set done = True every 10 minutes, and would end the loop and proceed with the rest of the script...until it realizes that its in another infinite loop and then its supposed to start all over.
Am I getting confused with how this command works, or is there something wrong with my code? Or is there even a better way to do it? The script will run, but it does not end the inner loop with that stopping condtion.

There are various ways to do it, but keeping close to your original design, I'd code the inner loop as:
while(not done):
if datetime.datetime.now().minute % 10 == 0:
done = True
else:
time.sleep(60-datetime.datetime.now().second)
(I'm assuming what you were trying to do with that second if was sleep until the next minute when you weren't ready to exit the loop.)

It stops every tenth minute: 1:10, 2:10, 3:10, etc. In order to do that, use something like:
import time
# in outer loop
inner_loop_start = time.time()
# in inner loop
now = time.time()
if now - inner_loop_start > 60*10:
# done= True and all that jazz

Rather than thrashing around constantly checking if it's 10 past the top of the hour, just do some math and sleep appropriately. Time is generally deterministic.
import datetime
import time
def seconds_till_10past():
now = datetime.datetime.now()
delta = now.replace(minute=10, second=0) - now
return delta.seconds % 3600
while True:
time.sleep(seconds_till_10past())
print "Do something at 10 past the hour."
Also, don't parenthesize arguments to statements (e.g. while(True)), it's bad form.

This does not directly answer the question, but just in case all you want to achieve in the first place is to run some python code every (say) 10 minutes, you'd better implement this using cron.
I assume you so far have a script that is somehow started at boot time. It mainly consists of an infinite loop, a main procedure, and a wait-until-next-execution-time component. For example like the following:
""" my_first_daemon.py
does something everytime the datetime minute part is fully divisible by ten
"""
while True:
# do something at 00,10,20,30,40,50 (minutes) every hour
print "Hello World!"
# wait until the next execution time
wait_until_next_ten_minute_time()
If this is indeed the case I'd suggest to move the main section of your script to a separate script. For example like the following:
""" my_first_cronjob.py
is run by cron everytime the datetime minute part is fully divisible by ten
"""
# do something at 00,10,20,30,40,50 (minutes) every hour
print "Hello World!"
The you go ahead and add to your crontab (use the command crontab -e to add the entry to that user's crontab that shall run the script, also have a look at the manpage). For example like this:
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * command to be executed
# example entry:
# (divide minute-star by 10 to get it run every 10 minutes)
*/10 * * * * python /path/to/my_first_cronjob.py
After the edit you should notice a message like crontab: installing new crontab and then you're done. Just wait and have a look if it works.
Some things to note as well:
The script's output to stdout and stderr are sent to you by mail after termination. Have a look at tail -f /var/mail/moooeeeep (specify your username). You can also configure Thunderbird to fetch these mails, in order to inspect them easier.
The corresponding Wikipedia page is a very good source of information for further details.
If you need to keep state information between independent runs of the script, consider using a configuration file to store this state information, e.g., using pickle, shelve, sqlite, whatever.

Expanding from my comment above:
tic = datetime.datetime.now()
while True:
# do stuff
toc = datetime.datetime.now() - tic
if toc.minute > 10:
tic = datetime.datetime.now();
time.sleep(60-datetime.datetime.now().second)
I have removed the "done" variable, since it conveys a meaning of finished. By reading your code, though, it seems you are actually pausing an ever ongoing process, actually.
Hope this helps!

Related

How to execute an if else statement in while loop without making the while loop stop?

Im trying to make a countdown timer with the feature to reset/stop/pause/resume. I tried implementing an if/elif/else statement to check for user input letter within the while loop while it continues looping. However, the loop stops running when the conditional statement executes. How do I implement my intended functionality into the code?
code:
import time
import datetime
def timeinput():
# Inputs for hours, minutes, seconds on timer
h = input("Enter the time in hours: ")
m = input("Enter the time in minutes: ")
s = input("Enter the time in seconds: ")
countdown(int(h), int(m), int(s))
# Create class that acts as a countdown
def countdown(h, m, s):
# Print options for user to do
print("Press w to reset \nPress s to stop \nPress a to pause \nPress d to resume")
# Calculate the total number of seconds
total_seconds = h * 3600 + m * 60 + s
# While loop that checks if total_seconds reaches zero
# If not zero, decrement total time by one second
while total_seconds > 0:
# Timer represents time left on countdown
timer = datetime.timedelta(seconds = total_seconds)
# Prints the time left on the timer
print(timer, end="\r")
# Delays the program one second
time.sleep(1)
# Reduces total time by one second
total_seconds -= 1
user=input()
if user=="w":
total_seconds=0
print("Timer will now reset")
timeinput()
print("Bzzzt! The countdown is at zero seconds!")
timeinput()
result:
outcome of code
As shown in the provided image, the loop stops at 20 secs. My intended action here was for the countdown to continue until the user presses the W key, which shall then reset the countdown back to its original set time at initialization.
Soo, I'm also kinda new to programming and python so I don't exactly how to make a timer with that functionality. What is happening is that when you use the input() function the code stops and waits for the user input.
I know you can use some libraries, like pygame, in order to check for user input inside the loop without stopping it, but without one I'm not sure how to do it.
You can also use the module keyboard, that comes inside python, and I think is great for the problem you shared. Check this post How to detect key presses?
The command input() is blocking. It waits until it get something from the console. Maybe take a look at this post and try to implement a non blocking version of input. Non-blocking console input?
The problem is that input is blocking - that is, it will stop the execution until the user presses < enter> .
Antique running environments, on 8 bit computers, using Basic, could check "what the user is pressing right now", without waiting for a confirmation with "enter" using the inkey command.
In Python programs running on terminal, terminedia, a 3rdy party library, implements inkey() as it was - but it needs the terminal input to be reconfigured for which it provides a "keyboard" context manager.
You can then run almost the same code you have -just install terminedia in your Python environment with "pip install terminedia"
import terminedia as TM
...
def countdown(h, m, s):
...
while total_seconds > 0:
# Timer represents time left on countdown
timer = datetime.timedelta(seconds = total_seconds)
# Prints the time left on the timer
print(timer, end="\r")
with TM.keyboard:
# Delays the program one second
time.sleep(1)
# Reduces total time by one second
total_seconds -= 1
user=TM.inkey()
if user=="w":
total_seconds=0
print("Timer will now reset")
timeinput()
print("Bzzzt! The countdown is at zero seconds!")
timeinput()
(disclaimer: I am terminedia author)
Taking inputs from the answers received here and subsequent follow-up questions on stack overflow and Reddit, this question has been solved using pygame. I'll provide the link below for future reference to this question.
problem solution

how to terminate python script using while loop and get the result?

i would like to generate random numbers for 5 minutes. after that, i would like to know the most common numbers.
my question is:
I am not sure about how much time I need. Can i MANUALLY terminate the script at 1 minute or 1 minute 19 seconds and get the result
my code is as follows:
import random
from collections import Counter
t_end = time.time() + 60*5
numbers = []
while time.time() < t_end:
number=''.join(map(str, [random.randint(0,9)for value in range(0,4)]))
numbers.append(number)
print(Counter(numbers))
There are a myriad number of options:
print an intermediate result every 5s or so and abort the script with ctrl+C
check if space or something else is pressed, if so, abort the loop (see here for how to)
listen to signals and handle accordingly (this could also gracefully terminate the script for option 1)
open a socket and listen to a socket if a stop signal comes in
create a gui with TKinter or what and add a stop button
...
The first one is the easiest but also the most quick&dirty one. For the second option, the following script should get you started (your script was working for me after adding whitespaces and linebreaks):
import keyboard
while True:
if keyboard.read_key() == "p":
print("You pressed p")
break
Assuming python3 and windows: Note that keyboard is not shipped with python on default. You need to install it with pip3 install keyboard. Pip3.exe might not be on your path. cd to where you installed python and look around. For me it was in the Scripts-folder (my installation is pretty messed up though).
import random
from collections import Counter
import time
t_end = time.time() + 60*5
numbers = []
while time.time() < t_end:
number=''.join(map(str, [random.randint(0,9)for value in range(0,4)]))
numbers.append(number)
if time.time()==(t_end-60*4): # break a 1 min
break
print(Counter(numbers))
As luigigi already mentioned in the comment, you could simply use the number of iterations as a limit, and use a for-loop:
number_of_iterations = 10000
for i in range(number_of_iterations):
place_your_function_here()
However, if you for some reason really want to use the time, here is how you can do it: Update the current time at the bottom of your while block, and in the continuing condition of the while loop, compare it to the finish time:
import datetime
delta_in_seconds = 60
finish = datetime.datetime.now() + datetime.timedelta(0, delta_in_seconds)
while time < finish:
place_your_function_here()
time = datetime.datetime.now()

How to use a function when the current time equals another time: Python

I am developing a script that utilizes a function I made to control the relays of an 8 channel relay board on a Raspberry Pi 3. The function works, and calling the function works. I am trying to develop this script so when the current time equals another time, such as Zone 1 start time, the relays turn on/off depending on the status that is received by another part in the code.
I have tested it without this time equals part, and everything works. I seem to be running into some problems when I add this level of complexity. Here is a sample of my code:
while True:
from datetime import datetime
import time
import smbus
ValveStatus='00000001' #0 is closed, 1 is open.
R1_1,R2_1,R3_1,R4_1,R5_1,R6_1,R7_1,R8_1=list(map(int, ValveStatus))
currenttime=datetime.today().strftime('%Y-%m-%d %H:%M:%S')
Z1S_Timestamp='2018-07-09 10:25:11'
if(currenttime==Z1S_Timestamp):
if(R8_1==1):
SetRelayState(BoardOne,8,"ON")
else:
SetRelayState(BoardOne,8,"OFF")
No matter how many times I changed the code, it will never work with this timing method. It never enters the loop and therefore the relay never opens. Is there a better way to do this rather than simply having if equal to statements? I am open to editing it, but the relays still need to open around the time of the start time. I think a margin of 1 or 2 minutes is okay, since timing it exactly equal is not 100% necessary.
Would something like:
currenttime= '2018-07-09 12:53:55' #hard coding just for example purposes
if('2018-07-09 12:52:55' <= currenttime <= '2018-07-09 12:54:55'):
do the things
Be a more valid/correct/pythonically correct method?
Sure - I would do the opposite though - convert all times to datetime() objects and use those for comparison:
TIME_MARGIN = datetime.timedelta(seconds=120) # use a margin of 2 minutes
time_compare = datetime.datetime(2018, 7, 9, 12, 52, 55)
current_time = datetime.datetime.now()
if (time_compare - TIME_MARGIN) < current_time < (time_compare + TIME_MARGIN):
#do something

How to print something at a specific time of the day

Is it possible to have python 2.7 print something at a specific time of the day. For example if I ran the program at 15:06 and coded it to print "Do task now" at 15:07 it prints it. So no matter what time you ran the program once it hit 15:07 it would print "Do task now." In addition is it possible to have it print every week at this time?
I would suggest installing the library schedule, if you're able to.
Use pip install schedule
Your code would look like this if utilizing schedule:
import schedule
import time
def task():
print("Do task now")
schedule.every().day.at("15:07").do(task)
while True:
schedule.run_pending()
time.sleep(1)
You can adjust time.sleep(1) as necessary to sleep for longer if a 1s interval is too long. Here is the schedule library page.
If you're not using cron, then the general solution is to find the time remaining until you need the event to occur, have the program sleep for that duration, then continue execution.
The tricky part is to have the program find the next occurrence of a given time. There are some modules for this, but you could also do it with plain code for a well-defined case where it is only a fixed time of day.
import time
target_time = '15:07:00'
current_epoch = time.time()
# get string of full time and split it
time_parts = time.ctime().split(' ')
# replace the time component to your target
time_parts[3] = target_time
# convert to epoch
future_time = time.mktime(time.strptime(' '.join(time_parts)))
# if not in the future, add a day to make it tomorrow
diff = future_time - current_epoch
if diff < 0:
diff += 86400
time.sleep(diff)
print 'Done waiting, lets get to work'
While python is not ideal to schedule something; there are better tools out there. Yet, if this is desired to be done in python below is a way to implement it:
Prints at scheduled_time of 11AM:
import datetime as dt
scheduled_time = dt.time(11,00,00,0)
while 1==1:
if (scheduled_time < dt.datetime.now().time() and
scheduled_time > (dt.datetime.now()- dt.timedelta(seconds=59)).time() ):
print "Now is the time to print"
break
There are two if conditions with an intent to print within one minute; a shorter duration can be chosen. But the break immediately after print ensures that print is executed only once.
You would need to extrapolate this so that code is run across days.
Refer: datetime Documentation

Python Function Caching?

I have encountered a very weird situation in Python.
I have a while True: loop in a script that constantly grabs real-time data. The data is only meant to be grabbed during business hours, therefore I have a helper function in a module called lib/date.py.
This is the function:
def isTime(utc_now=datetime.datetime.utcnow()):
'''
9:00 AM UTC - 5:00 PM UTC Mon - Fri.
The parameter utc_now can be used to specify a specific
datetime to be checked instead of the utcnow().
'''
week_day = utc_now.weekday()
# no good on Saturdays and Sundays
if 5 == week_day or 6 == week_day:
return False
now_int = (utc_now.hour * 60) + utc_now.minute
if (17 * 60) >= now_int >= (9 * 60):
return True
return False
I realize that the way times are checked is an overkill, but this is a simplified version of my function. My real function also includes a bunch of time zone conversions which make the above necessary.
This is my main script (simplified):
def main():
while True:
do_ten_seconds()
def do_ten_seconds():
log.info("alive")
for i in xrange(10):
if isTime():
log.info("its time")
# do data grabbing
sleep(1)
This works perfectly during the week, stopping every week night and continuing the next morning. However, on the weekend, it does not "recover" from the long break. In other words, isTime never returns True Monday morning.
I checked the log, and the script just prints "alive" every 10 seconds, but the data grabbing part never happens (and "its time" is never printed). Hence, I am assuming this means isTime is returning False.
Could it be that because the function is called so frequently over the weekend, and because it returns False so many times in a row, Python caches the results and never recovers from it?
When I spawn the same instance of code while the main script is still running in the faulty mode where isTime is returning False, the new instance gets True from isTime and begins to work perfectly.
What is going on?
The first time you import date.py, the following line is run once:
def isTime(utc_now=datetime.datetime.utcnow()):
When it's run, the default value for utc_now is set to the time at that instant, and is never recomputed.
Default arguments are calculated at compile time. Make the default value None, and check for that using is None.

Categories