Python test using mock with datetime.utcnow() - python

I have the following function in my utils.py basically count the seconds from 1970 till curent time:
import datetime
def get_utc_timestamp():
d = datetime.datetime.utcnow()
epoch = datetime.datetime(1970, 1, 1)
t = (d - epoch).total_seconds()
return t
I want to run a test case on that function but it's time dependent so i looked for a solution and stumble upon this question on SO link i tried to apply it in my test_utils.py:
import unittest
from utils import *
from unittest import mock
import datetime
class TestUtils(unittest.TestCase):
#mock.patch('utils.datetime.datetime')
def test_get_utc_timestamp(self, mock_dt):
mock_dt.utcnow = mock.Mock(return_value = datetime.datetime(2019, 8, 27, 8, 52, 12, 703618))
result = get_utc_timestamp()
self.assertEqual(result, 1566895932.703618)
if __name__ == '__main__':
unittest.main()
I tested the result of it in the console:
d = datetime.datetime(2019, 8, 27, 8, 52, 12, 703618)
epoch = datetime.datetime(1970, 1, 1)
t = (d - epoch).total_seconds()
return t
And it returned
1566895932.703618
But when i run the test i got AssertionError:
Traceback (most recent call last):
File "/usr/local/lib/python3.6/unittest/mock.py", line 1179, in patched
return func(*args, **keywargs)
File "/app/tests/test_utils.py", line 15, in test_get_utc_timestamp
self.assertEqual(result, 1566895932.703618)
AssertionError: <MagicMock name='datetime().__sub__().total_seconds()' id='140475857850040'> != 1566895932.703618
What am i doing wrong ?
Any help would be appreciate!
EDIT:
thank ipaleka for the explanation on what going on, since i can't change python built - in class with mock so i need to make a custom class of utcnow() to return the custom time in test_utils.py:
class NewDate(datetime.datetime):
#classmethod
def utcnow(cls):
return cls(2019, 8, 27, 8, 52, 12, 703618)
datetime.datetime = NewDate
and change test function to:
def test_get_utc_timestamp(self):
result = get_utc_timestamp()
self.assertEqual(result, 1566895932.703618)

You're not doing anything wrong related to referenced SO answer, but that example uses only utcnow function while you're using both utcnow and datetime (for creating epoch variable).
When you patch a module then every call to submodule, method or its function creates a MagicMock. That happens when you called epoch = datetime.datetime(1970, 1, 1). So basically you're comparing MagicMock to float.
You should either patch them both or patch just the utcnow with:
#mock.patch('utils.datetime.datetime.utcnow')
def test_get_utc_timestamp(self, mock_dt):
mock_dt.return_value = datetime.datetime(2019, 8, 27, 8, 52, 12, 703618)
result = get_utc_timestamp()
self.assertEqual(result, 1566895932.703618)

Related

How to correctly use enterabs from scheduler

I'm trying to schedule something to run periodically, using the code below:
import sched
import time
from datetime import datetime, timedelta
def foo(s, t_end):
now = datetime.now()
print(now)
# Schedule the next run of this function, but only if it'll be before 't_end'
next_run = now + timedelta(seconds=1)
if next_run <= t_end:
s.enter(1, 1, foo, (s, t_end,))
# s.enterabs(next_run, 1, foo, (s, t_end,)) # <-- why doesn't this work?
if __name__ == '__main__':
s = sched.scheduler(time.time, time.sleep)
t_end = datetime.now() + timedelta(seconds=5)
foo(s, t_end)
s.run()
This runs exactly like it should... the script calls foo exactly 5 times and then exits (with 1 second delay between the calls).
But if I change the s.enter(...) to s.enterabs(...) and pass in the calculated time when foo should be called again, it throws the following error:
Traceback (most recent call last):
File "/tmp/test.py", line 18, in <module>
s.run()
File "/usr/lib/python3.9/sched.py", line 141, in run
if time > now:
TypeError: '>' not supported between instances of 'datetime.datetime' and 'float'
What am I doing wrong?
OK I figured this out. In the call to s.enterabs(...), I need to pass next_run.timestamp() as the first argument (not just next_run).

How do I solve the NameError: name 'randomResponce' is not defined

This the code that has the error:
print("Through 1-10 write a number that is going to represent how far you should throw the ball for " + playerCMD4 + "to catch") ; sleep(float(speed))
playerNumberCMD = raw_input()
import random
def allResponses(arg1):
allResponses = arg1
allResponses = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def randomResponse(arg1):
randomResponse = arg1
randomResponse = random.choice(allResponses)
if randomResponce == playerNumberCMD:
print(playerCMD4 + " caught the ball.") ; sleep(float(speed))
The error I get is this:
Traceback (most recent call last):
File "classref3.py", line 3, in <module>
class Dog:
File "classref3.py", line 50, in Dog
if randomResponce == playerNumberCMD:
NameError: name 'randomResponce' is not defined
Was Defnot the correct way to go or is it something else?
As the error states you have not defined the variable "randomResponce".
If you look at the previous line where you think you have defined the variable, you have defined "randomResponse". Note the different spelling. The spelling needs to be the same.
I would also caution against using the same name for a variable and a function in the same script.

How to add tzinfo to date?

I have the following vars:
time_created = datetime.utcnow() and time_created_day = datetime.utcnow().date().
I cannot save time_created_day to the db because of AttributeError: 'datetime.date' object has no attribute 'tzinfo'
How to fix this (add tzinfo)?
Ok, here is one way that I did this.
If you need to add tzinfo, you may use the below.
from datetime import datetime
import pytz
time_created_day = datetime.datetime.utcnow().date()
time_created_day_with_tz_info = datetime(time_created_day.year,
time_created_day.month, time_created_day.day, tzinfo=pytz.timezone('UTC'))
> print time_created_day_with_tz_info
> datetime.datetime(2018, 5, 9, 0, 0, tzinfo=<UTC>)

JSON serializing Mongodb

I am using the python package pymongo to retrieve data from a mongodb database.
>>> r = collection.find() # returns an object of class 'Cursor'
Then I convert to a list
>>> l = list(r) # returns a 'list' of 'dict'
here is what print(l) returns:
>>> [{u'date': datetime.datetime(2009, 11, 10, 10, 45), u'_id': 1, u'name': u'name1', u'value': 11},{u'date': datetime.datetime(2013, 11, 10, 10, 45), u'_id': 2, u'name': u'name2', u'value': 22}]
Now I need to convert to JSON so that I can manipulate it.
>>> json.dumps(l)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/json/__init__.py", line 231, in dumps
return _default_encoder.encode(obj)
File "/usr/lib/python2.7/json/encoder.py", line 201, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python2.7/json/encoder.py", line 264, in iterencode
return _iterencode(o, 0)
File "/usr/lib/python2.7/json/encoder.py", line 178, in default
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: datetime.datetime(2009, 11, 12, 11, 14) is not JSON serializable
I have also tried to follow http://api.mongodb.org/python/1.7/api/pymongo/json_util.html without success:
Edit: the recent version of the link is http://api.mongodb.org/python/current/api/bson/json_util.html
>>> json.dumps(l, default=json_util.default)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'json_util' is not defined
Note: precisely I need to push this result to R using the R package rPython and its function rPython::python.get("l")
Side Question: What is the u (u'Date', u'name', etc..) before each field in the list of dict?
The pymongo documentation you pointed is obsolete. If you're using version 1.7 I recommend updating. With a more recent version you can do this:
from bson.json_util import dumps
dumps(l)
https://pymongo.readthedocs.io/en/stable/api/bson/json_util.html
Side answer: u'name', u'date', u'_id' etc are the names of the fields of the document on the database.
from bson import json_util
json.dumps(result,default=json_util.default)
In my situation, this error is due to mongo DB id object in flask
all you have to do is convert id (NOTE: if you need id convert it else you can pop it too)
I'm sharing my solution which I figured out hope this helps someone
from flask import jsonify
def get_data(self,data):
data['_id'] = str(data['_id'])
return data
app = Flask(__name__)
#app.route('/')
def apimethod():
temp = [self.get_data(i) for i in self.collection.find()]
return jsonify(temp)
also dumps from pymongo don't help alot
from bson.json_util import dumps,loads
because it is returning a string instead of dict which was expected in my situation to create API and I have to load again if I did dumps.
This thread helped me - thank you.
Wanted to share my final solution to get the JSON back into a JSON/Dictionary Object: (Based on your example)
from bson.json_util import dumps, loads
r = collection.find()
l = list(r) # Converts object to list
d = dumps(l) # Converts to String
dict_needed = loads(d[0]) # Serializes String and creates dictionary
Now you have the JSON in a dictionary object and can edit as needed.
I was facing the same issue, I wrote a code that converts document to dictionary. You can use that for reference. Pass the object obtained by find_one() into documentToJson() method and the results of find() into convertDocumentsToJson. There is type in the name Json, instead the code converts to Dict rather than json.
from bson.json_util import dumps
class UtilService:
def __init__(self):
pass
#staticmethod
def pinCodeParser(path):
location = {}
f = open(path)
for line in f:
words = line.split()
location[words[1]] = (words[-3],words[-2])
return location
#staticmethod
def listHelper(str):
s = []
str = str.split(',')
for e in str:
s.append(e.replace("[","").replace("]",""))
return s
#staticmethod
def parseList(str):
if ',' in str:
return UtilService.listHelper(str)
return str
#staticmethod
def trimStr(str):
return str.replace('"','')
#staticmethod
def documentToJson(document):
document = eval(dumps(document))
mp = {}
for key, value in document.iteritems():
if "_id" in key:
mp["id"] = str(value["$oid"])
else:
mp[ UtilService.trimStr(key) ] = UtilService.parseList( value )
return mp
#staticmethod
def convertDocumentsToJson(documents):
result = []
for document in documents:
result.append(UtilService.documentToJson(document))
return result

python debug straightforward datetime object

I cannot understand why this is failing, can someone please explain:
import datetime
def test(timestamp):
xt = datetime(2013, 4,4)
dt = datetime.strptime(timestamp, '%d/%m/%Y %H:%M:%S')
print (xt,dt)
test('04/04/2013 08:37:20')
The error is:
Traceback (most recent call last):
File "", line 12, in
File "", line 5, in test
TypeError: 'module' object is not callable
It seems to work ok with from datetime import datetime instead. I can't understand what the difference is.
Thanks.
Because in the datetime module, there is a datetime() function, but you didn't call it ( you instead tried to call the module, that's why you got a TypeError: 'module' object is not callable).
import datetime
def test(timestamp):
xt = datetime(2013, 4,4) # Problem is this line.
dt = datetime.strptime(timestamp, '%d/%m/%Y %H:%M:%S')
print (xt,dt)
test('04/04/2013 08:37:20')
To fix the line, change it to:
xt = datetime.datetime(2013,4,4)
The reason from datetime import datetime worked was because you imported the specific function, and so you wouldn't need to do datetime.datetime().
datetime is both a module AND a class in that module. In your example you need datetime.datetime.
The
xt = datetime(2013, 4,4)
should be
xt = datetime.datetime(2013, 4,4)
Here, datetime is the name of the module. The full name of the class is datetime.datetime.
datetime is the datetime module, datetime.datetime is the datetime type:
import datetime
def test(timestamp):
xt = datetime.datetime(2013, 4,4)
dt = datetime.datetime.strptime(timestamp, '%d/%m/%Y %H:%M:%S')
print (xt,dt)
Here datetime can refer to the module or the class within the module so you have to disambiguate
import datetime
def test(timestamp):
xt = datetime.datetime(2013, 4,4)
dt = datetime.datetime.strptime(timestamp, '%d/%m/%Y %H:%M:%S')
print (xt,dt)
test('04/04/2013 08:37:20')
gives the following output:
(datetime.datetime(2013, 4, 4, 0, 0), datetime.datetime(2013, 4, 4, 8, 37, 20))

Categories

Resources