Use resources loader to handle non-filesystem situations #120
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
While playing around with PyOxidizer, I realized that
certifi
was not working properly in self-contained executables generated by this tool. The typical traceback is the following:PyOxidizer maintainers made a complete explanation in this page of what is going on: https://pyoxidizer.readthedocs.io/en/stable/packaging_pitfalls.html. Short answer is: one cannot rely on
__file__
existence. Indeed the module may be exposed to Python runtime through an archive file, or even no file at all and directly from the RAM. In these cases__file__
is irrelevant or does not exist.However, the
certifi.core.where()
method is built on the assumption that__file__
exists and can be used to get the actual path ofcacert.pem
.In the same page about the Packaging Pitfalls (https://pyoxidizer.readthedocs.io/en/stable/packaging_pitfalls.html), PyOxidizer maintainers provide some decent alternatives to
__file__
in order to handle these situations, and these alternatives are using high level resources loaders APIs that have been added progressively to Python.Functional examples are provided, and I used them to reimplement the
certifi.core
module, in order to consume the available resource loaders if possible. Thanks to it, in non-filesystem situations:where()
will extract the file content ifpkg_resources
is available and put it as a temporary file in the current Python cache, then return its path.contents()
will directly return the file content ifimportlib.resources
is available (and so handling the path manipulations internally), or rely onwhere()
to return this content.As a general fallback, if no resources loader is available, the original behavior of
certifi.core
will be triggered: relying on__file__
existence. This fallback may happen on Python <3.7, and never on Python 3.7+, makingcertifi
(and sorequests
) work in any situation with this recent version of Python.Fixes #66