Is there a Python profiler that preserves function arguments?

2020-03-11 Thread Go Luhng
I'm profiling a Python function `foo()` that takes a single argument,
but that argument makes a huge difference in what the function
actually does.

Currently I'm using `cProfile`, which records every call to `foo()` as
if it was the same, preventing me from figuring out what's going on.

Is there a way to get `cProfile`, or any other Python profiler, to
preserve function call arguments, so when I look at the call stack
later (especially using a visualizer like SnakeViz) I can distinguish
between `foo('bar')` and `foo('qux')`?

P.S. arguably this is a code design issue: since `foo('bar')` and
`foo('qux')` do very different things, they should be distinctly-named
separate functions, like `foo_bar()` and `foo_qux()`. However, the
question is whether I can profile them as-is, without refactoring.
-- 
https://mail.python.org/mailman/listinfo/python-list


Profiler showing path dependency?

2020-03-13 Thread Go Luhng
Consider a simple call graph: `main()` calls `foo()`, which calls
`bar()`. Then `main()` calls `qux()` which also calls `bar()`, but
with different parameters.

When you run the above through cProfile and view the result in
SnakeViz, you will see `main()` calling `foo()` and `qux()`, with each
of them calling `bar()`. However, if you hover or click on `bar()`,
you will see the global aggregate statistics for it. For example, the
number of times it has been called, and their total time cost.

Is there a way to get a path-dependent profiler breakdown for `bar()`?
 Specifically for this example, statistics for when it is called by
`foo()`, and separately statistics for when it is called by `qux()`.
-- 
https://mail.python.org/mailman/listinfo/python-list


Automatically advancing a bi-directional generator to the point of accepting a non-None value?

2020-11-21 Thread Go Luhng
Suppose we write a very simple bi-directional generator in Python:

def share_of_total():
s = 0
new_num = 0
while True:
new_num = yield new_num / (s or 1)
s += new_num

share_calculator = share_of_total()
next(share_calculator)  # Without this we get the TypeError

for num in [1, 2, 3]:
print(share_calculator.send(num))

This generator just accepts a number and yields a float representing
its share of the sum of all previously provided numbers.

We would ideally like to just use it immediately as follows:

share_calculator = share_of_total()
print(share_calculator.send(num))

However, this causes `TypeError: can't send non-None value to a
just-started generator`. All users of the `share_of_total()` must
remember to execute `next(share_calculator)` before the generator is
usable as intended.

Is there an elegant way to make `share_calculator` immediately usable
- i.e. to be able to immediately call `share_calculator.send(num)`
after creating `share_calculator`?

I know it can be done with a fairly trivial wrapper, but I was hoping
for a more elegant solution that doesn't require boilerplate.
-- 
https://mail.python.org/mailman/listinfo/python-list