-
Notifications
You must be signed in to change notification settings - Fork 815
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
Simplify pywin32_bootstrap, avoid importing site #1651
Conversation
Since pywin32 has dropped support for Python 2.7 since version 300 and support for Python 3.0 through 3.4 since version 223, we can now rely on [PEP-420] semantics when importing names of directories available on `sys.path` and not containing an `__init__.py`. This applies to to `pywin32_system32`. The previous version of `win32.lib.pywin32_boostrap` manually searched site-packages directories, stopping at the first one found. Now we pass this responsibility off to Python's import machinery. When we `import pywin32_system32`, if successful, `pywin32_system32` will likely be a `_frozen_importlib_external._NamespacePath` object. We obtain its first entry, being careful that it didn't support sequence indexing until Python 3.7 or so (and in any case, that's all undocumented; the [documentation promises] only that `__path__: Iterable[str]`). This first entry is the path to the directory containing the DLLs, just as before, so the remainder of the code is unchanged. The only semantic differences between this implementation and the previous one is that 1. we no longer import `site` (always better to import less!), and 2. `pywin32_system32` is no longer _required_ to be in a site-packages directory Just as with the previous implementation, this one does not raise an exception if `pywin32_system32` cannot be found. I did one annoying thing in this patch to minimize the diff, which is that I redefine the variable `pywin32_system32`: first it refers to a namespace package module object, and then later it refers to the `str` directory name. It might be reasonable after initial review of the patch to rename the latter instance of the variable to something like `path`. Finally, I added no tests and no change log entry, but can if necessary. [PEP-420]: https://www.python.org/dev/peps/pep-0420/#specification [documentation promises]: https://docs.python.org/3/reference/import.html#__path__
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.
Thanks, I don't see a problem with this approach. I think renaming the var makes sense, and I'd also like a changelog entry just to help people should this have unintended consequences.
win32/Lib/pywin32_bootstrap.py
Outdated
# It needs to be searched when installed in virtual environments. | ||
level3_up_dir = dirname(dirname(dirname(__file__))) | ||
try: | ||
Exc = ModuleNotFoundError # Introduced in Python 3.6 |
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.
Is this strictly necessary? IIUC, ModuleNotFoundError
derives from ImportError
and I don't see a good reason to differentiate.
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.
My instinct is generally to pick the most specific possible exception for except
to match. This avoids hiding errors unrelated to the absence of pywin32_system32
. There would be some bizarre circumstances that would lead to such hiding, but the cost of being specific here seems low to me. Anyway, I won't die on this hill. If you insist, I'll just use ImportError
.
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 don't feel too strongly about it, and in general I'd agree, but in this case I feel the added complexity from changing behaviors based on the version, when in practice it's exactly the same behavior regardless, swings the argument to just catching ImportError.
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.
Fixed in 7a6de27
win32/Lib/pywin32_bootstrap.py
Outdated
|
||
for site_packages_dir in site_packages_dirs: | ||
pywin32_system32 = os.path.join(site_packages_dir, "pywin32_system32") | ||
try: |
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.
We can probably simplify this by putting the rest of the body under this else - ie, just have a pass
on the import error and everything else in the else
?
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 think df3e334 is responsive to this.
mhammond#1651 (comment) This very slightly changes semantics: if `pywin32_system32` is found to have multiple entries in `__path__` and the first one is not an existing directory, now the search will continue whereas before it would give up.
Per review comment: mhammond#1651 (comment)
Normally I wouldn't push the line on PEP 8 for importing `os`, but `win32.lib.pywin32_bootstrap` is imported by `site` at Python startup via pywin32.pth. Programs that care a lot about startup time might appreciate our caution here.
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.
Awesome, thanks!
I believe I've responded to everything. 74c96dc is not strictly necessary, just seemed like a good idea. See the commit message. I can revert it if you'd prefer. |
There is still one small behavior change. Previously it is possible to use the system python to manually call
Now However, this is no big deal as
Nice work @wkschwartz @mhammond! Really appreciate what you have been doing. |
Since pywin32 has dropped support for Python 2.7 since version 300 and support for Python 3.0 through 3.4 since version 223, we can now rely on PEP-420 semantics when importing names of directories available on
sys.path
and not containing an__init__.py
. This applies to topywin32_system32
. The previous version ofwin32.lib.pywin32_boostrap
manually searched site-packages directories, stopping at the first one found. Now we pass this responsibility off to Python's import machinery. When weimport pywin32_system32
, if successful,pywin32_system32
will likely be a_frozen_importlib_external._NamespacePath
object. We obtain its first entry, being careful that it didn't support sequence indexing until Python 3.7 or so (and in any case, that's all undocumented; the documentation promises only that__path__: Iterable[str]
). This first entry is the path to the directory containing the DLLs, just as before, so the remainder of the code is unchanged.The only semantic differences between this implementation and the previous one is that
site
(always better to import less!), andpywin32_system32
is no longer required to be in a site-packages directoryJust as with the previous implementation, this one does not raise an exception if
pywin32_system32
cannot be found.I did one annoying thing in this patch to minimize the diff, which is that I redefine the variable
pywin32_system32
: first it refers to a namespace package module object, and then later it refers to thestr
directory name. It might be reasonable after initial review of the patch to rename the latter instance of the variable to something likepath
.Finally, I added no tests and no change log entry, but can if necessary.