Archive for April, 2012

Throttling Your Network Connection on Mac OS X

Wednesday, April 11th, 2012

Sometimes you just need to sloooooow doooooooown to test how your software behaves when your internet connection is crappy.

Linux has tc to do this, but what about Mac OS X?

That’s where ipfw comes in. It does a lot of stuff. I mean a lot, but we’re just going to use it to slow down our internet connection today.

Here’s an example that throttles your web browsing experience to 50 KBytes/second:

sudo ipfw pipe 1 config bw 50KByte/s >/dev/null
sudo ipfw add 1 pipe 1 src-port 80
sudo ipfw add 1 pipe 1 dst-port 80

And to turn it off (this is an important step!):

sudo ipfw delete 1

To make this super easy to use, I wrote a handy little shell script called network-throttle, which you can put in your PATH and run like this:

network-throttle on --port 80 --rate 50KByte/s

And to turn it off:

network-throttle off

You can download the shell script below. Put it in your PATH and name it network-throttle.

Or, if you like things shiny, pointy, and clicky, you can use the Apple Network Link Conditioner by installing X-Code.

Here’s the magical shell script:

#!/bin/bash
#
# Throttles your Mac OS X internet connection on one port.
# Handy for testing

set -e

RATE=15KByte/s
PORT=80
PIPE_NUMBER=1
ACTION=

function usage()
{
    echo $1
    echo
    echo "Usage: `basename "$0"` <action> [options]"
    echo "  Action:"
    echo "     on"
    echo "     off"
    echo
    echo "  Options:"
    echo "  --rate <rate>"
    echo "      Example: --rate 100KByte/s"
    echo "  --port <port> (default is 80 if you don't specify --port)"
    echo "      Example: --port 80"
    exit 1
}

function turn_throttling_off()
{
    echo "Turning off network throttling"
    sudo ipfw delete $PIPE_NUMBER || echo "Is it already turned off?"
}

function turn_throttling_on()
{
    echo "Throttling traffic to port $PORT: $RATE"
    sudo ipfw pipe $PIPE_NUMBER config bw $RATE >/dev/null
    sudo ipfw add $PIPE_NUMBER pipe $PIPE_NUMBER src-port $PORT >/dev/null
    sudo ipfw add $PIPE_NUMBER pipe $PIPE_NUMBER dst-port $PORT >/dev/null
}

# Grab command line args:
while [ -n "$1" ]; do
  case $1 in
    --rate)
      shift
      RATE=$1
      ;;
    --port)
      shift
      PORT=$1
      ;;
    *)
      ACTION=$1
  esac
  shift
done

[ -n "$ACTION" ] || usage "Error: no action specified"

case $ACTION in
  on)
    turn_throttling_off >/dev/null 2>&1 # in case it's already on, clear out the old one
    turn_throttling_on
    ;;
  off)
    turn_throttling_off
    ;;
  *)
    usage "Error: Bad action specified"
    ;;
esac

Dangerous Python Default Arguments

Sunday, April 1st, 2012

Dangerous? Really? Well, not if you understand how it works.

Note: I’m not the first to write about this subject.

When writing a function in Python, it’s handy to use default argument values like this:

And you think this will provide an easy way to let lazy callers pass no arguments to your function, and it will simply be called as if they had passed ['some item'].

But you would be wrong.

What actually happens is this (interactive shell output):

That’s right. Python creates a new object, named some_list that persists as an attribute of the function. If callers don’t pass their own some_list object, this one, the same one, is used each time your function is called.

Weird, huh?

If users pass their own some_list object, then sanity is restored:

So why is this? Python stores each default argument value in a special attribute on the function called func_defaults. You can inspect any function’s default arguments like this:

Because functions in Python are just objects, you can store arbitrary attributes on them. And indeed, you can actually modify the default function arguments at run time, like this:

But remember: Just because you can doesn’t mean you should. I would not recommend making a habit of stuff like this, especially if you like not having your co-workers hate you.

It’s always good to know how your tools work. Inside and out.

I discovered this behavior when investigating the pylint W0102 message, and discovered that this message actually inspired an entire wiki of Pylint message descriptions.