Iterating through next after a certain amount of time - python

Let's say that I have the following code:
possibilities = range(20)
input_name = 4
for i in possibilities:
exec("import Function_" + str(i))
exec("Function_" + str(i) + ".solveproblem(" + str(input_name) + ")")
Since the second exec function, e.g. Function_3.solveproblem(4), can take infinity amount of time, I would just like to try (the function also can have some error) at most for 1,000 seconds and if the time exceeds, then I would like to stop that execution and go through the next function from another python file.
Any idea?

You could try a timer with time
base = time.time() # Get time before start
for each in possibilities:
''do your stuff''
if time.time()-base>100:
break # Time limit
Or if it is impossible to slide it in, consider starting another thread
def timer:
base = time.time() # Get time before start
while True:
if time.time()-base==100:
thread.interrupt_main()
break # Time limit
thread = threading.Thread(target=timer)
thread.start()
'''
now do the rest
'''
How to exit the entire application from a Python thread?

Related

Change the Scheduling Time - Python

I am using schedule module to automatically run a function...
I am thinking of changing the scheduling time dynamically, but the solution is not success
Code -
import schedule
import pandas
from time import gmtime, strftime, sleep
import time
import random
time = 0.1
def a():
global time
print(strftime("%Y-%m-%d %H:%M:%S", gmtime()))
index = random.randint(1, 9)
print(index, time)
if(index==2):
time = 1
print(strftime("%Y-%m-%d %H:%M:%S", gmtime()))
schedule.every(time).minutes.do(a) #specify the minutes to automatically run the api
while True:
schedule.run_pending()
In this program, I scheduled the program to run every 6 seconds. And if the random integer - index value becomes 2, then the time variable is assigned as 1(1 minute). I checked, the time variable is changed to 1 after the random integer index becomes 2. The issue - After changing the time variable to 1, the scheduling still runs the function a() every 6 seconds not 1 minute.
How to change the scheduling time dynamically?
Thank you
After changing the time variable to 1, the scheduling still runs the function a() every 6 seconds not 1 minute.
This is because schedule.every(time).minutes.do(a) # specify the minutes to automatically run the api sets time to 6 seconds at beginning which does not change even if you change the value of that variable because that line has executed just once where value of time was 6 seconds at that execution.
How to change the scheduling time dynamically?
After reading DOCUMENTATION, I found nothing(I think) regarding changing time manually(when certain condition becomes satisfies) but it has built in Random Interval function where that function itself specifies random time within the range.
In your case you could do:
schedule.every(5).to(10).seconds.do(a)
The problem is that you cannot change time when certain condition satisfies.
Maybe there might be some way to fix that issue but could not figure out. And these information may help to investigate further to solve your problem.
I usually use custom schedulers, as they allow greater control and are also less memory intensive. The variable "time" needs to be shared between processes. This is where Manager().Namespace() comes to rescue. It talks 'between' processes.
import time
import random
from multiprocessing import Process, Manager
ns = Manager().Namespace()
ns.time = 0.1
processes = []
def a():
print(time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()))
index = random.randint(1, 4)
if(index==2):
ns.time = 1
print(index, ns.time)
while True:
try:
s = time.time() + ns.time*60
for x in processes:
if not x.is_alive():
x.join()
processes.remove(x)
print('Sleeping :',round(s - time.time()))
time.sleep(round(s - time.time()))
p = Process(target = a)
p.start()
processes.append(p)
except:
print('Killing all to prevent orphaning ...')
[p.terminate() for p in processes]
[processes.remove(p) for p in processes]
break

Python - running a program every 10 seconds, datetime.now() changes behavior

I was testing a program to do something every N seconds, but I bumped into a weird problem.
If I use something simple like this:
import time
def main():
start_t = time.time()
while(True):
if (time.time()-start_t)%10 == 0:
print("Test")
if __name__ == "__main__":
main()
the program works as expected, i.e. it prints "Test" every 10 seconds.
However, I made a small modification, because I need to check at every iteration the current date...if I change the program to this:
import time
from datetime import datetime
def main():
start_t = time.time()
path_screenshots = "screenshots"
while(True):
path_screenshots_today = f"{path_screenshots}/{datetime.now().strftime('%Y_%m_%d')}/"
if (time.time()-start_t)%10 == 0:
print(f"Checking folder {path_screenshots_today}...")
if __name__ == "__main__":
main()
I would expect the program to print "Checking folder {path_screenshots_today}" every 10 seconds again, but instead it keeps running, without printing anything.
I understand that the result of the operation (time.time()-start_t)%10 is never precisely equal to 0, which might be creating the issue...but then, why does it even work in the first case?
I suspect it is working in the first case because the loop is running fast enough that it happens to line up. The lag created by creating path_screenshots_today (particularly the datetime.now() call) causes it not to line up as often. To actually do what you want, try:
import time
from datetime import datetime
def main():
last = time.time()
path_screenshots = "screenshots"
while True:
path_screenshots_today = f"{path_screenshots}/{datetime.now().strftime('%Y_%m_%d')}/"
if time.time() - last >= 10:
last = time.time()
print(f"Checking folder {path_screenshots_today}...")
if __name__ == "__main__":
main()
The first case works because the time is checked frequently enough, which does not happen in the second case because of the delay introduced by the string formatting. A more robust way is the following:
start_t = time.time()
while True:
path_screenshots_today = f"{path_screenshots}/{datetime.now().strftime('%Y_%m_%d')}/"
tt = time.time()
if tt - start_t >= 10:
print(f"Checking folder {path_screenshots_today}...")
start_t = tt # set last check time to "now"
And an even better way would be:
while True:
path_screenshots_today = f"{path_screenshots}/{datetime.now().strftime('%Y_%m_%d')}/"
print(f"Checking folder {path_screenshots_today}...")
time.sleep(10)
This avoids "busy waiting", i.e. keeping the CPU running like crazy.
It's a coincidence of how often the check is happening. If you actually loop over and print your value, you'll notice it's floating point:
while(True):
print('Current value is, ', (time.time()-start_t)%10)
You'll see output like this:
Current value is, 0.45271849632263184
Current value is, 0.45272231101989746
Given that you're doing so little in your loop, the odds are good that you'll coincidentally do that evaluation when the current value is exactly 0.0. But when you add some extra computation, even just the string formatting in datetime, each iteration of your loop will take a little longer and you might just happily skip over 0.0.
So strictly speaking, you should cast your value to an int before comparing it to 0. Eg, int((time.time() - start_t) % 10) == 0. That will be true for an entire second, until the modulus value is once again not zero, a second after it's first true.
A better solution, however, is to probably just use the time.sleep() function. You can call time.sleep to sleep for a number of seconds:
time.sleep(10) # Sleep for 10 seconds

python schedule module to periodically run a function

Goal: run a function every day at a randomized time between two times.
So, I wrote this function to randomly generate a time (please offer feedback on how to streamline. Couldn't find this in an existing package - it MUST already exist...)
def gen_rand_time(low, high):
hour = np.random.randint(low, high)
minute = np.random.randint(1,59)
if minute < 10:
time = str(hour)+':'+str(0)+str(minute)
return time
else:
time = str(hour) + ':' + str(minute)
return time
Next I define the function I would like to run. Keeping it nice and simple.
def test(a):
print('TEST: ' + str(a))
Now I want to run this runction on a periodic basis. I use the schedule package.
def run_bot():
time1 = str(gen_rand_time(18,19))
print(time1)
schedule.every(1).days.at(time1).do(test('TEST WORKED'))
while True:
schedule.run_pending()
time.sleep(1)
run_bot()
when I run run_bot() and put in a time in the immediate future (say, 1 minute into the future), the test() function returns "TEST: TEST WORKED" without waiting for the specified random time.
You should probably try ... do(test,'TEST WORKED')
instead of ... do(test('TEST WORKED')), see this.
Besides, it seems that you cannot use the same value for low and high (I wonder if you actually tried what you posted).

Python: how to interrupt, then return to while loop, without goto?

I'm running a simple PID control program in python. Basically an infinite while loop, which reads from sensors then calculates the appropriate control signal, as well as outputs diagnostic info to the terminal.
However, sometimes while watching the diagnostic info, I'd like to change the PID coefficients - which are essentially some constants used by the loop - by breaking from the loop, accepting user input, then returning to the very same loop. I'd like to do this an arbitrary number of times.
With 'goto' this would be simple and easy and just what I want. Can someone give me some python pseudo-code to do this? I can't really think of how to do it. I can interrupt the loop with a CTRL+C exception handler, but then I can't get back to the main loop.
There must be some very simple way to do this but I can't think of it. Thoughts?
Snippets from my code:
while True:
t0 = get_temp_deg_c(thermocouple1)
print "Hose Temperature = " + str(t0) + " deg C"
t1 = get_temp_deg_c(thermocouple2)
print "Valve Temperature = " + str(t1) + " deg C"
# write temps to file
fi.write(str(t0))
fi.write(" " + str(t1) + "\n")
error = setpoint - t0
print "Setpoint = " + str(setpoint) + " deg C"
print "Error = " + str(error) + " deg C"
percent_error = error/setpoint*100
print "Percent error = " + str(percent_error) + " %"
duty_out = p.GenOut(percent_error)
print "PID controller duty output: " + str(duty_out) + " %"
# clamp the output
if(duty_out) > 100:
duty_out = 100
if(duty_out < 0):
duty_out = 0
PWM.set_duty_cycle(PWM_pin, duty_out)
# do we need to increment the setpoint?
if( (setpoint - setpoint_precision) ... # omitted logic here
# Here we return to the top
As long as you're okay with restarting "from the top" after each interrupt (as opposed to returning to the exact point in the loop when the signal was raised, which is a much harder problem):
while True:
try:
controller.main_loop()
except KeyboardInterrupt:
controller.set_coefficients()
In case you don't want a separate thread for IO, generators may be used to preserve the state of your loop across KeyboardInterrupts.
some_parameter = 1
def do_pid_stuff():
while True:
sensor_data1 = 'data'
sensor_data2 = 'data'
sensor_data3 = 'data'
yield 'based on sensor_data1 ' * some_parameter
yield 'based on sensor_data2 ' * some_parameter
yield 'based on sensor_data3 ' * some_parameter
stuff = do_pid_stuff()
while True:
try:
for control_signal in stuff:
print(control_signal)
except KeyboardInterrupt:
some_parameter = int(input())
So the main loop will continue with new parameters from the last executed yield. This however would require to rewrite your loop. Probably, it should be splitted into a generator that will give you sensor data and into a function that will actually do stuff based on the sensor values.
You already have a few ways to interact with your loop, I'd like to point out another one: select(). Using select(), you can wait for either user input. If you add a timeout, you can then break into the normal loop if no user input is available and interact with your hardware there.
Notes:
Here's the documentation for select , but consider the warning on top and look at the selectors module instead.
This solution, like the one using a keyboard interrupt, will stop interacting with the hardware while parameters are being changed. If that isn't acceptable, using a background thread is necessary.
Using select() is more generally applicable, you could also wait for network traffic.
Your hardware will not be serviced as often as possible but with a fixed pause in between. On the upside, you also don't use a full CPU then.

Python loop delay without time.sleep()

In an MMO game client, I need to create a loop that will loop 30 times in 30 seconds (1 time every second).
To my greatest disappointment, I discovered that I can not use time.sleep() inside the loop because that causes the game to freeze during the loop.
The loop itself is pretty simple and the only difficulty is how to delay it.
limit = 31
while limit > 0 :
print "%s seconds remaining" % (limit)
limit = limit -1
The python libs exist in the client as .pyc files in a separate folder and I'm hoping that I can avoid messing with them.
Do you think that there is any way to accomplish this delay or is it a dead end?
Your game has a main loop. (Yes, it does.)
Each time through the loop when you go to check state, move the players, redraw the screen, etc., you check the time left on your timer. If at least 1 second has elapsed, you print out your "seconds remaining" quip. If At least 30 seconds has elapsed, you trigger whatever your action is.
You can't do it without blocking or threading unless you are willing to lose precision...
I'd suggest sometime like this, but threading is the correct way to do this...
import time
counter = 31
start = time.time()
while True:
### Do other stuff, it won't be blocked
time.sleep(0.1)
print "looping..."
### When 1 sec or more has elapsed...
if time.time() - start > 1:
start = time.time()
counter = counter - 1
### This will be updated once per second
print "%s seconds remaining" % counter
### Countdown finished, ending loop
if counter <= 0:
break
or even...
import time
max = 31
start = time.time()
while True:
### Do other stuff, it won't be blocked
time.sleep(0.1)
print "looping..."
### This will be updated every loop
remaining = max + start - time.time()
print "%s seconds remaining" % int(remaining)
### Countdown finished, ending loop
if remaining <= 0:
break

Categories