Skip to content
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

With keyring installed, cffi fails to upgrade on Windows #478

Closed
chrullrich opened this issue Dec 7, 2020 · 8 comments
Closed

With keyring installed, cffi fails to upgrade on Windows #478

chrullrich opened this issue Dec 7, 2020 · 8 comments

Comments

@chrullrich
Copy link

Describe the bug
keyring has no direct or indirect dependency on cffi, but with keyring installed, pip on Windows cannot uninstall, and hence not upgrade, cffi. The error indicates that _cffi_backend.cp38-win_amd64.pyd i.e. a C module, is loaded.

To Reproduce
Steps to reproduce the behavior:

  1. py -m venv test

  2. test\scripts\activate.bat

  3. python -m pip install -U pip setuptools

  4. pip install cffi==1.14.3

  5. pip install --upgrade cffi
    Result: Successful update to (as of now) 1.14.4.

  6. pip install cffi==1.14.3

  7. pip install keyring

  8. pip install --upgrade cffi
    Result:

    ERROR: Could not install packages due to an EnvironmentError: [WinError 5] Access is denied: 'c:\\daten\\test\\lib\\site-packages\\_cffi_backend.cp38-win_amd64.pyd'
    Consider using the `--user` option or check the permissions.
    
  9. pip uninstall keyring

  10. pip install --upgrade cffi
    Result: Successful update to 1.14.4 again.

Expected behavior
keyring should not interfere with updating unrelated packages.

Environment

  • OS: Windows
  • pip: 20.3.1
  • keyring: 21.5.0
> pip list | findstr keyring
keyring        21.5.0
> keyring --list-backends
keyring.backends.fail.Keyring (priority: 0)
keyring.backends.Windows.WinVaultKeyring (priority: 5)
keyring.backends.chainer.ChainerBackend (priority: -1)
@jaraco
Copy link
Owner

jaraco commented Dec 9, 2020

The WinVaultKeyring relies heavily on win32ctypes. My guess is that project uses cffi to bind to the backends.

@jaraco
Copy link
Owner

jaraco commented Dec 9, 2020

I was able to replicate the issue without keyring:

PS C:\> py -m venv test
PS C:\> test/scripts/activate.ps1
(test) PS C:\> py -m pip install -U pip setuptools -q
(test) PS C:\> pip uninstall -y -q cffi pywin32-ctypes
(test) PS C:\> pip install -q cffi==1.14.3 pywin32-ctypes
(test) PS C:\> python -c "import win32ctypes.pywin32; import pip; pip.main()" install --upgrade cffi
WARNING: pip is being invoked by an old script wrapper. This will fail in a future version of pip.
Please see https://github.com/pypa/pip/issues/5599 for advice on fixing the underlying issue.
To avoid this problem you can invoke Python with '-m pip' instead of running pip directly.
Requirement already satisfied: cffi in c:\test\lib\site-packages (1.14.3)
Collecting cffi
  Using cached cffi-1.14.4-cp39-cp39-win_amd64.whl (179 kB)
Requirement already satisfied: pycparser in c:\test\lib\site-packages (from cffi) (2.20)
Installing collected packages: cffi
  Attempting uninstall: cffi
    Found existing installation: cffi 1.14.3
    Uninstalling cffi-1.14.3:
      Successfully uninstalled cffi-1.14.3
ERROR: Could not install packages due to an EnvironmentError: [WinError 5] Access is denied: 'C:\\Users\\ContainerAdministrator\\AppData\\Local\\Temp\\pip-uninstall-vkvlmj5d\\_cffi_backend.cp39-win_amd64.pyd'
Consider using the `--user` option or check the permissions.

@jaraco
Copy link
Owner

jaraco commented Dec 9, 2020

The problem stems from the fact that pip imports keyring which imports win32ctypes.pywin32 which imports cffi if available:

(env) PS C:\> python -m pdb -m pip install -U cffi
> c:\env\lib\site-packages\pip\__main__.py(1)<module>() 
-> from __future__ import absolute_import
(Pdb) b win32ctypes/core/cffi/_dll.py:14 
Breakpoint 1 at c:\env\lib\site-packages\win32ctypes\core\cffi\_dll.py:14 
(Pdb) c 
> c:\env\lib\site-packages\win32ctypes\core\cffi\_dll.py(14)<module>() 
-> ffi.cdef("""
(Pdb) w 
  c:\python39\lib\bdb.py(580)run() 
-> exec(cmd, globals, locals)
  c:\env\lib\site-packages\pip\__main__.py(26)<module>()
-> sys.exit(_main())
  c:\env\lib\site-packages\pip\_internal\cli\main.py(73)main()
-> command = create_command(cmd_name, isolated=("--isolated" in cmd_args))
  c:\env\lib\site-packages\pip\_internal\commands\__init__.py(105)create_command()
-> module = importlib.import_module(module_path)
  c:\python39\lib\importlib\__init__.py(127)import_module()
-> return _bootstrap._gcd_import(name[level:], package, level)
  <frozen importlib._bootstrap>(1030)_gcd_import()
  <frozen importlib._bootstrap>(1007)_find_and_load()
  <frozen importlib._bootstrap>(986)_find_and_load_unlocked()
  <frozen importlib._bootstrap>(680)_load_unlocked()
  <frozen importlib._bootstrap_external>(790)exec_module()
  <frozen importlib._bootstrap>(228)_call_with_frames_removed()
  c:\env\lib\site-packages\pip\_internal\commands\install.py(17)<module>()
-> from pip._internal.cli.req_command import RequirementCommand, with_cleanup
  <frozen importlib._bootstrap>(1007)_find_and_load()
  <frozen importlib._bootstrap>(986)_find_and_load_unlocked()
  <frozen importlib._bootstrap>(680)_load_unlocked()
  <frozen importlib._bootstrap_external>(790)exec_module()
  <frozen importlib._bootstrap>(228)_call_with_frames_removed()
  c:\env\lib\site-packages\pip\_internal\cli\req_command.py(21)<module>()
-> from pip._internal.network.session import PipSession
  <frozen importlib._bootstrap>(1007)_find_and_load()
  <frozen importlib._bootstrap>(986)_find_and_load_unlocked()
  <frozen importlib._bootstrap>(680)_load_unlocked()
  <frozen importlib._bootstrap_external>(790)exec_module()
  <frozen importlib._bootstrap>(228)_call_with_frames_removed()
  c:\env\lib\site-packages\pip\_internal\network\session.py(26)<module>()
-> from pip._internal.network.auth import MultiDomainBasicAuth
  <frozen importlib._bootstrap>(1007)_find_and_load()
  <frozen importlib._bootstrap>(986)_find_and_load_unlocked()
  <frozen importlib._bootstrap>(680)_load_unlocked()
  <frozen importlib._bootstrap_external>(790)exec_module()
  <frozen importlib._bootstrap>(228)_call_with_frames_removed()
  c:\env\lib\site-packages\pip\_internal\network\auth.py(34)<module>()
-> import keyring  # noqa
  <frozen importlib._bootstrap>(1007)_find_and_load()
  <frozen importlib._bootstrap>(986)_find_and_load_unlocked()
  <frozen importlib._bootstrap>(680)_load_unlocked() 
  <frozen importlib._bootstrap_external>(790)exec_module()
  <frozen importlib._bootstrap>(228)_call_with_frames_removed()
  c:\env\lib\site-packages\keyring\__init__.py(1)<module>()
-> from .core import (
  <frozen importlib._bootstrap>(1007)_find_and_load()
  <frozen importlib._bootstrap>(986)_find_and_load_unlocked()
  <frozen importlib._bootstrap>(680)_load_unlocked()
  <frozen importlib._bootstrap_external>(790)exec_module()
  <frozen importlib._bootstrap>(228)_call_with_frames_removed()
  c:\env\lib\site-packages\keyring\core.py(186)<module>()
-> init_backend()
  c:\env\lib\site-packages\keyring\core.py(90)init_backend()
-> filter(limit, backend.get_all_keyring()),
  c:\env\lib\site-packages\keyring\util\__init__.py(22)wrapper()
-> func.always_returns = func(*args, **kwargs)
  c:\env\lib\site-packages\keyring\backend.py(215)get_all_keyring()
-> _load_plugins()
  c:\env\lib\site-packages\keyring\backend.py(202)_load_plugins()
-> init_func = ep.load()
  c:\python39\lib\importlib\metadata.py(77)load()
-> module = import_module(match.group('module'))
  c:\python39\lib\importlib\__init__.py(127)import_module()
-> return _bootstrap._gcd_import(name[level:], package, level)
  <frozen importlib._bootstrap>(1030)_gcd_import()
  <frozen importlib._bootstrap>(1007)_find_and_load()
  <frozen importlib._bootstrap>(986)_find_and_load_unlocked()
  <frozen importlib._bootstrap>(680)_load_unlocked()
  <frozen importlib._bootstrap_external>(790)exec_module()
  <frozen importlib._bootstrap>(228)_call_with_frames_removed()
  c:\env\lib\site-packages\keyring\backends\windows.py(12)<module>()
-> from win32ctypes.pywin32 import pywintypes
  <frozen importlib._bootstrap>(1007)_find_and_load()
  <frozen importlib._bootstrap>(986)_find_and_load_unlocked()
  <frozen importlib._bootstrap>(680)_load_unlocked()
  <frozen importlib._bootstrap_external>(790)exec_module()
  <frozen importlib._bootstrap>(228)_call_with_frames_removed()
  c:\env\lib\site-packages\win32ctypes\pywin32\__init__.py(11)<module>()
-> from win32ctypes.pywin32 import win32api
  <frozen importlib._bootstrap>(1058)_handle_fromlist()
  <frozen importlib._bootstrap>(228)_call_with_frames_removed()
  <frozen importlib._bootstrap>(1007)_find_and_load()
  <frozen importlib._bootstrap>(986)_find_and_load_unlocked()
  <frozen importlib._bootstrap>(680)_load_unlocked() 
  <frozen importlib._bootstrap_external>(790)exec_module()
  <frozen importlib._bootstrap>(228)_call_with_frames_removed()
  c:\env\lib\site-packages\win32ctypes\pywin32\win32api.py(12)<module>()
-> from win32ctypes.core import (
  <frozen importlib._bootstrap>(1058)_handle_fromlist()
  <frozen importlib._bootstrap>(228)_call_with_frames_removed()
  <frozen importlib._bootstrap>(1007)_find_and_load()
  <frozen importlib._bootstrap>(986)_find_and_load_unlocked()
  <frozen importlib._bootstrap>(664)_load_unlocked()
  <frozen importlib._bootstrap>(627)_load_backward_compatible()
  c:\env\lib\site-packages\win32ctypes\core\__init__.py(36)load_module()
-> module = importlib.import_module(self.redirect_module)
  c:\python39\lib\importlib\__init__.py(127)import_module()
-> return _bootstrap._gcd_import(name[level:], package, level)
  <frozen importlib._bootstrap>(1030)_gcd_import()
  <frozen importlib._bootstrap>(1007)_find_and_load()
  <frozen importlib._bootstrap>(986)_find_and_load_unlocked()
  <frozen importlib._bootstrap>(680)_load_unlocked()
  <frozen importlib._bootstrap_external>(790)exec_module()
  <frozen importlib._bootstrap>(228)_call_with_frames_removed()
> c:\env\lib\site-packages\win32ctypes\core\cffi\_dll.py(14)<module>()
-> ffi.cdef("""

@jaraco
Copy link
Owner

jaraco commented Dec 9, 2020

So I think that either cffi or pywin32-ctypes will need to provide a mechanism that doesn't lock the CFFI dll when it's in use. I don't see how there's anything keyring can do here.

@chrullrich
Copy link
Author

(test) PS C:> python -c "import win32ctypes.pywin32; import pip; pip.main()" install --upgrade cffi

The error reproduces with the import win32ctypes.pywin32 statement above, but does not without. cffi upgrades fine with pywin32-ctypes installed when using "normal" command lines (pip install -U cffi as well as python -m pip install -U cffi). As soon as keyring is also installed, it does not anymore.

I agree that the immediate cause for the bug is that the DLL is loaded by pywin32-ctypes, but the root cause is the presence of keyring.

@chrullrich
Copy link
Author

Argh. No, that's not it. The problem is that pip uses keyring if present: pypa/pip#6773, with the usual endless discussion. I was about to blame keyring for manipulating the environment in a nonobvious way that causes pip to load keyring, but pip manages all on its own.

pip breaks itself. How very surprising.

@dquitmann-op
Copy link

Just for your information, as I searched very long yesterday: the problem is tracked in pypa/pip#8443

@jaraco
Copy link
Owner

jaraco commented Dec 22, 2020

the root cause is the presence of keyring

I would say the root cause is not the presence of keyring, but the proximate cause. As you can see above, I was able to replicate the failure without involving keyring at all, demonstrating that keyring is just one path to the root cause. The root cause is that CFFI, when activated by pywin32-ctypes, takes a lock on the DLL and therefore cannot be updated.

I've filed pypy/cffi#484 to track the root cause and inquire for a mechanism by which pywin32-ctypes or one of the downstream consumers could avoid locking the package.

I don't believe there's anything keyring can do at this time, but I'll re-open if it turns out there is.

@jaraco jaraco closed this as completed Dec 22, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants