Add support for spy operations.
Review Request #11153 — Created Aug. 30, 2020 and submitted
A spy operation is an action or set of possible actions that are
performed when a function is called. They're an alternative to
overriding a function explicitly, and instead work as reusable shortcuts
for common patterns used when spying. Examples would be raising an
exception, or dispatching a function based on the arguments provided in
the call.These can be thought of as glorified functions, and in fact they
basically are, under the hood, but are designed to be extensible so that
projects can more easily maintain common spy patterns.There are some built-in operations provided by kgb:
-
SpyOpMatchAny
allows a caller to provide a list of all possible sets
of arguments that may be in one or more calls, triggering spy behavior
for the particular match (allowingcall_original
/call_fake
to be
conditional on the arguments). Any call not provided in the list will
raise anUnexpectedCallError
assertion. -
SpyOpMatchInOrder
is similar toSpyOpMatchAny
, but the calls
must be in the order specified (which is useful for ensuring an order
of operations). -
SpyOpRaise
takes an exception instance and raises it when the
function is called (preventing a caller from having to define a
wrapping function). -
SpyOpReturn
takes a return value and returns it when the function is
called (similar to defining a simple lambda, but better specifying the
intent).
These are set with an op=
argument, instead of a call_fake=
. For
example::
spy_on(pen.emit_poison, op=kgb.SpyOpRaise(PoisonEmptyError()))
Or, for one of the more complex examples:
spy_on(traps.trigger, op=kgb.SpyOpMatchAny([
{
'args': ('hallway_lasers',),
'call_fake': _send_wolves,
},
{
'args': ('trap_tile',),
'call_fake': _spill_hot_oil,
},
{
'args': ('infrared_camera',),
'kwargs': {
'sector': 'underground_passage',
},
'call_original': False,
},
]))
Unit tests pass on all supported versions of Python.
Plugged each of these operations into some real-world code and made sure
they worked as expected.
Summary | ID |
---|---|
7222f42482f4d8206ba3258681d01f4dcf543a85 |
Description | From | Last Updated |
---|---|---|
F401 'kgb.calls.SpyCall' imported but unused |
reviewbot | |
E501 line too long (82 > 79 characters) |
reviewbot | |
F401 'kgb.agency.SpyAgency' imported but unused |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot | |
F841 local variable 'spy' is assigned to but never used |
reviewbot |