In the modern world of programming, Python is almost unavoidable. No, not the snake. The programming language. You’ll find it everywhere, and I mean everywhere.
AI applications? They use Python.
Your computer? It uses Python.
Robotics? They use Python.
Embedded systems? They use Python.
Your microwave? Probably uses Python.
That random Wii homebrew toolchain from 2010? It uses—
Okay, you get it.
My point is, that over the years, Python has become an increasingly popular language.
But what if we went back to a time when Python wasn’t so popular?
A time when Python was still in beta?
Today I present to you, Python 0.9.1!
A blast from the past
Python 0.9.1 is a vintage programming language by today’s standards.
Released in early 1991, it’s almost 35 years old!
Do you know what else released in 1991?
That’s right, the freaking WORLD WIDE WEB.
Python 0.9.1 is older than the internet as we know it!
With this version of the programming language being that old, you might think it doesn’t have any practical purposes or that it’s just a gimmick in today’s modern world.
But what if I told you that you’re wrong?
The experiment begins
The other day, when I was reading about retro computing, I thought it would be a fun idea to try out Python 1.0!
But when I went to the Python releases page, I saw that there was a “pre-1.0/Ancient Releases” section. Out of curiosity, I clicked on it, to see what I would find.
Once I saw that they had found an archived version of Python 0.9.1, I knew that I had to try it.
They said they had a pre-built version on conda, so I tried using that first.
Though, unsurprisingly, it said it wasn’t available (probably because I’m on Linux ARM64 and not x86).
Therefore, I decided I’ll just compile it myself!
After cloning the GitHub repo for Python 0.9.1 and compiling it (tip: if you want to compile it yourself, use make with CFLAGS=”-std=c89”), I ran the outputted ./python binary and tried a couple basic things, like declaring variables and printing them out.
After a bit of tinkering and testing some things, I was immediately fascinated by the language design and how featureful this language was, even in 1991.
Surprisingly useful scripting?
Despite being 35 years old, Python 0.9.1 is quite… useful?
The entire interpreter is only 288KB when statically compiled with musl-gcc and stripped! You could probably get it down even more with some optimizations, but I won’t cover those in this article.
Even better, the standard library is only 364KB!
The total footprint? A little over 652KB or just over half a megabyte for a complete scripting language with classes and OOP.
Compare that to modern Python installations with 100-300MB, and you’ll understand why this feels refreshing.
Also, did I mention that it compiles in mere seconds?
The syntax is quite familiar to anyone who’s worked with Python before, which is most if not all developers.
For a pre-1.0 release of the language, it’s got quite a few features!
- Classes and OOP
- Exception handling
- Modules and imports
- range() from modern Python
- Run commands and capture their output/status codes
- and more untested!
It feels a lot like Python but… before it became bloated!
Guido really had the fundamentals right from the start.
I could totally see this language being used as a Bash alternative for scripting, or maybe in some embedded systems/IoT applications!
Debugging a 35-year-old bug
While experimenting with Python 0.9.1, I noticed that the random number generator (whrandom) wasn’t working! After looking at the code of whrandom.py, I had a theory.
“Hmmm, maybe the epoch timestamp seed from 2026 is too large?”
So, that’s it, right? Just seed the random number generator with your own values?
It turned out, no, seeding it with smaller values didn’t work either!
So I looked at what was failing again, and saw that it was the multiplication.
I tried to reproduce the steps myself, and BOOM, it overflowed!
“Why would 171 * 21 be failing? That shouldn’t be overflowing at all!” I thought to myself. As a joke, I tried 1 * 1 to see if it would fail, and to my surprise, it did!
That’s when I realised something fishy was going on. Apparently, all multiplication was causing an integer overflow!
So, I spent some time searching through the codebase and found the function that handles multiplication: int_mul from intobject.c. Now all I needed to do was find the issue. Luckily, the function was only a couple lines, and the issue was quite apparent.
One of the conditions that checks if the multiplication result overflowed was checking if the integer was less than 0x80000000. On a 32-bit system, this would be correct: it’s checking if the product was less than -2147483648.
But on a 64-bit system, suddenly, bit 31 is no longer the sign bit (the sign bit is now bit 63) and that 0x80000000 gets padded to 0x0000000080000000, which is positive 2147483648. This would make the check “if the result is less than 2147483648, then crash because we think it overflowed.”
As you can see, this is not ideal. To fix it, I patched the code to use the hardcoded numbers 2147483647 and -2147483648 in the overflow checks instead of hex, solving our overflow issue.
After patching, the code ran! Multiplication worked fine, and the random number generator was also working!
Conclusion
Python 0.9.1 is surprisingly quite a useful language and could possibly have some real-world applications! It seems well fit for a Bash/shell scripting alternative, or some embedded systems applications where you need a simple language with minimal footprint.
Although there were some hiccups with multiplication along the way, I have made a patched version that fixes this issue and a couple more, and also adds some QoL improvements that make it easy to get up and running on any modern system.
The improvements/fixes are as follows:
- Fixed int_mul integer overflow bug in src/intobject.c on 64-bit systems
- Added -std=c89 to CFLAGS in Makefile for compatibility with GCC 15+
- Added a
make installtarget that installs to /opt/python091 by default (python is at /opt/python091/bin/py091), this can be changed with the INSTALL_DIR env variable. - Fixed PYTHONPATH to be relative to the INSTALL_DIR used in
make installtarget
More information about how to get up and running with Python 0.9.1 is in the README.md of the GitHub repo for the patched version linked above. Feel free to open any GitHub issues or contact me if you have any further questions.
Thanks for reading my blog post!
I’m a 12-year-old developer making niche tech content here on my website and also on YouTube, so I really appreciate the support!
I’ll be coming out with a YouTube video corresponding to this blog post soon, and this page will be updated to show that when the time comes.
Feel free to subscribe to my YouTube channel and follow me on other socials!