PyGeoHash v3.3.1: A Round of Sharp-Edge Fixes
PyGeoHash v3.3.1 is out on PyPI. This one is not a performance release like the June run of optimization work was, it is a cleanup release. It goes after the sharp edges that were still hiding in the C extension, tightens up a couple of functions that returned the wrong answer in corner cases, and finally ships the typing marker that should have been in the wheel a long time ago.
Where the perf story left off
The last few PyGeoHash posts were a thread about speed. I benchmarked it against six other libraries, then trimmed the Python wrapper sitting in front of the C, then went into the C decode path itself. That work landed in v3.3.0. Once the hot paths were fast, the obvious next move was to go back through the same code with a different question in mind: not “is this fast” but “what happens when someone feeds this garbage.” v3.3.1 is the answer to that question.
What’s in it
Here is the whole changelog, in plain terms.
A buffer overrun in the C encoder. encode and encode_strictly now reject a precision outside the 1 to 12 range with a ValueError. If you reached past the Python wrapper and called pygeohash.cgeohash.geohash_module.encode directly with a bigger precision, the C code wrote past a fixed-size buffer. That is the kind of bug that is invisible until it corrupts memory somewhere unrelated, so this is the most important fix in the release.
Non-finite coordinates. The C encoders now reject NaN and infinite coordinates with a ValueError. Before this, passing an infinite longitude would hang the call outright, which is a rude thing for a library to do to your process.
Distance between identical geohashes. geohash_approximate_distance now returns 0.0 when you hand it the same geohash twice, instead of the precision-table value for their shared prefix. Two identical points are zero apart, which the function now agrees with.
Bounding boxes at the edge of the world. geohashes_in_box now clamps its sampling range to latitude plus or minus 90 and longitude plus or minus 180. Boxes that touched the world limits used to enumerate incorrectly, and now they don’t.
A matplotlib compatibility break. plot_geohashes works again with matplotlib 3.9 and later, which removed matplotlib.cm.get_cmap out from under us.
The typing marker. The py.typed marker now ships in both the wheel and the sdist. PyGeoHash has had inline type hints for a while, but type checkers could not see them because the marker was not in the built artifact. Now they can, and the “Typing :: Typed” classifier on PyPI is finally telling the truth.
A documentation correction. encode_strictly used to claim it performed extra validation. It does not. It is identical to encode and exists only as a back-compatible alias, and the docstring now says so.
None of these change the public API in a way that should break working code. If you were relying on encode silently accepting an infinite latitude, I have questions, but otherwise this is a safe upgrade.
How to upgrade
It is on PyPI, so the usual incantation does it:
pip install --upgrade pygeohash
Or if you pin versions, ask for it directly:
pip install pygeohash==3.3.1
There are no new runtime dependencies. If you use the plotting helpers, this is the version you want on modern matplotlib. If you run a type checker over your codebase, this is the version where PyGeoHash’s own hints start showing up. The project page has the quick-start if you are new to the library, and the full history lives in the CHANGELOG on GitHub.
Who actually wrote this
Here is the part I find more interesting than any single one of these fixes. I did not write most of v3.3.1. My autopilot did.
I have been documenting the system that runs my open source work lately. It is built on two pieces I wrote up recently: Swamp, a deterministic automation framework designed to be operated by AI agents, and the Open Knowledge Format, a small spec for handing curated context to those agents. The autopilot is a scheduled Swamp workflow that keeps a fleet of coding agents busy across my repos, and I wrote up the full loop, where the agents grow a knowledge base as they work and distill reusable skills out of it.
v3.3.1 is a good example of what that loop produces on its own. The ideation job looked at PyGeoHash after the performance work wrapped up and proposed hardening the input handling. The implementation job wrote the fixes, the tests, and the changelog entries, opened them as separate PRs, and drove them to green. Every one of those PR numbers in the git history is an agent that found a problem, fixed it, and defended the fix in review. My contribution was reading the diffs and clicking merge. The buffer overrun in particular is exactly the sort of thing that sits unnoticed in a fifteen-year-old library until something goes looking, and the thing that went looking was not me.
I am not going to pretend that is a substitute for judgment. I still gate the work and I still merge it by hand, on purpose, for reasons I got into in the Swamp post. But for a release that is entirely “go back through the code and find the places it lies to you,” an agent that never gets bored turns out to be a pretty good fit. Grab v3.3.1, and if you find an edge it missed, the issue tracker is open.
Stay in the loop
Get notified when I publish new posts. No spam, unsubscribe anytime.