python-poetry / poetry

Python packaging and dependency management made easy
https://python-poetry.org
MIT License
31.72k stars 2.27k forks source link

Keyring errors during non-publishing operations #1917

Closed wahuneke closed 1 year ago

wahuneke commented 4 years ago

I gather that keyring integration was added as a convenience feature so that you can publish packages using keys stored in your keyring. This ticket describes that feature:

https://github.com/python-poetry/poetry/issues/210

But it appears that poetry tries to access the keyring even for install operations. In one of my first times using poetry - I, as a security paranoid person, decided to abort installation of a py package I wanted because the installation process seemed to be wanting to access my keyring.

I think people should be careful about granting programs access to things they should not need to access. This behavior is an example of a problem that makes that hard to achieve.

For me, I worked around the problem by pip uninstalling the 'keyring' package from that virt env.

In the future, I think it would be beneficial to poetry's adoption and to its users, if users are not prompted for keyring unless keys should actually be needed (for a 'publish' operation).

Thanks! I'm happy to be trying out poetry.

sdispater commented 4 years ago

Do you have private repositories present in your pyproject.toml file?

I think you are affected by the issue that is fixed by #1892. The fix will be available in the next bug fix release.

colatkinson commented 4 years ago

I was affected by something similar on the latest release (1.0.5), but I'm not sure if it's entirely the same.

The keyring will be hit for private repos if there is an auth.toml with only the username set, even if credentials are provided via environment variables.

I believe the relevant code is here. In this case, auth will be truthy, causing the keyring path to be taken.

In our case, the "fix" was to remove any leftover auth.toml files in the environment. Manually uninstalling keyring had the same effect.

I was wondering your thoughts on adding a config variable that would prevent any keyring hits altogether (by e.g. unconditionally marking the keyring as unavailable). This would have largely the same effect as @wahuneke's workaround, but might be a bit cleaner for CI pipelines and such.

asandeep commented 4 years ago

+1 to this. I am facing similar issue while integrating poetry with Jenkins. Even though private repo credentials were provided via environment variables, poetry still tries to look into keyring for credentials.

dariobig commented 4 years ago

+1 running into this issue while working over ssh

bdowling commented 4 years ago

fwiw, I came across this issue and agree with the concerns mentioned above.

But fwiw, I came here due to a issue with my distro that shows a dependency mismatch on keyring. keyring==18 is required (circa 2019), where as my distro has keyring-21.2 available, not sure without looking on the compatibility issues, but thought I'd mention if in case that can be relaxed....

$ poetry init
Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 583, in _build_master
    ws.require(__requires__)
  File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 900, in require
    needed = self.resolve(parse_requirements(requirements))
  File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 791, in resolve
    raise VersionConflict(dist, req).with_context(dependent_req)
pkg_resources.ContextualVersionConflict: (keyring 21.2.1 (/usr/lib/python3.8/site-packages), Requirement.parse('keyring==18.*,>=18.0.0'), {'poetry'})
kakarukeys commented 3 years ago

I'm seeing this issue in poetry 1.1.6

My setup: MacOS Mojave 10.14.6 localhost private pypiserver with authentication required for upload only

[[tool.poetry.source]]
name = "foo"
url = "http://localhost/simple/"
secondary = true
$ poetry config --list
cache-dir = "..."
experimental.new-installer = true
installer.parallel = true
repositories.foo.url = "http://localhost"
virtualenvs.create = true
virtualenvs.in-project = null
virtualenvs.path = "{cache-dir}/virtualenvs" 
$ cat auth.toml
[http-basic]
[http-basic.foo]
username = "kakarukeys"

The commands that poetry asked for keyring access: poetry add lxml ??? why? it should get the package from the official PyPI poetry run python --version this makes no sense either.

if you need the installation scripts for the local private pypi let me know

alesdakshanin commented 2 years ago

+1 this

abn commented 2 years ago

Poetry searches for a package in all package sources by default.

Additionally you might want to add the publishing repository with a different name, if the source uses the same name authenticator will load the credentials.

erooke commented 2 years ago

Poetry 1.2 now hits the keyring for most operations (add, update, install, self update, etc) rendering it unusable for me. I can "fix" the problem by exporting PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring but it would be much nicer if poetry just didn't hit the keyring at all if it wasn't needed.

txtsd commented 2 years ago

I tried poetry for the first time today.


This command will guide you through creating your pyproject.toml config.

Package name [herpderp]:  
Version [0.1.0]:  
Description []:  
Author [txtsd <herp@derp.com>, n to skip]:  
License []:  GPL-3.0-only
Compatible Python versions [^3.10]:  

Would you like to define your main dependencies interactively? (yes/no) [yes] yes
You can specify a package in the following forms:
  - A single name (requests): this will search for matches on PyPI
  - A name and a constraint (requests@^2.23.0)
  - A git url (git+https://github.com/python-poetry/poetry.git)
  - A git url with a revision (git+https://github.com/python-poetry/poetry.git#develop)
  - A file path (../my-package/my-package.whl)
  - A directory (../my-package/)
  - A url (https://example.com/packages/my-package-0.1.0.tar.gz)

Package to add or search for (leave blank to skip): requests
Found 20 packages matching requests
Showing the first 10 matches

Enter package # to add, or the complete package name if it is not listed []:
 [ 0] requests
 [ 1] requests5
 [ 2] requests3
 [ 3] requests2
 [ 4] pycopy-requests
 [ 5] requests-middleware
 [ 6] grasspy-requests
 [ 7] requests-kerberos
 [ 8] fed-requests
 [ 9] requests-httpsproxy
 [10] 
 > 1
Enter the version constraint to require (or leave blank to use the latest version): 

Failed to open keyring: org.freedesktop.DBus.Error.ServiceUnknown: The name :1.146 was not provided by any .service files.

What a horrible experience. If there isn't a keyring, assume it doesn't exist and continue. Why hard fail at this stage? It's not even like I don't have a keyring. I use KeePassXC's freedesktop.org Secrets integration. It works fine system-wide.

neersighted commented 2 years ago

@txtsd Keyring support is a new feature, and this is a complex interaction across the matrix of possible platforms, configurations, versions, and backends that a user may invoke Poetry in. Many of these sharp edges simply haven't been filed off because of how large that matrix is and how dependent on external factors keychain interactions are.

MRs improving the authentication experience or docs are welcome -- we're going to have to gradually file down these rough edges as keychain support matures.

willforde commented 2 years ago

Had the same issue. Had to change the keyring package config to point to the keyring.backends.SecretService.Keyring backend. For me the keyring package was trying to use kwallet, but I'm using Gnome not KDE.

OS = Arch Linux DE = Gnome

txtsd commented 2 years ago

Okay, I created this file ~/.config/python_keyring/keyringrc.cfg and populated it with

[backend]
default-keyring=keyring.backends.SecretService.Keyring

Then I unset PYTHON_KEYRING_BACKEND and ran poetry init:

λ poetry init

This command will guide you through creating your pyproject.toml config.

Package name [test]:  
Version [0.1.0]:  
Description []:  
Author [txtsd <herp@derp.com>, n to skip]:  
License []:  
Compatible Python versions [^3.10]:  

Would you like to define your main dependencies interactively? (yes/no) [yes] 
You can specify a package in the following forms:
  - A single name (requests): this will search for matches on PyPI
  - A name and a constraint (requests@^2.23.0)
  - A git url (git+https://github.com/python-poetry/poetry.git)
  - A git url with a revision (git+https://github.com/python-poetry/poetry.git#develop)
  - A file path (../my-package/my-package.whl)
  - A directory (../my-package/)
  - A url (https://example.com/packages/my-package-0.1.0.tar.gz)

Package to add or search for (leave blank to skip): lxml
Found 20 packages matching lxml
Showing the first 10 matches

Enter package # to add, or the complete package name if it is not listed []:
 [ 0] lxml
 [ 1] lxml-stubs
 [ 2] zsi-lxml
 [ 3] lxml-wrapper
 [ 4] readability-lxml
 [ 5] lxml2dict
 [ 6] suds-lxml
 [ 7] json-lxml
 [ 8] lxml-asserts
 [ 9] gocept.lxml
 [10] 
 > 0
Enter the version constraint to require (or leave blank to use the latest version): 

Empty module name

Different error now.

neersighted commented 2 years ago

There's some busted keyring behavior with your particular backends that seem to

  1. invoke with poor error handling during init -- odd (though it's possible that the search code in poetry init was not made robust against keyring errors as an oversight)
  2. bypass most of our error handling and spit out unhelpful messages

Some more insight into your system (what backend should be available, OS version, package versions), etc would be helpful for those that attempt to address yours/similar issues @txtsd.

In the meantime, you can revert to 1.1-style writing to disk using the keyring.backends.null.Keyring backend, of course.

txtsd commented 2 years ago

@neersighted I use Arch Linux

λ pip freeze | grep poetry
poetry==1.2.0
poetry-core==1.1.0
poetry-plugin-export==1.0.6

I set the config to use keyring.backends.null.Keyring for now. Thanks!

croqaz commented 2 years ago

Happened to me today for the first time. I used Poetry until last month without any issues, but today I saw same error. export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring seems to fix the issue.

lazka commented 2 years ago

I'm seeing the same error on Windows with libsecret installed:

  • Installing urllib3 (1.26.12): Failed
  Error
  g-dbus-error-quark: The name org.freedesktop.secrets is unknown (2)
blakjak44 commented 2 years ago

I wanted to add my experience with this issue. We are running our CI on Github self-hosted runners (Ubuntu 20.04) and instead of getting an error, Poetry would just hang when running poetry install. After some digging I found that it was stalling on the collection.unlock() call in the keyring library, presumably because the gnome-keyring-daemon was not launching without a user login.

The PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring fix worked for me but it cost me a lot of time troubleshooting this.

My suggestion is to make the keychain backend an opt-in feature.

rdbisme commented 2 years ago

I have the same problem on WSL2:

 1  /usr/lib/python3.10/site-packages/keyring/core.py:72 in get_credential
        return get_keyring().get_credential(service_name, username)

  Error

  g-spawn-exit-error-quark: Error spawning command line “dbus-launch --autolaunch=93716890c157416eacbb2d8801a1e4f6 --binary-syntax --close-stderr”: Child process exited with code 1 (1)

disabling the keyring fixes the problem. I think this feature should be opt-in as suggested by @blakjak44

netopsengineer commented 2 years ago

I get the following on Ubuntu Mint 20.04.5 LTS and Python 3.10.7, using Poetry 1.2.1, when I remotely SSH to the machine.

It seems that if I do an RDP session to the GUI of this machine and unlock my keyring which is empty for what its worth, then I don't get this error. The fix mentioned about disabling it with PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring also worked as well.

clab_arista$ poetry install
Installing dependencies from lock file

Package operations: 8 installs, 0 updates, 0 removals

  • Installing markupsafe (2.1.1): Failed

  KeyringLocked

  Failed to unlock the collection!

  at ~/.local/share/pypoetry/venv/lib/python3.10/site-packages/keyring/backends/SecretService.py:67 in get_preferred_collection
       63│             raise InitError("Failed to create the collection: %s." % e)
       64│         if collection.is_locked():
       65│             collection.unlock()
       66│             if collection.is_locked():  # User dismissed the prompt
    →  67│                 raise KeyringLocked("Failed to unlock the collection!")
       68│         return collection
       69│
       70│     def unlock(self, item):
       71│         if hasattr(item, 'unlock'):
trygveaa commented 2 years ago

Poetry searches for a package in all package sources by default.

@abn: But it happens even with no configured package sources, i.e. when only going against PyPI

@txtsd Keyring support is a new feature, and this is a complex interaction across the matrix of possible platforms, configurations, versions, and backends that a user may invoke Poetry in. Many of these sharp edges simply haven't been filed off because of how large that matrix is and how dependent on external factors keychain interactions are.

MRs improving the authentication experience or docs are welcome -- we're going to have to gradually file down these rough edges as keychain support matures.

@neersighted: If that's the case, it shouldn't be enabled by default IMO. For me add just hanged for a while with no feedback on why it was hanging until the keyring password prompt appeared. I didn't want it to access the keyring, so I canceled that, and then the add was aborted. This is a pretty bad experience out of the box. poetry add --help doesn't mention anything about the keyring either, so I had to search and find this issue to figure out how to disable it. I found https://python-poetry.org/docs/repositories/ which mentions keyrings before this issue, but that doesn't mention disabling it either.

felix-ht commented 2 years ago

I just tried upgrading to poetry 1.2.x, did poetry install and got thousands of Failed to create the collection: Prompt dismissed.. errors. All other commands are broken as well even self update is broken without the mitigation.

Going back to 1.1.x until this is fixed. I don't want to tell all my developers that hey have to mess with PYTHON_KEYRING_BACKEND just to be able to use the most basic features.

And this while i was really looking to move to 1.2

romanzdk commented 2 years ago

keyring --disable solved the issue for me

felix-ht commented 2 years ago

keyring --disable solved the issue for me

It just changed to a new error with that.

zyv commented 2 years ago

This is happening on Poetry 1.2.2 for me, the following fixes the issue as mentioned in #5250:

export PYTHON_KEYRING_BACKEND=keyring.backends.fail.Keyring
remram44 commented 2 years ago

I'm running into this too. I see that there is already a fallback if the returned credential is None, so maybe this is just a matter of catching KeyringLocked and returning the default too?

https://github.com/python-poetry/poetry/blob/9df21a72138663e9d5a9cf80beb2f62751eed361/src/poetry/utils/password_manager.py#L50-L57

A better option would probably be for Authenticator to try the request without credentials, only trying to obtain credentials if they are required (for example HTTP 401). This is the way most clients work, including web browsers. Unlocking the keyring for every request is definitely the wrong behavior.

https://github.com/python-poetry/poetry/blob/9df21a72138663e9d5a9cf80beb2f62751eed361/src/poetry/utils/authenticator.py#L206-L211

edit: I'm happy to contribute a fix, but this requires a maintainer to pick between the options above. Or should I open 2 PRs?

swills1 commented 2 years ago

I had the same problem on Fedora 37 beta. Poetry had been working until I updated to latest. I was trying to do poetry add. I was having a problem where my lock file wasn't updating with the toml requirements. So I tried poetry add and was getting the error mentioned here.

@croqaz suggestion fixed it for me.

export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring

MiMiMeowMeow commented 2 years ago

I'm getting this issue when ssh session into a Windows 11 machine. poetry 1.2.2

poetry install -vvv Installing dependencies from lock file

Finding the necessary packages for the current system

Package operations: 26 installs, 1 update, 0 removals

• Installing shiboken6 (6.4.0.1) [keyring.backend] Loading KWallet [keyring.backend] Loading SecretService [keyring.backend] Loading Windows [keyring.backend] Loading chainer [keyring.backend] Loading libsecret [keyring.backend] Loading macOS

Stack trace:

2 C:\Program Files\Python310\lib\site-packages\win32ctypes\pywin32\pywintypes.py:35 in pywin32error 33│ def pywin32error(): 34│ try: → 35│ yield 36│ except WindowsError as exception: 37│ raise error(exception.winerror, exception.function, exception.strerror)

1 C:\Program Files\Python310\lib\site-packages\win32ctypes\pywin32\win32cred.py:70 in CredRead 68│ else: 69│ pcreds = _authentication.PCREDENTIAL() → 70│ _authentication._CredRead( 71│ TargetName, Type, flag, _common.byreference(pcreds)) 72│ try:

OSError

[WinError 1312] A specified logon session does not exist. It may already have been terminated.

at C:\Program Files\Python310\lib\site-packages\win32ctypes\core\ctypes_util.py:53 in check_zero 49│ 50│ def check_zero_factory(function_name=None): 51│ def check_zero(result, function, arguments, *args): 52│ if result == 0: → 53│ raise make_error(function, function_name) 54│ return result 55│ return check_zero 56│ 57│

The following error occurred when trying to handle this error:

Stack trace:

21 C:\Program Files\Python310\lib\site-packages\poetry\installation\executor.py:261 in _execute_operation 259│ 260│ try: → 261│ result = self._do_execute_operation(operation) 262│ except EnvCommandError as e: 263│ if e.e.returncode == -2:

20 C:\Program Files\Python310\lib\site-packages\poetry\installation\executor.py:334 in _do_execute_operation 332│ return 0 333│ → 334│ result: int = getattr(self, f"execute{method}")(operation) 335│ 336│ if result != 0:

19 C:\Program Files\Python310\lib\site-packages\poetry\installation\executor.py:454 in _execute_install 452│ 453│ def _execute_install(self, operation: Install | Update) -> int: → 454│ status_code = self._install(operation) 455│ 456│ self._save_url_reference(operation)

18 C:\Program Files\Python310\lib\site-packages\poetry\installation\executor.py:488 in _install 486│ archive = self._download_link(operation, Link(package.source_url)) 487│ else: → 488│ archive = self._download(operation) 489│ 490│ operation_message = self.get_operation_message(operation)

17 C:\Program Files\Python310\lib\site-packages\poetry\installation\executor.py:633 in _download 631│ 632│ def _download(self, operation: Install | Update) -> Path: → 633│ link = self._chooser.choose_for(operation.package) 634│ 635│ if link.yanked:

16 C:\Program Files\Python310\lib\site-packages\poetry\installation\chooser.py:77 in choose_for 75│ """ 76│ links = [] → 77│ for link in self._get_links(package): 78│ if link.is_wheel: 79│ if not self._no_binary_policy.allows(package.name):

15 C:\Program Files\Python310\lib\site-packages\poetry\installation\chooser.py:119 in _get_links 117│ else: 118│ repository = self._pool.repository("pypi") → 119│ links = repository.find_links_for_package(package) 120│ 121│ hashes = [f["hash"] for f in package.files]

14 C:\Program Files\Python310\lib\site-packages\poetry\repositories\pypi_repository.py:158 in find_links_for_package 156│ 157│ def find_links_for_package(self, package: Package) -> list[Link]: → 158│ json_data = self._get(f"pypi/{package.name}/{package.version}/json") 159│ if json_data is None: 160│ return []

13 C:\Program Files\Python310\lib\site-packages\poetry\repositories\pypi_repository.py:246 in _get 244│ ) -> dict[str, Any] | None: 245│ try: → 246│ json_response = self.session.get( 247│ self._base_url + endpoint, 248│ raise_for_status=False,

12 C:\Program Files\Python310\lib\site-packages\poetry\utils\authenticator.py:247 in get 245│ 246│ def get(self, url: str, kwargs: Any) -> requests.Response: → 247│ return self.request("get", url, kwargs) 248│ 249│ def post(self, url: str, **kwargs: Any) -> requests.Response:

11 C:\Program Files\Python310\lib\site-packages\poetry\utils\authenticator.py:188 in request 186│ headers = kwargs.get("headers") 187│ request = requests.Request(method, url, headers=headers) → 188│ credential = self.get_credentials_for_url(url) 189│ 190│ if credential.username is not None or credential.password is not None:

10 C:\Program Files\Python310\lib\site-packages\poetry\utils\authenticator.py:311 in get_credentials_for_url 309│ # no credentials were provided in the url, try finding the 310│ # best repository configuration → 311│ self._credentials[url] = self._get_credentials_for_url(url) 312│ else: 313│ # Split from the right because that's how urllib.parse.urlsplit()

9 C:\Program Files\Python310\lib\site-packages\poetry\utils\authenticator.py:272 in _get_credentials_for_url 270│ 271│ credential = ( → 272│ self._get_credentials_for_repository(repository=repository) 273│ if repository is not None 274│ else HTTPAuthCredential()

8 C:\Program Files\Python310\lib\site-packages\poetry\utils\authenticator.py:260 in _get_credentials_for_repository 258│ 259│ if key not in self._credentials: → 260│ self._credentials[key] = repository.get_http_credentials( 261│ password_manager=self._password_manager, username=username 262│ )

7 C:\Program Files\Python310\lib\site-packages\poetry\utils\authenticator.py:90 in get_http_credentials 88│ if credential.password is None: 89│ # fallback to url and netloc based keyring entries → 90│ credential = password_manager.keyring.get_credential( 91│ self.url, self.netloc, username=credential.username 92│ )

6 C:\Program Files\Python310\lib\site-packages\poetry\utils\password_manager.py:51 in get_credential 49│ 50│ for name in names: → 51│ credential = keyring.get_credential(name, username) 52│ if credential: 53│ return HTTPAuthCredential(

5 C:\Program Files\Python310\lib\site-packages\keyring\core.py:72 in get_credential 70│ ) -> typing.Optional[credentials.Credential]: 71│ """Get a Credential for the specified service.""" → 72│ return get_keyring().get_credential(service_name, username) 73│ 74│

4 C:\Program Files\Python310\lib\site-packages\keyring\backends\Windows.py:167 in get_credential 165│ # get any first password under the service name 166│ if not res: → 167│ res = self._get_password(service) 168│ if not res: 169│ return None

3 C:\Program Files\Python310\lib\site-packages\keyring\backends\Windows.py:108 in _get_password 106│ def _get_password(self, target): 107│ try: → 108│ res = win32cred.CredRead( 109│ Type=win32cred.CRED_TYPE_GENERIC, TargetName=target 110│ )

2 C:\Program Files\Python310\lib\site-packages\win32ctypes\pywin32\win32cred.py:63 in CredRead 61│ 62│ flag = 0 → 63│ with _pywin32error(): 64│ if _backend == 'cffi': 65│ ppcreds = _authentication.PPCREDENTIAL()

1 C:\Program Files\Python310\lib\contextlib.py:153 in exit 151│ value = typ() 152│ try: → 153│ self.gen.throw(typ, value, traceback) 154│ except StopIteration as exc: 155│ # Suppress StopIteration unless it's the same exception that

error

(1312, 'CredRead', 'A specified logon session does not exist. It may already have been terminated.')

at C:\Program Files\Python310\lib\site-packages\win32ctypes\pywin32\pywintypes.py:37 in pywin32error 33│ def pywin32error(): 34│ try: 35│ yield 36│ except WindowsError as exception: → 37│ raise error(exception.winerror, exception.function, exception.strerror) 38│

neersighted commented 2 years ago

Please try and refrain from "me too" style comments given the number of participants in this issue. A :+1: is a simple way to show support/interest, and doesn't generate unnecessary notifications.

Daniel451 commented 2 years ago

@neersighted is this expected behavior or is there any downside to making keyring optional alltogether? I am asking because I am also running poetry in (1) CI pipelines and also on (2) headless servers. Both do not have a keyring, thus Poetry 1.2 basically is unusable there without the PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring workaround.

CI pipelines usually do not need a keyring. For headless servers we SSH agent forwarding, which is super neat for Git and any ssh based stuff on the server, thus also no local keyring needed.

I would guess that there are lots of CI pipelines, headless servers, nodes in datacenters, ... where simply no keyring will be present and it is often also not an option to set one up.

cossio commented 2 years ago

I'm running into this issue now, which makes Poetry unusable out-of-the-box.

Setting PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring seems to work.

neersighted commented 1 year ago

Please refrain from "me too" comments in this issue given the number of participants.

@Daniel451, having Keyring in the loop by default is a requirement for many users (e.g. it's the only non-painful way to auth against Azure and GCP). Keyring does more than access the OS keyring.

Instead, we should really just improve the error handling so that a keyring error is a warning but does not terminate Poetry. No one has started on this; PRs are welcome.

felix-ht commented 1 year ago

@neersighted i would very much doubt that more than 10% of poetry all users are using keyrings or ever will. So while great for these few, it break the workflow for 90% of all poetry users. Having it as a default even with a warning is a bad idea, as most users will never need keyrings.

In general it would be great if the keyring is only checked for publishing operations. If i don't publish I will never need the keyring. And even in that scenario i question if most people will use keyrings. Most publishing happens via CD an for CD platforms like github offer their own option to store secrets.

cossio commented 1 year ago

Why not make PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring the default (so setting the ENV var is unnecessary), and then set PYTHON_KEYRING_BACKEND when a Keyring is actually needed?

neersighted commented 1 year ago

The keyring is required to access private package indexes and Git repositories, and is used to store credentials in an encrypted fashion when possible. Yes, this does not cover CI use-cases, but the intention was for this to be transparent to the end user. Keyring will not get in your way in CI; Poetry will transparently fall back to its own credential management.

Fundamentally, the issue here is that Keyring can jam on irregular/unexpected configurations (e.g. dbus present but no Keyring provider, Keyring provider refuses to unlock/report status, etc) and bubbles up exceptions from the underlying Python-platform API code to Poetry. Poetry needs to gracefully degrade here; modifying the existing code to fall back as if keyring was not present if this is the case should be the goal.

choppsv1 commented 1 year ago

The keyring is required to access private package indexes and Git repositories, and is used to store credentials in an encrypted fashion when possible.

I wonder how many people are using private package indexes and git repos that require authentication for dependencies? Is it the majority of users, seems unlikely to me, but I could be wrong. :)

In any case, until this is fixed, could a poetry config be added that stopped poetry from trying to use the keyring? The posted workaround of using an environment variable or uninstalling keyring affects all of python not just poetry.

Thanks, Chris..

Lamarcke commented 1 year ago

I was wrapping my head around this issue for over an hour. I'm using WSL2 and was testing on Ubuntu. I unregistered the VM and used an older clean version, and the problem was fixed.

I wish i had logged the values from keyring --list-backends. The current one is as follow: keyring.backends.chainer.ChainerBackend (priority: -10) keyring.backends.fail.Keyring (priority: 0)

But i'm 100% sure there was a third one when i was facing this issue. I can't seem to remember it's name... Can someone please run this command and post the output here too?

Using keyring.backends.null.Keyring didn't fix it for me.

Anyway, it seems like i'm now using fail as a backend, so maybe this can be helpful to someone: export PYTHON_KEYRING_BACKEND=keyring.backends.fail.Keyring

And to set it at startup (on bash) echo export PYTHON_KEYRING_BACKEND=keyring.backends.fail.Keyring >> $HOME/.profile

akdor1154 commented 1 year ago

In my case this is cased by https://github.com/jaraco/keyring/issues/496 - the chainer backend is getting priority, it checks the KDE one, and it's unable to check the KDE one without causing a prompt for me to set up a KDE Wallet (I'm on Gnome...)

anoduck commented 1 year ago

@dimbleby pointed this out to me, for which I am much grateful.

https://github.com/python-poetry/poetry/discussions/7119

vsedyshev-intermedia commented 1 year ago

Is it possible to make additional features delivery through plug-ins system, since such changes have a strong effect on CI/CD systems and breaking a painless transition to new versions and create such workaround search discussions?

daweifeng commented 1 year ago

Just found a way to disable keyring according to this post, which finally fixes my issue 😭 : https://connormcf.com/blog/poetry-failed-to-unlock/

emilh18 commented 1 year ago

Happened to me today for the first time. I used Poetry until last month without any issues, but today I saw same error. export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring seems to fix the issue.

Where should I add this export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring ?

hotenov commented 1 year ago

@emilh18 Hi, in your Unix/Linux shell (bash in most cases)

There are several ways to do that:

1) First, export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring will work for following poetry commands until you close (exit) your shell session 2) Add environment variable for each! poetry command, for example PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring poetry install 3) If you want to preserve (store) this environment variable between shell sessions or system reboot you can add it in .bashrc and .profile example for bash shell:

echo 'export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring' >> ~/.bashrc
echo 'export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring' >> ~/.profile
exec "$SHELL"

for case number 3) you can now run any poetry command as usual, even after system restart

Unintendedz commented 1 year ago

@emilh18 Hi, in your Unix/Linux shell (bash in most cases)

There are several ways to do that:

1. First, `export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring` will work for following poetry commands until you close (exit) your shell session

2. Add environment variable for **each**! poetry command, for example `PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring poetry install`

3. If you want to preserve (store) this environment variable between shell sessions or system reboot you can add it in `.bashrc` and `.profile`
   example for bash shell:
echo 'export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring' >> ~/.bashrc
echo 'export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring' >> ~/.profile
exec "$SHELL"

for case number 3) you can now run any poetry command as usual, even after system restart

If this doesn't work, check your ~/.bashrc or ~/.profile. It's most likely that it has been appended to the last commented line.


Ah, wtf, I'm searching Google then found my own comment..

bkcsfi commented 1 year ago

I have many pipenv based projects, today I tried Poetry for the first time and I hit this issue.

I am developing in vscode, on a remote host (often also in a devcontainer on a remote host)

I use ssh-agent forwarding and ssh based git access to local packages in gitlab when working with pipenv. That matches how my gitlab ci file is setup (and also works well with Dockerfile), so I can use one access method in multiple environments for the same project.

I suppose poetry might want to save gitlab access tokens (which would also work in gitlab-ci).

I think it might be a little friendlier if poetry didn't demand keyring access unless it knew it was actually needed. Perhaps it can set a flag in pyproject.toml (or parse it) to see if access to a keyring is actually required.

But since I've been using poetry for all of 15 minutes, I probably do not have the full picture.

I will be disappointed if I have to put env PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring in my gitlab-ci file.

raxod502 commented 1 year ago

As other commenters have noted, the default behavior is quite broken. I've worked around the issue by preparing a custom distribution package of Poetry that patches out the issue, as follows:

```bash #!/usr/bin/env bash set -euxo pipefail pkg="${PWD}/pkg" rm -rf "${pkg}" pip3 install poetry --root "${pkg}" --prefix /opt/poetry --ignore-installed ver="$(cat "${pkg}"/opt/poetry/local/lib/python*/dist-packages/poetry-*.dist-info/METADATA | grep "^Version:" | grep -Eo "[0-9.]+")" install -d "${pkg}/usr/bin" tee "${pkg}/usr/bin/poetry" </dev/null #!/usr/bin/env python3 import os import sys # https://github.com/python-poetry/poetry/issues/1917 os.environ["PYTHON_KEYRING_BACKEND"] = "keyring.backends.null.Keyring" # Set up PYTHONPATH, without using the PYTHONPATH environment variable # because if we use the envvar then it screws up virtual environments. sys.path.insert(0, "/$( cd "${pkg}" ls -d opt/poetry/local/lib/python*/dist-packages )") # Invoke the actual script. from poetry.console.application import main sys.exit(main()) EOF chmod +x "${pkg}/usr/bin/poetry" install -d "${pkg}/DEBIAN" tee "${pkg}/DEBIAN/control" </dev/null Package: python3-poetry Version: ${ver} Section: python Priority: optional Architecture: all Maintainer: Radon Rosborough Description: Python dependency management and packaging made easy EOF fakeroot dpkg-deb --build "${pkg}" "./python3-poetry-${ver}.deb" if dpkg -s python3-poetry &>/dev/null; then sudo apt remove -y python3-poetry fi sudo apt install -y "./python3-poetry-${ver}.deb" ```

Needless to say, I really hope this is fixed. Of course Poetry should not attempt to load a keyring until an operation is requested by the user which actually needs credentials.

wpietri commented 1 year ago

Hopefully this is helpful for maintainers. I just built a fresh server with Pop!_OS 22.04 LTS. I then did:

That gave me Failed to unlock the collection! which was not a helpful error message. Which collection? Searching brought me here.

Applying the export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring command made things work for me. But were I not a dedicated Poetry user, I would have decided it wasn't ready for prime time and uninstalled it.

GH0st3rs commented 1 year ago

Ok I solved it for my poetry inside windows machine without any GUI.

python -m pip install keyring
python -m keyring --disable

I tried to use PYTHON_KEYRING_BACKEND but it does not work for me because I had no installed keyring. Poetry need to avoid to use keyring. This is a huge problem for Windows and Linux especially when you want to use it inside Docker/Qemu

Enjoy!

Qadosch commented 1 year ago

I use the keyring for an different application. So i solved the issue by commenting out at ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/utils/authenticator.py:219

    def request(
        self, method: str, url: str, raise_for_status: bool = True, **kwargs: Any
    ) -> requests.Response:
        headers = kwargs.get("headers")
        request = requests.Request(method, url, headers=headers)
        # credential = self.get_credentials_for_url(url)

        # if credential.username is not None or credential.password is not None:
        #     request = requests.auth.HTTPBasicAuth(
        #         credential.username or "", credential.password or ""
        #     )(request)
        ...

It would be nice to have some config flag to disable this functionality. I imagine such flag would belong here

[[tool.poetry.source]]
name = "private"
url = "https://pypi.private_ip_protected_repo.com"
nokeyring = true
viteski commented 1 year ago

if you are using ssh to access your work environment add this function to your .bashrc file. from https://unix.stackexchange.com/questions/602313/unlock-gnome-keyring-daemon-from-command-line # Linux unlock gnome keyring function unlock-keyring () { read -rsp "Password: " pass export $(echo -n "$pass" | gnome-keyring-daemon --replace --unlock) unset pass }