Using mock.patch to disable a function behavior - python

I have a function adding subscription for my users. At the end of this function,
an instruction check the subscriptions and do some stuff with patron if necessary.
For my testing, I need to skip this last instruction ; so I tried to use mock.patch
but despite all my tries, the code is always executed (and my tests failed)
mymodule/classes.py
class User:
def add_subscription(self, start_date, end_date):
subscriptions = self.get('subscriptions', [])
subscriptions.append({'start': start_date, 'end': end_date})
self['subscriptions'] = subscriptions
check_subscriptions(self) # <-- skip this instruction for unitesting
def check_subscriptions(user):
print("Not need to print for unitesting")
# next doing more stuff...
...
tests/user_unitest.py
import mock
from mymodule.classes import User
from datetime import datetime, timedelta
...
def test_subscriptions(user_with_no_subscription_fixture):
user = user_with_no_subscription_fixture
start = datetime.now()
end = start + timedelta(days=10)
user.add_subscription(start, end)
...
How can I use the #mock.patch (or other mock function) to don't enter into the
User.check_subscriptions methods ?
What I tried (and didn't work)
#mock.patch('mymodule.classes.check_subscriptions')
def test_subscriptions(user_with_no_subscription_fixture):
user = user_with_no_subscription_fixture
start = datetime.now()
end = start + timedelta(days=10)
user.add_subscription(start, end)
def test_subscriptions(user_with_no_subscription_fixture):
with mock.patch('mymodule.classes.check_subscriptions'):
user = user_with_no_subscription_fixture
start = datetime.now()
end = start + timedelta(days=10)
user.add_subscription(start, end)
#mock.patch('mymodule.classes.check_subscriptions', MagicMock())
def test_subscriptions(user_with_no_subscription_fixture):
user = user_with_no_subscription_fixture
start = datetime.now()
end = start + timedelta(days=10)
user.add_subscription(start, end)
#mock.patch('mymodule.classes.check_subscriptions', side_effect=...)
def test_subscriptions(user_with_no_subscription_fixture):
user = user_with_no_subscription_fixture
start = datetime.now()
end = start + timedelta(days=10)
user.add_subscription(start, end)
thanks for your always usefull help

Related

Python loop to run after n minutes from start time

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)

Set timer for a Django view execution

I'm struggling trying to prevent a Django view from being executed more than once within an hour period. In other words, if the function runs at 15:00, all future requests for all users should be ignored until 17:00 when it's allowed to run once more again.
Tried with a timer, but it does get reset every time the view is called. Maybe someone can point me in the right direction? Thanks!!!
import threading as th
def hello():
print("hello, world")
def webhook(request):
tm = th.Timer(3600, hello)
if request.method == 'POST' and not tm.is_alive():
tm.start()
code_to.ecexute()
return HttpResponse("Webhook received!")
Ultimately, this is what I did and it seems to work fine. I actually need it to run no more than once a day, hence the conditional below.
Thanks for all the suggestions!!!
def webhook2 (request):
today = datetime.now().date()
with open('timestamp.txt') as f:
tstamp = f.read()
last_run = datetime.strptime(tstamp, '%Y-%m-%d')
last_run_date = datetime.date(last_run)
print ("last run: " + str(last_run_date))
if last_run_date < today:
file = open("timestamp.txt" ,"w")
file.write(str(today))
file.close()
if request.method == 'POST':
msg = str(request.body)
final_msg=msg[2:-1]
print("Data received from Webhook is: ", request.body)
# creates a google calendar event
function_logic()
return HttpResponse("Webhook received! Event added to calendar")
else:
print ("we already have a record for today")
return HttpResponse("Not adding a record. We already have one for today.")
Your timer is being reset everytime because it is inside a function that is execute everytime when request is being made.
You should try to set the timer globally, such as outside your function. ( be aware when your script will re-run, timer will be reset again ).
import threading as th
def hello():
print("hello, world")
tm = None
def webhook(request):
# check here if timer is dead then process the request.
if timer_is_dead || tm is None:
# accessing global value and setting it for first time
if tm is None:
global tm
tm = th.Timer(3600, hello)
tm.start()
if request.method == 'POST' and not tm.is_alive():
code_to.ecexute()
# start timer again for next hour
return HttpResponse("Webhook received!")
else:
return HttResponse("Not Allowed")
Edit: Handling first request and then starting timer

Python using class with loops working with time and datetime doesn't reset the timers

I have tried to work with a while loop to keep my script running, I use time to track the time it takes to run. Simpler code it's something like this:
import time
import datetime
class Test:
start = time.time()
dateStart = datetime.datetime.now()
def end(self):
time.sleep(10)
print(time.time() - self.start)
print(datetime.datetime.now() - self.dateStart)
while True:
test = Test()
test.end()
and also tried with:
import time
import datetime
class Test:
start = time.time()
dateStart = datetime.datetime.now()
def end(self):
time.sleep(10)
print(time.time() - self.start)
print(datetime.datetime.now() - self.dateStart)
def runClass(classtest):
test = classtest()
test.end()
while True:
runClass(Test)
But the timers are not set to 0 after each reset, they accumulate. The rest of the values I have in my big class do reset to the initial value they have after the class is created, but not dates. Is there something I'm missing about how python manage time and classes? I'm starting to use classes more, so maybe I'm missing something about how they are define in memory?
What you are missing is how classes are defined and instances created.
Your code:
class Test:
start = time.time()
dateStart = datetime.datetime.now()
executes exactly once when class Test is defined.
What you meant to write was this:
class Test:
def __init__(self)
self.start = time.time()
self.dateStart = datetime.datetime.now()
...
The __init__() method is executed every time test = Test() executes which is what you are after.
btw, I think that your line: test = classtest() should be test = Test()

How to update time variable string in python-telegram-bot

I've created echo chat telegram bot with python-telegram-bot. It'll echo everything that I typed in with time. But the problem is It always echoes same time string since the bot start.
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, Dispatcher
import logging
import datetime
logging.basicConfig(format='%(levelname)s - %(message)s',
level=logging.DEBUG)
logger = logging.getLogger(__name__)
updater = None
t = datetime.now()
dt_string = time.strftime("%b/%d/%Y %H:%M:%S", t)
sitepath=""
filename="output.txt"
def updatetime():
t = datetime.now()
dt_string = time.strftime("%b/%d/%Y %H:%M:%S", t)
def repeater(update, context):
updatetime()
update.message.reply_text("Done: " + update.message.text + "\n"+ dt_string)
def start_bot():
global updater
updater = Updater(
'##Token##', use_context=True)
dispatcher = updater.dispatcher
dispatcher.add_handler(MessageHandler(Filters.text, repeater))
updater.start_polling()
updater.idle()
start_bot()
Expected result is
Done: This is a message
Feb/05/2021 15:13:34
Done: 10 Second have passed
Feb/05/2021 15:13:44
Done: 10 Second have passed
Feb/05/2021 15:13:54
But this is the actual result
Done: This is a message
Feb/05/2021 15:13:34
Done: 10 Second have passed
Feb/05/2021 15:13:34
Done: 10 Second have passed
Feb/05/2021 15:13:34
You need to add global keyword in the methods to make sure you use the global variable dt_string else it will create a local variable and you would not be updating the global variable.
def updatetime():
global dt_string
t = datetime.now()
dt_string = time.strftime("%b/%d/%Y %H:%M:%S", t)
You need to do the same for all methods and all variables.
Note that the use of global is not recommended so you should try to refactor your code to avoid use of global variables.
The solution by Krishna Chaurasia is right about everything: the use of global variables and the not recommended use of global variables.
A simpler solution is to remove def updatetime() entirely and insert the dt_string on repeater function, like this:
def repeater(update, context):
## dt_string will update everytime you call repeater
## datetime.now() it's not necessary
dt_string = time.strftime("%b/%d/%Y %H:%M:%S")
update.message.reply_text("Done: " + update.message.text + "\n"+ dt_string)
This will make you code short and easier to read.

unusual result in measuring time in python

This is my stopwatch class
import time
class StopWatch:
def __init__(self, tag="", startTime=time.time(), elapsedTime=-1):
self._tag=tag
self._elapsedTime=elapsedTime
self._startTime=startTime
# other codes here............#
def stop(self, tag=None):
if tag is not None:
self._tag = tag
self._elapsedTime = time.time() - self._startTime
return self
def getStartTime(self):
return self._startTime
And stopwatch test file is:
import unittest
import time
from metric.StopWatch import StopWatch
class StopWatchTest(unittest.TestCase):
def test_stopwatch(self):
now=time.time()
print "now:%f" % now
stopwatch=StopWatch("firstHook")
print "start time:%r" % stopwatch.getStartTime()
self.assertTrue(stopwatch.getStartTime()>now, "start time is wrong")
What I get is error with:
now:1364791630.047630
start time:1364791629.158797
..
AssertionError: start time is wrong
The code to be executed later is showing earlier time. What is the reason behind this?
Default arguments are evaluated only once.
def __init__(self, tag="", startTime=time.time(), elapsedTime=-1):
self._tag=tag
self._elapsedTime = elapsedTime
self._startTime = startTime
To fix, we need to check the defaults explicitly and evaluate them in the function body:
def __init__(self, tag="", startTime=None, elapsedTime=-1):
self._tag=tag
self._elapsedTime = elapsedTime
if startTime is None:
startTime = time.time()
self._startTime = startTime
The reason is that this line:
def __init__(self, tag="", startTime=time.time(), elapsedTime=-1):
Is evaluated when it is reached by the Python interpreter (because despite the name it actually compiles the code, albeit to byte code). So startTime is set when the class is defined, not when __init__ is called.
Change it to:
def __init__(self, tag="", start_time=None, elapsed_time=-1):
if start_time is None:
start_time = time.time()
self._tag=tag
self._elapsed_time=elapsed_Time
self._start_time=start_time

Categories

Resources