18

I have a simple Python script that uses a signal handler for Ctl-C. If the program completes normally, the end time is passed into the "print_results" function. I wanted the print_results function to have an optional parameter that, if not passed, simply gets the current "now" time. But when I call it from the signal handler, it does not get the correct time.

Here is my simplified, but reproducible, program:

import sys
import signal
import urllib2
import urllib
import datetime
import time
import getopt,sys

def signal_handler(signal, frame):
    print_results()
    sys.exit(0)

def print_results(ended=datetime.datetime.now()):
    print "\nEnded at ",ended
    print "Total time: ",(ended - startTime)
    print "Finished ",numIterations," iterations, received ",totalRecords," records"

    numIterations = 0
    maxIterations = 8
    delaySecs = 3
    totalRecords = 0

    # set up signal handler
    signal.signal(signal.SIGINT, signal_handler)

    startTime = datetime.datetime.now()

    print "Starting at ",time.asctime(time.localtime())

    while (numIterations < maxIterations):

        iterStartTime = datetime.datetime.now()

        numIterations += 1

        print "Iteration: ",numIterations

        # sleep if necessary

        if delaySecs > 0:
            time.sleep(delaySecs)

        iterEndTime = datetime.datetime.now()

        print "Iteration time: ",(iterEndTime - iterStartTime)

    endTime = datetime.datetime.now()

    print "Ended at ",time.asctime(time.localtime())
    print "Total test time: ",(endTime - startTime)

    print_results(endTime)

Here is what happens when I type Ctl-C

$ python test.py                                                           
Starting at  Fri Jun 15 08:28:15 2012
Iteration:  1
Iteration time:  0:00:03.003101
Iteration:  2
Iteration time:  0:00:03.003105
Iteration:  3
^C
Ended at  2012-06-15 08:28:15.766496
Total time:  -1 day, 23:59:59.999964
Finished  3  iterations, received  0  records

It seems like that when print_results is called with no arguments that the 'ended' value is not being interpreted correctly as a datetime object. But since Python does not have a way to cast (as far as I can tell), I cannot tell what is wrong.

Thanks in advance,

Mitch

3
  • 2
    Change while (numIterations < maxIterations): to for numIteration in range(maxIterations): Commented Jun 15, 2012 at 12:43
  • 1
    ... or xrange, for python 2.x Commented Jun 15, 2012 at 12:46
  • ...and get rid of the numIterations += 1. Commented Jun 15, 2012 at 14:38

1 Answer 1

65

The problem you are having is that you are evaluating the function in the parameter. This means that ended=datetime.datetime.now() takes the value of the time when this is being parsed, not when it is called. What you should do is something like this:

def print_results(ended=None):
    if ended is None:
        ended = datetime.datetime.now()
    ...

Here there is a really good explanation of why this happens: “Least Astonishment” in Python: The Mutable Default Argument

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks... that makes sense, but I clearly expected it to be evaluated at run-time...
@mitchmcc I think we all did when we started learning Python :)
I introduced a bug this way by naively using datetime.date.today() as a default argument. Curiously calling it repeatedly returns a variety of different days - not just one.
how about passing the object like def print_results(ended=datetime.datetime.now):
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.