I was building Python applications into one artifact and using distlib for the packaging machinery around them. I wanted the interpreter, libraries, native extensions, and application to fit together without a separately assembled environment on the destination machine.
The later Zippy repository records much of that construction. The earlier distlib issues show it already in motion: ordinary packages meeting an unusual installation layout, and a draft metadata system meeting real build behavior. Vinay Sajip maintained distlib and its companion installer, Distil. I tried them against what I was building and sent back the places where my expectations and the implementation parted ways.
Finding a name was not finding a version
Issue 18 started with a local source cache followed by remote locators. I wanted local files preferred, with remote sources available when the cache could not satisfy a requirement.
The requirement was wheel (== 0.18.0). The directory contained wheel-0.17.0.tar.gz. With locator merging disabled, the first locator that recognized the project name won. Version matching happened afterward, when the caller discovered that the returned candidates could not satisfy the requirement. It was too late to continue to the remote locator.
The relevant distinction was between finding any version of this project and finding a version matching this request. My initial suggestions included yielding candidate sets or moving the search logic. Vinay instead made the requirement available to the locator selection path and closed the issue with that repair.
That retained ordered source preference without treating a stale local archive as a reason to stop the search.
The data changed underneath the library
A few days later, dependency resolution began raising an unhashable-dictionary error. The remote metadata had acquired nested requires wrappers where the consumer expected requirement entries. Updating my library checkout did not repair the data it was receiving.
I traced the unexpected shape and tried unwrapping it to check the diagnosis. Vinay confirmed that a script updating hosted metadata after changes to PEP 426 had produced the bad nesting. He repaired that dataset. The failure was not in the resolver’s selection algorithm.
That exchange also contains a useful early record of my application: a Python/uWSGI multicall binary with native extensions built in and a ZIP appended to the executable. I printed its builtin-module list and demonstrated importing uWSGI and a ZeroMQ extension as builtins. It was already a working experiment before the public Zippy repository began.
PEP 426 was still a draft. The library, its companion application, and the hosted metadata had to move together as that draft changed. I asked about versioning the endpoint because an unchanged client could otherwise receive data in a shape it no longer understood.
A directory inside a ZIP
My bundle also put the standard library below a prefix inside a ZIP, with distribution metadata beneath its site-packages. Python could import from that layout, but DistributionPath found no distributions. The directory-looking path was an importer resource, not an ordinary filesystem directory.
I sent a patch asking the discovery code to use distlib’s resource layer. The importer selected the resource finder; the finder could then enumerate the contents without requiring them to exist as loose files. Vinay integrated the change more broadly, including exports and records.
The related entry-point discussion was equally practical. Existing pkg_resources behavior did not fit the nested ZIP layout. I asked for a replacement for load_entry_point; Vinay pointed me to distlib’s already documented equivalent. Some contributions were patches. Some were finding the API I had missed.
Packaging the packaging tool
Distil was distributed as a self-contained script. I wanted it installed like another package, using the distlib copy my application already supplied. That would make it easier for the developers using my bundle to install additional libraries, and easier for me to inspect and patch the tool.
Vinay’s response explained the other side: the self-contained form avoided installing another copy into every environment and kept the application paired with the library revision it expected. It was still experimental, and he wanted user feedback before opening it to broader collaboration.
We worked through interim builds. One small failure initialized disabled console and GUI exports as empty lists even though later code expected mappings. The correction was an empty dictionary.
The more fundamental limit appeared in issue 24. Some package versions could not be represented under the version policy, leaving expected metadata absent. Other packages needed native headers or performed custom work in setup.py. Declarative metadata could describe dependencies without replacing all the environment-sensitive work required to build the package. Distil retained a path through the existing build tools for those cases.
What a shebang requests
A script path containing @ hit an overly restrictive shebang expression. The discussion with Vinay and Daniel Holth separated two stages: a wheel builder recognizing the source script’s interpreter line, and an installer replacing the wheel’s specified #!python marker with its selected interpreter.
A builder could accept more source forms and normalize them. That did not mean every installer should invent its own permissive rewriting policy. Arguments and paths containing spaces made the distinction especially important. The eventual correction was broader than adding one character to a regex.
This work explains the line in distlib’s contributor file much better than the credit alone. I was trying to make one particular build shape work; Vinay was maintaining a library and installer with other users and changing standards to consider. We could work through those competing requirements once a failure was specific enough to reproduce.