I have a Python application in which I would like to monitor the number of calls to functions in the standard random module, at runtime; is there any nice way to do this, or do I have to "manually" do it?
5 Answers
It seems to me that Python profiler should be able to do it "nicely". Look at a post about Python profiling on SO.
At runtime, it seems that the decorators are the way to go. Of course, the default random module does not have the decorators, so perhaps you need to define a monitoredrandom module with decorators that proxys the random module and counts the calls. If you keep the function names and signatures the same as in random, you only have to modify the imports in your code.
-
+1 For the idea. Unless of course they need access to this information for some reason programmatically or during run time (of course that would have been important information to include in the question) – Levon Jun 24 '12 at 14:24
-
Indeed, I need to monitor this at runtime (I edited the original question to reflect this). – adrianp Jun 24 '12 at 14:33
-
Thanks for the +1! I'd look into the profiler code to get further ideas on how. Once there was a PyMonitor project that used the garbage collector for run time monitoring of Python programs, but whether that would serve your purpose requires further investigation. – jpe Jun 24 '12 at 14:35
Here's a dirty solution:
import functools
def monitor(f):
@functools.wraps(f)
def decorated(*args, **kwargs):
print("{}: {}, {}".format(f.__name__, args, kwargs))
# Do whatever you want: increment some counter, etc.
return f(*args, **kwargs)
return decorated
import random
for name in dir(random):
f = getattr(random, name)
if callable(f) and not name.startswith('_'):
setattr(random, name, monitor(f))
This code should be executed at the very beginning of the program (can be in a separate module), before any other code, including imports.

- 33,184
- 10
- 89
- 99
Make a wrapper function that increments the count and then makes the call and returns the result.
If you consider that "manual", then yes.

- 62,466
- 11
- 102
- 153
-
1You could also monkey patch the `random` module to replace the functions with the wrappers, to reduce the impact on the codebase. – millimoose Jun 24 '12 at 14:17
The best I can think of is proxying - I don't believe replacing sys.modules with a custom dict-esque object is possible. Note I don't like this and on your own head be it...
class random(object):
def __init__(self):
from collections import defaultdict
import random as r_
self.R = r_
self.__lookups = defaultdict(int)
def __getattr__(self, name):
self.__lookups[name] += 1
return getattr(self.R, name)
@property
def freq(self):
return self.__lookups
A horrible kludge but is effectively a proxy to the standard module. How you get it into a namespace I'm really not sure - perhaps put it in a module, then (making names up now) from random_testing import random
instead of the normal import random
.
I do notice that running two randint
via this method gives me a count of six - so not entirely sure how the internals work there....

- 138,671
- 33
- 247
- 280
cProfile
returns the number of calls into each function.
Something like this should work:
import my_module
import cProfile
cProfile.run('my_module.my_func()')
where my_module.my_func is the entry point to your program. This outputs the following:
7 function calls in 0.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 bar.py:4(bar)
1 0.000 0.000 0.000 0.000 foo.py:4(foo)
1 0.000 0.000 0.000 0.000 my_module.py:4(my_func)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
2 0.000 0.000 0.000 0.000 {method 'random' of '_random.Random' objects}
The last line shows that overall there were 2 calls to the random method (which were in 2 distinct modules in my toy example, foo.py and bar.py).
edit: of course, this is only useful if you want the count offline, as pointed out in a comment.

- 8,434
- 3
- 31
- 54