There’s Something About Code

June 17, 2009

Twisted HTTP proxy

Filed under: Code — Tags: , , , — Knut Eldhuset @ 20:15

A couple of years ago I wrote an HTTP proxy that would save all viewed web pages. It was written in Java and used a relational database as storage. It had a streaming architecture, which meant that complete requests and responses were not kept in memory, but rather flowed through the proxy. This meant that it could handle responses of any size, including large file downloads, without using much memory. You could write filters that would modify requests and responses, as well as block them completely. Storing web pages was implemented as a special filter.

I never got around to implementing all the features I wanted. Hoping to change that, I am taking a shot at implementing an HTTP proxy in Python. Obviously, socket programming is needed on some level. While my original Java proxy used asynchronous sockets directly, I am thinking about using the Twisted framework in Python. Twisted is “an event-driven networking engine written in Python.” It provides lots of different protocol implementations, including HTTP. In fact, there is a simple HTTP proxy, too. Starting a proxy instance is easy:

1
2
3
4
5
6
7
8
9
from twisted.internet import reactor
from twisted.web import http
from twisted.web.proxy import Proxy
 
factory = http.HTTPFactory()
factory.protocol = Proxy
 
reactor.listenTCP(8000, factory)
reactor.run()

However, a this proxy isn’t very interesting. If we would like to print every site that is accessed through the proxy, we could subclass it like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from twisted.internet import reactor
from twisted.web import http
from twisted.web.proxy import Proxy, ProxyRequest
 
class VerboseProxyRequest(ProxyRequest):
    def process(self):
        print self.uri
        ProxyRequest.process(self)
 
class VerboseProxy(Proxy):
    requestFactory = VerboseProxyRequest
 
factory = http.HTTPFactory()
factory.protocol = VerboseProxy
 
reactor.listenTCP(8000, factory)
reactor.run()

The ProxyRequest implementation of process makes a new HTTP client, an instance of the class ProxyRequestClient. The HTTP client will simply forward anything it receives to the response transport channel of the parent ProxyRequest instance. Creating a simple ad blocker is easy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from twisted.internet import reactor
from twisted.web import http
from twisted.web.proxy import Proxy, ProxyRequest
 
class BlockingProxyRequest(ProxyRequest):
    def process(self):
        if "ads" in self.uri:
            print "Blocked:", self.uri
            self.transport.write("HTTP/1.0 404 Not found\r\n")
            self.transport.write("Content-Type: text/html\r\n")
            self.transport.write("\r\n")
            self.transport.write('''<H1>Resource not found</H1>''')
            self.transport.loseConnection()
 
        ProxyRequest.process(self)
 
class BlockingProxy(Proxy):
    requestFactory = BlockingProxyRequest
 
factory = http.HTTPFactory()
factory.protocol = BlockingProxy
 
reactor.listenTCP(8000, factory)
reactor.run()

This code will block any request that has the string “ads” in the URI. This may be overly aggressive for a real ad blocker, though.

Twisted handles all the details of the HTTP requests and responses, as well as the underlying transport protocol. One drawback is that the provided HTTP client implementation does not support HTTP 1.1 yet. This may affect performance, but will not be an issue for my uses.

To get at the response data, we need to subclass the ProxyClient class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from twisted.internet import reactor
from twisted.web import http
from twisted.web.proxy import Proxy, ProxyRequest, ProxyClientFactory, ProxyClient
from ImageFile import Parser
from StringIO import StringIO
 
class InterceptingProxyClient(ProxyClient):
    def __init__(self, *args, **kwargs):
        ProxyClient.__init__(self, *args, **kwargs)
        self.image_parser = None
 
    def handleHeader(self, key, value):
        if key == "Content-Type" and value in ["image/jpeg", "image/gif", "image/png"]:
            self.image_parser = Parser()
        if key == "Content-Length" and self.image_parser:
            pass
        else:
            ProxyClient.handleHeader(self, key, value)
 
    def handleEndHeaders(self):
        if self.image_parser:
            pass #Need to calculate and send Content-Length first
        else:
            ProxyClient.handleEndHeaders(self)
 
    def handleResponsePart(self, buffer):
        if self.image_parser:
            self.image_parser.feed(buffer)
        else:
            ProxyClient.handleResponsePart(self, buffer)
 
    def handleResponseEnd(self):
        if self.image_parser:
            image = self.image_parser.close()
            try:
                format = image.format
                image = image.rotate(180)
                s = StringIO()
                image.save(s, format)
                buffer = s.getvalue()
            except:
                buffer = ""
            ProxyClient.handleHeader(self, "Content-Length", len(buffer))
            ProxyClient.handleEndHeaders(self)
            ProxyClient.handleResponsePart(self, buffer)
        ProxyClient.handleResponseEnd(self)
 
class InterceptingProxyClientFactory(ProxyClientFactory):
    protocol = InterceptingProxyClient
 
class InterceptingProxyRequest(ProxyRequest):
    protocols = {'http': InterceptingProxyClientFactory}
 
class InterceptingProxy(Proxy):
    requestFactory = InterceptingProxyRequest
 
factory = http.HTTPFactory()
factory.protocol = InterceptingProxy
 
reactor.listenTCP(8000, factory)
reactor.run()

This proxy will rotate JPEG, GIF and PNG files 180 degrees, turning a Google image search into this:
Upside-down Google image search
Fun, but not very useful. It would have been just as easy to store the image, as well as other content, to disk or a database, but that is a topic for another blog post.

June 1, 2009

Python audio output

Filed under: Code — Tags: , , , — Knut Eldhuset @ 10:01

Playing MOD files requires outputting digital sound at sample rates proportional to the desired pitch. In the Amiga, this was accomplished by setting the sample rate of the hardware channels. Using high level audio interfaces may require setting a fixed sample rate for the lifetime of the audio channel. Converting the MOD file to a WAV file would also require a fixed sample rate. Thus, sample rate conversion is needed.

The most basic MOD files have four channel sequencing. When played on stereo hardware, two sequencer channels are output to each hardware channel. The sound samples need to be converted from mono to stereo, then mixed together with the other channels.

In the Amiga, the sequencer played a new sample each vertical blanking interval. The screen refresh rate in the PAL version of the computer was 50Hz. I have used the pyglet library to play audio. By subclassing the StreamingSource class and providing an implementation of the _get_audio_data method, the timing of the sequencer takes care of itself automatically. The _get_audio_data method returns an audio chunk equivalent to what the Amiga played per vertical blanking interval. Pyglet will simply request more data when needed.

The code for the MOD player can be found here. The code excerpts below are taken from the file player.py.

The Python Multimedia Services library contains functions for doing the necessary raw audio operations. The audio operations are located in the audioop module. The ratecv function takes care of sample rate conversion:

ratecv(fragment, width, nchannels, inrate, outrate, state[, weightA[, weightB]])

It takes an audio fragment as input, and returns the fragment converted to the desired sample rate, as well as the new state. The new state is passed as input the next time the function is invoked. Here is how it looks in the MOD player:

44
45
46
47
48
49
50
51
52
53
54
55
56
57
    def _ratecv(self, sounds):
        output = []
        for n, (sound, state) in enumerate(zip(sounds, self.ratecv_state)):
            while True:
                o, state = ratecv(sound, self.bytes, 1, 
                              int(round(len(sound) / self.tick_time) / self.bytes), 
                              self.rate, 
                              state)
                #Length may be off by one, so process until OK
                if len(o) == int(round(self.rate * self.tick_time * self.bytes)):
                    break
            output.append(o)
            self.ratecv_state[n] = state
        return output

Self.bytes is the number of bytes per sample. The number of channels is 1, since sound is a mono sample. The inrate will vary according to the pitch at which the sample fragment is played back. This is controlled by the sequencer by varying the length of the fragment. The inrate is then calculated based on the fact that this particular fragment fills self.tick_time seconds. The state is stored in an array for later use.

When mixing several channels into one, one needs to make sure that there will be no clipping of the resulting sound sample. This is done by dividing by the number of channels that are to be mixed:

63
64
   def _scale(self, output):
        return [mul(o, self.bytes, 1.0 / (len(output) / self.channels)) for o in output]

The mul function takes care of the scaling. As all the other audioop functions, it needs to know how many bytes are user per sample.

mul(fragment, width, factor)

The tostereo function takes a mono sample and returns a stereo sample. One can supply scaling factors for each of the left and right channels.

tostereo(fragment, width, lfactor, rfactor)

This is used below to put every even numbered sequencer channel in the left stereo channel, and every odd numbered sequencer channel in the right stereo channel.

66
67
    def _tostereo(self, output):
        return [tostereo(o, self.bytes, n % 2, (n + 1) % 2) for n, o in enumerate(output)]

Putting all this together, the variable sample rate sequencer channels can be transformed into a constant sample rate stereo output:

69
70
71
72
73
74
75
76
77
78
79
80
81
82
    def _get_audio_data(self, num_bytes):
        sound = self.sequencer.tick()
        if sound is None:
            pyglet.app.exit()
            return
        self._mute(sound)
        sound = [lin2lin(s, 1, self.bytes) for s in sound]
        output = self._ratecv(sound)
        output = self._scale(output)
        output = self._tostereo(output)
        stereo = mix(output, self.bytes)
        audio = AudioData(stereo, self.audio_length, self.timestamp, self.tick_time)
        self.timestamp += self.tick_time
        return audio

For a standard MOD file, the sequencer returns 4 samples per tick. These are converted from 8 bit to 16 bit using the lin2lin function:

lin2lin(fragment, width, newwidth)

The samples are then rate converted, scaled and converted to stereo. The mix function is a helper function to mix a list of samples. The audioop module only provides an add function to mix two channels, so I made the helper function to mix an arbitrary number of channels. The method ends with creating an AudioData object with the parameters needed for pyglet to play the sound.

May 19, 2009

Parsing binary files using Construct

Filed under: Code — Tags: , , , — Knut Eldhuset @ 12:00

Someone over at stackoverflow recommended using the Python library Construct for parsing binary files. Python already provides the struct module for parsing binary string data, but this gives a flat view of the data and does not reflect structure. Construct promises a more intuitive way of making a parser. Always happy to discover a new, shiny hammer, I decided to try it out on a real life, albeit “old skool”, problem.

Back in the days when I taught myself programming Turbo Pascal, I was very fascinated by the demo scene. An integral part of the scene, were the MOD, or Module, files. MOD is a file format for sequenced music, much like MIDI files, except MOD files also contain sampled instruments. The file format is more or less specified here. I’ll describe the relevant parts as we move along.

The files mod.py and utils.py can be found at GitHub.

To better understand how the parser is built, it’s best to start with the most high level construct first. The overall structure of a MOD file is as follows:

EDIT: This is from the file mod.py.

90
91
mod = Struct("mod", 
             String("title", 20, padchar="\x00"),

The String construct denotes a fixed length string, in this case 20 characters, named title. The string is padded with zeroes. Construct also provides PascalString for length prefixed string and CString for null terminated strings.

92
             StrictRepeater(31, sample_info),

The StrictRepeater is used to specify that the sample_info sub construct is to be repeated an exact number of times. A MOD file has 31 samples, but if less samples are needed, the sample_info contains a length of zero for the unused ones.

93
94
             UBInt8("num_positions"),
             Padding(1),

UBInt8 specifies an unsigned 8 bit integer in big endian format. The value num_positions is the number of patterns to play.

95
             StrictRepeater(128, ULInt8("pattern_table")),

The pattern table is a list of bytes that describe the order in which the patterns are to be played. I mistakenly used ULInt8 for these bytes, but with a byte length of 8 bits it doesn’t matter if the little endian or big endian interpretation is used.

96
97
98
             OneOf(String("signature", 4), signature_vs_channels.keys()),
             Value("num_channels", 
                   lambda ctx: signature_vs_channels[ctx.signature]),

The OneOf construct is used to specify that the sub construct must be equal to exactly one of a list of candidate values. The signature field tells us how many audio channels the MOD file has. I put the mapping from signatures to number of channels into a dictionary, so I gave the keys as the list of possible values for the OneOf construct. The Value construct can be used to compute a value from the previously parsed constructs. Value takes a function with a single parameter, the parsing context, applies the function and assigns the resulting value to the given name. Here, the value for num_channels is simply looked up in the signature vs number of channels dictionary.

99
100
             MetaRepeater(lambda ctx: int(max(ctx.pattern_table)) + 1,
                          patterns),

The MetaRepeater differs from the StrictRepeater in that the number of repetitions is given as a function of the parsing context. A MOD file can contain up to 128 patterns. The number of actual patterns is the maximum value from the pattern table plus one, so the number of repetitions is simply given as a function that calculates this value.

101
102
103
             MetaRepeater(lambda ctx: len(ctx.sample_info),
                          Bytes("samples", sample_length))
             )

The samples’ data is stored at the end of the file. The number of samples is given as the length of sample_info, but will 31 with the current parser. The sample data is stored as an array of bytes. This is where some ugliness creeps in. For each sample to be read, its length must be looked up in the sample_info array, but in Construct itself, there is no way to access the current index in the Repeater constructs. The function sample_length, defined in utils.py, takes care of looking up the length and keeping track of which sample that is read.

6
7
8
9
10
11
12
signature_vs_channels = {"M.K.": 4, 
                         "M!K!": 4, 
                         "6CHN": 6, 
                         "8CHN": 8, 
                         "12CH": 12, 
                         "28CH": 28, 
                         "FLT4": 4}

A simple mapping between signatures and number of channels.

14
15
16
sample_info = Struct("sample_info",
                String("name", 22, padchar="\x00"),
                WordsToBytesAdapter(UBInt16("length")),

The sample_info sub structure is repeated once for each sample. Nothing new here except the WordsToBytesAdapter, which is defined in utils.py. The length value stored in the file is the number of 16 bit words in the sample. The WordsToBytesAdapter simply doubles the length to give the number of bytes instead.

17
18
19
                Embed(BitStruct(None,
                          Padding(4),
                          BitField("finetune", 4, signed=True))),

One of the strengths of Construct is the ability to easily handle variable length bit fields. Normally, the parser operates on bytes. To operate on bits, a BitStruct is needed. In this case the BitStruct is enclosed in an Embed construct. This means that the members of the BitStruct will be included in the enclosing struct.

The finetune field is the lower 4 bits of a byte value, interpreted as a signed 4 bit integer, so we first insert a Padding struct to get rid of the upper 4 bits.

20
21
22
                UBInt8("volume"),
                WordsToBytesAdapter(UBInt16("repeat_offset")),
                WordsToBytesAdapter(UBInt16("repeat_length")))

Samples loop forever if the repeat_length field is positive. When the end of a sample is reached, playback is continued from the repeat_offset. Once repeat_offset + repeat_length is reached, playback is once again continued from the repeat_offset.

85
86
87
88
patterns = Struct("patterns",
                 StrictRepeater(64,
                                MetaRepeater(lambda ctx: ctx._.num_channels, 
                                             channel_data)))

Patterns consist of 64 divisions, each with 4 bytes of data per channel.

75
76
77
78
channel_data = BitStruct("channel_data",
                         Nibble("sample_hi"),
                         BitField("period", 12),
                         Nibble("sample_lo"),

For some reason the sample number is split into two nibbles, separated by the period, or pitch, so play the sample at. I have used the Value construct to join the nibbles into a byte below. This still leaves sample_hi and sample_lo hanging around and there is, as far as I can tell, no way to get rid of them.

79
80
                         major_effect,
                         Embed(efx_and_params),

The effect to be applied for this particular channel and division is a 4 bit integer in the range 0-15. An effect has two 4 bit parameters, x and y, that follow. However, an effect value of 14 is an extended effect. The x then specifies which extended effect, and the y is its parameter.

81
82
83
                         Value("sample", 
                               lambda ctx: ctx.sample_hi << 4 | ctx.sample_lo)
                        )
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
major_effect = Enum(Nibble("major_effect"),
                    Arpeggio = 0,
                    SlideUp = 1,
                    SlideDown = 2,
                    SlideToNote = 3,
                    Vibrato = 4,
                    ContinueSlideToNotePlusVolumeSlide = 5,
                    ContinueVibratoPlusVolumeSlide = 6,
                    Tremolo = 7,
                    SetSampleOffset = 9,
                    VolumeSlide = 10,
                    PositionJump = 11,
                    SetVolume = 12,
                    PatternBreak = 13,
                    extended_effect = 14, #This is never seen outside parser
                    SetSpeed = 15)
 
extended_effect = Enum(Nibble("extended_effect"),
                       ToggleFilter = 0,
                       FineslideUp = 1,
                       FineslideDown = 2,
                       ToggleGlissando = 3,
                       SetVibratoWaveform = 4,
                       SetFinetuneValue = 5,
                       LoopPattern = 6,
                       SetTremoloWaveform = 7,
                       RetriggerSample = 9,
                       FineVolumeSlideUp = 10,
                       FineVolumeSlideDown = 11,
                       CutSample = 12,
                       DelaySample = 13,
                       DelayPattern = 14,
                       InvertLoop = 15)

Enums are used to map from integers to symbols.

70
71
72
73
efx_and_params = IfThenElse(None, 
                            lambda ctx: ctx.major_effect == "extended_effect",
                            efx_params,
                            fx_params)

The IfThenElse construct allows for conditional parsing. Here we check if the effect is really an extended effect. If it is, the sub construct efx_params is tried, otherwise fx_params.

65
66
67
68
fx_params = Struct(None, 
                   Nibble("x"), 
                   Nibble("y"), 
                   Alias("effect", "major_effect"))

An effect has two parameters, x and y. The Alias construct creates an additional name for a symbol.

59
60
61
62
63
efx_params = Struct(None, 
                    extended_effect, 
                    Nibble("x"), 
                    Value("y", lambda ctx: None), 
                    Alias("effect", "extended_effect"))

As mentioned, if the effect is an extended effect, its first parameter, or nibble, tells us which extended effect. The second parameter is the one and only extended effect parameter. Since the extended effect doesn’t have a second paramter, we simply give it a value of None using the Value construct. This time, the alias effect is assigned the value contained in extended_effect.

To clarify how the channel data will look when parsed, we can do a test with GLiPy:
channel_data

We can try it out on a file as well. I chose the file demomod3.mod. The following screenshot shows how to access parts of the parsed file:
module properties
Printing the module object looks like this, except I have edited the output to a reasonable length:

Container:
    title = 'demomodul#3'
    sample_info = [
        Container:
            name = "by laxity/kefrens '9"
            length = 9444
            finetune = 0
            volume = 64
            repeat_offset = 0
            repeat_length = 2
        Container:
            name = ''
            length = 4780
            finetune = 0
            volume = 60
            repeat_offset = 458
            repeat_length = 3862
<29 more>
    ]
    num_positions = 40
    pattern_table = [
        2
        1
        3
        4
        14
        0
        7
        8
<120 more>
    ]
    signature = 'M.K.'
    num_channels = 4
    patterns = [
        Container:
            channel_data = [
                [
                    Container:
                        sample_hi = 1
                        period = 226
                        sample_lo = 7
                        major_effect = 'SetSpeed'
                        x = 0
                        y = 6
                        effect = 'SetSpeed'
                        sample = 23
                    Container:
                        sample_hi = 0
                        period = 254
                        sample_lo = 11
                        major_effect = 'Arpeggio'
                        x = 0
                        y = 0
                        effect = 'Arpeggio'
                        sample = 11
                    Container:
                        sample_hi = 0
                        period = 254
                        sample_lo = 1
                        major_effect = 'Arpeggio'
                        x = 0
                        y = 0
                        effect = 'Arpeggio'
                        sample = 1
                    Container:
                        sample_hi = 0
                        period = 0
                        sample_lo = 0
                        major_effect = 'Arpeggio'
                        x = 0
                        y = 0
                        effect = 'Arpeggio'
                        sample = 0
                ]
<63 more>
            ]
        Container:
            channel_data = [
                [
                    Container:
                        sample_hi = 0
                        period = 0
                        sample_lo = 0
                        major_effect = 'Arpeggio'
                        x = 0
                        y = 0
                        effect = 'Arpeggio'
                        sample = 0
                    Container:
                        sample_hi = 0
                        period = 0
                        sample_lo = 0
                        major_effect = 'Arpeggio'
                        x = 0
                        y = 0
                        effect = 'Arpeggio'
                        sample = 0
                    Container:
                        sample_hi = 0
                        period = 254
                        sample_lo = 1
                        major_effect = 'Arpeggio'
                        x = 0
                        y = 0
                        effect = 'Arpeggio'
                        sample = 1
                    Container:
                        sample_hi = 0
                        period = 0
                        sample_lo = 0
                        major_effect = 'extended_effect'
                        extended_effect = 'FineVolumeSlideDown'
                        x = 1
                        y = None
                        effect = 'FineVolumeSlideDown'
                        sample = 0
                ]
<63 more>
            ]
<32 more>
    ]
    samples = [
        "\x00\x00\x00\x00\x00\x00\x02\r\x16\x1b\x1b\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1b\x1b\x17\x16\x16\x13\r\x03\x00\x00\xed..."
<30 more>
    ]

Now, that was quite easy! Try it out for yourself.

May 16, 2009

GLiPy - The OpenGL IPython Terminal

Filed under: Tools — Tags: , , , — Knut Eldhuset @ 12:23

For those of you that use the standard Python shell when trying out stuff, IPython is an enhanced shell with lots of useful features, including tab completion and command history across sessions. GLiPy is an enhancement over IPython. It provides plotting of numpy structures using OpenGL, as shown below.GLiPy and numpy
A side effect of the graphical nature of GLiPy and the fact that it has a GUI, is that it has a message pump going. This means you can experiment with Wx or pyglet without starting additional threads to get an application object up and running. The short example below shows me loading the Sine source from pyglet and playing a short sine wave.GLiPy and pyglet
The difference between running these three lines in GLiPy and IPython, is that the sound plays for 4 seconds when run in GLiPy, while using IPython you only get a short beep. The reason for this is that there is no timer set up to get the next chunk of data from the sine source. To get the rest of the sine wave, you have to do pyglet.app.run() which then blocks your main thread.

May 15, 2009

When True is not None

Filed under: Code — Tags: , , — Knut Eldhuset @ 14:16

Sometimes, when you try to explain something using an example, people will pick on the details of your example instead of seeing the greater picture. Scott Hanselman got some flack for daring to write something == true. Whether or not he was justified in writing his code that way, there certainly are good reasons to check for explicit truth values, especially in Python.

When using keyword arguments with a default value of None, it’s easy to check if the argument was supplied or not:

def do_expensive_thing(times):
    for n in range(times + 2):
        print "Index: ", n
 
def do_it(argument_for_expensive_operation=None):
    if argument_for_expensive_operation:
        do_expensive_thing(argument_for_expensive_operation)
    print "It's done"

Let’s try it out with some different parameters:

In [3]: do_it()
It's done

In [4]: do_it(2)
Index:  0
Index:  1
Index:  2
Index:  3
It's done

Works nicely. Let’s try a third time:

In [5]: do_it(0)
It's done

Whooah! What happened this time? Conveniently, not only None gives a truth value of False. Other examples are the integer value zero and the empty list. This explains why the above example fails. The correct way is to explicitly check if the supplied argument is None:

def do_it_right(argument_for_expensive_operation=None):
    if argument_for_expensive_operation is not None:
        do_expensive_thing(argument_for_expensive_operation)
    print "It's done"

Now it prints what we would expect:

In [7]: do_it_right(0)
Index:  0
Index:  1
It's done

The Python documentation covers truth value testing in detail.

Swallowed by a Python

Filed under: Code — Tags: , , — Knut Eldhuset @ 13:57

Programming Python is mostly straightforward. Not thinking, however, can lead to subtle bugs, and this one had me stumped for a while. If a program raises an exception that is not caught further up the call stack, I expect the interpreter to exit with a whimper. Consider the following code:

def throwing_function():
    try:
        print 1 / 0
    finally:
        print "Finally"
        return "Useful string"
 
print throwing_function()

Running this will print

Finally
Useful string

If you can spot the bug immediately, good for you, but I didn’t. I expected this code to fail with a ZeroDivisionError exception. Not so. It turns out that the return statement hides the exception.

The correct code should look like this:

def throwing_function():
    try:
        print 1 / 0
    finally:
        print "Finally"
    return "Useful string"
 
print throwing_function()

Now it behaves like expected:

Finally
Traceback (most recent call last):
  File "swallowed_exception_fixed.py", line 8, in 
    print throwing_function()
  File "swallowed_exception_fixed.py", line 3, in throwing_function
    print 1 / 0
ZeroDivisionError: integer division or modulo by zero

A good rule of thumb would be to never put a return statement inside a finally clause.

Coding is Power

Filed under: Rant — Knut Eldhuset @ 13:56

There’s something about code. Programming a computer to do what I want is one of the most rewarding intellectual experiences I know of. With the right tools and language, it’s possible to accomplish anything. You could build The Next Big Thing, make an operating system, a music player, a blog engine, Facebook, Twitter, or anything else that you can think of. The tools to do so are at your finger tips. One (wo)man, his computer and the Internet, being able to disrupt the order of things on a global scale. That is what I call power!

This blog will be about my experience as a software developer and coder. I’ll cover tools that I use, code that I’ve written, bugs that have bit me and languages that are fascinating to me. I can’t promise perfect code in my examples. If you spot mistakes or know of better ways to accomplish things, please leave a comment.

Why should you listen to me? Well, to quote Jeff Atwood:

There’s absolutely no reason any of you should listen to me!

I started out coding BASIC and some assembly on my C64 back in the beginning of the nineties, then switched to a PC and did some Pascal, Visual Basic and PHP. Then I got my MSc in Computer Science, and software development has been my bread and butter since 2001. I have been coding C++, Java, C# and Python, but do not claim to be an all-knowing expert in any of these languages. So that’s my experience; take my ramblings for what they are.

Powered by WordPress