-
-
Notifications
You must be signed in to change notification settings - Fork 33.2k
gh-121607: Edited source file import recipe to make it more clear #121519
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
Changes from 6 commits
08917bd
bab2fa8
81885ad
7905ab0
c78a1a6
b9cb5d3
73e4c94
fed1797
e5a864e
e6a1965
5a74186
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1584,20 +1584,29 @@ Note that if ``name`` is a submodule (contains a dot), | |
| Importing a source file directly | ||
| '''''''''''''''''''''''''''''''' | ||
|
|
||
| ``SourceFileLoader.load_module()`` has been deprecated -- this recipe should be used instead. | ||
|
|
||
| To import a Python source file directly, use the following recipe:: | ||
|
|
||
| import importlib.util | ||
| import sys | ||
| import importlib.util | ||
| import sys | ||
|
|
||
| # For illustrative purposes. | ||
| import tokenize | ||
| file_path = tokenize.__file__ | ||
| module_name = tokenize.__name__ | ||
|
|
||
| spec = importlib.util.spec_from_file_location(module_name, file_path) | ||
| module = importlib.util.module_from_spec(spec) | ||
| sys.modules[module_name] = module | ||
| spec.loader.exec_module(module) | ||
|
|
||
| def import_from_path(module_name, file_path): | ||
| spec = importlib.util.spec_from_file_location(module_name, file_path) | ||
| module = importlib.util.module_from_spec(spec) | ||
| sys.modules[module_name] = module | ||
| spec.loader.exec_module(module) | ||
| return module | ||
|
|
||
|
|
||
| # For illustrative purposes only (use of `json` is arbitrary). | ||
| import json | ||
| file_path = json.__file__ | ||
| module_name = json.__name__ | ||
|
|
||
| # Similar outcome as `import json`. | ||
| json = import_from_path(module_name, file_path) | ||
|
Comment on lines
+1609
to
+1614
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I included this example because there had been a similar one in the previous version. However, it is (always?) a bad idea to use this recipe to import a module on sys.path, particularly an stdlib one -- so maybe better to simply not provide a runnable example? I expect the code itself is clear enough on how it can be used. |
||
|
|
||
|
|
||
| Implementing lazy imports | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be changed to be a warning about the recipe and to consider alternatives before using it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really don't feel qualified to write that warning -- I suppose I could take some of @ncoghlan's note above and try, but maybe someone else could propose text.
FWIW, I guess ncoghlan's note is why this is a recipe in the docs, and not a function in importlib, but I DO think this is a useful thing to do -- yes, i suppose a bit of a foot gun, but still useful.
In regards to the potential double import problem, in this case of the json module - yes, using the recipe for something on the import path is a bad idea, for sure -- that's only there to provide a example that can be run -- I borrowed that from the existing docs, but maybe it would be better to not provide a runnable example, and rather something like:
my_mod = import_from_path("my_mod", "path/to/a_python_file.py")For the use-case that got me here, I'm using Python as a configuration definition -- yes, very dangerous if you are getting the config from an untrusted source, but that's not the case here (internal data analysis tool) -- and I very much do want a module, not a dict of a namespace.
I think this would be very useful for, e.g. a scriptable application as well, so users could provide external python scripts that could be run.
But, as @ZeroIntensity said, this is already in the docs -- I'm just trying to make it a bit more clear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In most cases,
runpycan do that, but that's beside the point anyway.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can take what I outlined as what should be in the paragraph and just write better prose: