I am trying to build a Strategy Pattern using S.Lott's response.
Problem is function returns None.
Am using Hickey's Simple vs Easy {what, how, who}-logic.
-[WHAT] I/O
class base_fnc(object):
def fncExc(self,data1,data2):
return
-[HOW] DATA <> queue[where,when] (direct injection)
class stump( base_fnc ):
def fncExc(self, d1, aContext):
return d1
class MAB(base_fnc ):
def fncExc(self, d, aContext ):
return d+10
-[WHO] API
class context( object ):
def __init__(self, alt_how_class ):
self.how = alt_how_class
def exc(self, d ):
return self.how.fncExc(d, self)
if __name__ == "__main__":
c1 = context(MAB())
ss=c1.exc(10)
print ss
ss prints None
You're not returning in exc. You need to do return self.how.fncExc(d, self).
Related
#these classes live inside exchanges/impl/tse/mixins.py
class PacketContext:
capture_tstamp = None
def __init__(self, capture_tstamp=None):
self.capture_tstamp = capture_tstamp
class SubParserMixin():
def __init__(self):
self.context = PacketContext()
def on_packet(self, packet):
self.context.capture_tstamp = packet.capture_timestamp
self.parse_er_data(packet.payload)
#this mock test lives in another python file
from exchanges.impl.tse.mixins import PacketContext
#patch.object(PacketContext, 'capture_tstamp', 1655417400314635000)
def test_receive_timestamp(self):
"""
test receive_timestamp is passed down correctly from PacketContext to on_packet()
"""
assert self.context.capture_tstamp == 1655417400314635000
I am trying to mock the self.capture_tstamp attribute in the PacketContext() class.
But in the above, I am getting an error that says
AssertionError: assert None == 1655417400314635000
E + where None = <exchanges.impl.tse.mixins.PacketContext object at 0x7fb324ac04c0>.capture_tstamp
E + where <exchanges.impl.tse.mixins.PacketContext object at 0x7fb324ac04c0> = <tests.unit.exchanges.tse.test_quote_write.TestTSE testMethod=test_receive_timestamp>.context
It seems very strange that the program is not recognising PacketContext().
You can make use of the patch.object decorator as below
class PacketContext:
capture_tstamp = None
def __init__(self, capture_tstamp=None):
self.capture_tstamp = capture_tstamp
<import_PacketContext_here>
#patch.object(PacketContext, 'capture_tstamp', 1655417400314635000)
def test_receive_timestamp():
test_instance = PacketContext()
assert test_instance.capture_tstamp == 1655417400314635000
I have several string processing functions like:
def func1(s):
return re.sub(r'\s', "", s)
def func2(s):
return f"[{s}]"
...
I want to combine them into one pipeline function: my_pipeline(), so that I can use it as an argument, for example:
class Record:
def __init__(self, s):
self.name = s
def apply_func(self, func):
return func(self.name)
rec = Record(" hell o")
output = rec.apply_func(my_pipeline)
# output = "[hello]"
The goal is to use my_pipeline as an argument, otherwise I need to call these functions one by one.
Thank you.
You can write a simple factory function or class to build a pipeline function:
>>> def pipeline(*functions):
... def _pipeline(arg):
... result = arg
... for func in functions:
... result = func(result)
... return result
... return _pipeline
...
>>> rec = Record(" hell o")
>>> rec.apply_func(pipeline(func1, func2))
'[hello]'
This is a more refined version written with reference to this using functools.reduce:
>>> from functools import reduce
>>> def pipeline(*functions):
... return lambda initial: reduce(lambda arg, func: func(arg), functions, initial)
I didn't test it, but according to my intuition, each loop will call the function one more time at the python level, so the performance may not be as good as the loop implementation.
You can just create a function which calls these functions:
def my_pipeline(s):
return func1(func2(s))
Using a list of functions (so you can assemble these elsewhere):
def func1(s):
return re.sub(r'\s', "", s)
def func2(s):
return f"[{s}]"
def func3(s):
return s + 'tada '
def callfuncs(s, pipeline):
f0 = s
pipeline.reverse()
for f in pipeline:
f0 = f(f0)
return f0
class Record:
def __init__(self, s):
self.name = s
def apply_func(self, pipeline):
return callfuncs(s.name, pipeline)
# calling order func1(func2(func3(s)))
my_pipeline = [func1, func2, func3]
rec = Record(" hell o")
output = rec.apply_func(my_pipeline)
TypeError: _slow_trap_ramp() takes 1 positional argument but 2 were given
def demag_chip(self):
coil_probe_constant = float(514.5)
field_sweep = [50 * i * (-1)**(i + 1) for i in range(20, 0, -1)] #print as list
for j in field_sweep:
ramp = self._slow_trap_ramp(j)
def _set_trap_ramp(self):
set_trap_ramp = InstrumentsClass.KeysightB2962A.set_trap_ramp
return set_trap_ramp
def _slow_trap_ramp(self):
slow_trap_ramp = ExperimentsSubClasses.FraunhoferAveraging.slow_trap_ramp
return slow_trap_ramp
The error is straightforward.
ramp = self._slow_trap_ramp(j)
You are calling this method with an argument j, but the method doesn't take an argument (other than self, which is used to pass the object).
Re-define your method to accept an argument if you want to pass it one:
def _slow_trap_ramp(self, j):
It looks like your code extract contains methods of some class, whose full definition is not shown, and you are calling one method from another method (self._slow_trap_ramp(j)). When you call a method, Python automatically passes self before any other arguments. So you need to change def _slow_trap_ramp(self) to def _slow_trap_ramp(self, j).
Update in response to comment
To really help, we would need to see more of the class you are writing, and also some info on the other objects you are calling. But I am going to go out on a limb and guess that your code looks something like this:
InstrumentsClass.py
class KeysightB2962A
def __init__(self):
...
def set_trap_ramp(self):
...
ExperimentsSubClasses.py
class FraunhoferAveraging
def __init__(self):
...
def slow_trap_ramp(self, j):
...
Current version of main.py
import InstrumentsClass, ExperimentsSubClasses
class MyClass
def __init__(self)
...
def demag_chip(self):
coil_probe_constant = float(514.5)
field_sweep = [50 * i * (-1)**(i + 1) for i in range(20, 0, -1)] #print as list
for j in field_sweep:
ramp = self._slow_trap_ramp(j)
def _set_trap_ramp(self):
set_trap_ramp = InstrumentsClass.KeysightB2962A.set_trap_ramp
return set_trap_ramp
def _slow_trap_ramp(self):
slow_trap_ramp = ExperimentsSubClasses.FraunhoferAveraging.slow_trap_ramp
return slow_trap_ramp
if __name__ == "__main__":
my_obj = MyClass()
my_obj.demag_chip()
If this is the case, then these are the main problems:
Python passes self and j to MyClass._slow_trap_ramp, but you've only defined it to accept self (noted above),
you are using class methods from KeysightB2962A and FraunhoferAveraging directly instead of instantiating the class and using the instance's methods, and
you are returning references to the methods instead of calling the methods.
You can fix all of these by changing the code to look like this (see embedded comments):
New version of main.py
import InstrumentsClass, ExperimentsSubClasses
class MyClass
def __init__(self)
# create instances of the relevant classes (note parentheses at end)
self.keysight = InstrumentsClass.KeysightB2962A()
self.fraun_averaging = ExperimentsSubClasses.FraunhoferAveraging()
def demag_chip(self):
coil_probe_constant = float(514.5)
field_sweep = [50 * i * (-1)**(i + 1) for i in range(20, 0, -1)] #print as list
for j in field_sweep:
ramp = self._slow_trap_ramp(j)
def _set_trap_ramp(self):
# call instance method (note parentheses at end)
return self.keysight.set_trap_ramp()
def _slow_trap_ramp(self, j): # accept both self and j
# call instance method (note parentheses at end)
return self.fraun_averaging.slow_trap_ramp(j)
if __name__ == "__main__":
my_obj = MyClass()
my_obj.demag_chip()
I am trying to understand how to mock in python external dependencies while doing mock methods argument matching and argument capture.
1) Argument matching:
class ExternalDep(object):
def do_heavy_calc(self, anInput):
return 3
class Client(object):
def __init__(self, aDep):
self._dep = aDep
def invokeMe(self, aStrVal):
sum = self._dep.do_heavy_calc(aStrVal)
aNewStrVal = 'new_' + aStrVal
sum += self._dep.do_heavy_calc(aNewStrVal)
class ClientTest(unittest.TestCase):
self.mockDep = MagicMock(name='mockExternalDep', spec_set=ExternalDep)
###
self.mockDep.do_heavy_calc.return_value = 5
### this will be called twice regardless of what parameters are used
### in mockito-python, it is possible to create two diff mocks (by param),like
###
### when(self.mockDep).do_heavy_calc('A').thenReturn(7)
### when(self.mockDep).do_heavy_calc('new_A').thenReturn(11)
###
### QUESTION: how could I archive the same result in MagicMock?
def setUp(self):
self.cut = Client(self.mockDep)
def test_invokeMe(self):
capturedResult = self.cut.invokeMe('A')
self.assertEqual(capturedResult, 10, 'Unexpected sum')
# self.assertEqual(capturedResult, 18, 'Two Stubs did not execute')
2) Argument Capturing
I cannot find good docs or examples on neither MagicMock or mockito-python able to accommodate the following mocking scenario:
class ExternalDep(object):
def save_out(self, anInput):
return 17
class Client(object):
def __init__(self, aDep):
self._dep = aDep
def create(self, aStrVal):
aNewStrVal = 'new_' + aStrVal if aStrVal.startswith('a')
self._dep.save_out(aNewStrVal)
class ClientTest(unittest.TestCase):
self.mockDep = MagicMock(name='mockExternalDep', spec_set=ExternalDep)
###
self.mockDep.save_out.return_value = 5
### this will be called with SOME value BUT how can I capture it?
### mockito-python does not seem to provide an answer to this situation either
### (unline its Java counterpart with ArgumentCaptor capability)
###
### Looking for something conceptually like this (using MagicMock):
### self.mockDep.save_out.argCapture(basestring).return_value = 11
###
### QUESTION: how could I capture value of parameters with which
### 'save_out' is invoked in MagicMock?
def setUp(self):
self.cut = Client(self.mockDep)
def test_create(self):
capturedResult = self.cut.create('Z')
self.assertEqual(capturedResult, 5, 'Unexpected sum')
### now argument will be of different value but we cannot assert on what it is
capturedResult = self.cut.create('a')
self.assertEqual(capturedResult, 5, 'Unexpected sum')
If anyone could show me how to accomplish these two mocking scenarios (using MagicMock), I would be very grateful! (Please ask if something is unclear.)
Something that might help you is to use assert_called_with with a Matcher.
This will allow you to have a finer grain access to the arguments on your calls. i.e.:
>>> def compare(self, other):
... if not type(self) == type(other):
... return False
... if self.a != other.a:
... return False
... if self.b != other.b:
... return False
... return True
>>> class Matcher(object):
def __init__(self, compare, some_obj):
self.compare = compare
self.some_obj = some_obj
def __eq__(self, other):
return self.compare(self.some_obj, other)
>>> match_foo = Matcher(compare, Foo(1, 2))
>>> mock.assert_called_with(match_foo)
I am working on a django site and want to define some instance methods like below.
Class Auth(models.Model):
def wrap_has_perm(self, perm):
def wrap():
if self.is_staff and self.has_perm(perm):
return True
else:
return False
can_add_order = wrap_has_perm('finance.normal')
can_review_order = wrap_has_perm('finance.review')
is_leader = wrap_has_perm('finance.leader')
is_finance = wrap_has_perm('finance.finance')
I want to use can_add_order, can_review_order, is_leader, is_finance as django admin site's list_display element. But now these instance methods is illegal.(TypeError: wrap_has_perm() takes exactly 2 arguments (1 given))
How can I achieve these methods?
If I use partial:
def staff_has_perm(self, perm):
return self.is_staff and self.has_perm(perm)
can_add_order = partial(staff_has_perm, perm='finance.normal')
can_review_order = partial(staff_has_perm, perm='finance.review')
is_leader = partial(staff_has_perm, perm='finance.leader')
is_finance = partial(staff_has_perm, perm='finance.finance')
It raises (* TypeError: staff_has_perm() takes exactly 2 arguments (1 given));
Should I pass self to and how to ?
Move the self to wrap()'s definition:
def wrap_has_perm(perm):
def wrap(self):
However, a more Pythonic way to do this might be to use functools.partial:
from functools import partial
class Auth(models.Model):
def has_perm(self, perm):
# ...
can_add_order = partial(has_perm, perm='finance.normal')
can_review_order = partial(has_perm, perm='finance.review')
is_leader = partial(has_perm, perm='finance.leader')
is_finance = partial(has_perm, perm='finance.finance')