What an amusing coincidence. The dust has barely settled after the Apple’s mishap with extraneous goto fail;
in OpenSSL (used widely by OSX and iOS), that made it ripe for exploits, before we’ve heard another similar story. This time it was about multiple goto cleanup;
statements that comprised a vulnerability in GnuTLS, a rough equivalent of OpenSSL on various Linux distributions.
That goto
! Everyone warns it cannot be trusted, it shouldn’t be used, it’s basically the work of the devil. Yet, some people just don’t listen, and we can witness firsthand all the woes that befall them. Will it serve as a warning for the others? Can we hope for at least this much?…
The answer is no. Not only we can’t, but also shouldn’t. Despite what many would proclaim, these two fiascoes are hardly about the goto
statement. It’s very tempting, of course, to pigeonhole them into that box, but the truth is just more complex than that.
The goto
statement is only one example, drawn from a certain class of programming techniques and language features. What they all have in common is that they overstep the safety guarantees a language offers, however elaborate or scarce they are. In return, those techniques offer additional capabilities, exceeding the usual “power level” available to developers.
Pretty much every language hides something of this kind. It may be goto
in C. It could be reflection in Java. It might be metaclasses in Python, or import hooks. Even the pure and graceful Haskell has the infamous unsafePerformIO
, the use of which potentially throws all the normal guarantees of correctness out of the window.
What all those features have in common is their inevitability. You may try to ignore them, or write off as bad practices at best. But come a sufficiently complex program, an amply convoluted system, a problem hairy enough, and they suddenly become adequate, acceptable – or even necessary. The malevolent goto
, to stick with the most prominent example, becomes basically mandatory whenever C code mixes error checking with handling resources that require proper cleanup.
Therefore, the only reasonable solution is to learn to cope with it all, and just be extra careful. Translated to software engineering practices, this means taking every precaution we can reasonably afford to minimize the risk of bugs. Most of this stuff is pretty simple, really. It doesn’t necessarily involve any gymnastics with automatic testing, too, which is every developer’s “favorite” discipline. The OpenSSL’s fiasco would have been prevented by any tool that looks for unreachable code; that’s not really asking a lot.
So if we could learn anything from those high profile fail
ures, it’s that basic stuff, low hanging fruit – like code reviews and static analyzers – actually help, for little to no cost. I’m sure such conclusion is much more valuable than yet another tirade, coming from a You Should Not Code Like That department.
People’s coding styles tend to evolve and change over time. One particular habit I seem to have picked up is to sprinkle the code liberally with numerous TODO
markers. I wish I could say it’s clear a sign of my ever-present dissatisfaction with imperfect solutions, but I suspect I simply adopted it while working at the current company :)
In any case, TODO
s (and FIXME
s &c.) are not actually something to scoff at, not too much at least. They are certainly better than the alternative, which is to commit shady code without explanation, rationale, or ideas for improvement. With TODO
s, you are at least making the technical debt apparent and explicit, thus increasing the likelihood you’ll eventually come around to pay it off.
When that glorious day comes, though, it would be nice to get a quick overview of the code’s shortcomings, so that you can decide what to work on first. Getting a list of TODO
s scattered over many files sounds like a great task for grep
, and a relatively simple one at that. But somehow, every time I wanted to do that I ended up spending some non-negligible time just working out the details of grep
‘s syntax and flags.
Thus, the logical course of action would be craft a simple script which would relieve me from doing that ever again. However, when I got around to writing it, I quickly realized the task it’s not actually that simple. In fact, it’s totally impossible to do it with just grep
, since it would require matching a regex against multiple subsequent lines of input . Standard GNU grep
doesn’t support that at all.
Well, at this point I should’ve probably taken the hint and realize it’s not exactly the best idea to use a shell script here. But hey, not everything has to be written in Python, right? :) So I rolled up my sleeves and after a fair amount of googling (and stack-overflowing), I unleashed a horror that I hereby present:
For best result, it is necessary to have pcregrep
installed, which is an extended version of grep
that supports the full spectrum of Perl-compatible regular expressions. On most popular Linux distros, pcregrep
is just one apt-get install
away.
When writing tests, ideally you should verify your code’s behavior not only in the usual, “happy” cases, but also in the erroneous ones. Although you may very well accept that a function blows when feed with incorrect data, it should blow up predictably and consistently. An error, exception or panic is still an output; and it should be possible to capture and examine it in tests.
The Python unittest
module has a couple of ways to deal with expected error. Probably the most useful among them is the TestCase.assertRaises
method. It does pretty much exactly what it names hints to: asserting that a piece of code raises a specific type of exception:
In Python 2.7 (or with the unittest2 shim library), it can be also used much more conveniently as a context manager:
To clarify, assertRaises
will execute given block of code (or a callable, like in the first example) and throw AssertionError
if an exception of given type was not raised by the code. By calling the tested function with incorrect data, we intend to provoke the exception, affirm the assertion, and ultimately have our test pass.
I mentioned, however, that it doesn’t just matter if your code blows up in response to invalid input or state, but also how it does so. Even though assertRaises
will verify that the exception is of correct type, it is often not nearly enough for a robust test. In Python, exception types tend to be awfully broad, insofar that a simple information about throwing TypeError
may tell you next to nothing about the error’s true nature.
Ironically, designers of the unittest
module seemed to be vaguely aware of the problem. One of their solutions, though, was to introduce a proverbial second problem, taking the form of assertRaisesRegexp
method. While it may kinda-sorta work in simple cases, I wouldn’t be very confident relying on regular expressions for anything more complex.
Especially when the other possible approach appears much more sound anyway. Using the feature of with
statement, we can capture the exception object and examine it ourselves, in a normal Python code. Not just the type or message (though these are typically the only reliable things), but also whatever other data it may carry:
Sometimes, those checks might grow quite sophisticated. For example, in Python 3 you have exception chaining; it allows you to look not only at the immediate exception object, but also its __cause__
, which is analogous to Throwable.getCause
in Java or Exception.InnerException
in C#. If you need to dig this deep, I’d suggest extracting a function with all that code – essentially a specialized version of assertRaises
, preferably with all the context manager goodness that would enable us to use it like the original.
As it turns out, this can be very simple.
Over the course of several past months and years I was coding in Python, I’ve created quite a few Python packages: both open source and for private projects. Even though their most important part was always the code, there are numerous additional files that are necessary for the package to correctly serve its purpose. Rather than part of the Python language, they are more closely related to the Python platform.
But if you look for any definite, systemic info about them, you will at best find some scattered pieces of knowledge in various unrelated places. At worst, the only guidance would come in the form of a multitude of existing Python package sources, available on GitHub and similar sites. Parroting them is certainly an option, although I believe it’s much more advantageous to acquire firm understanding of how those different cogs fit together. Without it, following the modern Python’s best development practices – which are all hugely beneficial – is largely impossible.
So, I want to fill this void by outlining the structure of a Python package, as completely as possible. You can follow it as a step-by-step guide when creating your next project. Or just skim through it to see what you’re missing, and whether it’d be worthwhile to address such gaps. Any additional element or file will usually provide some tangible benefit, but of course not every project requires all bells and whistles.
Without further ado, let’s see what’s necessary for a complete Python software bundle.
If you code in Python, then chances are that at some point, you have written a check similar to this one:
Some would of course argue against putting such an explicit if
in the code, insisting to rely on duck typing instead. But while this is an easy target of critique, it’s nowhere near the biggest problem you can find in the snippet above.
This code has a subtle bug. The bug is not even limited to checks like this one; it can occur in many different situations. It surfaces rarely, too, so it’s all the more surprising when it actually rears its ugly head.
The bug is related to string formatting, which in this case points to this expression:
Most of the time, it is perfectly fine and works flawlessly. But since arg
is a value we do not have any control over, sometimes it may not work correctly. Sometimes, it can just blow the whole thing up, likely in a way we have not intended.
All it takes is for arg
to be a tuple – any tuple. Tuples are special, because the string formatting operator (%
) expects you’ll use them to pass more than one argument to fill in placeholders in the string:
The construct of a string followed by percent sign, followed by parenthesis, is very likely familiar to you. Notice, however, that there is nothing exceptional about using a tuple literal: what is important is the tuple type. Indeed, we could rewrite the above in the following manner:
and the end result would be exactly the same. The only reason we prefer the first version is its obviously superior readability.
Comparing that last piece of code with the first one, we can see quite clearly how everything will go horribly wrong should we try to format the TypeError
‘s message using arg
which happens to be a tuple. Not just one, but three different failure modes are possible here:
Last one is particularly jarring. It raises no exceptions on by itself, and can additionally result in confusing messages, along the lines of:
Much head-scratching would probably ensue if you stumbled upon exception that reports something like this.
To avoid these problems, one solution is to engage in some sort of pythonic homeopathy. As it turns out, we can cure the malady of tuples by adding even more tuples:
Through this weird (arg,)
singleton (1-tuple), we are explicitly sidestepping the error-prone feature of %
operator, where it allows a single right-hand side argument to be passed directly. Instead, we are always wrapping all the arguments in a tuple – yes, even if it means using the bizarre (1,)
syntax. This way, we can fully control how many of arguments we actually give to the formatter, regardless of what they are and where did they come from.
It’s not pretty, I know – it adds some visual clutter. But the total alternative, the format
method, is even more verbose and ridden with issues. C’est la vie.
There’s no better way to start a new year than a hearty, poignant rant. To set the bar up high right off the bat, I’m not gonna be picking on some usual, easy target like JavaScript or PHP. To the contrary, I will lash out on everyone-and-their-mother’s favorite language; the one sitting comfortably in the middle between established, mature, boring technologies of the enterprise; and the cutting edge, misbegotten phantasms of the GitHub generation.
That’s, of course, Python. A language so great that nearly all its flaws people talk about are shared evenly by others of its kin: the highly dynamic, interpreted languages. One would be hard-pressed to find anything that’s not simply a rehashed argument about maintainability, runtime safety or efficiency – concerns that apply equally well to Ruby, Perl and the like. What about anything specifically “pythonic”?…
Well, let’s talk about Python’s exceptions, shall we?
Every computer program expands until it can read e-mail – or so they say. But many applications need not to read, but to send e-mails; web apps or web services are probably the most prominent examples. If you happen to develop them, you may sometimes want a local, dummy SMTP server just for testing this functionality. It doesn’t even have to send anything (it must not, actually), but it should allow you to see what would be sent if the app worked in a production environment.
By far the easiest way to setup such a server involves, quite surprisingly, Python. There is a standard library module called smtpd
, which is built exactly for this purpose. Amusingly, you don’t even have to write any code that uses it; you can invoke it straight from the command line:
This will start a server that listens on port 8025 and dumps every message “sent” through it to the standard output. A custom port is chosen because on *nix systems, only the ports above 1024 are accessible to an ordinary user. For the standard SMTP port 25, you need to start the server as root:
While it’s more typing, it frees you from having to change the SMTP port number inside your application’s code.
If you plan to use smtpd
more extensively, though, you may want to look at the small runner script I’ve prepared. By default, it tries to listen on port 25, but you can supply a port number as its sole argument.