Python Function Caching? - python

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.

Related

How can I Stop/Skip the execution of a particular function for a day

In my python Script I need to Stop/Skip the execution of a particular function for a day if the required condition gets false even for a single time. That means if a single error arises while the execution of particular function in my python script ...I want that function to become non-callable for a given duration of time and instead use alternate function for that particular duration. When the duration ends the function becomes again callable
Please share your code next time to receive the proper help...
From what I understood, you need to start storing error times somehow.
if not your_required_condition:
error_time = datetime.datetime.now()
if (datetime.datetime.now() - error_time).days >= 1:
your_other_function()
else:
your_main_function()
You need to make your code check if the condition is verified each time...

Datetime object switches values during while loop interations

I'm seeing this weird issue in my Python code, and not quite sure what is going on. So quick backstory on the code, I've got a function that my UI script calls on startup that runs in a separate thread. This thread is intended to call a function in a separate module that checks to see if the stored timestamp in a database is older than x_value, and does stuff if that's true.
So here's the function (I can post more code later if needed, but this function is where the issue is happening):
def tableUpdateCheck(sensor_manager):
last_check = datetime.now()
print(f"last_check: {last_check.strftime('%m/%d/%Y_%H:%M:%S')}")
while RUN_TABLE_UPDATE_THREAD:
print(f"Now: {datetime.now().strftime('%H:%M:%S')}\tLast check time: {last_check.strftime('%H:%M:%S')}")
if datetime.now() - last_check > timedelta(seconds=60):
print('Running update table update check...')
sensor_manager.updateTables()
last_check = datetime.now()
time.sleep(5)
if __name__ == '__main__':
sensor_mgr = SensorManager()
table_thread = Thread(target=tableUpdateCheck, args=[sensor_mgr])
table_thread.start()
# Start webserver stuff
The resulting output looks like this:
Now: 17:49:13 Last check time: 17:49:13
Now: 17:49:18 Last check time: 17:49:10
Now: 17:49:23 Last check time: 17:49:13
Now: 17:49:28 Last check time: 17:49:10
Now: 17:49:33 Last check time: 17:49:13
Now: 17:49:38 Last check time: 17:49:10
And continues to do this until the if statement runs, and "last_check" goes back to fluctuating between two values.
Any thoughts on why this might be? It doesn't stop the overall program from ultimately doing what it's supposed to do, but it's super annoying.
So for anyone who might stumble across this in the future, it turns out that "app.run_server(debug=True)" in Flask apps causes an extra thread to run at start. I'm not 100% why this is the case, but that seems to be what's causing the problem I'm describing.
However, using a production Flask app, or simply setting the "debug" argument to False on a development server will not start the multiple threads, and this issue goes away.

How do I assign a conditional loop from a Microsecond?

I have a very simple code that is supposed to print out '2' every time the microsecond of the current time is 2, or once per second. However, when I run this code, it returns nothing. How can I fix this?
import datetime
while True:
if datetime.datetime.now().microsecond == 2:
print (datetime.datetime.now().microsecond)
You can't reliably build a loop that will only print exactly every second at the point where the number of microseconds in the current time is equal to 2.
Computer clocks don't necessarily tick every single microsecond, nor is Python always fast enough to produce a datetime instance for a given microsecond. On many systems, calling datetime.now() will produce the exact same value for several microseconds at a stretch, all the way up to only producing a new, distinct time once a second. Your code also creates multiple datetime objects, so after you test for the microsecond attribute, the next one created when you call print() may easily have a higher microsecond value.
See the time module which documents available clocks in a little more detail. datetime uses the same clock as time.time:
The precision of the various real-time functions may be less than suggested by the units in which their value or argument is expressed. E.g. on most Unix systems, the clock “ticks” only 50 or 100 times a second.
and
Note that even though the time is always returned as a floating point number, not all systems provide time with a better precision than 1 second. While this function normally returns non-decreasing values, it can return a lower value than a previous call if the system clock has been set back between the two calls.
It depends both on your OS and your computer hardware what the precision of your system clock is; it is not the resolution of the clock that matters (how much information can be given in the clock value) but rather how often that value is refreshed.
If you want to limit printing to once a second, just track the last time you printed and make sure the new time is at least 1 second newer:
import datetime
last = datetime.datetime.now()
while True:
now = datetime.datetime.now()
if (now - last).total_seconds() >= 1:
print(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

Python date rollover

I'm trying to send a file after a loop completes while inside an endless loop. Everything works except the trigger for sending the file. How can I check if midnight passed when I was in the loop?
from datetime import date
while(True):
stamp = date.today()
for site in siteList:
# Long series of url requests and data processing
check = date.today()
if stamp != check: # <--- This doesn't work I don't know why
# Send today's file
As far as I can see, your code should work. Perhaps the problem lies elsewhere.
Have you tried printing stamp and check so you can see what values are being used?
Is your timezone set properly?
This is fragile and is going to cause you problems. The only way this will ever fire is if your processing actually spans midnight. This is because you update your check date every loop as well as your original date.
If you are committed to this, set the value of stamp outside the loop, and then update it inside your if block so that your send file function will always fire once per day.
However, starting a script on a machine and leaving it running for a long period of time with no supervision is just going to lead to heartache. You need some kind of job processor- I either have my webapp's work queue fire the periodic task, or if it is really external I have my Jenkins server run it- in either case I can look somewhere and see when things ran last and get notified if something goes wrong.
How can I check if midnight passed when I was in the loop?
Your code already does it if you mean the internal loop: date.today() returns only the date i.e., unless the day ends during the inner loop; stamp and check are always equal.
If you meant the outer loop then move stamp = date.today() outside of the outer loop and update stamp only on if the trigger is executed i.e., if stamp != check. btw, you should use a better names such as last_date and today. You could set last_date = None initially, to trigger the execution on the first pass.
I want it to do something once a day after the inner loop finishes
from datetime import date
last_date = None
while True: # outer loop
for site in siteList: # inner loop
"hours long computations here"
today = date.today()
if last_date is None or last_date < today:
last_date = today # jobs are executed on different days
"send today's file here"
In general, it is more flexible to separate the code that schedules a task from the code that defines the task itself e.g., you could create a cron job, to run something once a day. You could use locks, to wait for the inner loop to finish and to avoid running multiple jobs in parallel (cron doesn't protect against it). Two jobs may happen on the same day if the previous job took more than a day.

Categories