I need to patch a logic in an instance method of a library I am using.
Sample code for brevity. The process method is using the connect method of the Client library as is but I want to modify it to use a different logic instead of parsing_logic_1. What is the best approach to do this? How do I access the class variables like url if I add a new _patched_connect method in the Usage class?
class Client:
def __init__(self, url)
self.url = url
def connect (self):
if self.url == 'a':
self._parsing_logic_1()
if self.url == 'b':
self._parsing_logic_2()
else:
pass
def _parsing_logic_1(self):
pass
def _parsing_logic_2(self):
pass
def send(self):
pass
# ----separate file --------
class Usage:
def __init__(self, client: Client):
self.client = client
def process(input):
self.client.connect() # the connect method should use a different parsing logic for one case
self.client.send()
You need to import your class and just replace the method with a modified method.
In python you can replace a method dynamically as you wish.
Example:
from x.y.z import Client
def mynewconnect(self):
# your code here
self.url = "..."
pass
Client.connect = mynewconnect
I want to create an Abstract Factory in order to abstract hardware differences between computers (say a RaspberryPi and an Arduino) in Python 2.7.
I am using the following implementation of an Abstract Factory:
'''
Provide a device-agnostic display interface
'''
from hardware import sysname
class DisplayBase(object):
def __init__(self):
pass
def show(self, message):
pass
def __str__(self):
return "DisplayBase"
def __repr__(self):
return self.__str__()
class RPIDisplay(DisplayBase):
def __new__(cls, *args, **kwargs):
from rpi_display import writeline
instance = super(RPIDisplay, cls).__new__(cls, *args, **kwargs)
return instance
def __str__(self):
return "RPIDisplay"
def show(self, message):
writeline(message)
class ArduinoDisplay(DisplayBase):
def __new__(cls, *args, **kwargs):
import arduino_display
instance = super(ArduinoDisplay, cls).__new__(cls, *args, **kwargs)
return instance
def __str__(self):
return "ArduinoDisplay"
def show(self, message):
return arduino_display.println(message)
class Display(DisplayBase): # Display Factory
def __new__(cls, *args, **kwargs):
platform = sysname()
if platform == "RaspberryPi":
return RPIDisplay()
elif platform == "Arduino":
return ArduinoDisplay()
else:
return MockDisplay()
if __name__ == "__main__":
display = Display()
print display
display.show("hello world")
The instantiation works fine, but when I try to run this, I get:
ArduinoDisplay
Traceback (most recent call last):
File "tt.py", line 56, in <module>
display.show("hello world")
File "tt.py", line 41, in show
return arduino_display.println(message)
NameError: global name 'arduino_display' is not defined
So the import of arduino_display does sorta work, but I cannot find a way to use it within the object.
Conditional imports are needed since different platforms will have different modules installed.
Any idea how to use those conditional imports?
I tried self.arduino_display and ArduinoDisplay.arduino_display but to no avail.
I could obviously catch import errors, as in, add to the top:
try:
import arduino_display
except:
pass
...and that would fail on a RPI, which would be fine, but there must be a better way...
The problem is due to import only binding names in its current scope. Doing an import in a function/method does not make it available in other methods.
Perform the imports where you actually need them. For example, ArduinoDisplay should import arduino_display where it is used:
class ArduinoDisplay(DisplayBase):
# no new, no import
def __str__(self):
return "ArduinoDisplay"
def show(self, message):
# import where needed
import arduino_display
return arduino_display.println(message)
Note that import is idempotent -- if the module has already been loaded before, import just binds the name again. This makes such nested import statements fast enough for most cases.
If your classes need many imports or speed is an issue, isolate classes into separate modules and import the entire module conditionally. You can directly assign the correct class using the common name, instead of having a dummy type that constructs another.
# ## display/arduino.py ##
# other systems accordingly
from .base import DisplayBase
# import once, globally
import arduino_display
class ArduinoDisplay(DisplayBase):
# no new, no import
def __str__(self):
return "ArduinoDisplay"
def show(self, message):
# import where needed
return arduino_display.println(message)
# ## display/__init__.py ##
from hardware import sysname
platform = sysname()
# resolve and import appropriate type once
if platform == "RaspberryPi":
from .rpi import RPIDisplay as Display
elif platform == "Arduino":
from .arduino import ArduinoDisplay as Display
else:
from .mock import MockDisplay as Display
Have you tried using from arduino_display import println then use println just like you did with the RPIs writeline?
Edit: missed the obvious...
You can do somthing like this:
from hardware import sysname
class Display(object):
def __init__(self, print_function, display_name):
self._print_function = print_function
self._display_name = display_name
def show(self, message):
self.print_function(message)
def __str__(self):
return self._display_name
def __repr__(self):
return self.__str__()
def create_display():
platform = sysname()
if platform == "RaspberryPi":
from rpi_display import writeline
return Display(writeline, "RPIDisplay")
elif platform == "Arduino":
from arduino_display import println
return Display(println, "ArduinoDisplay")
In case you need the classes and the nested factory, you can apply the same principle of storing the function object there too.
The problem is that the function you import aren't saved anywhere for the other methods to see them, so they are not visible anywhere else but locally. Try setting ArduinoDisplay.arduino_display = arduino_display after you import.
Here's some sample code that illustrates my meaning:
class ADisplay:
def __init__(self):
from builtins import print as display_write
def show(self, msg):
display_write(msg)
disp = ADisplay()
disp.show("mymsg")
This fails with NameError: name 'display_write' is not defined.
Now, bind the import to your class.
class ADisplay:
def __init__(self):
from builtins import print as display_write
ADisplay.display_write = display_write
def show(self, msg):
self.display_write(msg)
disp = ADisplay()
disp.show("mymsg")
This works. In fact you can even dispense with show by assigning it directly, if all those hardware print methods only take a string as a parameter and if you don't need to format or modify anything.
class ADisplay:
def __init__(self):
#do other init stuff...
pass
#only bind show the first time.
if getattr(ADisplay, "show", None):
return
from builtins import print as display_write
ADisplay.show = display_write
disp = ADisplay()
disp.show("mymsg")
I want to use my own class RetrySession that is inherited from requests.Session, but with more robust try/else checking (for non-200 status codes, timeouts, etc.) and extra logging around the get() function.
# retry_session.py
import requests
import logging
logger = logging.getLogger(__name__)
# Oversimplified...
class RetrySession(requests.Session):
def get_(self, url, **kwargs):
logger.info(url)
return self.get(url, **kwargs)
Is there any way to keep the original function name of get() without like temporary copying the function? Right now, I'm doing something like:
from retry_session import *
# Logging defined here
s = RetrySession()
# Would like to do s.get(...) instead of s.get_(...)
r = s.get_("https://httpbin.org/get")
You need to use super() to call the method from the superclass, also there is no new keyword to instanciate object
class RetrySession(requests.Session):
def get(self, url, **kwargs):
print("Requesting", url)
return super().get(url, **kwargs)
if __name__ == '__main__':
s = RetrySession()
r = s.get("https://httpbin.org/get")
I am trying to mock the return of db.collection.find to avoid database calls. I tried to create a mock.patch for mongoclient() and attached on it a return_value. But when my_call() call db.collection.find it just return a Mock Object. Somebody have a idea how mock it?
#dao.py
class MyDao():
def my_call():
db = mongoclient().db_name
result = db.collection.find()
return result
#test_dao.py
import dao
def test_my_call():
result = dao.my_call()
assert result == list()
I think it would help to see your current mock to edit. In general, this is how I would mock the pymongo collection.find() as a standalone function.
test_dao.py
import unittest
import mock
from mock import Mock
import pymongo
class Test_Dao(unittest.TestCase):
"""
Set up the mock
"""
#classmethod
#mock.patch('pymongo.collection')
def setUpClass(self, mock_mongo):
a = Mock()
a.find.side_effect = self.findResponse # Assign side effect for mocked method
mock_mongo.return_value = a # Importing pymongo.collection returns the mock
"""
Response Data
"""
findResponse = 'some data'
def test_my_call():
result = dao.my_call()
assert result == list()
...
In your case that is worth a try but might not work because you are calling collection.find() from a variable.
You may need to mock the MongoClient() such that db_name has a side_effect to return a fake class. Then the fake class would need a collection.find() method you define. That would be a little longer and look a little bit like this:
class FakeCollection:
def find:
return 'data'
class FakeDatabase:
def collection:
return FakeCollection()
...
#classmethod
#mock.patch('pymongo.MongoClient')
def setUpClass(self, mock_mongo):
a = Mock()
a.db_name.side_effect = self.dbResponse
mock_mongo.return_value = a
dbResponse = FakeDatabase()
Using python 2.7, celery 3.0.24 and mock 1.0.1. I have this:
class FancyTask(celery.Task):
#classmethod
def helper_method1(cls, name):
"""do some remote request depending on name"""
return 'foo' + name + 'bar'
def __call__(self, *args, **kwargs):
funcname = self.name.split()[-1]
bigname = self.helper_method1(funcname)
return bigname
#celery.task(base=FancyTask)
def task1(*args, **kwargs):
pass
#celery.task(base=FancyTask)
def task2(*args, **kwargs):
pass
how can I patch helper_method1 while testing either task?
I've tried something like:
import mock
from mymodule import tasks
class TestTasks(unittest.TestCase):
def test_task1(self):
task = tasks.task1
task.helper_method1 = mock.MagickMock(return_value='42')
res = task.delay('blah')
task.helper_method1.assert_called_with('blah')
and the test is failing. The original function is the one being called. And no, this question didn't help me.
(I don't have a celery instance up and running so it's difficult for me to test this)
The target function in your application code is a classmethod. The function your test code is mocking is an instance method.
Does changing the test_task1 like this help -
def test_task1(self):
FancyTask.helper_method1 = mock.MagickMock(return_value='42')
task = tasks.task1
res = task.delay('blah')
task.helper_method1.assert_called_with('blah')
You probably also need to change the assert_called_with so it is called from the class level instead of the instance level.
change
task.helper_method1.assert_called_with('blah')
to
FancyTask.helper_method1.assert_called_with('blah')