Is Python's code marshalling reversible? - python

Consider the following script:
from marshal import dumps, loads
def square_fn(x):
return x * x
def test_serialize_marshal():
code_string = dumps(square_fn.__code__)
code_string1 = dumps(loads(code_string))
assert code_string == code_string1
Run in Python 3.6 with pytest.
This test is failing on my computer. Why is this so?
I am trying to serialize a Python function in a way that I can test, from its serialized representation, that the function is the same... This test suggests that this is not the case for marshal dumps/loads.
EDIT:
Interestingly, this test passes:
def test_serialize_marshal2():
import types
code = dumps(square_fn.__code__)
ff = types.FunctionType(loads(code), globals())
assert square_fn.__code__ == ff.__code__
which is what this answer tests. However, I do not want the equality of the Python object, but of the string.

Related

Python Django - How to Pass HTTPS URL as function parameter in manage.py shell

I'm trying to call a function insert_db in the python shell. insert_db requires an https url to call some other functions.
In Terminal in my Django python project folder, I type './manage.py shell' to get the python shell for Django going.
After that:
>>> from send_values.api.send_to_db import insert_db
>>> insert_db('https://www.encodeproject.org/search/?searchTerm=H3K4ME3&type=Experiment&replication_type=isogenic&assembly=GRCh38&award.rfa=ENCODE4&format=json')
I'm getting the following error:
requests.exceptions.MissingSchema: Invalid URL 'h': No schema supplied. Perhaps you meant http://h?
send_to_db.py code:
# python send_to_db.py 'https://www.encodeproject.org/search/?searchTerm=H3K4ME3&type=Experiment&replication_type=isogenic&assembly=GRCh38&award.rfa=ENCODE4&format=json'
from ..models import Correlations
from .lambda_async_s3_uri import filter_complete
import sys
def insert_db(args):
print(args)
table_values = filter_complete(args)
for value_set in table_values:
new_value_set = Correlations.objects.create(value_set)
# new_value_set = Correlations.objects.create(experimentName=item[3], colLabel=item[5], rowLabel=item[4], rowNum=item[0], colNum=item[1], cor=item[2])
new_value_set.save()
def main(args):
insert_db(args)
if __name__ == '__main__':
main(sys.argv[1:])
Any help is appreciated. Please let me know if you need more information.
Notes:
1) I know the insert_db function works like it is supposed to (taking the url, downloading some files, parsing through them for some data, and making some calculations from that data). Before making this Django project, in a pure Python project, I used to call it like this:
python send_to_db.py 'https://www.encodeproject.org/search/?searchTerm=H3K4ME3&type=Experiment&replication_type=isogenic&assembly=GRCh38&award.rfa=ENCODE4&format=json'
At that earlier point, instead of trying to add it to the Django database, I was just printing the values insert_db would calculate (which it successfully outputted in the terminal). Passing the url through the terminal with this bash command worked then, and I'm wondering how to do this in a python.
2) I also tried the following in ./manage.py shell:
>>> from send_values.api.send_to_db import insert_db
>>> url = 'https://www.encodeproject.org/search/?searchTerm=H3K4ME3&type=Experiment&replication_type=isogenic&assembly=GRCh38&award.rfa=ENCODE4&format=json'
>>> insert_db(url)
But I still get the same error:
requests.exceptions.MissingSchema: Invalid URL 'h': No schema supplied. Perhaps you meant http://h?
Just a wild guess - I didn't check what filter_complete() is supposed to do -, but given what your working code looks like and the error message, it seems it expects a list or tuple, not a string. If that's right, you should call it this way:
>>> from send_values.api.send_to_db import insert_db
>>> url = 'https://www.encodeproject.org/search/?searchTerm=H3K4ME3&type=Experiment&replication_type=isogenic&assembly=GRCh38&award.rfa=ENCODE4&format=json'
>>> # make the argument a list
>>> insert_db([url])
If this solves the issue, you may then want to update your code to use *varargs instead:
from ..models import Correlations
def insert_db(*args):
print(args)
table_values = filter_complete(args)
for value_set in table_values:
new_value_set = Correlations.objects.create(value_set)
new_value_set.save()
def main(*args):
insert_db(*args)
if __name__ == '__main__':
main(*sys.argv[1:])
And then you can call it as expected:
>>> insert_db('https://some.url.here')

PyTest-Mock not working due to AttributeError

I am trying to use PyTest_Mock in order to do some testing in my Python project. I created a very simple test to try it out, but I am getting an AttributeError and I don't know why.
model.py
def square(x):
return x * x
if __name__ == '__main__':
res = square(5)
print("result: {}".format(res))
test_model.py
import pytest
from pytest_mock import mocker
import model
def test_model():
mocker.patch(square(5))
assert model.square(5) == 25
After running python -m pytest I get a failure and the following error:
def test_model():
> mocker.patch(square(5))
E AttributeError: 'function' object has no attribute 'patch'
test_model.py:7: AttributeError
You don't need to import mocker, it's available as fixture, so you just pass it as a parameter in the test function:
def test_model(mocker):
mocker.patch(...)
square(5) evaluates to 25, so mocker.patch(square(5)) will effectively try to patch a number 25. Instead, pass the function name as parameter: either
mocker.patch('model.square')
or
mocker.patch.object(model, 'square')
Once patched, square(5) will not return 25 anymore since the original function is replaced with a mock object that can return anything and will return a new mock object by default. assert model.square(5) == 25 will thus fail. Usually, you patch stuff either to avoid complex test setup or simulate behaviour of components that is desired in test scenario (for example, a website being unavailable). In your example, you don't need mocking at all.
Complete working example:
import model
def test_model(mocker):
mocker.patch.object(model, 'square', return_value='foo')
assert model.square(5) == 'foo'

Running a Python unittest test on multiple files

I want to add unit testing to the assessment of my high school programming class.
If I have twenty submissions of files that look like this:
def calculateReturn(principle, rate, freq, time):
final = principle * (1 + (rate/freq)) ** (freq * time)
return final
Can I use a test case like this?
import unittest
class test(unittest.TestCase):
def test1(self):
value = calculateReturn(5000, 0.05, 12, 11)
self.assertAlmostEqual(value, 8235.05, 2)
if __name__ == '__main__':
unittest.main()
How do I run this one simple test on twenty modules?
FURTHER INFORMATION
For testing I have created three "submissions" all of which show different ways of calculating x^y.
submission1.py:
from math import pow
def powerFunction(base, power):
result = pow(base, power)
return result
submission2.py:
def powerFunction(base, power):
result = base ** power
return result
submission3.py:
def powerFunction(base, power):
result = 1
for i in range(power):
result = result * base
return result
The test code is:
import unittest
import importlib
class MyTest(unittest.TestCase):
def setUp(self):
pass
def test_power_3_4(self):
self.assertEqual(module.powerFunction(2, 3), 8)
files = ['submission1', 'submission2', 'submission3']
for file in files:
module = importlib.import_module(file)
print module
unittest.main()
if the test code is run the console output shows only submission1 being tested:
/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7
/Users/staff/PycharmProjects/UnitTest/powerTest.py
<module 'submission1' from '/Users/staff/PycharmProjects/UnitTest/
submission1.pyc'>
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Process finished with exit code 0
Interestingly if I don't use unit testing I can correctly import and test using this approach:
import importlib
files = ['submission1', 'submission2', 'submission3']
for file in files:
module = importlib.import_module(file)
print module
print module.powerFunction(2,3)
The console output here is:
/System/Library/Frameworks/Python.framework/Versions/2.7/bin/
python2.7 /Users/staff/PycharmProjects/UnitTest/importlib1.py
<module 'submission1' from '/Users/staff/PycharmProjects/UnitTest/
submission1.pyc'>
8.0
<module 'submission2' from '/Users/staff/PycharmProjects/UnitTest/
submission2.pyc'>
8
<module 'submission3' from '/Users/staff/PycharmProjects/UnitTest/
submission3.pyc'>
8
Process finished with exit code 0
It may well be that the unittest module is not the best approach here but I'm still interested on how to implement it.
You can use importlib to load Python modules from specific files, then run the test cases on each one.
glob may be helpful to create the list of files.
Given that this has been active for a month with no answers I have come the the realisation that it is because I'm asking for the wrong thing.
From what I can gather, unittest is for running a suite of tests on a single application. It is not designed to run a single test on a suite of applications.
John's suggestion to investigate importlib helped set me on the path to success. Thanks John.
The code posted in the original post update seems to be the most appropriate solution to my problem.

How can I mock/patch an associative array in python

I have a module with a dictionary as associative array to implement a kind-of switch statement.
def my_method1():
return "method 1"
def my_method2():
return "method 2"
map_func = {
'0': my_method1,
'1': my_method2
}
def disptach(arg):
return map_func[arg]()
How can I mock my_method1 in tests? I've tried the following without success:
import my_module as app
#patch('my_module.my_method1')
def test_mocking_sample(self, my_mock):
my_mock.return_value = 'mocked'
assert_equal('mocked',app.dispatch('0'))
Any idea?
This piece of patch documentation says the following:
patch works by (temporarily) changing the object that a name points to
with another one. There can be many names pointing to any individual
object, so for patching to work you must ensure that you patch the
name used by the system under test.
Basically, your dispatcher won't see it, as the mapping is built to reference the original method, before the patch is applied.
The simplest thing you can do to make it mockable is to fold the mapping into the dispatch function:
def dispatch(arg):
return {
'0': my_method1,
'1': my_method2
}[arg]()
This does have the downside that it rebuilds that mapping every time you call it, so it will be slower.
Trying to get a bit clever, it seems that Python lets you swap out the actual code of a function, like so:
>>> f = lambda: "foo"
>>> a = f
>>> g = lambda: "bar"
>>> f.func_code = g.func_code
>>> a()
'bar'
I won't recommend that you do it this way, but maybe you can find a mocking framework that supports something similar.
As you've discovered, patching my_Method1() does not work. This is because map_func['0'] was defined when my_module was imported and subsequent changes to my_Method1() do not update map_func for your test. Instead, we need to patch the value in dictionary map_func for key '0' directly. The unittest.mock documentation explains how to patch a dictionary entry. Below is a working implementation of your test:
""" test_my_module.py
"""
import unittest
import unittest.mock as mock
import my_module as app
my_mock = mock.MagicMock()
class Test_mock_sample(unittest.TestCase):
#mock.patch.dict('my_module.map_func', {'0': my_mock})
def test_mocking_sample(self):
my_mock.return_value = 'mocked'
self.assertEqual('mocked', app.dispatch('0'))
if __name__ == '__main__':
unittest.main()
After changing disptach to dispatch in your original my_module...
""" my_module.py
"""
def my_method1():
return "method 1"
def my_method2():
return "method 2"
map_func = {
'0': my_method1,
'1': my_method2
}
def dispatch(arg):
return map_func[arg]()
Then the command python -m unittest test_my_module gives the following output:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
It worked!

Mocking urllib2.urlopen().read() for different responses

I am trying to mock the urllib2.urlopen library in a way that I should get different responses for different urls I pass into the function.
The way I am doing it in my test file now is like this
#patch(othermodule.urllib2.urlopen)
def mytest(self, mock_of_urllib2_urllopen):
a = Mock()
a.read.side_effect = ["response1", "response2"]
mock_of_urllib2_urlopen.return_value = a
othermodule.function_to_be_tested() #this is the function which uses urllib2.urlopen.read
I expect the the othermodule.function_to_be_tested to get the value "response1" on first call and "response2" on second call which is what side_effect will do
but the othermodule.function_to_be_tested() receives
<MagicMock name='urlopen().read()' id='216621051472'>
and not the actual response. Please suggest where I am going wrong or an easier way to do this.
The argument to patch needs to be a description of the location of the object, not the object itself. So your problem looks like it may just be that you need to stringify your argument to patch.
Just for completeness, though, here's a fully working example. First, our module under test:
# mod_a.py
import urllib2
def myfunc():
opened_url = urllib2.urlopen()
return opened_url.read()
Now, set up our test:
# test.py
from mock import patch, Mock
import mod_a
#patch('mod_a.urllib2.urlopen')
def mytest(mock_urlopen):
a = Mock()
a.read.side_effect = ['resp1', 'resp2']
mock_urlopen.return_value = a
res = mod_a.myfunc()
print res
assert res == 'resp1'
res = mod_a.myfunc()
print res
assert res == 'resp2'
mytest()
Running the test from the shell:
$ python test.py
resp1
resp2
Edit: Whoops, initially included the original mistake. (Was testing to verify how it was broken.) Code should be fixed now.

Categories

Resources