I may have inadvertently contributed to the Coffeescriptification of Python. And in doing so, I have learned way too much about Python internals.

Coffeescript probably wasn't the first programming language that transpiled to Javascript, but it was the first that caught on. Coffeescript and all its successors, like Clojurescript, Gorilla, and Earl Grey, exist because Javascript is a terrible language written on top of a truly universal runtime: the browser. Then some bright person took that runtime and put it on the server and called it Node, and then someone else took it and put it in the database and called it PLv8, and folks who'd been battle-hardened living in the browser could now be productive on the full stack. Transpilers exists because, rather than write in that terrible language, they allow people to write code in a language they like and it ends up all running in Javascript in the end.

Python doesn't have a universe like this. Because Python is actually a fairly good dynamic language, and the syntax is pretty much the best of the pick of its competition from Perl, Ruby, Visual Basic, or obscurities like Pike or Io. But the Python runtime is not much loved; the global interpreter lock constrains Python's performance in many ways.

There are a few, though, including:

  * [Hy](http://docs.hylang.org/en/latest/), a Lisp
  * [Mochi](https://github.com/i2y/mochi), a Python derivative with Erlang actors
  * [Dogelang](http://pyos.github.io/dg/), an ML-imitating... thing

With the coming Gilectomy, Python may finally move past some of its more egregious runtime limitations, and when it does, maybe alternative language developer will start to look at it more seriously, and develop languages that run on top of it the way Pure or Elm run on top of Javascript, yet manage to do so while honoring functional purity in a very real way.

But Python has one other limitation: the extension ".py" is hard coded into the import library that ships with python. Hy had a fairly good work-around that let it load its libraries and python side-by-side, and Doge seems to have a similar solution, but none of them were universal.

Then I learned that Hy doesn't work with Django.

This all started out because I wanted to write a Django app. It had been awhile, and I had a specific desire to take what I'd learned writing mp_suggest and do something a little more complex with it, as a way of organizing the several thousands of CDs and their rips I have on my home NAS server.

I'm sure there are Django apps out there that already do this, but I wanted to write my own, and as you'll see if you follow that like mp_suggest runs on Hy. Most of Django worked, but custom commands couldn't be found by a Hy-based version of manage.hy, which annoyed me.

An itch that needed scratching! It took me two weeks; I probably put about 20 hours into untangling all of it. The real issue was that Hy was hooking into Python's sys.meta_path, when what was needed was something even earlier in the import chain, a sys.path_hook. Path hooks are black magic. I'll have a more technical entry up in a little bit.

In the meantime, here's the patch. At first, it was just hooking up the path_hook, but as I was looking at the code, I realized that the hy-specific parts were actually fairly limited. It came down to two items: filename extensions, and a compiler. Everything else was just... boilerplate.

So I abstracted it out.

It's now possible, by adding a few extra imports to your manage.py file, to build a Django app where:

  * Your views are written in Hy
  * Your models are written in Mochi
  * Your custom commands are written in Doge

And it all just works.