where_am_i for Python

Have you ever found yourself within a very long function and wondered, "Where am i?"?

The where_am_i utility outputs the class and function name/s that a specified statement belongs to. It also outputs the conditions that must be satisfied for program execution to reach the specified statement.

Development of where_am_i
where_am_i in action

Development of where_am_i

I used TDD ("Test Driven Development") to develop where_am_i.

I am a TDD fanatic! With TDD quality is not an afterthought.

Tests drive development in TDD: Automated tests are written first, the code to pass those tests (the application) is written second.

Tests are written first, code to pass those tests is written second. For too long it's been the norm for application code to be written first, and then sufficient tests are written, well... maybe never.

With TDD sufficient tests are written. The application code which passes those tests is then written.

I often add tests to my test suite after starting to implement an application. Tests can be added incrementally: Special cases that need to be handled may first be realised while the code to handle a general case is being implemented. So a new test case is added and the code to get it to pass is then written. Important to note is that nothing new is implemented in the application until a new test has been written: If a feature isn't important enough to bother testing, then don't bother implementing it!

While doing TDD I look for ways to "break" the application. Looking for ways to break an application (and then fixing it) makes TDD enjoyable and satisfying. It really is fun! And it is satisfying: I can feel confident that what I develop will function reliably.

TDD helps turn a complex development job into manageable tasks: Get each test to pass in succession and you're done!

When considering whether to use TDD: Do you want to just churn out lines of code, or do you want to develop software that works reliably?

Write your tests first!

where_am_i in action

Seeing where_am_i in action will help understand what it does.

Here is part of edit_sizers.py from wxGlade:

664            misc.wxCallAfter(
665                wx.MessageBox, _('Name "%s" is already in use.\n'
666                'Please enter a different one.') % value, _("Error"),
667                wx.OK|wx.ICON_ERROR)
668            self.name_prop.set_value(self.name)
669            return
670        if not re.match(self.set_name_pattern, value):
671            self.name_prop.set_value(self.name)
672        else:
673            oldname = self.name
674            self.name = value
675            self._btn.set_menu_title(value)
676            try: common.app_tree.refresh_name(self.node, oldname) #, self.name)
677            except AttributeError:

Let's say I'm interested in finding out when line 675 is reached.

where_am_i.py edit_sizers.py 675 outputs:

class SizerBase(Sizer):
    def set_name(self, value):
        if not config.preferences.allow_duplicate_names and \
               (self.widget and common.app_tree.has_name(value, self.node)):
        if not re.match(self.set_name_pattern, value):

where_am_i has output just those statements that relate to whether line 675 is reached.

The first two lines state the class name and the function name. The statement defining the start of the class was approximately two hundred lines above: It's a good thing I didn't have to hunt that down myself.

Lines 3 and 4 specify a condition that if true will result in the function being exited via the return statement on line 669: If this condition is true line 675 cannot be reached. Note that if the statements that are conditionally executed due to the if didn't contain a return then where_am_i would not output the condition because line 670 would be reached regardless of whether the condition was true or not.


Last update: 8 September 2011