First, it looks like code/speed.py has no control on the output filename... Hardcoded output filenames are usually considered bad practice in scons (see yacc tool). It would read better like this:
speed = Command('speed_analysis.tex', [], 'python code/speed.py -o $TARGET')
Now, the PDF target produces a report.pdf from report.tex. I'm guessing there's an implicit dependency from report.text to speed_analysis.tex (through Tex include or something like that).
This:
Depends(report, speed)
Is correct to express that dependency if it's missing. Though I'm surprised the scanner for the PDF builder did not see that implicit dependency...
You should verify the dep tree using:
scons --tree=all
It should look something like this:
+ report.pdf
+ report.tex
+ speed_analysis.tex
+ code/speed.py
+ /usr/bin/python
+ /usr/bin/pdflatex
Now, to answer your question about the script (speed.py) always running, that's because it has no input. There's nothing for scons to check against. That script file must be reading something as an input, if only the py file itself. You need to tell scons about all direct and implicit dependencies for it to short-circuit subsequent runs:
Command('speed_analysis.text', 'code/speed.py', 'python $SOURCE -o $TARGET')