Repository navigation
Move vendored modules into a vendor directory #129222
Description
Feature or enhancement
Proposal:
In CPython, we have some vendored libraries namely libmpdec, hacl, and expat. Those libraries are meant to be clone of their upstream (think of them as git submodules) and many times have I been exluding them from code search as they usually have non-CPython code involved.
We have a plan to remove vendored libmpdec (#115119) and we're almost here. In the long term, we could also try to unvendor expat (probably not hacl as it's used to implement hash functions fallbacks when OpenSSL is not present).
Affected modules:
libmpdecexpat_hacl
Some advantages:
- Easy exclusion of vendored modules from code search.
- Easy addition and deletion of vendored modules.
- We don't expect redistributors to edit vendored code (especially not
hacl). - Hopefully less work than the
Python/Programssplit - Could be using git submodules (though this requires an internet connection, so maybe not)
Some inconvenients:
- Quite painful refactoring since it would affect existing PRs.
- Quite painful refactoring since it would affect distributors editing those files.
We can start with some modules that should be kept untouched such as HACL* sources and progressively move the others to reduce the work and conflicts. It doesn't need to happen in one go (for instance, we may well ignore the libmpdec case if we manage to make it unvendored before). I don't think we have much open PRs with expat (by the way, we could have a refresh script for expat to ease maintenance like #126623).
Now, the question is how this could affect downstream redistributors. I'm asking first on Github since I don't know whether they are active on Discourse or not. If everyone tells "it's fine", then I'll ask on Discourse to see if there are more redistributors that could be concerned.
cc
- @zooba (Windows)
- @mhsmith (Android) ✅
- @doko42 / @stefanor (Ubuntu/Debian)
- @mitya57 (Debian)
- @hroncok / @vstinner (Fedora/RHEL) ✅
- @mgorny (Gentoo) ✅
- @danigm / @mcepl (openSUSE) ✅
- @freakboy3742 / @ned-deily (macOS / iOS) ✅
- @zanieb (relocatable builds)
- @hugovk / @erlend-aasland (CPython)
- @mweinelt (NixOS) ✅
- @gpshead / @ncoghlan / @brettcannon / @zware (involved through Discord messages or reactions)
I don't know how moving mimalloc related stuff would affect the free-threaded build in particular, so I'm also going to ask @kumaraditya303 and @colesbury about it. EDIT: Turns out it's a no go for mimalloc as there is some CPython dedicated stuff, so we can put it out of the list (see #129222 (comment)).
For the hacl includes, I can take care of it.
Activity
mhsmith commentedon Jan 23, 2025
Android doesn't do anything special with these libraries, so it shouldn't be affected.
mgorny commentedon Jan 23, 2025
Gentoo used to remove these libraries entirely, but we don't do that anymore. Which reminds me that I was supposed to fix the issue that prevented us from doing so. Also, I was supposed to see if we can remove them. Days are too short.
ned-deily commentedon Jan 23, 2025
s/nedbat/ned-deily/
s/universal macOS builds/relocatable build/
Such a change should have no impact on macOS builds, macOS installer builds, or iOS builds.
picnixz commentedon Jan 23, 2025
Oups, sorry for mistagging you and for the miswording (forgive my lack of macOS knowledge).
picnixz commentedon Jan 23, 2025
Er... this is funny but... who should I ask for Ubuntu actually?
hugovk commentedon Jan 23, 2025
Bikeshedding: call it
_vendorinstead ofvendor?For example, pip and setuptools have
_vendordirectories.colesbury commentedon Jan 23, 2025
Mimalloc has substantial modifications from upstream. Daan Leijen was working on changes to mimalloc to support the features needed by free-threaded CPython upstream, but it's probably going to take a while. Even then, it will require CPython specific build configuration -- it's never going to be something where you can just drop in a shared library provided by the OS.
ned-deily commentedon Jan 23, 2025
@doko42 had/has been the Debian/Ubuntu contact
mgorny commentedon Jan 23, 2025
I think the point of
_vendoris that it's installed as a Python package but not meant as a public API, so it's not really applicable here.picnixz commentedon Jan 23, 2025
Thank you, I wasn't aware of it! I've removed from the list of possible vendored modules.
I don't mind. At least, vendoring modules could be included last (or first) and be well-separated from other includes thanks to their name starting with
_. Note that we might have some additional files next to the vendored modules to ease our life (e.g., for HACL, we redefine some names to avoid name clashes, I don't know how it is for other modules).colesbury commentedon Jan 23, 2025
It may still make sense to include it in the vendored modules directory. I don't have a strong opinion one way or the other. It depends on what you are primarily trying to communicate with the vendored directory. For example, it's third-party code in terms of license, attribution, and code style. But Linux distributions should probably treat it more like internal CPython code. For example, they should not substitute their own mimalloc package.
zanieb commentedon Jan 23, 2025
Thanks for the ping!
I think there would be some minor pain downstream because we have a weird custom build system for the extension modules, but it's fine.
What about changes like #127932?
picnixz commentedon Jan 23, 2025
For me
vendoredwas in the sense of something that is not ours but just a bundled copy of a project to avoid installing separate packages. If it's internal CPython code, even if it heavily relies on an upstream package, I think we can keep it where it is now. Since mimalloc is primarily for free-threading-related stuff, I think it's fine to keep it where it is (it would also avoid the needs of changing the configure script I think, so I don't mind leaving mimalloc alone).I'm not sure I understand your question but I'll try to reply with what I understood. If the upstream has issues, we can't really change the vendored copy even if we want to because we have some SBOM checks. So we can't just "hotfix" HACL* when we want to (we need to wait for upstream updates). So whether we move it to
vendoror not, we would have the same issues.4 remaining items
eli-schwartz commentedon Jan 23, 2025
Cloning the cpython git repo already requires an internet connection. Doing a recursive clone doesn't require more of one.
The release tarballs should absolutely include the contents of a submodule as well -- in fact you have to since you can't later clone the submodules, Internet connection or not, since there is no submodule metadata.
The advantage of using submodules is that it becomes very very very clear what is vendored versus what is patched.
The Meson build system does something similar using "wrap files" built into the core build system, that contain an URL download location for the upstream release tarballs and a checksum, which works even without git metadata. This also means that Meson can distribute your choice of "fat" release tarballs with vendored dependency fallbacks, or "thin" release tarballs with just the wrap definition, and at build time it will download and checksum the vendored dependency only if you don't have a system copy, or conditional on configure options such as "force vendor dependencies for standalone builds" or "forbid vendor dependencies for distro policy". The code to handle this robustly is not trivial, which is why we built it into the core so that it only has to be maintained in a single place. You probably want to stick with submodules.
hroncok commentedon Jan 23, 2025
Moving the vendored projects to an explicit directory would not impact Fedora/RHEL. We would need to change the paths for those that we rm, but that should be it.
gpshead commentedon Jan 23, 2025
If you're bikeshedding the name, github code search already excludes directorys named
third_party/as that is a common industry norm place to put... third party software that you've vendored.HACL*belongs specifically withhashlibextension modules so colocating it in a sub-tree under that could would make sense to me, but I'm not picky, it's just a matter of updating include paths and our script that updates the specific version we use.Ideal: We'd make a top level
third_party/tree to contain all of these vendored bits in their own subdirectories.Doing that would be good for project health IMNSHO rather than scattering stuff around as we do today. re-packagers should appreciate it as well as they each have their own ideas of what they do and don't want to include vs pull from external sources and it makes it more obvious what we've got that could be considered for that.
Non-goal 1: Vendored things each need their own management in terms of how we get them, update them, and patch them if necessary for our repo. Don't try for a single approach there, it won't fit everything. There may be some commonalities on how that gets done but that kind of thing can be worked out on a case by case basis later.
Non-goal 2: Vendored things in our tree do NOT need to match upstream. applying patches is part of vendoring. the important thing is to track those patches and automate recreating such changes when updates are needed rather than expecting the next person doing it to fully understand.
gpshead commentedon Jan 23, 2025
BTW, the reason
pipandsetuptoolsput a_prefix on their vendor tree is likely to make it clear that it is private in a Python naming sense. The things we vendor within CPython are not Python code. We shouldn't need to do that.eli-schwartz commentedon Jan 23, 2025
... or
thirdparty/orthird-party/, the latter of which I personally feel I've seen more often than the version with an underscore. :D I wonder if github excludes those too?picnixz commentedon Jan 23, 2025
Oh... then maybe we can use that name :') If it's supported by Github, I would vote for 'third-party' because I don't need to press shift for
_(and it reads better than thirdparty).Yes, that's what I think as well. I don't think we can have a unified approach.
I'm ok with this as well. I don't mind having each vendored module with its own patching system (I mean, we're already doing it somehow).
Now, AFAICT, there doesn't seem to many issues about just moving files which was my primary concern here. I'm no expert in redistribution but since I raised the issue on Discord, I thought it was also good to actually first ask whether this is something that would be interesting for CPython in the long term. And I'd like to thank everyone involved in this (constructive) discussion.
eli-schwartz commentedon Jan 23, 2025
By the way, Meson explicitly mandates that all "subprojects" (thirdparty code with their own build system that can be autoconfigured) must exist in a single top-level directory (default:
subprojects/but you can define it in yourmeson.buildfile if you use a different directory instead) for precisely this reason. It is extremely nice to be able to tell what code is third-party and what code is first-party.mcepl commentedon Jan 27, 2025
If the question is just whether the vendored libraries would be concentrated in the one directory called
vendor/, then I am all for it. I could runrm -rfover one directory much more easily.If the question is how happy I am with the continuing tendency to vendor many libraries and instead of collaborating with the canonical author of the library, use modified version of the third party library, then I am absolutely against, and I consider such action (except for the very temporary important security fixes or such) always wrong. We have then situations like #92875 where only SUSE and RedHat complain about a bug, and because of local modifications it doesn’t show in CI.
eli-schwartz commentedon Jan 27, 2025
I don't see how that ticket has anything to do with using modified versions of a library.
mcepl commentedon Jan 27, 2025
Modified libraries were mentioned, and generally this ticket has a lot to do with a bigger acceptance of the practice I strongly oppose.
eli-schwartz commentedon Jan 27, 2025
Sorry but I don't see how you can possibly compare:
If it were a modified version of the third-party library, then the modifications would make it impossible to have a system copy at all. That's an unrelated topic, and is the case for things like mimalloc -- regardless of what directory it is stored in, you and I don't have the option to
--with-system-mimallocand it cannot be deleted and this ticket won't change that at all.What is your objection to expat being moved to a different directory?
mcepl commentedon Jan 28, 2025
picnixz commentedon Jan 31, 2025
That was my main intent. I knew how it could affect downstream redistributors, so I wanted to first know if just moving stuff around is already a problem or not. By centralising what is vendored (possibly patched, possibly amended for our needs, but essentially something that can be thought as an external dependency that does not need additional installation steps), from "pure" CPython code (namely something written from scratch), it would be easier to manage those dependencies (whatever "manage" means here; it can be "maintained", "removed", "patched" or even "added"!)
I am personally not in favor of vendoring stuff as it adds unnecessary files that could be found elsewhere as dependencies but deciding whether we continue vendoring stuff or not should be a separate question IMO.
Maybe a bit more of a context. I was actually fixing UBSan failures and reviewing files in the project tree, hunting for possible UAFs and I was like "oh but those folders are just unnecessary for me, those are vendored stuff" and I wished I didn't need my IDE to render them (though they should be indexed for autocompletion to work if needed). If everything was in a single folder, it would be easier to just exclude them from code search and to also avoid modifying them by mistake. It also makes the project more structured (for instance, I thought that mimalloc was entirely vendored but that wasn't the case so it's also a good thing to know what's ours and what's not).
eli-schwartz commentedon Feb 2, 2025
Ironically enough, both rust and go do NOT use source vendoring at all. They require rebuilding statically linked dependencies in local build tree scope, which is binary vendoring, but fetch it from the canonical source using a language-specific package manager each time... it is again unlike using a
vendordirectory (for multiple reasons).Because cpython did not simply vendor mimalloc, cpython forked and patched mimalloc and thus it is impossible to build against it even if you try.
#113141 is a tracking issue discussing those patches, which proposes to get cpython to align with upstream mimalloc sufficient to use a system mimalloc. Please address your comments there instead. :)
mcepl commentedon Feb 2, 2025
Thank you, I will.