[ python module variable not mutable as a function_default ]
I need to unit-test a python module by changing a default in the head of the application module called process.py. The declared default is a fixed int. I could change it to use something else from os.environ, but I was hoping to simply assign the global, but clearly I am missing some comprehension around 'def'.
process.default_timeout_secs = 2
# --name: process
default_timeout_secs = 120
def call_process(cmd, timeout=default_timeout_secs):
print 'run:', cmd, 'timeout:', timeout
...
# --name: test_process
from nose.tools import *
import process
@ raises(TimeoutExpired)
def test_process_run_modified_default():
process.default_timeout_secs = 5
run(sleep 10)
I understand from other posts, that the process.call_process.func_defaults value for default_timeout_secs is not the one in the top of the module when the module is imported. How do I change the default used in the function?
process.default_timeout_secs = 5 process.call_process.func_globals['default_timeout'] 5 process.call_process.func_defaults (120)
Out[21]: 5
>>> process.call_process(cmd)
Out[22]: call_process: cmd= sleep 2 timeout= 120 <----- 120?
Executing cmd sleep 2 verbose=True
The output should show exception TimoutExpired.
Answer 1
Function defaults are evaluated at definition time, not call time (see "Least Astonishment" in Python: The Mutable Default Argument).
The only way to access and modify the function defaults are through its __defaults__
property (func_defaults
in older versions):
>>> def f(a=5):
... return a
>>> f.__defaults__
(5,)
>>> f.__defaults__ = (99,)
>>> f()
99
Note that __defaults__
is a tuple so you cannot assign its members individually, but you can assign it as a whole.
Answer 2
d = list(process.call_process.func_defaults)
In [10]: d
Out[10]: [True, True, 120]
In [11]: d[-1] = 5
In [12]: d
Out[12]: [True, True, 5]
In [13]: t = tuple(d)
In [14]: t
Out[14]: (True, True, 5)
In [15]: process.call_process.func_defaults = t
process.call_process('sleep 8')
call_process(cmd, verbose, shell, timeout, **kwargs)
94 except:
95 print(str(c.getName()) + 'running cmd "'+ cmd+ '" could not be terminated')
---> 96 raise TimeoutExpired('calling '+cmd)
97 else:
98 return c.response
TimeoutExpired: calling sleep 8
In [17]: call_process result: 0 :--:-None-:
Answer 3
Concerning your original question about changing a default for testing purpose, you might want to use a dictionary which is a mutable object. See my answer there for details.