-
-
Notifications
You must be signed in to change notification settings - Fork 31.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SourceFileLoader does not (fully) accept path-like objects #87005
Comments
If one uses the loader created by importlib.machinery.SourceFileLoader(module_name, path) in a meta path finder, where path is not a str, then the following error would happen at the moment the module is being imported. Note that, the error would not occur if the corresponding bytecode cache (pycache/*.pyc) is present. File "/usr/lib/python3.9/importlib/init.py", line 127, in import_module Here is an example that could trigger the error. The package unipath is not very important except that unipath.Path is not marshallable. (Any library with a fancy, unmarshallable path-like object would work.) The choice of 'local' and the use of 'os.getcwd()' are also irrelevant. The actual production code is quite complicated, using different path-like objects and convoluted logic. I made up this example from scratch. import os
import sys
import importlib
import importlib.abc
import importlib.util
from unipath import Path # just an example
class DummyFinder(importlib.abc.MetaPathFinder):
def __init__(self):
self.cwd = os.getcwd()
def find_spec(self, fullname, path, target=None):
if fullname == 'local':
initpath = os.path.join(self.cwd, '__init__.py')
if os.path.isfile(initpath):
loader = importlib.machinery.SourceFileLoader(fullname, Path(initpath)) # some non-str Path-like object here
return importlib.util.spec_from_file_location(fullname, initpath,
loader = loader, submodule_search_locations=[self.cwd])
else:
return None
sys.meta_path.append(DummyFinder())
importlib.import_module("local.test2") If this is indeed a bug, there might be other classes and functions in importlib that share the same problem. |
Maybe Brett can help? |
importlib is probably not os.PathLike-clean due to its bootstrapping restrictions of not getting to use anything implemented in Python from 'os' (i.e. if it isn't being managed in posixmodule.c then it probably won't work). If you follow the traceback it's trying to marshal a code object for the eventual .pyc file and failing ( cpython/Lib/importlib/_bootstrap_external.py Line 604 in faf4957
Best guess? The compile() function is being given the path-like object (via cpython/Lib/importlib/_bootstrap_external.py Line 848 in faf4957
|
If Brett's theory is right, compile() has a bug though -- it shouldn't plug a non-string into the code object. Or possibly the code object constructor should reject such objects (or extract the string). |
I just filed the slightly more general bpo-45127 ("Code objects can contain unmarshallable objects"). |
This hasn't been touched for a while, but I just ran into the issue. I'm trying to dynamically load a module from a file on disk with a path provided by code. using:
if
which is all as expected, and why this issue exists. So: would it be possible to simply call: Or, as Brett suggested: "due to its bootstrapping restrictions of not getting to use anything implemented in Python from 'os' " is still the case. Is there a simpler option: Could you do If It's simply not possible, then it could be clearly documented that it only takes a string path, and this issue can be closed as won't fix. |
That's still the case.
That exact sort of shortcut is why
I would be fine with that unless someone wants to put in the effort to do the work to make importlib support path-like objects. |
sure -- which is what I meant by "not robust" -- but I think supporting the PathLike in the stdlib would be a win, even if no others would work. NOTE: I see in
It is wrapping the path in Looking closer, making all of
which would then accept anything with an However, thinking now -- So really, the only reason that folks might want to use
Is not a very user-friendly way to do it -- it sure feels like poking into internals in a way I shouldn't be. And if I'm reading the current source correctly So, maybe what we should do is add a function to
Which, in fact, is a lot like the function I put in my code for this very reason. NOTE: It seems the non-deprecated way to do this is to use Which makes a utility function like the above all the more a good idea. [*] importlib is not well documented for end-users -- again, a reason that a utility function would be a good idea |
If you feel there are improvements to be made to the documentation, then please consider opening PRs to improve it. But I would also ask that you please not state your opinion as fact as those of us who have put significant time into the module may not agree w/ your view and feel demotivated by your phrasing. |
I apologize for the harsh wording. I should have said: "I have found the module hard to understand" When I was struggling to understand it, I assumed the docs had not been written with someone like me in mind -- i.e. someone trying use the I honestly couldn't begin to write a PR, as I don't understand the module. If it would be helpful, I could comment on at least some of what I found confusing. In any case, what I am trying to say is that And maybe there are other utilities that could be helpful as well -- in the spirit of Thanks, Chris |
Yep, separate issue(s) outlining what you found confusing would be helpful.
It's a balancing act. We don't necessarily want to make it too easy to circumvent the import system as that's a big hammer w/ subtlety that a lot of people (accidentally) ignore but still end up getting bit by. That's why we have e.g. https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly to tell people what they can basically do for common cases. This is also often a situation where people expect different things to be handled for them, so coming up w/ an encompassing API is tough. As such, we have typically encouraged people to look to PyPI for higher-level stuff that doesn't directly tie into how import functions. |
Ahh -- thanks -- I hadn't found that. That looks to me like it's the non-deprecated way to do:
Is that correct -- will it do the same thing? In which case, I propose that that recipe be added to
I understand that it was a deliberate decision to put the recipe in the docs, rather than a utility function, though unless it's really something that is "not recommended" (in which case perhaps it should say so in the docs) -- then this is just complex enough that a utility function is called for -- particularly since Does this need another issue or PR? Meanwhile, for the original issue: I suggest that checking for Though for my use-case the proposed utility function would solve the problem at hand. |
Roughly.
This is being discussed in your PR, but yes, the recipe needs a warning on it.
Yes as I'm going to close this issue. |
Closing as making this change is a lot of work for something that people in general should be discouraged from doing (circumventing the import system to import something). |
Someone working on #121604 said this should be raising a deprecation warning and a quick check of the source that should be happening. Was that not happening for you? |
It was not -- but I was (likely) testing in an older version. Let's assume it is, and I'll re-post if I do find a problem when I check 3.12. |
Confirmed -- deprecation warning is there -- I didn't see it because I didn't run with -Wd -- which is a issue that is unrelated to this one. All good here. -Thanks |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
Linked PRs
The text was updated successfully, but these errors were encountered: