Showing posts with label tip-n-trick. Show all posts
Showing posts with label tip-n-trick. Show all posts

Tuesday, November 5, 2013

Pattern of Error Reporting in Python

Instead of Intro


That is not an uncommon and even not rare when an application which was supposed to be a prototype suddenly becomes a tool for everyday usage. Not a perfect thing here is that in a rush to make the program more or less user-friendly a developer has to hide internals by preserving error messages with internal details. Here I am going to share the pattern I mostly use in my "so-called-prototype" Python scripts; especially when they are translated to binaries using py2exe.

The pattern allows to introduce an extended error reporting in Python scripts w/o any extra costs.

Own Errors


The only rule I follow when there is a need to raise an error is to forget about standard ready-to-use Python exceptions. Due the following reasons:

  • Need to distinguish where an error came from: from the "Batteries" or from the application's logic;
  • Need to express an application's domain in the code.
The rule is true even for a spaghetti-style code which is supposed to be thrown away tomorrow or even today; this will cost nothing but might help with debugging.

So introduce own exception class:

class Error(Exception):
    def __init__(self, message, innerError = None):
        msg = message
        if innerError:
            msg += " *- {0}".format(str(innerError))

        Exception.__init__(self, msg)

The exception class here is straight forward for the sake of simplicity. In serious applications it is much better to introduce a field for an inner error, environment etc.

Respect Each Error


When a script's function pass flow control to another one there is a bell that the flow goes to another virtual layer. Each new layer is worth to have own logged error if there is any.

Suppose below that foo() is a first layer, bar() is a second one. So the application might look like:

def tryRussianRoulette():
    ### NOTE: that is a very bad practice to put import statements somewhere in a logic
    import random
    
    isFired = (0 == random.randint(0, 5))
    if isFired:
        raise Error("bang!")
        
def bar():
    try:
        tryRussianRoulette()
    except Error, e:
        raise Error("Russian Rouletter has fired", e)

def foo():
    try:
        bar()
    except Error, e:
        raise Error("Failed to bar-bar", e)

def main():
    foo()

def propagateToUser(error):
    if isinstance(error, Error):
        print "[Error]", str(e)
    else:
        print "[Unknown error]", str(e)

if "__main__" == __name__:   
    try:
        main()
    except Exception, e:
        propagateToUser(e)
        sys.exit(1)

    sys.exit(0)


tryRussianRoulette() is a function which may cause an error.
When application is accidentally in a production, an error for end-user (!) would look like:

[Error] Failed to bar-bar *- Russian Rouletter has fired *- bang!

In most cases (if you have not skipped error handling on each layer), the error is descriptive enough to understand the problem.

Pattern in Action


How to introduce an ability for an extended error tracing w/o writing tons of extra code? The answer is to vary try/except statement's behavior depending on system's environment variable bound to an application.

The application's code above is just extended with the function:

def isDebug():
    withLettersOnly = lambda string: filter(lambda ch: ch.isalpha(), string)
    appName = os.path.basename(sys.argv[0])
    debugKey = "{appName}_DEBUG".format(appName = withLettersOnly(appName))

    return os.environ.get(debugKey, False)

and try/except statement will be replaced with the following code:

    try:
        main()
    except Exception, e:
        if isDebug():
            raise
        else:
            propagateToUser(e)
            sys.exit(1)

If the newly developed application is run from a file named bing-bang.py, environment variable bingbangpy_DEBUG set to a non-empty value will cause a raw Python's stack trace instead user-friendly error. The similar is true for a Python's script compiled using py2exe; guess a bound environment variable name.

Instead of Summary


  1. The pattern code has been intentionally left primitive for one reason: to allow your to play around and find a suitable implementation;
  2. Introduced own exception class could contain locals() and globals() of a corresponding layer; or system details. That totally depends on your fantasy;
  3. The pattern works well for small scripts; and for "proof-of-concepts" applications which might be used in real-life until RTM. Avoid the approach in the case of more or less serious applications.


Wednesday, September 4, 2013

Run Unit Test automatically for Python code

Have you ever thought how often you write the tests for the newly developed code? I believe that more likely -- every time. Even a tiny tool which will never leave the developer's sandbox, which is supposed to show that the implemented logic is correct might be called a "test"; it is not perfect, not maintainable, but it is intended to do at least one thing -- to check the code. I did so. Currently I prefer to write unit tests rather than such tiny tools; for the new as well as for the legacy code. If there is a file with the implementation -- there should be a corresponding file with the tests.

I am still Emacs user but sometimes for reasons unknown I use Sublime Text to create something using Python. So currently it is become boring a bit to:
  1. Introduce the change in a file with a SUT;
  2. Switch to the next tab in Sublime Text with a source code of the test;
  3. Press a hot key to run the test to check the fixes.
I would prefer to replace these steps with the one. Here goes a simple straightforward solution to run unit tests for Python code in Sublime Text automatically; these tips are also applicable to other editors.

The test we have implemented for our code might look like:

import unittest
import hello as SUT

class TestHello(unittest.TestCase):
 def test_returnsHelloGretting(self):
  self.assertEqual("hello", SUT.greet())

The code we work on:
def greet():
 return "hello"

Way #0: Unit Tests inside


The most obvious way is to keep the SUT and the tests in the same file. In short I do not like this because of possible problems with the further maintainability.

Way #1: Native/Python


If we want to reduce the amount of switches between the tabs in the editor we need to make the SUT run the associated unit tests when the SUT is run as main python source. This could be done by introducing an intermediate helper layer to run the tests. Call it test.py:

import unittest
import inspect
import os

def main(**kwargs):
 ### stack[0] contains the frame information about the current function, since it was called first
 ### stack[1] contains the frame information about the caller
 callerFullPath = inspect.stack()[1][1]
 callerFileName = os.path.basename(callerFullPath)
 associatedTestFileName = "test_{0}".format(callerFileName)
 associatedTest = os.path.splitext(associatedTestFileName)[0]

 return unittest.main(associatedTest, verbosity = 2, **kwargs)

The function main() simply does the following:
  1. Gets the file name of the SUT it was called from;
  2. Composes a name of the file where the unit test for SUT is located in;
  3. Redirects the call to the real test runner -- unittest.main()

... and slightly update contents of the file with the SUT:

def greet():
 return "hello"

if "__main__" == __name__:
 import test
 test.main()

Since now the each press of Ctrl-B (default hot key to run the build in Sublime Text) will lead to the run of unit tests.

Way #2: Custom build script


Sublime Text provides with a good ability to set up a custom build system for the projects a developer works on.

The final configuration might look like:

where python-wrapper.sh is a shell script to launch Python unit tests directly from Sublime Text on Ctrl-B. The script supposes that:

  1. The tests are located in the same directory where the SUT is;
  2. The file with a test has a prefix 'test_'.
... and it is compatible with bash:

#!/bin/bash

RUN_DIRECTORY="${1}"
SOURCE_NAME="${2}"
TEST_OF_SOURCE_NAME="test_${SOURCE_NAME}"

pushd "${RUN_DIRECTORY}"

if [ -e "${TEST_OF_SOURCE_NAME}.py" ]; then
 python -m unittest "${TEST_OF_SOURCE_NAME}"
else
 echo "[Warning] Associated test '${TEST_OF_SOURCE_NAME}' not found"
 python "${SOURCE_NAME}.py"
fi

RC=$?

popd

exit ${RC}

The simplicity of the solution might make you write and launch unit tests more often! But the solution should not be a reason not to run all unit tests periodically in the project.

Saturday, February 12, 2011

Customizing GNOME screensaver's lock dialog

Using bleeding edge Linux there are absolutely no guarantees that something doesn't go wrong. Several days ago I updated my Debian GNU/Linux "Testing" [branch] installation in the way I usually do using aptitude upgrade. Some things got broken some didn't. After this update I found that appearance of GNOME screensaver's lock dialog had changed: it became collapsed in it's total width. So, how to return the dialog's previous appearance and geometry back?

Google says nothing regarding this issue. But nothing tells us that it is impossible to solve it out:
  • GNOME Screensaver's lock dialog appearance is not "hardcoded" to the executable binary;
  • The dialog's appearance is built up before showing to the user from Glade's UI file located in /usr/share/gnome-screensaver directory; the default one is called lock-dialog-default.ui.
  • There could be several different .ui files:
    • The text between "lock-dilalog-" and ".ui" in the file's name called "name of the dialog's theme";
    • Switching between available themes of GNOME Screensaver's lock dialog is made using /apps/gnome-screensaver/lock_dialog_theme key in gconf.
Glade UI file is an ordinary XML file which describes declaratively the appearance and layout of some dialogs or widgets shown to the end-user. This one looks like:

<?xml version="1.0"?>
<!--*- mode: xml -*-->
<interface>
      <object class="GtkFrame" id="lock-dialog">
        <property name="visible">True</property>
        <property name="label_xalign">0</property>
        <property name="label_yalign">0.5</property>
        <property name="shadow_type">GTK_SHADOW_OUT</property>
        <child>
          ...
          <object class="GtkAlignment" id="alignment1">
          ...
        </child>
        <child>
          <placeholder/>
        </child>
      </object>
</interface>

So having known these things now it is possible for us to fix the original problem out:
  1. Make a copy of lock-dialog-default.ui to the same dir; name it, say, lock-dialog-extended-default.ui; the next step is made with the newly copied file;
  2. Add a new property called width_request to GtkFrame object with ID lock-dialog; the value must be set to the number of preferred pixels;
  3. Set /apps/gnome-screensaver/lock_dialog_theme value to extended-default since we have created a new dialog's theme.
Gotcha! The dialog looks in the way we like it now.

And some related remarks:
  1. Actually it is not a hack way; it is absolutely official one;
  2. It is possible to use any text editor to extend UI file as well as to use Glade application (usually it is not included to the default system's installation) for such purposes;
  3. There are already "ready-to-use" themes for GNOME screensaver's lock dialog. Just google for "GNOME screensaver lock dialog theme" and you may find something out you really like.

Tuesday, June 8, 2010

Linux, Thinkpad, HDAPS: battery mode

ThinkPad laptops have a lot of amazing features. HDAPS is one of them. HDAPS stands for Hard Disk Active Protection System; it is intended to protect ThinkPad's HDD from damage in case of a notebook drop or other kind of impact while it is running.

But HDAPS doesn't work 'out of the box' on Linux by default. On my Debian too. But there is so indispensable website named ThinkWiki with the tons of information how to make Linux and ThinkPad friends forever. And there is a cool article how to enable HDD APS (http://www.thinkwiki.org/wiki/How_to_protect_the_harddisk_through_APS): just compile few modules and install a special daemon -- nothing difficult. And APS will begin to work. Every time.

But there are cases when there are no need in active HDD protection when laptop is running from AC but required when the battery mode. How to handle this? The answer is to use capabilities of pm-utils package -- it allows to make hooks on special events like 'Laptop is on battery more' or vice-versa. So here a little tip'n'trick.

1. Create an executable hdaps file in /etc/pm/power.d folder with the following contents:
#!/bin/sh

HDAPS_MODULES="thinkpad_ec tp_smapi hdaps"
HDAPSD_INIT_SCRIPT=/etc/init.d/hdapsd
MODPROBE=/sbin/modprobe
RMMOD=/sbin/rmmod

case "$1" in
    ### CASE: Work on battery 
    true)
        echo "Enabling HDAPS"

        for module in $HDAPS_MODULES; do
            $MODPROBE $module
        done

        $HDAPSD_INIT_SCRIPT start
        ;;

    ### CASE: Work on AC-adapter
    false)
        echo "Disabling HDAPS"

        $HDAPSD_INIT_SCRIPT stop

        ### NOTE: modules should be unloaded in the reverse order
        for module in `echo $HDAPS_MODULES | tac -s' '`; do
            $RMMOD $module
        done
        ;;
esac
2. Disable hdaps modules autoloading from /etc/modules
3. Disable hdapsd daemon autorunning on system startup: # update-rc.d -f hdapsd remove

That's all! On Battery the script will load required HDAPS modules and run the daemon. Or will stop the daemon and unload the corresponding modules otherwise.

Tested and works well for my Debian Squeeze and Thinkpad T410.

Wednesday, April 28, 2010

'cp' command with a wget-like progress bar

Can not help re-posting: a tip and trick to make 'cp' command have a wget-like progress bar.

#!/bin/sh
cp_p()
{
   strace -q -ewrite cp -- "${1}" "${2}" 2>&1 \
      | awk '{
        count += $NF
            if (count % 10 == 0) {
               percent = count / total_size * 100
               printf "%3d%% [", percent
               for (i=0;i<=percent;i++)
                  printf "="
               printf ">"
               for (i=percent;i<100;i++)
                  printf " "
               printf "]\r"
            }
         }
         END { print "" }' total_size=$(stat -c '%s' "${1}") count=0
}

% cp_p /mnt/raid/pub/iso/debian/debian-2.2r4potato-i386-netinst.iso /dev/null
76% [===========================================>                    ]

Source: http://chris-lamb.co.uk/2008/01/24/can-you-get-cp-to-give-a-progress-bar-like-wget/

I think that's amazing idea!

Tuesday, March 16, 2010

How to return back buttons layout in Ubuntu 10.04

Ubuntu wants to be more user-friendly than other Linux`es. It is the fact. Each new Ubuntu release always introduces a set of features which make user's life easy. But not all these features are cool. Is the fact too.

Upcoming Ubuntu 10.04 also introduces a Mac-like approach when window-control buttons (to minimize, to maximize and to close a window) are placed on the top-left side of the window, not on the top-right. Not everyone will definitely like it. Or not all GNOME (Metacity, if be more specific) themes will be look OK in this case. Anyway there should be a way to affect on window's button layout in Ubuntu. 

So how to return it back?

Ubuntu 10.04 new theme
(the screenshot from http://news.softpedia.com/news/Ubuntu-10-04-Drops-the-Human-Theme-for-a-Fresh-New-Look-136537.shtml)

The solution is simple, but not so obvious (as usual :)). After execution of the following command in a terminal window

gconftool-2 --set "/apps/metacity/general/button_layout" --type string ":minimize,maximize,close"

all buttons responsible for minimization, maximization and closing will be shown on the right side of the windows as it was before in the previous versions of Ubuntu. It is also a fact :).